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:
parent
938112d65d
commit
70cfdfc94c
8 changed files with 114 additions and 101 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
||||
|
|
55
src/util.cpp
55
src/util.cpp
|
@ -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
|
||||
{
|
||||
|
|
22
src/util.h
22
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<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;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue