From 53f50070a7bd4cd7107ee83578d2f307faf6b843 Mon Sep 17 00:00:00 2001 From: Volker Fischer Date: Sat, 28 Mar 2020 16:27:45 +0100 Subject: [PATCH] some more work for 64 samples frame size support --- src/buffer.cpp | 32 +++++++++--------- src/buffer.h | 41 ++++++++++++++++++++--- src/channel.cpp | 7 ++++ src/client.cpp | 37 ++++++++++----------- src/clientsettingsdlg.cpp | 9 +++++ src/global.h | 22 ++++++++----- src/protocol.cpp | 3 +- src/server.cpp | 69 ++++++++++++++++++++------------------- src/util.h | 7 ++-- 9 files changed, 139 insertions(+), 88 deletions(-) diff --git a/src/buffer.cpp b/src/buffer.cpp index ef9f278f..dd0dcdff 100755 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -98,16 +98,16 @@ CNetBufWithStats::CNetBufWithStats() : // Avoid the buffer length 1 because we do not have a solution for a // sample rate offset correction. Caused by the jitter we usually get bad // performance with just one buffer. - viBufSizesForSim[0] = 2; - viBufSizesForSim[1] = 3; - viBufSizesForSim[2] = 4; - viBufSizesForSim[3] = 5; - viBufSizesForSim[4] = 6; - viBufSizesForSim[5] = 7; - viBufSizesForSim[6] = 8; - viBufSizesForSim[7] = 9; - viBufSizesForSim[8] = 10; - viBufSizesForSim[9] = 11; + viBufSizesForSim[0] = 2; + viBufSizesForSim[1] = 3; + viBufSizesForSim[2] = 4; + viBufSizesForSim[3] = 5; + viBufSizesForSim[4] = 6; + viBufSizesForSim[5] = 7; + viBufSizesForSim[6] = 8; + viBufSizesForSim[7] = 9; + viBufSizesForSim[8] = 10; + viBufSizesForSim[9] = 11; // set all simulation buffers in simulation mode for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ ) @@ -276,11 +276,9 @@ void CNetBufWithStats::UpdateAutoSetting() // the current jitter buffer size significantly. // For the initialization phase, use lower weight values to get faster // adaptation. - // Note that the following definitions of the weigh constants assume a block - // size of 128 samples at a sampling rate of 48 kHz. - double dWeightUp = 0.999995; - double dWeightDown = 0.9999; - const double dHysteresisValue = 0.1; + double dWeightUp = IIR_WEIGTH_UP_NORMAL; + double dWeightDown = IIR_WEIGTH_DOWN_NORMAL; + const double dHysteresisValue = FILTER_DECISION_HYSTERESIS; bool bUseFastAdaptation = false; // check for initialization phase @@ -304,8 +302,8 @@ void CNetBufWithStats::UpdateAutoSetting() if ( bUseFastAdaptation ) { // overwrite weigth values with lower values - dWeightUp = 0.9995; - dWeightDown = 0.999; + dWeightUp = IIR_WEIGTH_UP_FAST; + dWeightDown = IIR_WEIGTH_DOWN_FAST; } // apply non-linear IIR filter diff --git a/src/buffer.h b/src/buffer.h index 67658760..a1857649 100755 --- a/src/buffer.h +++ b/src/buffer.h @@ -29,9 +29,15 @@ /* Definitions ****************************************************************/ -// each regular buffer access lead to a count for put and get, assuming 2.66 ms -// blocks we have 15 s / 2.66 ms * 2 = approx. 11000 -#define MAX_STATISTIC_COUNT 11000 +// number of simulation network jitter buffers for evaluating the statistic +// NOTE If you want to change this number, the code has to modified, too! +#define NUM_STAT_SIMULATION_BUFFERS 10 + +// hysteresis for buffer size decision to avoid fast changes if close to the bound +#define FILTER_DECISION_HYSTERESIS 0.1 + + +// TODO the following values must be adjusted for 64 samples frame size: // definition of the upper error bound of the jitter buffers #define ERROR_RATE_BOUND 0.001 @@ -41,8 +47,33 @@ // size state #define UP_MAX_ERROR_BOUND 0.01 -// number of simulation network jitter buffers for evaluating the statistic -#define NUM_STAT_SIMULATION_BUFFERS 10 + +// #if ( SYSTEM_FRAME_SIZE_SAMPLES == 64 ) +// // each regular buffer access lead to a count for put and get, assuming 2.66 ms +// // blocks we have 15 s / 1.33 ms * 2 = approx. 22500 +// #define MAX_STATISTIC_COUNT 22500 +// +// // convert numbers from 128 samples case using http://www.tsdconseil.fr/tutos/tuto-iir1-en.pdf +// // and https://octave-online.net: +// // gamma = 1-exp(-Ts/tau) +// // after some calculations we get: x=0.999995;1-exp(64/128*log(1-x)) +// // TEST x=0.999995;1-exp(128/64*log(1-x)) +// # define IIR_WEIGTH_UP_NORMAL 0.999999999975 // 0.997763932022 +// # define IIR_WEIGTH_DOWN_NORMAL 0.99999999 // 0.99 +// # define IIR_WEIGTH_UP_FAST 0.99999975 // 0.97763932022 +// # define IIR_WEIGTH_DOWN_FAST 0.999999 // 0.9683772233 +// #else +// each regular buffer access lead to a count for put and get, assuming 2.66 ms +// blocks we have 15 s / 2.66 ms * 2 = approx. 11000 +# define MAX_STATISTIC_COUNT 11000 + +// Note that the following definitions of the weigh constants assume a block +// size of 128 samples at a sampling rate of 48 kHz. +# define IIR_WEIGTH_UP_NORMAL 0.999995 +# define IIR_WEIGTH_DOWN_NORMAL 0.9999 +# define IIR_WEIGTH_UP_FAST 0.9995 +# define IIR_WEIGTH_DOWN_FAST 0.999 +// #endif /* Classes ********************************************************************/ diff --git a/src/channel.cpp b/src/channel.cpp index 72938aad..8dcd597d 100755 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -346,10 +346,17 @@ void CChannel::OnNetTranspPropsReceived ( CNetworkTransportProps NetworkTranspor if ( bIsServer ) { // OPUS codec is the only supported codec right now +#if ( SYSTEM_FRAME_SIZE_SAMPLES == 64 ) + if ( NetworkTransportProps.eAudioCodingType != CT_OPUS64 ) + { + return; + } +#else if ( NetworkTransportProps.eAudioCodingType != CT_OPUS ) { return; } +#endif Mutex.lock(); { diff --git a/src/client.cpp b/src/client.cpp index 062a3279..a2ef9947 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -600,7 +600,11 @@ void CClient::OnSndCrdReinitRequest ( int iSndCrdResetType ) void CClient::Start() { // always use the OPUS codec +#if ( SYSTEM_FRAME_SIZE_SAMPLES == 64 ) + eAudioCompressionType = CT_OPUS64; +#else eAudioCompressionType = CT_OPUS; +#endif // init object Init(); @@ -996,10 +1000,11 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) { - if ( eAudioChannelConf == CC_MONO ) + // encode current audio frame + if ( ( eAudioCompressionType == CT_OPUS ) || + ( eAudioCompressionType == CT_OPUS64 ) ) { - // encode current audio frame - if ( eAudioCompressionType == CT_OPUS ) + if ( eAudioChannelConf == CC_MONO ) { opus_custom_encode ( OpusEncoderMono, &vecsStereoSndCrd[i * SYSTEM_FRAME_SIZE_SAMPLES], @@ -1007,11 +1012,7 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) &vecCeltData[0], iCeltNumCodedBytes ); } - } - else - { - // encode current audio frame - if ( eAudioCompressionType == CT_OPUS ) + else { opus_custom_encode ( OpusEncoderStereo, &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES], @@ -1048,9 +1049,10 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) // flag bIsInitializationPhase = false; - if ( eAudioChannelConf == CC_MONO ) + if ( ( eAudioCompressionType == CT_OPUS ) || + ( eAudioCompressionType == CT_OPUS64 ) ) { - if ( eAudioCompressionType == CT_OPUS ) + if ( eAudioChannelConf == CC_MONO ) { opus_custom_decode ( OpusDecoderMono, &vecbyNetwData[0], @@ -1058,10 +1060,7 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES], SYSTEM_FRAME_SIZE_SAMPLES ); } - } - else - { - if ( eAudioCompressionType == CT_OPUS ) + else { opus_custom_decode ( OpusDecoderStereo, &vecbyNetwData[0], @@ -1074,9 +1073,10 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) else { // lost packet - if ( eAudioChannelConf == CC_MONO ) + if ( ( eAudioCompressionType == CT_OPUS ) || + ( eAudioCompressionType == CT_OPUS64 ) ) { - if ( eAudioCompressionType == CT_OPUS ) + if ( eAudioChannelConf == CC_MONO ) { opus_custom_decode ( OpusDecoderMono, nullptr, @@ -1084,10 +1084,7 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES], SYSTEM_FRAME_SIZE_SAMPLES ); } - } - else - { - if ( eAudioCompressionType == CT_OPUS ) + else { opus_custom_decode ( OpusDecoderStereo, nullptr, diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 7c5e2740..91385ae5 100755 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -128,12 +128,21 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, "connection properties.
" "Three buffer sizes are supported:" "
    " +#if ( SYSTEM_FRAME_SIZE_SAMPLES == 64 ) + "
  • 64 samples: This is the preferred setting since it gives lowest " + "latency but does not work with all sound cards.
  • " + "
  • 128 samples: This setting should work on most of the available " + "sound cards.
  • " + "
  • 256 samples: This setting should only be used if only a very slow " + "computer or a slow internet connection is available.
  • " +#else "
  • 128 samples: This is the preferred setting since it gives lowest " "latency but does not work with all sound cards.
  • " "
  • 256 samples: This setting should work on most of the available " "sound cards.
  • " "
  • 512 samples: This setting should only be used if only a very slow " "computer or a slow internet connection is available.
  • " +#endif "
