diff --git a/src/channel.cpp b/src/channel.cpp index d1f2ca1b..f10af749 100755 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -188,10 +188,7 @@ bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames, // this would give us problems with different threads (e.g. the // timer thread) and the protocol mechanism (problem with // qRegisterMetaType(), etc.) - -// TODO create new message, this one only works for client... -//emit ReqJittBufSize(); - + emit ServerAutoSockBufSizeChange ( iNewNumFrames ); } return false; // -> no error @@ -295,16 +292,24 @@ void CChannel::OnSendProtMessage ( CVector vecMessage ) void CChannel::OnJittBufSizeChange ( int iNewJitBufSize ) { - // first check for special case: auto setting - if ( iNewJitBufSize == AUTO_NET_BUF_SIZE_FOR_PROTOCOL ) + // for server apply setting, for client emit message + if ( bIsServer ) { - SetDoAutoSockBufSize ( true ); + // first check for special case: auto setting + if ( iNewJitBufSize == AUTO_NET_BUF_SIZE_FOR_PROTOCOL ) + { + SetDoAutoSockBufSize ( true ); + } + else + { + // manual setting is received, turn OFF auto setting and apply new value + SetDoAutoSockBufSize ( false ); + SetSockBufNumFrames ( iNewJitBufSize, true ); + } } else { - // manual setting is received, turn OFF auto setting and apply new value - SetDoAutoSockBufSize ( false ); - SetSockBufNumFrames ( iNewJitBufSize, true ); + emit JittBufSizeChanged ( iNewJitBufSize ); } } diff --git a/src/channel.h b/src/channel.h index c1ada71f..999136e3 100755 --- a/src/channel.h +++ b/src/channel.h @@ -183,6 +183,8 @@ signals: void MessReadyForSending ( CVector vecMessage ); void NewConnection(); void ReqJittBufSize(); + void JittBufSizeChanged ( int iNewJitBufSize ); + void ServerAutoSockBufSizeChange ( int iNNumFra ); void ReqConnClientsList(); void ConClientListMesReceived ( CVector vecChanInfo ); void NameHasChanged(); diff --git a/src/client.cpp b/src/client.cpp index 7fef71e5..a4625d30 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,988 +1,1000 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "client.h" - - -/* Implementation *************************************************************/ -CClient::CClient ( const quint16 iPortNumber ) : - vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), - strName ( "" ), - Channel ( false ), /* we need a client channel -> "false" */ - iCeltNumCodedBytes ( CELT_NUM_BYTES_MONO_NORMAL_QUALITY ), - bCeltDoHighQuality ( false ), - bUseStereo ( false ), - Socket ( &Channel, &ConnLessProtocol, iPortNumber ), - Sound ( AudioCallback, this ), - iAudioInFader ( AUD_FADER_IN_MIDDLE ), - bReverbOnLeftChan ( false ), - iReverbLevel ( 0 ), - iSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ), - iSndCrdFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ), - bSndCrdConversionBufferRequired ( false ), - iSndCardMonoBlockSizeSamConvBuff ( 0 ), - bFraSiFactPrefSupported ( false ), - bFraSiFactDefSupported ( false ), - bFraSiFactSafeSupported ( false ), - bOpenChatOnNewMessage ( true ), - eGUIDesign ( GD_ORIGINAL ), - strCentralServerAddress ( "" ), - bUseDefaultCentralServerAddress ( true ), - iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ) -{ - // init audio encoder/decoder (mono) - CeltModeMono = celt_mode_create ( - SYSTEM_SAMPLE_RATE_HZ, 1, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); - - CeltEncoderMono = celt_encoder_create ( CeltModeMono ); - CeltDecoderMono = celt_decoder_create ( CeltModeMono ); - -#ifdef USE_LOW_COMPLEXITY_CELT_ENC - // set encoder low complexity - celt_encoder_ctl ( CeltEncoderMono, - CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); -#endif - - // init audio encoder/decoder (stereo) - CeltModeStereo = celt_mode_create ( - SYSTEM_SAMPLE_RATE_HZ, 2, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); - - CeltEncoderStereo = celt_encoder_create ( CeltModeStereo ); - CeltDecoderStereo = celt_decoder_create ( CeltModeStereo ); - -#ifdef USE_LOW_COMPLEXITY_CELT_ENC - // set encoder low complexity - celt_encoder_ctl ( CeltEncoderStereo, - CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); -#endif - - - // Connections ------------------------------------------------------------- - // connection for protocol - QObject::connect ( &Channel, - SIGNAL ( MessReadyForSending ( CVector ) ), - this, SLOT ( OnSendProtMessage ( CVector ) ) ); - - QObject::connect ( &Channel, - SIGNAL ( DetectedCLMessage ( CVector, int ) ), - this, SLOT ( OnDetectedCLMessage ( CVector, int ) ) ); - - QObject::connect ( &Channel, SIGNAL ( ReqJittBufSize() ), - this, SLOT ( OnReqJittBufSize() ) ); - - QObject::connect ( &Channel, SIGNAL ( ReqChanName() ), - this, SLOT ( OnReqChanName() ) ); - - QObject::connect ( &Channel, - SIGNAL ( ConClientListMesReceived ( CVector ) ), - SIGNAL ( ConClientListMesReceived ( CVector ) ) ); - - QObject::connect ( &Channel, - SIGNAL ( Disconnected() ), - SIGNAL ( Disconnected() ) ); - - QObject::connect ( &Channel, SIGNAL ( NewConnection() ), - this, SLOT ( OnNewConnection() ) ); - - QObject::connect ( &Channel, - SIGNAL ( ChatTextReceived ( QString ) ), - SIGNAL ( ChatTextReceived ( QString ) ) ); - - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLMessReadyForSending ( CHostAddress, CVector ) ), - this, SLOT ( OnSendCLProtMessage ( CHostAddress, CVector ) ) ); - - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ) ); - - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLPingReceived ( CHostAddress, int ) ), - this, SLOT ( OnCLPingReceived ( CHostAddress, int ) ) ); - - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLPingWithNumClientsReceived ( CHostAddress, int, int ) ), - this, SLOT ( OnCLPingWithNumClientsReceived ( CHostAddress, int, int ) ) ); - - QObject::connect ( &Sound, SIGNAL ( ReinitRequest() ), - this, SLOT ( OnSndCrdReinitRequest() ) ); -} - -void CClient::OnSendProtMessage ( CVector vecMessage ) -{ - // the protocol queries me to call the function to send the message - // send it through the network - Socket.SendPacket ( vecMessage, Channel.GetAddress() ); -} - -void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, - CVector vecMessage ) -{ - // the protocol queries me to call the function to send the message - // send it through the network - Socket.SendPacket ( vecMessage, InetAddr ); -} - -void CClient::OnDetectedCLMessage ( CVector vecbyData, - int iNumBytes ) -{ - // this is a special case: we received a connection less message but we are - // in a connection - ConnLessProtocol.ParseConnectionLessMessage ( vecbyData, - iNumBytes, - Channel.GetAddress() ); -} - -void CClient::OnNewConnection() -{ - // a new connection was successfully initiated, send name and request - // connected clients list - Channel.SetRemoteName ( strName ); - - // We have to send a connected clients list request since it can happen - // that we just had connected to the server and then disconnected but - // the server still thinks that we are connected (the server is still - // waiting for the channel time-out). If we now connect again, we would - // not get the list because the server does not know about a new connection. - // Same problem is with the jitter buffer message. - Channel.CreateReqConnClientsList(); - CreateServerJitterBufferMessage(); -} - -void CClient::CreateServerJitterBufferMessage() -{ - // per definition in the client: if auto jitter buffer is enabled, both, - // the client and server shall use an auto jitter buffer - if ( GetDoAutoSockBufSize() ) - { - // in case auto jitter buffer size is enabled, we have to transmit a - // special value - Channel.CreateJitBufMes ( AUTO_NET_BUF_SIZE_FOR_PROTOCOL ); - } - else - { - Channel.CreateJitBufMes ( GetServerSockBufNumFrames() ); - } -} - -void CClient::OnCLPingReceived ( CHostAddress InetAddr, - int iMs ) -{ - // make sure we are running and the server address is correct - if ( IsRunning() && ( InetAddr == Channel.GetAddress() ) ) - { - // take care of wrap arounds (if wrapping, do not use result) - const int iCurDiff = EvaluatePingMessage ( iMs ); - if ( iCurDiff >= 0 ) - { - emit PingTimeReceived ( iCurDiff ); - } - } -} - -void CClient::OnCLPingWithNumClientsReceived ( CHostAddress InetAddr, - int iMs, - int iNumClients ) -{ - // take care of wrap arounds (if wrapping, do not use result) - const int iCurDiff = EvaluatePingMessage ( iMs ); - if ( iCurDiff >= 0 ) - { - emit CLPingTimeWithNumClientsReceived ( InetAddr, - iCurDiff, - iNumClients ); - } -} - -int CClient::PreparePingMessage() -{ - // transmit the current precise time (in ms) - return PreciseTime.elapsed(); -} - -int CClient::EvaluatePingMessage ( const int iMs ) -{ - // calculate difference between received time in ms and current time in ms - return PreciseTime.elapsed() - iMs; -} - -void CClient::SetDoAutoSockBufSize ( const bool bValue ) -{ - // only act on new parameter if it is different from the current one - if ( Channel.GetDoAutoSockBufSize() != bValue ) +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "client.h" + + +/* Implementation *************************************************************/ +CClient::CClient ( const quint16 iPortNumber ) : + vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), + strName ( "" ), + Channel ( false ), /* we need a client channel -> "false" */ + iCeltNumCodedBytes ( CELT_NUM_BYTES_MONO_NORMAL_QUALITY ), + bCeltDoHighQuality ( false ), + bUseStereo ( false ), + Socket ( &Channel, &ConnLessProtocol, iPortNumber ), + Sound ( AudioCallback, this ), + iAudioInFader ( AUD_FADER_IN_MIDDLE ), + bReverbOnLeftChan ( false ), + iReverbLevel ( 0 ), + iSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ), + iSndCrdFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ), + bSndCrdConversionBufferRequired ( false ), + iSndCardMonoBlockSizeSamConvBuff ( 0 ), + bFraSiFactPrefSupported ( false ), + bFraSiFactDefSupported ( false ), + bFraSiFactSafeSupported ( false ), + bOpenChatOnNewMessage ( true ), + eGUIDesign ( GD_ORIGINAL ), + strCentralServerAddress ( "" ), + bUseDefaultCentralServerAddress ( true ), + iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ) +{ + // init audio encoder/decoder (mono) + CeltModeMono = celt_mode_create ( + SYSTEM_SAMPLE_RATE_HZ, 1, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); + + CeltEncoderMono = celt_encoder_create ( CeltModeMono ); + CeltDecoderMono = celt_decoder_create ( CeltModeMono ); + +#ifdef USE_LOW_COMPLEXITY_CELT_ENC + // set encoder low complexity + celt_encoder_ctl ( CeltEncoderMono, + CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); +#endif + + // init audio encoder/decoder (stereo) + CeltModeStereo = celt_mode_create ( + SYSTEM_SAMPLE_RATE_HZ, 2, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); + + CeltEncoderStereo = celt_encoder_create ( CeltModeStereo ); + CeltDecoderStereo = celt_decoder_create ( CeltModeStereo ); + +#ifdef USE_LOW_COMPLEXITY_CELT_ENC + // set encoder low complexity + celt_encoder_ctl ( CeltEncoderStereo, + CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); +#endif + + + // Connections ------------------------------------------------------------- + // connection for protocol + QObject::connect ( &Channel, + SIGNAL ( MessReadyForSending ( CVector ) ), + this, SLOT ( OnSendProtMessage ( CVector ) ) ); + + QObject::connect ( &Channel, + SIGNAL ( DetectedCLMessage ( CVector, int ) ), + this, SLOT ( OnDetectedCLMessage ( CVector, int ) ) ); + + QObject::connect ( &Channel, SIGNAL ( ReqJittBufSize() ), + this, SLOT ( OnReqJittBufSize() ) ); + + QObject::connect ( &Channel, SIGNAL ( JittBufSizeChanged ( int ) ), + this, SLOT ( OnJittBufSizeChanged ( int ) ) ); + + QObject::connect ( &Channel, SIGNAL ( ReqChanName() ), + this, SLOT ( OnReqChanName() ) ); + + QObject::connect ( &Channel, + SIGNAL ( ConClientListMesReceived ( CVector ) ), + SIGNAL ( ConClientListMesReceived ( CVector ) ) ); + + QObject::connect ( &Channel, + SIGNAL ( Disconnected() ), + SIGNAL ( Disconnected() ) ); + + QObject::connect ( &Channel, SIGNAL ( NewConnection() ), + this, SLOT ( OnNewConnection() ) ); + + QObject::connect ( &Channel, + SIGNAL ( ChatTextReceived ( QString ) ), + SIGNAL ( ChatTextReceived ( QString ) ) ); + + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLMessReadyForSending ( CHostAddress, CVector ) ), + this, SLOT ( OnSendCLProtMessage ( CHostAddress, CVector ) ) ); + + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), + SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ) ); + + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLPingReceived ( CHostAddress, int ) ), + this, SLOT ( OnCLPingReceived ( CHostAddress, int ) ) ); + + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLPingWithNumClientsReceived ( CHostAddress, int, int ) ), + this, SLOT ( OnCLPingWithNumClientsReceived ( CHostAddress, int, int ) ) ); + + QObject::connect ( &Sound, SIGNAL ( ReinitRequest() ), + this, SLOT ( OnSndCrdReinitRequest() ) ); +} + +void CClient::OnSendProtMessage ( CVector vecMessage ) +{ + // the protocol queries me to call the function to send the message + // send it through the network + Socket.SendPacket ( vecMessage, Channel.GetAddress() ); +} + +void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, + CVector vecMessage ) +{ + // the protocol queries me to call the function to send the message + // send it through the network + Socket.SendPacket ( vecMessage, InetAddr ); +} + +void CClient::OnDetectedCLMessage ( CVector vecbyData, + int iNumBytes ) +{ + // this is a special case: we received a connection less message but we are + // in a connection + ConnLessProtocol.ParseConnectionLessMessage ( vecbyData, + iNumBytes, + Channel.GetAddress() ); +} + +void CClient::OnJittBufSizeChanged ( int iNewJitBufSize ) +{ + // we received a jitter buffer size changed message from the server, + // only apply this value if auto jitter buffer size is disabled + if ( !GetDoAutoSockBufSize() ) { - // first, set new value in the channel object - Channel.SetDoAutoSockBufSize ( bValue ); - - // inform the server about the change - CreateServerJitterBufferMessage(); - } -} - -bool CClient::SetServerAddr ( QString strNAddr ) -{ - CHostAddress HostAddress; - if ( LlconNetwUtil().ParseNetworkAddress ( strNAddr, - HostAddress ) ) - { - // apply address to the channel - Channel.SetAddress ( HostAddress ); - - return true; - } - else - { - return false; // invalid address - } -} - -void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor ) -{ - // first check new input parameter - if ( ( iNewFactor == FRAME_SIZE_FACTOR_PREFERRED ) || - ( iNewFactor == FRAME_SIZE_FACTOR_DEFAULT ) || - ( iNewFactor == FRAME_SIZE_FACTOR_SAFE ) ) - { - // init with new parameter, if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - // set new parameter - iSndCrdPrefFrameSizeFactor = iNewFactor; - - // init with new block size index parameter - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } - } -} - -void CClient::SetCELTHighQuality ( const bool bNCeltHighQualityFlag ) -{ - // init with new parameter, if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - // set new parameter - bCeltDoHighQuality = bNCeltHighQualityFlag; - Init(); - - if ( bWasRunning ) - { - Sound.Start(); - } -} - -void CClient::SetUseStereo ( const bool bNUseStereo ) -{ - // init with new parameter, if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - // set new parameter - bUseStereo = bNUseStereo; - Init(); - - if ( bWasRunning ) - { - Sound.Start(); - } -} - -QString CClient::SetSndCrdDev ( const int iNewDev ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - const QString strReturn = Sound.SetDev ( iNewDev ); - - // init again because the sound card actual buffer size might - // be changed on new device - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } - - return strReturn; -} - -void CClient::SetSndCrdLeftInputChannel ( const int iNewChan ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - Sound.SetLeftInputChannel ( iNewChan ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::SetSndCrdRightInputChannel ( const int iNewChan ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - Sound.SetRightInputChannel ( iNewChan ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::SetSndCrdLeftOutputChannel ( const int iNewChan ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - Sound.SetLeftOutputChannel ( iNewChan ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::SetSndCrdRightOutputChannel ( const int iNewChan ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - Sound.SetRightOutputChannel ( iNewChan ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::OnSndCrdReinitRequest() -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - // reinit the driver (we use the currently selected driver) and - // init client object, too - Sound.SetDev ( Sound.GetDev() ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::Start() -{ - // init object - Init(); - - // enable channel - Channel.SetEnable ( true ); - - // start audio interface - Sound.Start(); -} - -void CClient::Stop() -{ - // stop audio interface - Sound.Stop(); - - // disable channel - Channel.SetEnable ( false ); - - // wait for approx. 100 ms to make sure no audio packet is still in the - // network queue causing the channel to be reconnected right after having - // received the disconnect message (seems not to gain much, disconnect is - // still not working reliably) - QTime DieTime = QTime::currentTime().addMSecs ( 100 ); - while ( QTime::currentTime() < DieTime ) - { - // exclude user input events because if we use AllEvents, it happens - // that if the user initiates a connection and disconnection quickly - // (e.g. quickly pressing enter five times), the software can get into - // an unknown state - QCoreApplication::processEvents ( - QEventLoop::ExcludeUserInputEvents, 100 ); - } - - // Send disconnect message to server (Since we disable our protocol - // receive mechanism with the next command, we do not evaluate any - // respond from the server, therefore we just hope that the message - // gets its way to the server, if not, the old behaviour time-out - // disconnects the connection anyway). - ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); - - // reset current signal level and LEDs - SignalLevelMeter.Reset(); - PostWinMessage ( MS_RESET_ALL, 0 ); -} - -void CClient::Init() -{ - // check if possible frame size factors are supported - const int iFraSizePreffered = - FRAME_SIZE_FACTOR_PREFERRED * SYSTEM_FRAME_SIZE_SAMPLES; - - bFraSiFactPrefSupported = - ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); - - const int iFraSizeDefault = - FRAME_SIZE_FACTOR_DEFAULT * SYSTEM_FRAME_SIZE_SAMPLES; - - bFraSiFactDefSupported = - ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); - - const int iFraSizeSafe = - FRAME_SIZE_FACTOR_SAFE * SYSTEM_FRAME_SIZE_SAMPLES; - - bFraSiFactSafeSupported = - ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); - - // translate block size index in actual block size - const int iPrefMonoFrameSize = - iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; - - // get actual sound card buffer size using preferred size - iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); - - // Calculate the current sound card frame size factor. In case - // the current mono block size is not a multiple of the system - // frame size, we have to use a sound card conversion buffer. - if ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) || - ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) || - ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) - { - // regular case: one of our predefined buffer sizes is available - iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES; - - // no sound card conversion buffer required - bSndCrdConversionBufferRequired = false; - } - else - { - // An unsupported sound card buffer size is currently used -> we have - // to use a conversion buffer. Per definition we use the smallest buffer - // size as llcon frame size - - // store actual sound card buffer size (stereo) - iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam; - const int iSndCardStereoBlockSizeSamConvBuff = 2 * iMonoBlockSizeSam; - - // overwrite block size by smallest supported llcon buffer size - iSndCrdFrameSizeFactor = FRAME_SIZE_FACTOR_PREFERRED; - iMonoBlockSizeSam = - SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED; - - iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; - - // inits for conversion buffer (the size of the conversion buffer must - // be the sum of input/output sizes which is the worst case fill level) - const int iConBufSize = - iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff; - - SndCrdConversionBufferIn.Init ( iConBufSize ); - SndCrdConversionBufferOut.Init ( iConBufSize ); - vecDataConvBuf.Init ( iStereoBlockSizeSam ); - - // the output conversion buffer must be filled with the inner - // block size for initialization (this is the latency which is - // introduced by the conversion buffer) to avoid buffer underruns - const CVector vZeros ( iStereoBlockSizeSam, 0 ); - SndCrdConversionBufferOut.Put ( vZeros, vZeros.Size() ); - - bSndCrdConversionBufferRequired = true; - } - - // calculate stereo (two channels) buffer size - iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; - - vecsAudioSndCrdMono.Init ( iMonoBlockSizeSam ); - vecdAudioStereo.Init ( iStereoBlockSizeSam ); - - // init response time evaluation - CycleTimeVariance.Init ( iMonoBlockSizeSam, - SYSTEM_SAMPLE_RATE_HZ, TIME_MOV_AV_RESPONSE_SECONDS ); - - CycleTimeVariance.Reset(); - - // init reverberation - AudioReverbL.Init ( SYSTEM_SAMPLE_RATE_HZ ); - AudioReverbR.Init ( SYSTEM_SAMPLE_RATE_HZ ); - - // inits for CELT coding - if ( bCeltDoHighQuality ) - { - if ( bUseStereo ) - { - iCeltNumCodedBytes = CELT_NUM_BYTES_STEREO_HIGH_QUALITY; - } - else - { - iCeltNumCodedBytes = CELT_NUM_BYTES_MONO_HIGH_QUALITY; - } - } - else - { - if ( bUseStereo ) - { - iCeltNumCodedBytes = CELT_NUM_BYTES_STEREO_NORMAL_QUALITY; - } - else - { - iCeltNumCodedBytes = CELT_NUM_BYTES_MONO_NORMAL_QUALITY; - } - } - vecCeltData.Init ( iCeltNumCodedBytes ); - - // inits for network and channel - vecbyNetwData.Init ( iCeltNumCodedBytes ); - if ( bUseStereo ) - { - vecsNetwork.Init ( iStereoBlockSizeSam ); - - // set the channel network properties - Channel.SetAudioStreamProperties ( iCeltNumCodedBytes, - iSndCrdFrameSizeFactor, - 2 ); - } - else - { - vecsNetwork.Init ( iMonoBlockSizeSam ); - - // set the channel network properties - Channel.SetAudioStreamProperties ( iCeltNumCodedBytes, - iSndCrdFrameSizeFactor, - 1 ); - } -} - -void CClient::AudioCallback ( CVector& psData, void* arg ) -{ - // get the pointer to the object - CClient* pMyClientObj = reinterpret_cast ( arg ); - - // process audio data - pMyClientObj->ProcessSndCrdAudioData ( psData ); -} - -void CClient::ProcessSndCrdAudioData ( CVector& vecsStereoSndCrd ) -{ - // check if a conversion buffer is required or not - if ( bSndCrdConversionBufferRequired ) - { - // add new sound card block in conversion buffer - SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); - - // process all available blocks of data - while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam ) - { - // get one block of data for processing - SndCrdConversionBufferIn.Get ( vecDataConvBuf ); - - // process audio data - ProcessAudioDataIntern ( vecDataConvBuf ); - - SndCrdConversionBufferOut.Put ( vecDataConvBuf, vecDataConvBuf.Size() ); - } - - // get processed sound card block out of the conversion buffer - SndCrdConversionBufferOut.Get ( vecsStereoSndCrd ); - } - else - { - // regular case: no conversion buffer required - // process audio data - ProcessAudioDataIntern ( vecsStereoSndCrd ); - } -} - -void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) -{ - int i, j; - - // Transmit signal --------------------------------------------------------- - // update stereo signal level meter - SignalLevelMeter.Update ( vecsStereoSndCrd ); - - // convert data from short to double - for ( i = 0; i < iStereoBlockSizeSam; i++ ) - { - vecdAudioStereo[i] = static_cast ( vecsStereoSndCrd[i] ); - } - - // add reverberation effect if activated - if ( iReverbLevel != 0 ) - { - // calculate attenuation amplification factor - const double dRevLev = - static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 2; - - if ( bUseStereo ) - { - // for stereo always apply reverberation effect on both channels - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // left channel - vecdAudioStereo[i] += - dRevLev * AudioReverbL.ProcessSample ( vecdAudioStereo[i] ); - - // right channel - vecdAudioStereo[i + 1] += - dRevLev * AudioReverbR.ProcessSample ( vecdAudioStereo[i + 1] ); - } - } - else - { - if ( bReverbOnLeftChan ) - { - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // left channel - vecdAudioStereo[i] += - dRevLev * AudioReverbL.ProcessSample ( vecdAudioStereo[i] ); - } - } - else - { - for ( i = 1; i < iStereoBlockSizeSam; i += 2 ) - { - // right channel - vecdAudioStereo[i] += - dRevLev * AudioReverbR.ProcessSample ( vecdAudioStereo[i] ); - } - } - } - } - - // mix both signals depending on the fading setting, convert - // from double to short - if ( iAudioInFader == AUD_FADER_IN_MIDDLE ) - { - if ( bUseStereo ) - { - // perform type conversion - for ( i = 0; i < iStereoBlockSizeSam; i++ ) - { - vecsNetwork[i] = Double2Short ( vecdAudioStereo[i] ); - } - } - else - { - // mix channels together - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - vecsNetwork[i] = - Double2Short ( ( vecdAudioStereo[j] + - vecdAudioStereo[j + 1] ) / 2 ); - } - } - } - else - { - if ( bUseStereo ) - { - // stereo - const double dAttFactStereo = static_cast ( - AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / - AUD_FADER_IN_MIDDLE; - - if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on right channel - vecsNetwork[j] = Double2Short ( - vecdAudioStereo[j] ); - - vecsNetwork[j + 1] = Double2Short ( - dAttFactStereo * vecdAudioStereo[j + 1] ); - } - } - else - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel - vecsNetwork[j] = Double2Short ( - dAttFactStereo * vecdAudioStereo[j] ); - - vecsNetwork[j + 1] = Double2Short ( - vecdAudioStereo[j + 1] ); - } - } - } - else - { - // mono - // make sure that in the middle position the two channels are - // amplified by 1/2, if the pan is set to one channel, this - // channel should have an amplification of 1 - const double dAttFactMono = static_cast ( - AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / - AUD_FADER_IN_MIDDLE / 2; - - const double dAmplFactMono = 0.5 + static_cast ( - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / - AUD_FADER_IN_MIDDLE / 2; - - if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on right channel - vecsNetwork[i] = Double2Short ( - dAmplFactMono * vecdAudioStereo[j] + - dAttFactMono * vecdAudioStereo[j + 1] ); - } - } - else - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel - vecsNetwork[i] = Double2Short ( - dAmplFactMono * vecdAudioStereo[j + 1] + - dAttFactMono * vecdAudioStereo[j] ); - } - } - } - } - - for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) - { - if ( bUseStereo ) - { - // encode current audio frame with CELT encoder - celt_encode ( CeltEncoderStereo, - &vecsNetwork[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES], - NULL, - &vecCeltData[0], - iCeltNumCodedBytes ); - } - else - { - // encode current audio frame with CELT encoder - celt_encode ( CeltEncoderMono, - &vecsNetwork[i * SYSTEM_FRAME_SIZE_SAMPLES], - NULL, - &vecCeltData[0], - iCeltNumCodedBytes ); - } - - // send coded audio through the network - Socket.SendPacket ( Channel.PrepSendPacket ( vecCeltData ), - Channel.GetAddress() ); - } - - - // Receive signal ---------------------------------------------------------- - for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) - { - // receive a new block - const bool bReceiveDataOk = - ( Channel.GetData ( vecbyNetwData ) == GS_BUFFER_OK ); - - if ( bReceiveDataOk ) - { - PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN ); - } - else - { - PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED ); - } - - // CELT decoding - if ( bReceiveDataOk ) - { - if ( bUseStereo ) - { - celt_decode ( CeltDecoderStereo, - &vecbyNetwData[0], - iCeltNumCodedBytes, - &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES] ); - } - else - { - celt_decode ( CeltDecoderMono, - &vecbyNetwData[0], - iCeltNumCodedBytes, - &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES] ); - } - } - else - { - // lost packet - if ( bUseStereo ) - { - celt_decode ( CeltDecoderStereo, - NULL, - 0, - &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES] ); - } - else - { - celt_decode ( CeltDecoderMono, - NULL, - 0, - &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES] ); - } - } - } - - // check if channel is connected - if ( Channel.IsConnected() ) - { - if ( !bUseStereo ) - { - // copy mono data in stereo sound card buffer - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = - vecsAudioSndCrdMono[i]; - } - } - } - else - { - // if not connected, clear data - vecsStereoSndCrd.Reset ( 0 ); - } - - // update response time measurement - CycleTimeVariance.Update(); - - // calculate current buffer setting - const double dAudioBufferDurationMs = - static_cast ( GetSndCrdActualMonoBlSize() + - GetSndCrdConvBufAdditionalDelayMonoBlSize() ) * - 1000 / SYSTEM_SAMPLE_RATE_HZ; - - // update socket buffer size - Channel.UpdateSocketBufferSize ( dAudioBufferDurationMs, - CycleTimeVariance.GetStdDev() ); -} - -int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) -{ -/* - For estimating the overall delay, use the following assumptions: - - the mean delay of a cyclic buffer is half the buffer size (since - for the average it is assumed that the buffer is half filled) - - consider the jitter buffer on the server side, too -*/ - // the buffer sizes at client and server divided by 2 (half the buffer - // for the delay) is the total socket buffer size - const double dTotalJitterBufferDelayMs = SYSTEM_BLOCK_DURATION_MS_FLOAT * - static_cast ( GetSockBufNumFrames() + - GetServerSockBufNumFrames() ) / 2; - - // we assume that we have two period sizes for the input and one for the - // output, therefore we have "3 *" instead of "2 *" (for input and output) - // the actual sound card buffer size, also consider delay introduced by - // sound card conversion buffer by using - // "GetSndCrdConvBufAdditionalDelayMonoBlSize" - const double dTotalSoundCardDelayMs = - ( 3 * GetSndCrdActualMonoBlSize() + - GetSndCrdConvBufAdditionalDelayMonoBlSize() ) * - 1000 / SYSTEM_SAMPLE_RATE_HZ; - - // network packets are of the same size as the audio packets per definition - // if no sound card conversion buffer is used - const double dDelayToFillNetworkPacketsMs = - GetSystemMonoBlSize() * 1000 / SYSTEM_SAMPLE_RATE_HZ; - - // CELT additional delay at small frame sizes is half a frame size - const double dAdditionalAudioCodecDelayMs = - SYSTEM_BLOCK_DURATION_MS_FLOAT / 2; - - const double dTotalBufferDelayMs = - dDelayToFillNetworkPacketsMs + - dTotalJitterBufferDelayMs + - dTotalSoundCardDelayMs + - dAdditionalAudioCodecDelayMs; - - return LlconMath::round ( dTotalBufferDelayMs + iPingTimeMs ); -} + // Note: Do not use the "SetServerSockBufNumFrames" function for setting + // the new server jitter buffer size since then a message would be sent + // to the server which is incorrect. + iServerSockBufNumFrames = iNewJitBufSize; + } +} + +void CClient::OnNewConnection() +{ + // a new connection was successfully initiated, send name and request + // connected clients list + Channel.SetRemoteName ( strName ); + + // We have to send a connected clients list request since it can happen + // that we just had connected to the server and then disconnected but + // the server still thinks that we are connected (the server is still + // waiting for the channel time-out). If we now connect again, we would + // not get the list because the server does not know about a new connection. + // Same problem is with the jitter buffer message. + Channel.CreateReqConnClientsList(); + CreateServerJitterBufferMessage(); +} + +void CClient::CreateServerJitterBufferMessage() +{ + // per definition in the client: if auto jitter buffer is enabled, both, + // the client and server shall use an auto jitter buffer + if ( GetDoAutoSockBufSize() ) + { + // in case auto jitter buffer size is enabled, we have to transmit a + // special value + Channel.CreateJitBufMes ( AUTO_NET_BUF_SIZE_FOR_PROTOCOL ); + } + else + { + Channel.CreateJitBufMes ( GetServerSockBufNumFrames() ); + } +} + +void CClient::OnCLPingReceived ( CHostAddress InetAddr, + int iMs ) +{ + // make sure we are running and the server address is correct + if ( IsRunning() && ( InetAddr == Channel.GetAddress() ) ) + { + // take care of wrap arounds (if wrapping, do not use result) + const int iCurDiff = EvaluatePingMessage ( iMs ); + if ( iCurDiff >= 0 ) + { + emit PingTimeReceived ( iCurDiff ); + } + } +} + +void CClient::OnCLPingWithNumClientsReceived ( CHostAddress InetAddr, + int iMs, + int iNumClients ) +{ + // take care of wrap arounds (if wrapping, do not use result) + const int iCurDiff = EvaluatePingMessage ( iMs ); + if ( iCurDiff >= 0 ) + { + emit CLPingTimeWithNumClientsReceived ( InetAddr, + iCurDiff, + iNumClients ); + } +} + +int CClient::PreparePingMessage() +{ + // transmit the current precise time (in ms) + return PreciseTime.elapsed(); +} + +int CClient::EvaluatePingMessage ( const int iMs ) +{ + // calculate difference between received time in ms and current time in ms + return PreciseTime.elapsed() - iMs; +} + +void CClient::SetDoAutoSockBufSize ( const bool bValue ) +{ + // first, set new value in the channel object + Channel.SetDoAutoSockBufSize ( bValue ); + + // inform the server about the change + CreateServerJitterBufferMessage(); +} + +bool CClient::SetServerAddr ( QString strNAddr ) +{ + CHostAddress HostAddress; + if ( LlconNetwUtil().ParseNetworkAddress ( strNAddr, + HostAddress ) ) + { + // apply address to the channel + Channel.SetAddress ( HostAddress ); + + return true; + } + else + { + return false; // invalid address + } +} + +void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor ) +{ + // first check new input parameter + if ( ( iNewFactor == FRAME_SIZE_FACTOR_PREFERRED ) || + ( iNewFactor == FRAME_SIZE_FACTOR_DEFAULT ) || + ( iNewFactor == FRAME_SIZE_FACTOR_SAFE ) ) + { + // init with new parameter, if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + // set new parameter + iSndCrdPrefFrameSizeFactor = iNewFactor; + + // init with new block size index parameter + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } + } +} + +void CClient::SetCELTHighQuality ( const bool bNCeltHighQualityFlag ) +{ + // init with new parameter, if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + // set new parameter + bCeltDoHighQuality = bNCeltHighQualityFlag; + Init(); + + if ( bWasRunning ) + { + Sound.Start(); + } +} + +void CClient::SetUseStereo ( const bool bNUseStereo ) +{ + // init with new parameter, if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + // set new parameter + bUseStereo = bNUseStereo; + Init(); + + if ( bWasRunning ) + { + Sound.Start(); + } +} + +QString CClient::SetSndCrdDev ( const int iNewDev ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + const QString strReturn = Sound.SetDev ( iNewDev ); + + // init again because the sound card actual buffer size might + // be changed on new device + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } + + return strReturn; +} + +void CClient::SetSndCrdLeftInputChannel ( const int iNewChan ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + Sound.SetLeftInputChannel ( iNewChan ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::SetSndCrdRightInputChannel ( const int iNewChan ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + Sound.SetRightInputChannel ( iNewChan ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::SetSndCrdLeftOutputChannel ( const int iNewChan ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + Sound.SetLeftOutputChannel ( iNewChan ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::SetSndCrdRightOutputChannel ( const int iNewChan ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + Sound.SetRightOutputChannel ( iNewChan ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::OnSndCrdReinitRequest() +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + // reinit the driver (we use the currently selected driver) and + // init client object, too + Sound.SetDev ( Sound.GetDev() ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::Start() +{ + // init object + Init(); + + // enable channel + Channel.SetEnable ( true ); + + // start audio interface + Sound.Start(); +} + +void CClient::Stop() +{ + // stop audio interface + Sound.Stop(); + + // disable channel + Channel.SetEnable ( false ); + + // wait for approx. 100 ms to make sure no audio packet is still in the + // network queue causing the channel to be reconnected right after having + // received the disconnect message (seems not to gain much, disconnect is + // still not working reliably) + QTime DieTime = QTime::currentTime().addMSecs ( 100 ); + while ( QTime::currentTime() < DieTime ) + { + // exclude user input events because if we use AllEvents, it happens + // that if the user initiates a connection and disconnection quickly + // (e.g. quickly pressing enter five times), the software can get into + // an unknown state + QCoreApplication::processEvents ( + QEventLoop::ExcludeUserInputEvents, 100 ); + } + + // Send disconnect message to server (Since we disable our protocol + // receive mechanism with the next command, we do not evaluate any + // respond from the server, therefore we just hope that the message + // gets its way to the server, if not, the old behaviour time-out + // disconnects the connection anyway). + ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); + + // reset current signal level and LEDs + SignalLevelMeter.Reset(); + PostWinMessage ( MS_RESET_ALL, 0 ); +} + +void CClient::Init() +{ + // check if possible frame size factors are supported + const int iFraSizePreffered = + FRAME_SIZE_FACTOR_PREFERRED * SYSTEM_FRAME_SIZE_SAMPLES; + + bFraSiFactPrefSupported = + ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); + + const int iFraSizeDefault = + FRAME_SIZE_FACTOR_DEFAULT * SYSTEM_FRAME_SIZE_SAMPLES; + + bFraSiFactDefSupported = + ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); + + const int iFraSizeSafe = + FRAME_SIZE_FACTOR_SAFE * SYSTEM_FRAME_SIZE_SAMPLES; + + bFraSiFactSafeSupported = + ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); + + // translate block size index in actual block size + const int iPrefMonoFrameSize = + iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; + + // get actual sound card buffer size using preferred size + iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); + + // Calculate the current sound card frame size factor. In case + // the current mono block size is not a multiple of the system + // frame size, we have to use a sound card conversion buffer. + if ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) || + ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) || + ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) + { + // regular case: one of our predefined buffer sizes is available + iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES; + + // no sound card conversion buffer required + bSndCrdConversionBufferRequired = false; + } + else + { + // An unsupported sound card buffer size is currently used -> we have + // to use a conversion buffer. Per definition we use the smallest buffer + // size as llcon frame size + + // store actual sound card buffer size (stereo) + iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam; + const int iSndCardStereoBlockSizeSamConvBuff = 2 * iMonoBlockSizeSam; + + // overwrite block size by smallest supported llcon buffer size + iSndCrdFrameSizeFactor = FRAME_SIZE_FACTOR_PREFERRED; + iMonoBlockSizeSam = + SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED; + + iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; + + // inits for conversion buffer (the size of the conversion buffer must + // be the sum of input/output sizes which is the worst case fill level) + const int iConBufSize = + iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff; + + SndCrdConversionBufferIn.Init ( iConBufSize ); + SndCrdConversionBufferOut.Init ( iConBufSize ); + vecDataConvBuf.Init ( iStereoBlockSizeSam ); + + // the output conversion buffer must be filled with the inner + // block size for initialization (this is the latency which is + // introduced by the conversion buffer) to avoid buffer underruns + const CVector vZeros ( iStereoBlockSizeSam, 0 ); + SndCrdConversionBufferOut.Put ( vZeros, vZeros.Size() ); + + bSndCrdConversionBufferRequired = true; + } + + // calculate stereo (two channels) buffer size + iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; + + vecsAudioSndCrdMono.Init ( iMonoBlockSizeSam ); + vecdAudioStereo.Init ( iStereoBlockSizeSam ); + + // init response time evaluation + CycleTimeVariance.Init ( iMonoBlockSizeSam, + SYSTEM_SAMPLE_RATE_HZ, TIME_MOV_AV_RESPONSE_SECONDS ); + + CycleTimeVariance.Reset(); + + // init reverberation + AudioReverbL.Init ( SYSTEM_SAMPLE_RATE_HZ ); + AudioReverbR.Init ( SYSTEM_SAMPLE_RATE_HZ ); + + // inits for CELT coding + if ( bCeltDoHighQuality ) + { + if ( bUseStereo ) + { + iCeltNumCodedBytes = CELT_NUM_BYTES_STEREO_HIGH_QUALITY; + } + else + { + iCeltNumCodedBytes = CELT_NUM_BYTES_MONO_HIGH_QUALITY; + } + } + else + { + if ( bUseStereo ) + { + iCeltNumCodedBytes = CELT_NUM_BYTES_STEREO_NORMAL_QUALITY; + } + else + { + iCeltNumCodedBytes = CELT_NUM_BYTES_MONO_NORMAL_QUALITY; + } + } + vecCeltData.Init ( iCeltNumCodedBytes ); + + // inits for network and channel + vecbyNetwData.Init ( iCeltNumCodedBytes ); + if ( bUseStereo ) + { + vecsNetwork.Init ( iStereoBlockSizeSam ); + + // set the channel network properties + Channel.SetAudioStreamProperties ( iCeltNumCodedBytes, + iSndCrdFrameSizeFactor, + 2 ); + } + else + { + vecsNetwork.Init ( iMonoBlockSizeSam ); + + // set the channel network properties + Channel.SetAudioStreamProperties ( iCeltNumCodedBytes, + iSndCrdFrameSizeFactor, + 1 ); + } +} + +void CClient::AudioCallback ( CVector& psData, void* arg ) +{ + // get the pointer to the object + CClient* pMyClientObj = reinterpret_cast ( arg ); + + // process audio data + pMyClientObj->ProcessSndCrdAudioData ( psData ); +} + +void CClient::ProcessSndCrdAudioData ( CVector& vecsStereoSndCrd ) +{ + // check if a conversion buffer is required or not + if ( bSndCrdConversionBufferRequired ) + { + // add new sound card block in conversion buffer + SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); + + // process all available blocks of data + while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam ) + { + // get one block of data for processing + SndCrdConversionBufferIn.Get ( vecDataConvBuf ); + + // process audio data + ProcessAudioDataIntern ( vecDataConvBuf ); + + SndCrdConversionBufferOut.Put ( vecDataConvBuf, vecDataConvBuf.Size() ); + } + + // get processed sound card block out of the conversion buffer + SndCrdConversionBufferOut.Get ( vecsStereoSndCrd ); + } + else + { + // regular case: no conversion buffer required + // process audio data + ProcessAudioDataIntern ( vecsStereoSndCrd ); + } +} + +void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) +{ + int i, j; + + // Transmit signal --------------------------------------------------------- + // update stereo signal level meter + SignalLevelMeter.Update ( vecsStereoSndCrd ); + + // convert data from short to double + for ( i = 0; i < iStereoBlockSizeSam; i++ ) + { + vecdAudioStereo[i] = static_cast ( vecsStereoSndCrd[i] ); + } + + // add reverberation effect if activated + if ( iReverbLevel != 0 ) + { + // calculate attenuation amplification factor + const double dRevLev = + static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 2; + + if ( bUseStereo ) + { + // for stereo always apply reverberation effect on both channels + for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) + { + // left channel + vecdAudioStereo[i] += + dRevLev * AudioReverbL.ProcessSample ( vecdAudioStereo[i] ); + + // right channel + vecdAudioStereo[i + 1] += + dRevLev * AudioReverbR.ProcessSample ( vecdAudioStereo[i + 1] ); + } + } + else + { + if ( bReverbOnLeftChan ) + { + for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) + { + // left channel + vecdAudioStereo[i] += + dRevLev * AudioReverbL.ProcessSample ( vecdAudioStereo[i] ); + } + } + else + { + for ( i = 1; i < iStereoBlockSizeSam; i += 2 ) + { + // right channel + vecdAudioStereo[i] += + dRevLev * AudioReverbR.ProcessSample ( vecdAudioStereo[i] ); + } + } + } + } + + // mix both signals depending on the fading setting, convert + // from double to short + if ( iAudioInFader == AUD_FADER_IN_MIDDLE ) + { + if ( bUseStereo ) + { + // perform type conversion + for ( i = 0; i < iStereoBlockSizeSam; i++ ) + { + vecsNetwork[i] = Double2Short ( vecdAudioStereo[i] ); + } + } + else + { + // mix channels together + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + vecsNetwork[i] = + Double2Short ( ( vecdAudioStereo[j] + + vecdAudioStereo[j + 1] ) / 2 ); + } + } + } + else + { + if ( bUseStereo ) + { + // stereo + const double dAttFactStereo = static_cast ( + AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / + AUD_FADER_IN_MIDDLE; + + if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) + { + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // attenuation on right channel + vecsNetwork[j] = Double2Short ( + vecdAudioStereo[j] ); + + vecsNetwork[j + 1] = Double2Short ( + dAttFactStereo * vecdAudioStereo[j + 1] ); + } + } + else + { + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // attenuation on left channel + vecsNetwork[j] = Double2Short ( + dAttFactStereo * vecdAudioStereo[j] ); + + vecsNetwork[j + 1] = Double2Short ( + vecdAudioStereo[j + 1] ); + } + } + } + else + { + // mono + // make sure that in the middle position the two channels are + // amplified by 1/2, if the pan is set to one channel, this + // channel should have an amplification of 1 + const double dAttFactMono = static_cast ( + AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / + AUD_FADER_IN_MIDDLE / 2; + + const double dAmplFactMono = 0.5 + static_cast ( + abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / + AUD_FADER_IN_MIDDLE / 2; + + if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) + { + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // attenuation on right channel + vecsNetwork[i] = Double2Short ( + dAmplFactMono * vecdAudioStereo[j] + + dAttFactMono * vecdAudioStereo[j + 1] ); + } + } + else + { + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // attenuation on left channel + vecsNetwork[i] = Double2Short ( + dAmplFactMono * vecdAudioStereo[j + 1] + + dAttFactMono * vecdAudioStereo[j] ); + } + } + } + } + + for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) + { + if ( bUseStereo ) + { + // encode current audio frame with CELT encoder + celt_encode ( CeltEncoderStereo, + &vecsNetwork[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES], + NULL, + &vecCeltData[0], + iCeltNumCodedBytes ); + } + else + { + // encode current audio frame with CELT encoder + celt_encode ( CeltEncoderMono, + &vecsNetwork[i * SYSTEM_FRAME_SIZE_SAMPLES], + NULL, + &vecCeltData[0], + iCeltNumCodedBytes ); + } + + // send coded audio through the network + Socket.SendPacket ( Channel.PrepSendPacket ( vecCeltData ), + Channel.GetAddress() ); + } + + + // Receive signal ---------------------------------------------------------- + for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) + { + // receive a new block + const bool bReceiveDataOk = + ( Channel.GetData ( vecbyNetwData ) == GS_BUFFER_OK ); + + if ( bReceiveDataOk ) + { + PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN ); + } + else + { + PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED ); + } + + // CELT decoding + if ( bReceiveDataOk ) + { + if ( bUseStereo ) + { + celt_decode ( CeltDecoderStereo, + &vecbyNetwData[0], + iCeltNumCodedBytes, + &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES] ); + } + else + { + celt_decode ( CeltDecoderMono, + &vecbyNetwData[0], + iCeltNumCodedBytes, + &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES] ); + } + } + else + { + // lost packet + if ( bUseStereo ) + { + celt_decode ( CeltDecoderStereo, + NULL, + 0, + &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES] ); + } + else + { + celt_decode ( CeltDecoderMono, + NULL, + 0, + &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES] ); + } + } + } + + // check if channel is connected + if ( Channel.IsConnected() ) + { + if ( !bUseStereo ) + { + // copy mono data in stereo sound card buffer + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = + vecsAudioSndCrdMono[i]; + } + } + } + else + { + // if not connected, clear data + vecsStereoSndCrd.Reset ( 0 ); + } + + // update response time measurement + CycleTimeVariance.Update(); + + // calculate current buffer setting + const double dAudioBufferDurationMs = + static_cast ( GetSndCrdActualMonoBlSize() + + GetSndCrdConvBufAdditionalDelayMonoBlSize() ) * + 1000 / SYSTEM_SAMPLE_RATE_HZ; + + // update socket buffer size + Channel.UpdateSocketBufferSize ( dAudioBufferDurationMs, + CycleTimeVariance.GetStdDev() ); +} + +int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) +{ +/* + For estimating the overall delay, use the following assumptions: + - the mean delay of a cyclic buffer is half the buffer size (since + for the average it is assumed that the buffer is half filled) + - consider the jitter buffer on the server side, too +*/ + // the buffer sizes at client and server divided by 2 (half the buffer + // for the delay) is the total socket buffer size + const double dTotalJitterBufferDelayMs = SYSTEM_BLOCK_DURATION_MS_FLOAT * + static_cast ( GetSockBufNumFrames() + + GetServerSockBufNumFrames() ) / 2; + + // we assume that we have two period sizes for the input and one for the + // output, therefore we have "3 *" instead of "2 *" (for input and output) + // the actual sound card buffer size, also consider delay introduced by + // sound card conversion buffer by using + // "GetSndCrdConvBufAdditionalDelayMonoBlSize" + const double dTotalSoundCardDelayMs = + ( 3 * GetSndCrdActualMonoBlSize() + + GetSndCrdConvBufAdditionalDelayMonoBlSize() ) * + 1000 / SYSTEM_SAMPLE_RATE_HZ; + + // network packets are of the same size as the audio packets per definition + // if no sound card conversion buffer is used + const double dDelayToFillNetworkPacketsMs = + GetSystemMonoBlSize() * 1000 / SYSTEM_SAMPLE_RATE_HZ; + + // CELT additional delay at small frame sizes is half a frame size + const double dAdditionalAudioCodecDelayMs = + SYSTEM_BLOCK_DURATION_MS_FLOAT / 2; + + const double dTotalBufferDelayMs = + dDelayToFillNetworkPacketsMs + + dTotalJitterBufferDelayMs + + dTotalSoundCardDelayMs + + dAdditionalAudioCodecDelayMs; + + return LlconMath::round ( dTotalBufferDelayMs + iPingTimeMs ); +} diff --git a/src/client.h b/src/client.h index 323c8fa2..010ff3a3 100755 --- a/src/client.h +++ b/src/client.h @@ -328,6 +328,7 @@ public slots: void OnSendProtMessage ( CVector vecMessage ); void OnDetectedCLMessage ( CVector vecbyData, int iNumBytes ); void OnReqJittBufSize() { CreateServerJitterBufferMessage(); } + void OnJittBufSizeChanged ( int iNewJitBufSize ); void OnReqChanName() { Channel.SetRemoteName ( strName ); } void OnNewConnection(); void OnCLPingReceived ( CHostAddress InetAddr, diff --git a/src/global.h b/src/global.h index a3c934c9..58cedb51 100755 --- a/src/global.h +++ b/src/global.h @@ -116,7 +116,7 @@ LED bar: lbr // minimum/maximum network buffer size (which can be chosen by slider) #define MIN_NET_BUF_SIZE_NUM_BL 1 // number of blocks #define MAX_NET_BUF_SIZE_NUM_BL 20 // number of blocks -#define AUTO_NET_BUF_SIZE_FOR_PROTOCOL -1 // auto set parameter (only used for protocol) +#define AUTO_NET_BUF_SIZE_FOR_PROTOCOL ( MAX_NET_BUF_SIZE_NUM_BL + 1 ) // auto set parameter (only used for protocol) // default network buffer size #define DEF_NET_BUF_SIZE_NUM_BL 10 // number of blocks diff --git a/src/server.cpp b/src/server.cpp index 19094ed0..7118638e 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -398,6 +398,20 @@ CServer::CServer ( const int iNewNumChan, QObject::connect ( &vecChannels[9], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh9 ( QString ) ) ); QObject::connect ( &vecChannels[10], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh10 ( QString ) ) ); QObject::connect ( &vecChannels[11], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh11 ( QString ) ) ); + + // auto socket buffer size change + QObject::connect ( &vecChannels[0], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh0 ( int ) ) ); + QObject::connect ( &vecChannels[1], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh1 ( int ) ) ); + QObject::connect ( &vecChannels[2], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh2 ( int ) ) ); + QObject::connect ( &vecChannels[3], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh3 ( int ) ) ); + QObject::connect ( &vecChannels[4], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh4 ( int ) ) ); + QObject::connect ( &vecChannels[5], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh5 ( int ) ) ); + QObject::connect ( &vecChannels[6], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh6 ( int ) ) ); + QObject::connect ( &vecChannels[7], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh7 ( int ) ) ); + QObject::connect ( &vecChannels[8], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh8 ( int ) ) ); + QObject::connect ( &vecChannels[9], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh9 ( int ) ) ); + QObject::connect ( &vecChannels[10], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh10 ( int ) ) ); + QObject::connect ( &vecChannels[11], SIGNAL ( ServerAutoSockBufSizeChange ( int ) ), this, SLOT ( OnServerAutoSockBufSizeChangeCh11 ( int ) ) ); } void CServer::OnSendProtMessage ( int iChID, CVector vecMessage ) diff --git a/src/server.h b/src/server.h index acd08f65..f81b7403 100755 --- a/src/server.h +++ b/src/server.h @@ -369,6 +369,19 @@ public slots: void OnChatTextReceivedCh9 ( QString strChatText ) { CreateAndSendChatTextForAllConChannels ( 9, strChatText ); } void OnChatTextReceivedCh10 ( QString strChatText ) { CreateAndSendChatTextForAllConChannels ( 10, strChatText ); } void OnChatTextReceivedCh11 ( QString strChatText ) { CreateAndSendChatTextForAllConChannels ( 11, strChatText ); } + + void OnServerAutoSockBufSizeChangeCh0 ( int iNNumFra ) { vecChannels[0].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh1 ( int iNNumFra ) { vecChannels[1].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh2 ( int iNNumFra ) { vecChannels[2].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh3 ( int iNNumFra ) { vecChannels[3].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh4 ( int iNNumFra ) { vecChannels[4].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh5 ( int iNNumFra ) { vecChannels[5].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh6 ( int iNNumFra ) { vecChannels[6].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh7 ( int iNNumFra ) { vecChannels[7].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh8 ( int iNNumFra ) { vecChannels[8].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh9 ( int iNNumFra ) { vecChannels[9].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh10 ( int iNNumFra ) { vecChannels[10].CreateJitBufMes ( iNNumFra ); } + void OnServerAutoSockBufSizeChangeCh11 ( int iNNumFra ) { vecChannels[11].CreateJitBufMes ( iNNumFra ); } }; #endif /* !defined ( SERVER_HOIHGE7LOKIH83JH8_3_43445KJIUHF1912__INCLUDED_ ) */