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 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

View file

@ -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<uint8_t>& 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<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()
{
const int iAudioSizeOut = iNetwFrameSizeFact * iAudioFrameSizeSamples;

View file

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

View file

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

View file

@ -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();

View file

@ -1663,7 +1663,6 @@ bool CServer::CreateLevelsForAllConChannels ( const int i
const CVector<CVector<int16_t> > vecvecsData,
CVector<uint16_t>& 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<int16_t>& 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<double> ( 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<uint16_t> ( ceil ( dCurSigLevelForMeterdB ) );
}
}

View file

@ -28,11 +28,10 @@
/* 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
//
// Speed optimization:
@ -43,32 +42,50 @@ void CStereoSignalLevelMeter::Update ( const CVector<short>& 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] );
// right channel
sMaxR = std::min ( sMaxR, vecsAudio[i + 1] );
// 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] );
}
dCurLevelL = UpdateCurLevel ( dCurLevelL, -sMaxL );
dCurLevelR = UpdateCurLevel ( dCurLevelR, -sMaxR );
// 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] );
}
}
// 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 )
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
{

View file

@ -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<short>& vecsAudio );
double GetLevelForMeterdBLeft() { return CalcLogResultForMeter ( dCurLevelL ); }
void Update ( const CVector<short>& 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;
dCurLevelLOrMono = 0.0;
dCurLevelR = 0.0;
}
protected:
double UpdateCurLevel ( double dCurLevel,
double dMax );
const double dMax );
double dCurLevelL;
double dCurLevelLOrMono;
double dCurLevelR;
double dSmoothingFactor;
bool bIsStereoOut;
};