" "Some sound card driver do not allow the buffer delay to be changed " "from within the " ) + APP_NAME + diff --git a/src/global.h b/src/global.h index e50851eb..e6112f5a 100755 --- a/src/global.h +++ b/src/global.h @@ -95,6 +95,11 @@ LED bar: lbr // default oldest item to draw in history graph (days ago) #define DEFAULT_DAYS_HISTORY 60 +// System block size, this is the block size on which the audio coder works. +// All other block sizes must be a multiple of this size. +// Note that the UpdateAutoSetting() function assumes a value of 128. +#define SYSTEM_FRAME_SIZE_SAMPLES 128 + // default server address #define DEFAULT_SERVER_ADDRESS "jamulus.fischvolk.de" #define DEFAULT_SERVER_NAME "Central Server" @@ -104,16 +109,15 @@ LED bar: lbr #define LLCON_DOWNLOAD_URL "http://sourceforge.net/projects/llcon/files" // defined port number for client and server -#define LLCON_DEFAULT_PORT_NUMBER 22124 +#if ( SYSTEM_FRAME_SIZE_SAMPLES == 64 ) +# define LLCON_DEFAULT_PORT_NUMBER 22064 // different port number for 64 samples frame size version +#else +# define LLCON_DEFAULT_PORT_NUMBER 22124 +#endif // system sample rate (the sound card and audio coder works on this sample rate) #define SYSTEM_SAMPLE_RATE_HZ 48000 // Hz -// System block size, this is the block size on which the audio coder works. -// All other block sizes must be a multiple of this size. -// Note that the UpdateAutoSetting() function assumes a value of 128. -#define SYSTEM_FRAME_SIZE_SAMPLES 128 - #define SYSTEM_BLOCK_DURATION_MS_FLOAT \ ( static_cast ( SYSTEM_FRAME_SIZE_SAMPLES ) / \ SYSTEM_SAMPLE_RATE_HZ * 1000 ) @@ -121,9 +125,9 @@ LED bar: lbr // define the allowed audio frame size factors (since the // "SYSTEM_FRAME_SIZE_SAMPLES" is quite small, it may be that on some // computers a larger value is required) -#define FRAME_SIZE_FACTOR_PREFERRED 1 // 128 (for frame size 128) -#define FRAME_SIZE_FACTOR_DEFAULT 2 // 256 (for frame size 128) -#define FRAME_SIZE_FACTOR_SAFE 4 // 512 (for frame size 128) +#define FRAME_SIZE_FACTOR_PREFERRED 1 // 128 (for frame size 128), 64 (for frame size 64) +#define FRAME_SIZE_FACTOR_DEFAULT 2 // 256 (for frame size 128), 128 (for frame size 64) +#define FRAME_SIZE_FACTOR_SAFE 4 // 512 (for frame size 128), 256 (for frame size 64) // low complexity CELT encoder (if defined) #define USE_LOW_COMPLEXITY_CELT_ENC diff --git a/src/protocol.cpp b/src/protocol.cpp index 33ce501f..7120274d 100755 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1148,7 +1148,8 @@ bool CProtocol::EvaluateNetwTranspPropsMes ( const CVector& vecData ) // note that CT_NONE is not a valid setting but only used for server // initialization if ( ( iRecCodingType != CT_CELT ) && - ( iRecCodingType != CT_OPUS ) ) + ( iRecCodingType != CT_OPUS ) && + ( iRecCodingType != CT_OPUS64 ) ) { return true; } diff --git a/src/server.cpp b/src/server.cpp index d49c6954..85121dd8 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -30,9 +30,9 @@ CHighPrecisionTimer::CHighPrecisionTimer() { // add some error checking, the high precision timer implementation only - // supports 128 samples frame size at 48 kHz sampling rate -#if ( SYSTEM_FRAME_SIZE_SAMPLES != 128 ) -# error "Only system frame size of 128 samples is supported by this module" + // supports 64 and 128 samples frame size at 48 kHz sampling rate +#if ( SYSTEM_FRAME_SIZE_SAMPLES != 64 ) && ( SYSTEM_FRAME_SIZE_SAMPLES != 128 ) +# error "Only system frame size of 64 and 128 samples is supported by this module" #endif #if ( SYSTEM_SAMPLE_RATE_HZ != 48000 ) # error "Only a system sample rate of 48 kHz is supported by this module" @@ -41,14 +41,18 @@ CHighPrecisionTimer::CHighPrecisionTimer() // Since QT only supports a minimum timer resolution of 1 ms but for our // server we require a timer interval of 2.333 ms for 128 samples // frame size at 48 kHz sampling rate. - // To support this interval, we use a timer with 2 ms - // resolution and fire the actual frame timer if the error to the actual + // To support this interval, we use a timer with 2 ms resolution for 128 + // samples frame size and 1 ms resolution for 64 samples frame size. + // Then we fire the actual frame timer if the error to the actual // required interval is minimum. veciTimeOutIntervals.Init ( 3 ); - // for 128 sample frame size at 48 kHz sampling rate: + // for 128 sample frame size at 48 kHz sampling rate with 2 ms timer resolution: // actual intervals: 0.0 2.666 5.333 8.0 // quantized to 2 ms: 0 2 6 8 (0) + // for 64 sample frame size at 48 kHz sampling rate with 1 ms timer resolution: + // actual intervals: 0.0 1.333 2.666 4.0 + // quantized to 2 ms: 0 1 3 4 (0) veciTimeOutIntervals[0] = 0; veciTimeOutIntervals[1] = 1; veciTimeOutIntervals[2] = 0; @@ -64,8 +68,13 @@ void CHighPrecisionTimer::Start() iCurPosInVector = 0; iIntervalCounter = 0; - // start internal timer with 2 ms resolution +#if ( SYSTEM_FRAME_SIZE_SAMPLES == 64 ) + // start internal timer with 1 ms resolution for 64 samples frame size + Timer.start ( 1 ); +#else + // start internal timer with 2 ms resolution for 128 samples frame size Timer.start ( 2 ); +#endif } void CHighPrecisionTimer::Stop() @@ -762,26 +771,24 @@ JitterMeas.Measure(); bChannelIsNowDisconnected = true; } - // CELT decode received data stream + // OPUS decode received data stream if ( eGetStat == GS_BUFFER_OK ) { - if ( iCurNumAudChan == 1 ) + if ( ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) || + ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS64 ) ) { - // mono - if ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) + if ( iCurNumAudChan == 1 ) { + // mono opus_custom_decode ( OpusDecoderMono[iCurChanID], &vecbyCodedData[0], iCeltNumCodedBytes, &vecvecsData[i][0], SYSTEM_FRAME_SIZE_SAMPLES ); } - } - else - { - // stereo - if ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) + else { + // stereo opus_custom_decode ( OpusDecoderStereo[iCurChanID], &vecbyCodedData[0], iCeltNumCodedBytes, @@ -793,23 +800,21 @@ JitterMeas.Measure(); else { // lost packet - if ( iCurNumAudChan == 1 ) + if ( ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) || + ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS64 ) ) { - // mono - if ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) + if ( iCurNumAudChan == 1 ) { + // mono opus_custom_decode ( OpusDecoderMono[iCurChanID], nullptr, iCeltNumCodedBytes, &vecvecsData[i][0], SYSTEM_FRAME_SIZE_SAMPLES ); } - } - else - { - // stereo - if ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) + else { + // stereo opus_custom_decode ( OpusDecoderStereo[iCurChanID], nullptr, iCeltNumCodedBytes, @@ -866,12 +871,13 @@ JitterMeas.Measure(); const int iCeltNumCodedBytes = vecChannels[iCurChanID].GetNetwFrameSize(); - // OPUS/CELT encoding - if ( vecChannels[iCurChanID].GetNumAudioChannels() == 1 ) + // OPUS encoding + if ( ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) || + ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS64 ) ) { - // mono: - if ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) + if ( vecChannels[iCurChanID].GetNumAudioChannels() == 1 ) { + // mono: // TODO 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 @@ -885,12 +891,9 @@ opus_custom_encoder_ctl ( OpusEncoderMono[iCurChanID], &vecbyCodedData[0], iCeltNumCodedBytes ); } - } - else - { - // stereo: - if ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_OPUS ) + else { + // stereo: // TODO 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 diff --git a/src/util.h b/src/util.h index 98a6e6d2..ac646f3b 100755 --- a/src/util.h +++ b/src/util.h @@ -515,9 +515,10 @@ enum EAudChanConf enum EAudComprType { // used for protocol -> enum values must be fixed! - CT_NONE = 0, - CT_CELT = 1, - CT_OPUS = 2 + CT_NONE = 0, + CT_CELT = 1, + CT_OPUS = 2, + CT_OPUS64 = 3 // using OPUS with 64 samples frame size };