use signal level meter class for meter calculation in the server (avoid doubling code), i.e., max calc and smoothing

This commit is contained in:
Volker Fischer 2020-06-25 17:38:25 +02:00
parent 938112d65d
commit 70cfdfc94c
8 changed files with 114 additions and 101 deletions

View file

@ -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 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 TODO https://github.com/corrados/jamulus/issues/341#issuecomment-647172946
- generate .qm on compile time with lrelease - generate .qm on compile time with lrelease
- download nsProcess.dll on Windows installer creation instead of put it in the repo - download nsProcess.dll on Windows installer creation instead of put it in the repo

View file

@ -35,7 +35,8 @@ CChannel::CChannel ( const bool bNIsServer ) :
iFadeInCntMax ( FADE_IN_NUM_FRAMES_DBLE_FRAMESIZE ), iFadeInCntMax ( FADE_IN_NUM_FRAMES_DBLE_FRAMESIZE ),
bIsEnabled ( false ), bIsEnabled ( false ),
bIsServer ( bNIsServer ), 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 // reset network transport properties
ResetNetworkTransportProperties(); ResetNetworkTransportProperties();
@ -568,6 +569,9 @@ EPutDataStat CChannel::PutAudioData ( const CVector<uint8_t>& vecbyData,
// init audio fade-in counter // init audio fade-in counter
iFadeInCnt = 0; iFadeInCnt = 0;
// init level meter
SignalLevelMeter.Reset();
} }
// reset time-out counter (note that this must be done after the // 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<short>& 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() int CChannel::GetUploadRateKbps()
{ {
const int iAudioSizeOut = iNetwFrameSizeFact * iAudioFrameSizeSamples; const int iAudioSizeOut = iNetwFrameSizeFact * iAudioFrameSizeSamples;

View file

@ -175,8 +175,9 @@ public:
bool ChannelLevelsRequired() const { return bChannelLevelsRequired; } bool ChannelLevelsRequired() const { return bChannelLevelsRequired; }
double GetPrevLevel() const { return dPrevLevel; } double UpdateAndGetLevelForMeterdB ( const CVector<short>& vecsAudio,
void SetPrevLevel ( const double nPL ) { dPrevLevel = nPL; } const int iInSize,
const bool bIsStereoIn );
protected: protected:
bool ProtocolIsEnabled(); bool ProtocolIsEnabled();
@ -190,8 +191,6 @@ protected:
iNetwFrameSizeFact = FRAME_SIZE_FACTOR_PREFERRED; iNetwFrameSizeFact = FRAME_SIZE_FACTOR_PREFERRED;
iNetwFrameSize = CELT_MINIMUM_NUM_BYTES; iNetwFrameSize = CELT_MINIMUM_NUM_BYTES;
iNumAudioChannels = 1; // mono iNumAudioChannels = 1; // mono
dPrevLevel = 0.0;
} }
// connection parameters // connection parameters
@ -235,7 +234,8 @@ protected:
QMutex MutexConvBuf; QMutex MutexConvBuf;
bool bChannelLevelsRequired; bool bChannelLevelsRequired;
double dPrevLevel;
CStereoSignalLevelMeter SignalLevelMeter;
public slots: public slots:
void OnSendProtMessage ( CVector<uint8_t> vecMessage ); void OnSendProtMessage ( CVector<uint8_t> vecMessage );

View file

@ -989,7 +989,9 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
// Transmit signal --------------------------------------------------------- // Transmit signal ---------------------------------------------------------
// update stereo signal level meter // update stereo signal level meter
SignalLevelMeter.Update ( vecsStereoSndCrd ); SignalLevelMeter.Update ( vecsStereoSndCrd,
iMonoBlockSizeSam,
true );
// add reverberation effect if activated // add reverberation effect if activated
if ( iReverbLevel != 0 ) if ( iReverbLevel != 0 )

View file

@ -117,7 +117,7 @@ public:
bool IsRunning() { return Sound.IsRunning(); } bool IsRunning() { return Sound.IsRunning(); }
bool SetServerAddr ( QString strNAddr ); bool SetServerAddr ( QString strNAddr );
double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeft(); } double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); }
double GetLevelForMeterdBRight() { return SignalLevelMeter.GetLevelForMeterdBRight(); } double GetLevelForMeterdBRight() { return SignalLevelMeter.GetLevelForMeterdBRight(); }
bool GetAndResetbJitterBufferOKFlag(); bool GetAndResetbJitterBufferOKFlag();

View file

