diff --git a/ChangeLog b/ChangeLog index 2d333c7c..7140882c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -31,12 +31,10 @@ -TODO use signal level meter class for meter calculation in the server (avoid doubling code), i.e., max calc and smoothing +TODO add new register message which contains version and, e.g., max number of clients TODO the new translation loading does not work on MacOS -TODO add new register message which contains version and, e.g., max number of clients - TODO https://github.com/corrados/jamulus/issues/341#issuecomment-647172946 - generate .qm on compile time with lrelease - download nsProcess.dll on Windows installer creation instead of put it in the repo diff --git a/src/channel.cpp b/src/channel.cpp index cfc4e118..ce2b7c8e 100755 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -35,7 +35,8 @@ CChannel::CChannel ( const bool bNIsServer ) : iFadeInCntMax ( FADE_IN_NUM_FRAMES_DBLE_FRAMESIZE ), bIsEnabled ( false ), bIsServer ( bNIsServer ), - iAudioFrameSizeSamples ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) + iAudioFrameSizeSamples ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ), + SignalLevelMeter ( false, 0.5 ) // server mode with mono out and faster smoothing { // reset network transport properties ResetNetworkTransportProperties(); @@ -568,6 +569,9 @@ EPutDataStat CChannel::PutAudioData ( const CVector& vecbyData, // init audio fade-in counter iFadeInCnt = 0; + + // init level meter + SignalLevelMeter.Reset(); } // reset time-out counter (note that this must be done after the @@ -658,6 +662,18 @@ void CChannel::PrepAndSendPacket ( CHighPrioSocket* pSocket, } } +double CChannel::UpdateAndGetLevelForMeterdB ( const CVector& vecsAudio, + const int iInSize, + const bool bIsStereoIn ) +{ + // update the signal level meter and immediately return the current value + SignalLevelMeter.Update ( vecsAudio, + iInSize, + bIsStereoIn ); + + return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); +} + int CChannel::GetUploadRateKbps() { const int iAudioSizeOut = iNetwFrameSizeFact * iAudioFrameSizeSamples; diff --git a/src/channel.h b/src/channel.h index 723d9ad4..cb81fc51 100755 --- a/src/channel.h +++ b/src/channel.h @@ -173,10 +173,11 @@ public: CNetworkTransportProps GetNetworkTransportPropsFromCurrentSettings(); - bool ChannelLevelsRequired() const { return bChannelLevelsRequired; } + bool ChannelLevelsRequired() const { return bChannelLevelsRequired; } - double GetPrevLevel() const { return dPrevLevel; } - void SetPrevLevel ( const double nPL ) { dPrevLevel = nPL; } + double UpdateAndGetLevelForMeterdB ( const CVector& vecsAudio, + const int iInSize, + const bool bIsStereoIn ); protected: bool ProtocolIsEnabled(); @@ -190,52 +191,51 @@ protected: iNetwFrameSizeFact = FRAME_SIZE_FACTOR_PREFERRED; iNetwFrameSize = CELT_MINIMUM_NUM_BYTES; iNumAudioChannels = 1; // mono - - dPrevLevel = 0.0; } // connection parameters - CHostAddress InetAddr; + CHostAddress InetAddr; // channel info - CChannelCoreInfo ChannelInfo; + CChannelCoreInfo ChannelInfo; // mixer and effect settings - CVector vecdGains; - CVector vecdPannings; + CVector vecdGains; + CVector vecdPannings; // network jitter-buffer - CNetBufWithStats SockBuf; - int iCurSockBufNumFrames; - bool bDoAutoSockBufSize; + CNetBufWithStats SockBuf; + int iCurSockBufNumFrames; + bool bDoAutoSockBufSize; // network output conversion buffer - CConvBuf ConvBuf; + CConvBuf ConvBuf; // network protocol - CProtocol Protocol; + CProtocol Protocol; - int iConTimeOut; - int iConTimeOutStartVal; - int iFadeInCnt; - int iFadeInCntMax; + int iConTimeOut; + int iConTimeOutStartVal; + int iFadeInCnt; + int iFadeInCntMax; - bool bIsEnabled; - bool bIsServer; + bool bIsEnabled; + bool bIsServer; - int iNetwFrameSizeFact; - int iNetwFrameSize; - int iAudioFrameSizeSamples; + int iNetwFrameSizeFact; + int iNetwFrameSize; + int iAudioFrameSizeSamples; - EAudComprType eAudioCompressionType; - int iNumAudioChannels; + EAudComprType eAudioCompressionType; + int iNumAudioChannels; - QMutex Mutex; - QMutex MutexSocketBuf; - QMutex MutexConvBuf; + QMutex Mutex; + QMutex MutexSocketBuf; + QMutex MutexConvBuf; - bool bChannelLevelsRequired; - double dPrevLevel; + bool bChannelLevelsRequired; + + CStereoSignalLevelMeter SignalLevelMeter; public slots: void OnSendProtMessage ( CVector vecMessage ); diff --git a/src/client.cpp b/src/client.cpp index 02c880ff..b8ad0edb 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -989,7 +989,9 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) // Transmit signal --------------------------------------------------------- // update stereo signal level meter - SignalLevelMeter.Update ( vecsStereoSndCrd ); + SignalLevelMeter.Update ( vecsStereoSndCrd, + iMonoBlockSizeSam, + true ); // add reverberation effect if activated if ( iReverbLevel != 0 ) diff --git a/src/client.h b/src/client.h index fb3be0be..0487e2ec 100755 --- a/src/client.h +++ b/src/client.h @@ -117,7 +117,7 @@ public: bool IsRunning() { return Sound.IsRunning(); } bool SetServerAddr ( QString strNAddr ); - double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeft(); } + double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } double GetLevelForMeterdBRight() { return SignalLevelMeter.GetLevelForMeterdBRight(); } bool GetAndResetbJitterBufferOKFlag(); diff --git a/src/server.cpp b/src/server.cpp index dde183c4..0f689327 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -1663,7 +1663,6 @@ bool CServer::CreateLevelsForAllConChannels ( const int i const CVector > vecvecsData, CVector& vecLevelsOut ) { - int i, j, k; bool bLevelsWereUpdated = false; // low frequency updates @@ -1675,44 +1674,15 @@ bool CServer::CreateLevelsForAllConChannels ( const int i // init return vector with zeros since we mix all channels on that vector vecLevelsOut.Reset ( 0 ); - for ( j = 0; j < iNumClients; j++ ) + for ( int j = 0; j < iNumClients; j++ ) { - // get a reference to the audio data - const CVector& vecsData = vecvecsData[j]; - - // Speed optimization: - // - we only make use of the negative values and ignore the positive ones (since - // int16 has range {-32768, 32767}) -> we do not need to call the fabs() function - // - we only evaluate every third sample - int16_t sMax = 0; - - if ( vecNumAudioChannels[j] == 1 ) - { - // mono - for ( i = 0; i < iServerFrameSizeSamples; i += 3 ) - { - sMax = std::min ( sMax, vecsData[i] ); - } - } - else - { - // stereo - for ( i = 0, k = 0; i < iServerFrameSizeSamples; i += 3, k += 6 ) // 2 * 3 = 6 -> stereo - { - // left/right channels separately - sMax = std::min ( sMax, vecsData[k] ); - sMax = std::min ( sMax, vecsData[k + 1] ); - } - } - - // smoothing - const int iChId = vecChanIDsCurConChan[j]; - const double dCurLevel = std::max ( -static_cast ( sMax ), vecChannels[iChId].GetPrevLevel() * 0.5 ); - vecChannels[iChId].SetPrevLevel ( dCurLevel ); - - // logarithmic measure - double dCurSigLevelForMeterdB = CStereoSignalLevelMeter::CalcLogResultForMeter ( dCurLevel ); + // update and get signal level for meter in dB for each channel + const double dCurSigLevelForMeterdB = vecChannels[vecChanIDsCurConChan[j]]. + UpdateAndGetLevelForMeterdB ( vecvecsData[j], + iServerFrameSizeSamples, + vecNumAudioChannels[j] > 1 ); + // map value to integer for transmission via the protocol (4 bit available) vecLevelsOut[j] = static_cast ( ceil ( dCurSigLevelForMeterdB ) ); } } diff --git a/src/util.cpp b/src/util.cpp index 5936d688..8b4b9667 100755 --- a/src/util.cpp +++ b/src/util.cpp @@ -28,11 +28,10 @@ /* Implementation *************************************************************/ // Input level meter implementation -------------------------------------------- -void CStereoSignalLevelMeter::Update ( const CVector& vecsAudio ) +void CStereoSignalLevelMeter::Update ( const CVector& vecsAudio, + const int iMonoBlockSizeSam, + const bool bIsStereoIn ) { - // get the stereo vector size - const int iStereoVecSize = vecsAudio.Size(); - // Get maximum of current block // // Speed optimization: @@ -43,32 +42,50 @@ void CStereoSignalLevelMeter::Update ( const CVector& vecsAudio ) // With these speed optimizations we might loose some information in // special cases but for the average music signals the following code // should give good results. - short sMaxL = 0; - short sMaxR = 0; + short sMinLOrMono = 0; + short sMinR = 0; - for ( int i = 0; i < iStereoVecSize; i += 6 ) // 2 * 3 = 6 -> stereo + if ( bIsStereoIn ) { - // left channel - sMaxL = std::min ( sMaxL, vecsAudio[i] ); + // stereo in + for ( int i = 0; i < 2 * iMonoBlockSizeSam; i += 6 ) // 2 * 3 = 6 -> stereo + { + // left (or mono) and right channel + sMinLOrMono = std::min ( sMinLOrMono, vecsAudio[i] ); + sMinR = std::min ( sMinR, vecsAudio[i + 1] ); + } - // right channel - sMaxR = std::min ( sMaxR, vecsAudio[i + 1] ); + // in case of mono out use minimum of both channels + if ( !bIsStereoOut ) + { + sMinLOrMono = std::min ( sMinLOrMono, sMinR ); + } + } + else + { + // mono in + for ( int i = 0; i < iMonoBlockSizeSam; i += 3 ) + { + sMinLOrMono = std::min ( sMinLOrMono, vecsAudio[i] ); + } } - dCurLevelL = UpdateCurLevel ( dCurLevelL, -sMaxL ); - dCurLevelR = UpdateCurLevel ( dCurLevelR, -sMaxR ); + // apply smoothing, if in stereo out mode, do this for two channels + dCurLevelLOrMono = UpdateCurLevel ( dCurLevelLOrMono, -sMinLOrMono ); + + if ( bIsStereoOut ) + { + dCurLevelR = UpdateCurLevel ( dCurLevelR, -sMinR ); + } } -double CStereoSignalLevelMeter::UpdateCurLevel ( double dCurLevel, - double dMax ) +double CStereoSignalLevelMeter::UpdateCurLevel ( double dCurLevel, + const double dMax ) { // decrease max with time if ( dCurLevel >= METER_FLY_BACK ) { -// TODO Calculate factor from sample rate and frame size (64 or 128 samples frame size). -// But tests with 128 and 64 samples frame size have shown that the meter fly back -// is ok for both numbers of samples frame size. - dCurLevel *= 0.97; + dCurLevel *= dSmoothingFactor; } else { diff --git a/src/util.h b/src/util.h index 29ebf378..60aa19bc 100755 --- a/src/util.h +++ b/src/util.h @@ -712,25 +712,35 @@ enum ESkillLevel class CStereoSignalLevelMeter { public: - CStereoSignalLevelMeter() { Reset(); } +// TODO Calculate smoothing factor from sample rate and frame size (64 or 128 samples frame size). +// But tests with 128 and 64 samples frame size have shown that the meter fly back +// is ok for both numbers of samples frame size with a factor of 0.97. + CStereoSignalLevelMeter ( const bool bNIsStereoOut = true, + const double dNSmoothingFactor = 0.97 ) : + dSmoothingFactor ( dNSmoothingFactor ), bIsStereoOut ( bNIsStereoOut ) { Reset(); } - void Update ( const CVector& vecsAudio ); - double GetLevelForMeterdBLeft() { return CalcLogResultForMeter ( dCurLevelL ); } - double GetLevelForMeterdBRight() { return CalcLogResultForMeter ( dCurLevelR ); } + void Update ( const CVector& vecsAudio, + const int iInSize, + const bool bIsStereoIn ); + + double GetLevelForMeterdBLeftOrMono() { return CalcLogResultForMeter ( dCurLevelLOrMono ); } + double GetLevelForMeterdBRight() { return CalcLogResultForMeter ( dCurLevelR ); } static double CalcLogResultForMeter ( const double& dLinearLevel ); void Reset() { - dCurLevelL = 0.0; - dCurLevelR = 0.0; + dCurLevelLOrMono = 0.0; + dCurLevelR = 0.0; } protected: - double UpdateCurLevel ( double dCurLevel, - double dMax ); + double UpdateCurLevel ( double dCurLevel, + const double dMax ); - double dCurLevelL; + double dCurLevelLOrMono; double dCurLevelR; + double dSmoothingFactor; + bool bIsStereoOut; };