diff --git a/ChangeLog b/ChangeLog index 01760a61b7..63c512f7a5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ ### 3.12.0dev <- NOTE: the release version number will be 4.0.0 ### +- Add uncompressed audio transmission - dedicated to the memory of Hans Petter Selasky (1982 - 2023) + (contributed by @dingodoppelt) ### 3.12.0 (2026-05-02) ### diff --git a/src/channel.cpp b/src/channel.cpp index 9bd409889a..47a492dcb9 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -83,6 +83,8 @@ CChannel::CChannel ( const bool bNIsServer ) : QObject::connect ( &Protocol, &CProtocol::ClientIDReceived, this, &CChannel::ClientIDReceived ); + QObject::connect ( &Protocol, &CProtocol::RawAudioSupported, this, &CChannel::RawAudioSupported ); + QObject::connect ( &Protocol, &CProtocol::MuteStateHasChangedReceived, this, &CChannel::MuteStateHasChangedReceived ); QObject::connect ( &Protocol, &CProtocol::ChangeChanInfo, this, &CChannel::OnChangeChanInfo ); diff --git a/src/channel.h b/src/channel.h index 1567268307..096a88c9e9 100644 --- a/src/channel.h +++ b/src/channel.h @@ -149,6 +149,7 @@ class CChannel : public QObject } } void CreateClientIDMes ( const int iChanID ) { Protocol.CreateClientIDMes ( iChanID ); } + void CreateRawAudioSupportedMes() { Protocol.CreateRawAudioSupportedMes(); } void CreateReqNetwTranspPropsMes() { Protocol.CreateReqNetwTranspPropsMes(); } void CreateReqSplitMessSupportMes() { Protocol.CreateReqSplitMessSupportMes(); } void CreateReqJitBufMes() { Protocol.CreateReqJitBufMes(); } @@ -272,6 +273,7 @@ public slots: void ConClientListMesReceived ( CVector vecChanInfo ); void ChanInfoHasChanged(); void ClientIDReceived ( int iChanID ); + void RawAudioSupported(); void MuteStateHasChanged ( int iChanID, bool bIsMuted ); void MuteStateHasChangedReceived ( int iChanID, bool bIsMuted ); void ReqChanInfo(); diff --git a/src/client.cpp b/src/client.cpp index 417f08a404..13eac289b6 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -70,7 +70,8 @@ CClient::CClient ( const quint16 iPortNumber, bJitterBufferOK ( true ), bEnableIPv6 ( bNEnableIPv6 ), bMuteMeInPersonalMix ( bNMuteMeInPersonalMix ), - iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ) + iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ), + bRawAudioIsSupported ( false ) { int iOpusError; @@ -131,6 +132,8 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( &Channel, &CChannel::ClientIDReceived, this, &CClient::OnClientIDReceived ); + QObject::connect ( &Channel, &CChannel::RawAudioSupported, this, &CClient::OnRawAudioSupported ); + QObject::connect ( &Channel, &CChannel::MuteStateHasChangedReceived, this, &CClient::OnMuteStateHasChangedReceived ); QObject::connect ( &Channel, &CChannel::LicenceRequired, this, &CClient::LicenceRequired ); @@ -989,6 +992,27 @@ void CClient::OnClientIDReceived ( int iServerChanID ) emit ClientIDReceived ( iChanID ); } +void CClient::OnRawAudioSupported() +{ + if ( !bRawAudioIsSupported ) + { + const bool bWasRunning = Sound.IsRunning(); + + if ( bWasRunning ) + { + Sound.Stop(); + } + + bRawAudioIsSupported = true; + Init(); + + if ( bWasRunning ) + { + Sound.Start(); + } + } +} + void CClient::Start() { // init object @@ -1017,6 +1041,10 @@ void CClient::Stop() // disable channel Channel.SetEnable ( false ); + // Fall back to opus in case raw was used + bRawAudioIsSupported = false; + Init(); + // 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 @@ -1156,6 +1184,21 @@ void CClient::Init() case AQ_HIGH: iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY_DBLE_FRAMESIZE; break; + case AQ_RAW: + if ( bRawAudioIsSupported ) + { + // no OPUS encoding or decoding + CurOpusEncoder = nullptr; + CurOpusDecoder = nullptr; + + iCeltNumCodedBytes = sizeof ( int16_t ) * iNumAudioChannels * iOPUSFrameSizeSamples; + } + else + { + // fall back to highest OPUS quality + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY_DBLE_FRAMESIZE; + } + break; } } else @@ -1175,6 +1218,21 @@ void CClient::Init() case AQ_HIGH: iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE; break; + case AQ_RAW: + if ( bRawAudioIsSupported ) + { + // no OPUS encoding or decoding + CurOpusEncoder = nullptr; + CurOpusDecoder = nullptr; + + iCeltNumCodedBytes = sizeof ( int16_t ) * iNumAudioChannels * iOPUSFrameSizeSamples; + } + else + { + // fall back to highest OPUS quality + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE; + } + break; } } } @@ -1199,6 +1257,21 @@ void CClient::Init() case AQ_HIGH: iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY; break; + case AQ_RAW: + if ( bRawAudioIsSupported ) + { + // no OPUS encoding or decoding + CurOpusEncoder = nullptr; + CurOpusDecoder = nullptr; + + iCeltNumCodedBytes = sizeof ( int16_t ) * iNumAudioChannels * iOPUSFrameSizeSamples; + } + else + { + // fall back to highest OPUS quality + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY; + } + break; } } else @@ -1218,6 +1291,21 @@ void CClient::Init() case AQ_HIGH: iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY; break; + case AQ_RAW: + if ( bRawAudioIsSupported ) + { + // no OPUS encoding or decoding + CurOpusEncoder = nullptr; + CurOpusDecoder = nullptr; + + iCeltNumCodedBytes = sizeof ( int16_t ) * iNumAudioChannels * iOPUSFrameSizeSamples; + } + else + { + // fall back to highest OPUS quality + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY; + } + break; } } } @@ -1229,8 +1317,12 @@ void CClient::Init() vecZeros.Init ( iStereoBlockSizeSam, 0 ); vecsStereoSndCrdMuteStream.Init ( iStereoBlockSizeSam ); - opus_custom_encoder_ctl ( CurOpusEncoder, - OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iOPUSFrameSizeSamples ) ) ); + // In case we are connected to a non raw audio server or we don't use raw audio we need to initialze the codec + if ( CurOpusEncoder != nullptr ) + { + opus_custom_encoder_ctl ( CurOpusEncoder, + OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iOPUSFrameSizeSamples ) ) ); + } // inits for network and channel vecbyNetwData.Init ( iCeltNumCodedBytes ); @@ -1391,9 +1483,10 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) for ( i = 0, j = 0; i < iSndCrdFrameSizeFactor; i++, j += iNumAudioChannels * iOPUSFrameSizeSamples ) { - // OPUS encoding + // OPUS encoding or copying RAW audio? if ( CurOpusEncoder != nullptr ) { + // OPUS encoding if ( bMuteOutStream ) { iUnused = opus_custom_encode ( CurOpusEncoder, &vecZeros[j], iOPUSFrameSizeSamples, &vecCeltData[0], iCeltNumCodedBytes ); @@ -1403,6 +1496,20 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) iUnused = opus_custom_encode ( CurOpusEncoder, &vecsStereoSndCrd[j], iOPUSFrameSizeSamples, &vecCeltData[0], iCeltNumCodedBytes ); } } + else if ( bRawAudioIsSupported ) + { + // RAW audio + if ( bMuteOutStream ) + { + // output muted - fill with silence + memset ( &vecCeltData[0], 0, iCeltNumCodedBytes ); + } + else + { + // copy raw audio data + memcpy ( &vecCeltData[0], &vecsStereoSndCrd[j], iCeltNumCodedBytes ); + } + } // send coded audio through the network Channel.PrepAndSendPacket ( &Socket, vecCeltData, iCeltNumCodedBytes ); @@ -1437,11 +1544,26 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) bJitterBufferOK = false; } - // OPUS decoding + // OPUS decoding or copying RAW audio? if ( CurOpusDecoder != nullptr ) { + // OPUS decoding iUnused = opus_custom_decode ( CurOpusDecoder, pCurCodedData, iCeltNumCodedBytes, &vecsStereoSndCrd[j], iOPUSFrameSizeSamples ); } + else if ( bRawAudioIsSupported ) + { + // RAW audio + if ( pCurCodedData != nullptr ) + { + // copy raw audio data + memcpy ( &vecsStereoSndCrd[j], pCurCodedData, iCeltNumCodedBytes ); + } + else + { + // missing audio - fill with silence + memset ( &vecsStereoSndCrd[j], 0, iCeltNumCodedBytes ); + } + } } // for muted stream we have to add our local data here @@ -1488,7 +1610,7 @@ int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) // length. Since that is usually not the case but the buffers are usually // a bit larger than necessary, we introduce some factor for compensation. // Consider the jitter buffer on the client and on the server side, too. - const float fTotalJitterBufferDelayMs = fSystemBlockDurationMs * ( GetSockBufNumFrames() + GetServerSockBufNumFrames() ) * 0.7f; + const float fTotalJitterBufferDelayMs = fSystemBlockDurationMs * ( GetSockBufNumFrames() + GetServerSockBufNumFrames() ) * JITTBUF_COMP_FACTOR; // consider delay introduced by the sound card conversion buffer by using // "GetSndCrdConvBufAdditionalDelayMonoBlSize()" @@ -1519,7 +1641,7 @@ int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) const float fDelayToFillNetworkPacketsMs = GetSystemMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; // OPUS additional delay at small frame sizes is half a frame size - const float fAdditionalAudioCodecDelayMs = fSystemBlockDurationMs / 2; + const float fAdditionalAudioCodecDelayMs = CurOpusDecoder != nullptr ? fSystemBlockDurationMs / 2 : 0.0f; const float fTotalBufferDelayMs = fDelayToFillNetworkPacketsMs + fTotalJitterBufferDelayMs + fTotalSoundCardDelayMs + fAdditionalAudioCodecDelayMs; diff --git a/src/client.h b/src/client.h index 6139ef599e..c71fc414ab 100644 --- a/src/client.h +++ b/src/client.h @@ -410,7 +410,8 @@ class CClient : public QObject QMutex MutexDriverReinit; // server settings - int iServerSockBufNumFrames; + int iServerSockBufNumFrames; + bool bRawAudioIsSupported; // for ping measurement QElapsedTimer PreciseTime; @@ -453,6 +454,7 @@ protected slots: void OnControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); void OnControllerInMuteMyself ( bool bMute ); void OnClientIDReceived ( int iServerChanID ); + void OnRawAudioSupported(); void OnMuteStateHasChangedReceived ( int iServerChanID, bool bIsMuted ); void OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void OnConClientListMesReceived ( CVector vecChanInfo ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index d9f2e6ad11..f1c6af5dbe 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -494,6 +494,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet cbxAudioQuality->addItem ( tr ( "Low" ) ); // AQ_LOW cbxAudioQuality->addItem ( tr ( "Normal" ) ); // AQ_NORMAL cbxAudioQuality->addItem ( tr ( "High" ) ); // AQ_HIGH + cbxAudioQuality->addItem ( tr ( "Max" ) ); // AQ_RAW cbxAudioQuality->setCurrentIndex ( static_cast ( pClient->GetAudioQuality() ) ); // GUI design (skin) combo box diff --git a/src/global.h b/src/global.h index 100b4039de..e93837c831 100644 --- a/src/global.h +++ b/src/global.h @@ -230,6 +230,9 @@ LED bar: lbr // defines the time interval at which the ping time is updated in the GUI #define PING_UPDATE_TIME_MS 500 // ms +// defines a factor to compensate for larger than ideal jitter buffer sizes for estimated overall delay calculation +#define JITTBUF_COMP_FACTOR 0.7f + // defines the time interval at which the ping time is updated for the server list #define PING_UPDATE_TIME_SERVER_LIST_MS 2500 // ms diff --git a/src/protocol.cpp b/src/protocol.cpp index 47be8bc265..a30534bc72 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -235,6 +235,11 @@ MESSAGES (with connection) note: does not have any data -> n = 0 +- PROTMESSID_RAWAUDIO_SUPPORTED: informs client that server supports raw (uncompressed) audio + + note: does not have any data -> n = 0 + + - PROTMESSID_RECORDER_STATE: notifies of changes in the server jam recorder state +--------------+ @@ -813,6 +818,10 @@ void CProtocol::ParseMessageBody ( const CVector& vecbyMesBodyData, con EvaluateSplitMessSupportedMes(); break; + case PROTMESSID_RAWAUDIO_SUPPORTED: + EvaluateRawAudioSupportedMes(); + break; + case PROTMESSID_LICENCE_REQUIRED: EvaluateLicenceRequiredMes ( vecbyMesBodyDataRef ); break; @@ -1520,6 +1529,16 @@ bool CProtocol::EvaluateSplitMessSupportedMes() return false; // no error } +void CProtocol::CreateRawAudioSupportedMes() { CreateAndSendMessage ( PROTMESSID_RAWAUDIO_SUPPORTED, CVector ( 0 ) ); } + +bool CProtocol::EvaluateRawAudioSupportedMes() +{ + // invoke message action + emit RawAudioSupported(); + + return false; // no error +} + void CProtocol::CreateLicenceRequiredMes ( const ELicenceType eLicenceType ) { CVector vecData ( 1 ); // 1 bytes of data diff --git a/src/protocol.h b/src/protocol.h index b76e66607b..03ac9f328f 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -62,6 +62,7 @@ #define PROTMESSID_RECORDER_STATE 33 // contains the state of the jam recorder (ERecorderState) #define PROTMESSID_REQ_SPLIT_MESS_SUPPORT 34 // request support for split messages #define PROTMESSID_SPLIT_MESS_SUPPORTED 35 // split messages are supported +#define PROTMESSID_RAWAUDIO_SUPPORTED 36 // raw (uncompressed) audio is supported // message IDs of connection less messages (CLM) // DEFINITION -> start at 1000, end at 1999, see IsConnectionLessMessageID @@ -124,6 +125,7 @@ class CProtocol : public QObject void CreateReqNetwTranspPropsMes(); void CreateReqSplitMessSupportMes(); void CreateSplitMessSupportedMes(); + void CreateRawAudioSupportedMes(); void CreateLicenceRequiredMes ( const ELicenceType eLicenceType ); void CreateOpusSupportedMes(); @@ -258,6 +260,7 @@ class CProtocol : public QObject bool EvaluateReqNetwTranspPropsMes(); bool EvaluateReqSplitMessSupportMes(); bool EvaluateSplitMessSupportedMes(); + bool EvaluateRawAudioSupportedMes(); bool EvaluateLicenceRequiredMes ( const CVector& vecData ); bool EvaluateVersionAndOSMes ( const CVector& vecData ); bool EvaluateRecorderStateMes ( const CVector& vecData ); @@ -321,6 +324,7 @@ public slots: void ReqNetTranspProps(); void ReqSplitMessSupport(); void SplitMessSupported(); + void RawAudioSupported(); void LicenceRequired ( ELicenceType eLicenceType ); void VersionAndOSReceived ( COSUtil::EOpSystemType eOSType, QString strVersion ); void RecorderStateReceived ( ERecorderState eRecorderState ); diff --git a/src/server.cpp b/src/server.cpp index a477771fde..b26ae5d401 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -383,6 +383,9 @@ void CServer::OnNewConnection ( int iChID, int iTotChans, CHostAddress RecHostAd // must be the first message to be sent for a new connection) vecChannels[iChID].CreateClientIDMes ( iChID ); + // inform the client that the server supports raw (uncompressed) audio + vecChannels[iChID].CreateRawAudioSupportedMes(); + // Send an empty channel list in order to force clients to reset their // audio mixer state. This is required to trigger clients to re-send their // gain levels upon reconnecting after server restarts. @@ -885,16 +888,41 @@ void CServer::DecodeReceiveData ( const int iChanCnt, const int iNumClients ) pCurCodedData = nullptr; } - // OPUS decode received data stream - if ( CurOpusDecoder != nullptr ) + // Recognise a raw audio packet by its size: + // The client doesn't pass a value for the selected audio quality implicitly. + // Rather the server is passed the length of the data sent by the client in iClientFrameSizeSamples. + // We know the exact size to expect from a client sending raw audio packets. + // The length is calculated in the client by: iNumAudioChannels * iOPUSFrameSizeSamples * sizeof ( int16_t ) + // iOPUSFrameSizeSamples can be either 64 or 128 (small network buffers enabled|disabled) + // iNumAudioChannels is either 1 for mono or 2 for stereo and mono-in/stereo-out + // sizeof ( int16_t ) is the size in bytes for the raw pcm audio data = 2 + // Sizes other than that are considered OPUS coded because those depend on hardcoded sizes in client.h + const bool bIsRawAudio = + ( iCeltNumCodedBytes == static_cast ( sizeof ( int16_t ) * iClientFrameSizeSamples * vecNumAudioChannels[iChanCnt] ) ); + + const int iOffset = iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt]; + + if ( !bIsRawAudio ) { - const int iOffset = iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt]; - - iUnused = opus_custom_decode ( CurOpusDecoder, - pCurCodedData, - iCeltNumCodedBytes, - &vecvecsData[iChanCnt][iOffset], - iClientFrameSizeSamples ); + // OPUS decode received data stream + if ( CurOpusDecoder != nullptr ) + { + iUnused = opus_custom_decode ( CurOpusDecoder, + pCurCodedData, + iCeltNumCodedBytes, + &vecvecsData[iChanCnt][iOffset], + iClientFrameSizeSamples ); + } + } + else if ( pCurCodedData != nullptr ) + { + // copy received raw data stream + memcpy ( &vecvecsData[iChanCnt][iOffset], pCurCodedData, iCeltNumCodedBytes ); + } + else + { + // lost packet - fill with silence + memset ( &vecvecsData[iChanCnt][iOffset], 0, iCeltNumCodedBytes ); } } @@ -1107,7 +1135,7 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt, const int iNumClients } int iClientFrameSizeSamples = 0; // initialize to avoid a compiler warning - OpusCustomEncoder* pCurOpusEncoder = nullptr; + OpusCustomEncoder* CurOpusEncoder = nullptr; // get current number of CELT coded bytes const int iCeltNumCodedBytes = vecChannels[iCurChanID].GetCeltNumCodedBytes(); @@ -1119,11 +1147,11 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt, const int iNumClients if ( vecNumAudioChannels[iChanCnt] == 1 ) { - pCurOpusEncoder = OpusEncoderMono[iCurChanID]; + CurOpusEncoder = OpusEncoderMono[iCurChanID]; } else { - pCurOpusEncoder = OpusEncoderStereo[iCurChanID]; + CurOpusEncoder = OpusEncoderStereo[iCurChanID]; } } else if ( vecAudioComprType[iChanCnt] == CT_OPUS64 ) @@ -1132,11 +1160,11 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt, const int iNumClients if ( vecNumAudioChannels[iChanCnt] == 1 ) { - pCurOpusEncoder = Opus64EncoderMono[iCurChanID]; + CurOpusEncoder = Opus64EncoderMono[iCurChanID]; } else { - pCurOpusEncoder = Opus64EncoderStereo[iCurChanID]; + CurOpusEncoder = Opus64EncoderStereo[iCurChanID]; } } @@ -1154,25 +1182,40 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt, const int iNumClients DoubleFrameSizeConvBufOut[iCurChanID].GetAll ( vecsSendData, DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ); } - // OPUS encoding - if ( pCurOpusEncoder != nullptr ) + if ( iCeltNumCodedBytes != static_cast ( sizeof ( int16_t ) * iClientFrameSizeSamples * vecNumAudioChannels[iChanCnt] ) ) { - //### TODO: BEGIN ###// - // find a better place than this: the setting does not change all the time so for speed - // optimization it would be better to set it only if the network frame size is changed - opus_custom_encoder_ctl ( pCurOpusEncoder, - OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iClientFrameSizeSamples ) ) ); - //### TODO: END ###// + // OPUS encoding + if ( CurOpusEncoder != nullptr ) + { + //### TODO: BEGIN ###// + // find a better place than this: the setting does not change all the time so for speed + // optimization it would be better to set it only if the network frame size is changed + opus_custom_encoder_ctl ( CurOpusEncoder, + OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iClientFrameSizeSamples ) ) ); + //### TODO: END ###// + + for ( int iB = 0; iB < vecNumFrameSizeConvBlocks[iChanCnt]; iB++ ) + { + const int iOffset = iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt]; + iUnused = opus_custom_encode ( CurOpusEncoder, + &vecsSendData[iOffset], + iClientFrameSizeSamples, + &vecvecbyCodedData[iChanCnt][0], + iCeltNumCodedBytes ); + + // send separate mix to current clients + vecChannels[iCurChanID].PrepAndSendPacket ( &Socket, vecvecbyCodedData[iChanCnt], iCeltNumCodedBytes ); + } + } + } + else + { for ( int iB = 0; iB < vecNumFrameSizeConvBlocks[iChanCnt]; iB++ ) { const int iOffset = iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt]; - iUnused = opus_custom_encode ( pCurOpusEncoder, - &vecsSendData[iOffset], - iClientFrameSizeSamples, - &vecvecbyCodedData[iChanCnt][0], - iCeltNumCodedBytes ); + memcpy ( &vecvecbyCodedData[iChanCnt][0], &vecsSendData[iOffset], iCeltNumCodedBytes ); // send separate mix to current clients vecChannels[iCurChanID].PrepAndSendPacket ( &Socket, vecvecbyCodedData[iChanCnt], iCeltNumCodedBytes ); diff --git a/src/settings.cpp b/src/settings.cpp index af594c8bbe..daa2fd5736 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -605,7 +605,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, } // audio quality - if ( GetNumericIniSet ( IniXMLDocument, "client", "audioquality", 0, 2 /* AQ_HIGH */, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "audioquality", 0, 3 /* AQ_RAW */, iValue ) ) { pClient->SetAudioQuality ( static_cast ( iValue ) ); } diff --git a/src/util.h b/src/util.h index e83e1f764a..ac13d8c37d 100644 --- a/src/util.h +++ b/src/util.h @@ -505,7 +505,8 @@ enum EAudioQuality // used for settings and the comobo box index -> enum values must be fixed! AQ_LOW = 0, AQ_NORMAL = 1, - AQ_HIGH = 2 + AQ_HIGH = 2, + AQ_RAW = 3 }; // Get data status enum --------------------------------------------------------