@ -1663,7 +1663,6 @@ bool CServer::CreateLevelsForAllConChannels ( const int i
const CVector<CVector<int16_t> > vecvecsData, const CVector<CVector<int16_t> > vecvecsData,
CVector<uint16_t>& vecLevelsOut ) CVector<uint16_t>& vecLevelsOut )
{ {
int i, j, k;
bool bLevelsWereUpdated = false; bool bLevelsWereUpdated = false;
// low frequency updates // 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 // init return vector with zeros since we mix all channels on that vector
vecLevelsOut.Reset ( 0 ); vecLevelsOut.Reset ( 0 );
for ( j = 0; j < iNumClients; j++ ) for ( int j = 0; j < iNumClients; j++ )
{ {
// get a reference to the audio data // update and get signal level for meter in dB for each channel
const CVector<int16_t>& vecsData = vecvecsData[j]; const double dCurSigLevelForMeterdB = vecChannels[vecChanIDsCurConChan[j]].
UpdateAndGetLevelForMeterdB ( vecvecsData[j],
// Speed optimization: iServerFrameSizeSamples,
// - we only make use of the negative values and ignore the positive ones (since vecNumAudioChannels[j] > 1 );
// 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<double> ( sMax ), vecChannels[iChId].GetPrevLevel() * 0.5 );
vecChannels[iChId].SetPrevLevel ( dCurLevel );
// logarithmic measure
double dCurSigLevelForMeterdB = CStereoSignalLevelMeter::CalcLogResultForMeter ( dCurLevel );
// map value to integer for transmission via the protocol (4 bit available)
vecLevelsOut[j] = static_cast<uint16_t> ( ceil ( dCurSigLevelForMeterdB ) ); vecLevelsOut[j] = static_cast<uint16_t> ( ceil ( dCurSigLevelForMeterdB ) );
} }
} }

View file

@ -28,11 +28,10 @@
/* Implementation *************************************************************/ /* Implementation *************************************************************/
// Input level meter implementation -------------------------------------------- // Input level meter implementation --------------------------------------------
void CStereoSignalLevelMeter::Update ( const CVector<short>& vecsAudio ) void CStereoSignalLevelMeter::Update ( const CVector<short>& vecsAudio,
const int iMonoBlockSizeSam,
const bool bIsStereoIn )
{ {
// get the stereo vector size
const int iStereoVecSize = vecsAudio.Size();
// Get maximum of current block // Get maximum of current block
// //
// Speed optimization: // Speed optimization:
@ -43,32 +42,50 @@ void CStereoSignalLevelMeter::Update ( const CVector<short>& vecsAudio )
// With these speed optimizations we might loose some information in // With these speed optimizations we might loose some information in
// special cases but for the average music signals the following code // special cases but for the average music signals the following code
// should give good results. // should give good results.
short sMaxL = 0; short sMinLOrMono = 0;
short sMaxR = 0; short sMinR = 0;
for ( int i = 0; i < iStereoVecSize; i += 6 ) // 2 * 3 = 6 -> stereo if ( bIsStereoIn )
{ {
// left channel // stereo in
sMaxL = std::min ( sMaxL, vecsAudio[i] ); for ( int i = 0; i < 2 * iMonoBlockSizeSam; i += 6 ) // 2 * 3 = 6 -> stereo
{
// right channel // left (or mono) and right channel
sMaxR = std::min ( sMaxR, vecsAudio[i + 1] ); sMinLOrMono = std::min ( sMinLOrMono, vecsAudio[i] );
sMinR = std::min ( sMinR, vecsAudio[i + 1] );
} }
dCurLevelL = UpdateCurLevel ( dCurLevelL, -sMaxL ); // in case of mono out use minimum of both channels
dCurLevelR = UpdateCurLevel ( dCurLevelR, -sMaxR ); if ( !bIsStereoOut )
{
sMinLOrMono = std::min ( sMinLOrMono, sMinR );
}
}
else
{
// mono in
for ( int i = 0; i < iMonoBlockSizeSam; i += 3 )
{
sMinLOrMono = std::min ( sMinLOrMono, vecsAudio[i] );
}
}
// 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 CStereoSignalLevelMeter::UpdateCurLevel ( double dCurLevel,
double dMax ) const double dMax )
{ {
// decrease max with time // decrease max with time
if ( dCurLevel >= METER_FLY_BACK ) if ( dCurLevel >= METER_FLY_BACK )
{ {
// TODO Calculate factor from sample rate and frame size (64 or 128 samples frame size). dCurLevel *= dSmoothingFactor;
// 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;
} }
else else
{ {

View file

@ -712,25 +712,35 @@ enum ESkillLevel
class CStereoSignalLevelMeter class CStereoSignalLevelMeter
{ {
public: 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<short>& vecsAudio ); void Update ( const CVector<short>& vecsAudio,
double GetLevelForMeterdBLeft() { return CalcLogResultForMeter ( dCurLevelL ); } const int iInSize,
const bool bIsStereoIn );
double GetLevelForMeterdBLeftOrMono() { return CalcLogResultForMeter ( dCurLevelLOrMono ); }
double GetLevelForMeterdBRight() { return CalcLogResultForMeter ( dCurLevelR ); } double GetLevelForMeterdBRight() { return CalcLogResultForMeter ( dCurLevelR ); }
static double CalcLogResultForMeter ( const double& dLinearLevel ); static double CalcLogResultForMeter ( const double& dLinearLevel );
void Reset() void Reset()
{ {
dCurLevelL = 0.0; dCurLevelLOrMono = 0.0;
dCurLevelR = 0.0; dCurLevelR = 0.0;
} }
protected: protected:
double UpdateCurLevel ( double dCurLevel, double UpdateCurLevel ( double dCurLevel,
double dMax ); const double dMax );
double dCurLevelL; double dCurLevelLOrMono;
double dCurLevelR; double dCurLevelR;
double dSmoothingFactor;
bool bIsStereoOut;
}; };