speed optimizations

This commit is contained in:
Volker Fischer 2009-02-14 00:46:58 +00:00
parent 85d37b53b5
commit 55c680e6fd
7 changed files with 186 additions and 172 deletions

View file

@ -27,7 +27,8 @@
/* Implementation *************************************************************/
CClient::CClient ( const quint16 iPortNumber ) : bRun ( false ),
iSndCrdBlockSizeSam ( MIN_SND_CRD_BLOCK_SIZE_SAMPLES ),
iSndCrdMonoBlockSizeSam ( MIN_SND_CRD_BLOCK_SIZE_SAMPLES ),
iSndCrdStereoBlockSizeSam ( 2 * MIN_SND_CRD_BLOCK_SIZE_SAMPLES ),
Sound ( MIN_SND_CRD_BLOCK_SIZE_SAMPLES * 2 /* stereo */ ),
Socket ( &Channel, iPortNumber ),
iAudioInFader ( AUD_FADER_IN_MAX / 2 ),
@ -172,30 +173,27 @@ void CClient::OnProtocolStatus ( bool bOk )
void CClient::Init()
{
// set block size (in samples)
iBlockSizeSam = MIN_BLOCK_SIZE_SAMPLES;
iMonoBlockSizeSam = MIN_BLOCK_SIZE_SAMPLES;
iStereoBlockSizeSam = 2 * MIN_BLOCK_SIZE_SAMPLES;
vecsAudioSndCrd.Init ( iSndCrdBlockSizeSam * 2 ); // stereo
vecdAudioSndCrdL.Init ( iSndCrdBlockSizeSam );
vecdAudioSndCrdR.Init ( iSndCrdBlockSizeSam );
vecsAudioSndCrd.Init ( iSndCrdStereoBlockSizeSam );
vecdAudioSndCrd.Init ( iSndCrdStereoBlockSizeSam );
vecdAudioL.Init ( iBlockSizeSam );
vecdAudioR.Init ( iBlockSizeSam );
vecdAudio.Init ( iStereoBlockSizeSam );
Sound.InitRecording();
Sound.InitPlayback();
// resample objects are always initialized with the input block size
// record
ResampleObjDownL.Init ( iSndCrdBlockSizeSam, SND_CRD_SAMPLE_RATE, SYSTEM_SAMPLE_RATE );
ResampleObjDownR.Init ( iSndCrdBlockSizeSam, SND_CRD_SAMPLE_RATE, SYSTEM_SAMPLE_RATE );
ResampleObjDown.Init ( iSndCrdMonoBlockSizeSam, SND_CRD_SAMPLE_RATE, SYSTEM_SAMPLE_RATE );
// playback
ResampleObjUpL.Init ( iBlockSizeSam, SYSTEM_SAMPLE_RATE, SND_CRD_SAMPLE_RATE );
ResampleObjUpR.Init ( iBlockSizeSam, SYSTEM_SAMPLE_RATE, SND_CRD_SAMPLE_RATE );
ResampleObjUp.Init ( iMonoBlockSizeSam, SYSTEM_SAMPLE_RATE, SND_CRD_SAMPLE_RATE );
// init network buffers
vecsNetwork.Init ( iBlockSizeSam );
vecdNetwData.Init ( iBlockSizeSam );
vecsNetwork.Init ( iMonoBlockSizeSam );
vecdNetwData.Init ( iMonoBlockSizeSam );
// init moving average buffer for response time evaluation
RespTimeMoAvBuf.Init ( LEN_MOV_AV_RESPONSE );
@ -208,7 +206,7 @@ void CClient::Init()
void CClient::run()
{
int i, iInCnt;
int i, j;
// Set thread priority (The working thread should have a higher
// priority than the GUI)
@ -242,7 +240,7 @@ void CClient::run()
}
// runtime phase ------------------------------------------------------------
// runtime phase -----------------------------------------------------------
// enable channel
Channel.SetEnable ( true );
@ -261,21 +259,17 @@ void CClient::run()
PostWinMessage ( MS_SOUND_IN, MUL_COL_LED_GREEN );
}
// copy data from one stereo buffer in two separate buffers
iInCnt = 0;
for ( i = 0; i < iSndCrdBlockSizeSam; i++ )
// convert data from short to double
for ( i = 0; i < iSndCrdStereoBlockSizeSam; i++ )
{
vecdAudioSndCrdL[i] = (double) vecsAudioSndCrd[iInCnt++];
vecdAudioSndCrdR[i] = (double) vecsAudioSndCrd[iInCnt++];
vecdAudioSndCrd[i] = (double) vecsAudioSndCrd[i];
}
// resample data for each channel seaparately
ResampleObjDownL.Resample ( vecdAudioSndCrdL, vecdAudioL );
ResampleObjDownR.Resample ( vecdAudioSndCrdR, vecdAudioR );
ResampleObjDown.Resample ( vecdAudioSndCrd, vecdAudio );
// update signal level meters
SignalLevelMeterL.Update ( vecdAudioL );
SignalLevelMeterR.Update ( vecdAudioR );
// update stereo signal level meter
SignalLevelMeter.Update ( vecdAudio );
// add reverberation effect if activated
if ( iReverbLevel != 0 )
@ -285,44 +279,47 @@ void CClient::run()
if ( bReverbOnLeftChan )
{
for ( i = 0; i < iBlockSizeSam; i++ )
for ( i = 0; i < iStereoBlockSizeSam; i += 2 )
{
// left channel
vecdAudioL[i] +=
dRevLev * AudioReverb.ProcessSample ( vecdAudioL[i] );
vecdAudio[i] +=
dRevLev * AudioReverb.ProcessSample ( vecdAudio[i] );
}
}
else
{
for ( i = 0; i < iBlockSizeSam; i++ )
for ( i = 1; i < iStereoBlockSizeSam; i += 2 )
{
// right channel
vecdAudioR[i] +=
dRevLev * AudioReverb.ProcessSample ( vecdAudioR[i] );
vecdAudio[i] +=
dRevLev * AudioReverb.ProcessSample ( vecdAudio[i] );
}
}
}
// mix both signals depending on the fading setting
const int iMiddleOfFader = AUD_FADER_IN_MAX / 2;
const int iMiddleOfFader = AUD_FADER_IN_MAX / 2;
const double dAttFact =
(double) ( iMiddleOfFader - abs ( iMiddleOfFader - iAudioInFader ) ) /
iMiddleOfFader;
for ( i = 0; i < iBlockSizeSam; i++ )
if ( iAudioInFader > iMiddleOfFader )
{
double dMixedSignal;
if ( iAudioInFader > iMiddleOfFader )
for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 )
{
dMixedSignal = vecdAudioL[i] + dAttFact * vecdAudioR[i];
// attenuation on right channel
vecsNetwork[i] =
Double2Short ( vecdAudio[j] + dAttFact * vecdAudio[j + 1] );
}
else
}
else
{
for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 )
{
dMixedSignal = vecdAudioR[i] + dAttFact * vecdAudioL[i];
// attenuation on left channel
vecsNetwork[i] =
Double2Short ( vecdAudio[j + 1] + dAttFact * vecdAudio[j] );
}
vecsNetwork[i] = Double2Short ( dMixedSignal );
}
// send it through the network
@ -344,7 +341,7 @@ void CClient::run()
// fid=fopen('v.dat','r');x=fread(fid,'int16');fclose(fid);
static FILE* pFileDelay = fopen("v.dat", "wb");
short sData[2];
for (i = 0; i < iBlockSizeSam; i++)
for (i = 0; i < iMonoBlockSizeSam; i++)
{
sData[0] = (short) vecdNetwData[i];
fwrite(&sData, size_t(2), size_t(1), pFileDelay);
@ -356,30 +353,24 @@ fflush(pFileDelay);
if ( Channel.IsConnected() )
{
// write mono input signal in both sound-card channels
for ( i = 0; i < iBlockSizeSam; i++ )
for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 )
{
vecdAudioL[i] = vecdAudioR[i] = vecdNetwData[i];
vecdAudio[j] = vecdAudio[j + 1] = vecdNetwData[i];
}
}
else
{
// if not connected, clear data
for ( i = 0; i < iBlockSizeSam; i++ )
{
vecdAudioL[i] = vecdAudioR[i] = 0.0;
}
vecdAudio.Reset ( 0.0 );
}
// resample data for each channel separately
ResampleObjUpL.Resample ( vecdAudioL, vecdAudioSndCrdL );
ResampleObjUpR.Resample ( vecdAudioR, vecdAudioSndCrdR );
// resample data
ResampleObjUp.Resample ( vecdAudio, vecdAudioSndCrd );
// copy data from one stereo buffer in two separate buffers
iInCnt = 0;
for ( i = 0; i < iSndCrdBlockSizeSam; i++ )
// convert data from double to short type
for ( i = 0; i < iSndCrdStereoBlockSizeSam; i++ )
{
vecsAudioSndCrd[iInCnt++] = Double2Short ( vecdAudioSndCrdL[i] );
vecsAudioSndCrd[iInCnt++] = Double2Short ( vecdAudioSndCrdR[i] );
vecsAudioSndCrd[i] = Double2Short ( vecdAudioSndCrd[i] );
}
// play the new block
@ -404,8 +395,7 @@ fflush(pFileDelay);
Sound.Close();
// reset current signal level and LEDs
SignalLevelMeterL.Reset();
SignalLevelMeterR.Reset();
SignalLevelMeter.Reset();
PostWinMessage ( MS_RESET_ALL, 0 );
}

View file

@ -68,8 +68,8 @@ public:
bool Stop();
bool IsRunning() { return bRun; }
bool SetServerAddr ( QString strNAddr );
double MicLevelL() { return SignalLevelMeterL.MicLevel(); }
double MicLevelR() { return SignalLevelMeterR.MicLevel(); }
double MicLevelL() { return SignalLevelMeter.MicLevelLeft(); }
double MicLevelR() { return SignalLevelMeter.MicLevelRight(); }
bool IsConnected() { return Channel.IsConnected(); }
/* We want to return the standard deviation. For that we need to calculate
@ -143,59 +143,54 @@ public:
// settings
QString strIPAddress;
QString strName;
QString strIPAddress;
QString strName;
protected:
virtual void run();
void UpdateTimeResponseMeasurement();
void UpdateSocketBufferSize();
virtual void run();
void UpdateTimeResponseMeasurement();
void UpdateSocketBufferSize();
// only one channel is needed for client application
CChannel Channel;
bool bDoAutoSockBufSize;
CChannel Channel;
bool bDoAutoSockBufSize;
CSocket Socket;
CSound Sound;
CSignalLevelMeter SignalLevelMeterL;
CSignalLevelMeter SignalLevelMeterR;
CSocket Socket;
CSound Sound;
CStereoSignalLevelMeter SignalLevelMeter;
bool bRun;
CVector<double> vecdNetwData;
bool bRun;
CVector<double> vecdNetwData;
int iAudioInFader;
bool bReverbOnLeftChan;
int iReverbLevel;
CAudioReverb AudioReverb;
int iAudioInFader;
bool bReverbOnLeftChan;
int iReverbLevel;
CAudioReverb AudioReverb;
int iSndCrdBlockSizeSam;
int iBlockSizeSam;
int iSndCrdMonoBlockSizeSam;
int iSndCrdStereoBlockSizeSam;
int iMonoBlockSizeSam;
int iStereoBlockSizeSam;
int iNetwBufSizeFactIn;
int iNetwBufSizeFactIn;
bool bOpenChatOnNewMessage;
bool bOpenChatOnNewMessage;
CVector<short> vecsAudioSndCrd;
CVector<double> vecdAudioSndCrdL;
CVector<double> vecdAudioSndCrdR;
CVector<double> vecdAudioL;
CVector<double> vecdAudioR;
CVector<short> vecsNetwork;
CVector<short> vecsAudioSndCrd;
CVector<double> vecdAudioSndCrd;
CVector<double> vecdAudio;
CVector<short> vecsNetwork;
// resample objects
CAudioResample ResampleObjDownL; // left channel
CAudioResample ResampleObjDownR; // right channel
CAudioResample ResampleObjUpL; // left channel
CAudioResample ResampleObjUpR; // right channel
CStereoAudioResample ResampleObjDown;
CStereoAudioResample ResampleObjUp;
// for ping measurement and standard deviation of audio interface
CPreciseTime PreciseTime;
CPreciseTime PreciseTime;
// debugging, evaluating
CMovingAv<double> RespTimeMoAvBuf;
int TimeLastBlock;
CMovingAv<double> RespTimeMoAvBuf;
int TimeLastBlock;
public slots:
void OnSendProtMessage ( CVector<uint8_t> vecMessage );

View file

@ -1,5 +1,5 @@
/******************************************************************************\
* Copyright (c) 2004-2008
* Copyright (c) 2004-2009
*
* Author(s):
* Volker Fischer
@ -92,10 +92,6 @@
// (just search for the tag in the entire code)
#define MAX_NUM_CHANNELS 6 // max number channels for server
// sample rate offset estimation algorithm
// time interval for sample rate offset estimation
#define TIME_INT_SAM_OFFS_EST 60 // s
// length of the moving average buffer for response time measurement
#define TIME_MOV_AV_RESPONSE 30 // seconds
#define LEN_MOV_AV_RESPONSE ( TIME_MOV_AV_RESPONSE * 1000 / MIN_BLOCK_DURATION_MS )

View file

@ -86,7 +86,7 @@ int CResample::Resample ( CVector<double>& vecdInput,
// convolution
double dy1 = 0.0;
double dy2 = 0.0;
for (int i = 0; i < NUM_TAPS_PER_PHASE1; i++)
for ( int i = 0; i < NUM_TAPS_PER_PHASE1; i++ )
{
dy1 += fResTaps1[ip1 * INTERP_DECIM_I_D1 + i] * vecdIntBuff[in1 - i];
dy2 += fResTaps1[ip2 * INTERP_DECIM_I_D1 + i] * vecdIntBuff[in2 - i];
@ -134,10 +134,10 @@ void CResample::Init ( const int iNewInputBlockSize )
/******************************************************************************\
* Audio Resampler *
* Stereo Audio Resampler *
\******************************************************************************/
void CAudioResample::Resample ( CVector<double>& vecdInput,
CVector<double>& vecdOutput )
void CStereoAudioResample::Resample ( CVector<double>& vecdInput,
CVector<double>& vecdOutput )
{
int j;
@ -148,50 +148,58 @@ void CAudioResample::Resample ( CVector<double>& vecdInput,
}
else
{
const int iTwoTimesNumTaps = 2 * iNumTaps;
/* move old data from the end to the history part of the buffer and
add new data (shift register) */
// shift old values
int iMovLen = iInputBlockSize;
for ( j = 0; j < iNumTaps; j++ )
int iMovLen = iStereoInputBlockSize;
for ( j = 0; j < iTwoTimesNumTaps; j++ )
{
vecdIntBuff[j] = vecdIntBuff[iMovLen++];
}
// add new block of data
int iBlockEnd = iNumTaps;
for ( j = 0; j < iInputBlockSize; j++ )
int iBlockEnd = iTwoTimesNumTaps;
for ( j = 0; j < iStereoInputBlockSize; j++ )
{
vecdIntBuff[iBlockEnd++] = vecdInput[j];
}
// main loop
for ( j = 0; j < iOutputBlockSize; j++ )
for ( j = 0; j < iMonoOutputBlockSize; j++ )
{
// calculate filter phase
const int ip = (int) ( j * iI / dRation ) % iI;
// sample position in input vector
const int in = (int) ( j / dRation ) + iNumTaps - 1;
// sample position in stereo input vector
const int in = 2 * ( (int) ( j / dRation ) + iNumTaps - 1 );
// convolution
double dy = 0.0;
double dyL = 0.0;
double dyR = 0.0;
for ( int i = 0; i < iNumTaps; i++ )
{
dy += pFiltTaps[ip + i * iI] * vecdIntBuff[in - i];
const double dCurFiltTap = pFiltTaps[ip + i * iI];
const int iCurSamplePos = in - 2 * i;
dyL += dCurFiltTap * vecdIntBuff[iCurSamplePos];
dyR += dCurFiltTap * vecdIntBuff[iCurSamplePos + 1];
}
vecdOutput[j] = dy;
vecdOutput[2 * j] = dyL;
vecdOutput[2 * j + 1] = dyR;
}
}
}
void CAudioResample::Init ( const int iNewInputBlockSize,
const int iFrom,
const int iTo )
void CStereoAudioResample::Init ( const int iNewMonoInputBlockSize,
const int iFrom,
const int iTo )
{
dRation = ( (double) iTo ) / iFrom;
iInputBlockSize = iNewInputBlockSize;
iOutputBlockSize = (int) ( iInputBlockSize * dRation );
dRation = ( (double) iTo ) / iFrom;
iStereoInputBlockSize = 2 * iNewMonoInputBlockSize;
iMonoOutputBlockSize = (int) ( iNewMonoInputBlockSize * dRation );
// set correct parameters
if ( iFrom == SND_CRD_SAMPLE_RATE ) // downsampling case
@ -205,22 +213,22 @@ void CAudioResample::Init ( const int iNewInputBlockSize,
break;
case ( SND_CRD_SAMPLE_RATE * 7 / 12 ): // 48 kHz to 28 kHz
pFiltTaps = fResTaps12_7;
iNumTaps = INTERP_I_12_7 * NUM_TAPS_PER_PHASE12_7;
iI = DECIM_D_12_7;
pFiltTaps = fResTaps12_7;
iNumTaps = INTERP_I_12_7 * NUM_TAPS_PER_PHASE12_7;
iI = DECIM_D_12_7;
break;
case ( SND_CRD_SAMPLE_RATE * 2 / 3 ): // 48 kHz to 32 kHz
pFiltTaps = fResTaps3_2;
iNumTaps = INTERP_I_3_2 * NUM_TAPS_PER_PHASE3_2;
iI = DECIM_D_3_2;
pFiltTaps = fResTaps3_2;
iNumTaps = INTERP_I_3_2 * NUM_TAPS_PER_PHASE3_2;
iI = DECIM_D_3_2;
break;
case SND_CRD_SAMPLE_RATE: // 48 kHz to 48 kHz
// no resampling needed
pFiltTaps = NULL;
iNumTaps = 0;
iI = 1;
pFiltTaps = NULL;
iNumTaps = 0;
iI = 1;
break;
default:
@ -234,21 +242,21 @@ void CAudioResample::Init ( const int iNewInputBlockSize,
switch ( iFrom )
{
case ( SND_CRD_SAMPLE_RATE / 2 ): // 24 kHz to 48 kHz
pFiltTaps = fResTaps2;
iNumTaps = DECIM_D_2 * NUM_TAPS_PER_PHASE2;
iI = INTERP_I_2;
pFiltTaps = fResTaps2;
iNumTaps = DECIM_D_2 * NUM_TAPS_PER_PHASE2;
iI = INTERP_I_2;
break;
case ( SND_CRD_SAMPLE_RATE * 7 / 12 ): // 28 kHz to 48 kHz
pFiltTaps = fResTaps12_7;
iNumTaps = DECIM_D_12_7 * NUM_TAPS_PER_PHASE12_7;
iI = INTERP_I_12_7;
pFiltTaps = fResTaps12_7;
iNumTaps = DECIM_D_12_7 * NUM_TAPS_PER_PHASE12_7;
iI = INTERP_I_12_7;
break;
case ( SND_CRD_SAMPLE_RATE * 2 / 3 ): // 32 kHz to 48 kHz
pFiltTaps = fResTaps3_2;
iNumTaps = DECIM_D_3_2 * NUM_TAPS_PER_PHASE3_2;
iI = INTERP_I_3_2;
pFiltTaps = fResTaps3_2;
iNumTaps = DECIM_D_3_2 * NUM_TAPS_PER_PHASE3_2;
iI = INTERP_I_3_2;
break;
default:
@ -258,6 +266,8 @@ void CAudioResample::Init ( const int iNewInputBlockSize,
}
}
// allocate memory for internal buffer, clear sample history
vecdIntBuff.Init ( iInputBlockSize + iNumTaps, 0.0 );
// allocate memory for internal buffer, clear sample history (we have
// to consider stereo data here -> two times the number of taps of
// additional memory is required)
vecdIntBuff.Init ( iStereoInputBlockSize + 2 * iNumTaps, 0.0 );
}

View file

@ -52,27 +52,27 @@ protected:
int iInputBlockSize;
};
class CAudioResample
class CStereoAudioResample
{
public:
CAudioResample() {}
virtual ~CAudioResample() {}
CStereoAudioResample() {}
virtual ~CStereoAudioResample() {}
void Init ( const int iNewInputBlockSize, const int iFrom, const int iTo );
void Init ( const int iNewMonoInputBlockSize, const int iFrom, const int iTo );
void Resample ( CVector<double>& vecdInput, CVector<double>& vecdOutput );
protected:
double dRation;
double dRation;
CVector<double> vecdIntBuff;
int iHistorySize;
CVector<double> vecdIntBuff;
int iHistorySize;
int iInputBlockSize;
int iOutputBlockSize;
int iStereoInputBlockSize;
int iMonoOutputBlockSize;
float* pFiltTaps;
int iNumTaps;
int iI;
float* pFiltTaps;
int iNumTaps;
int iI;
};

View file

@ -27,10 +27,10 @@
/* Implementation *************************************************************/
// Input level meter implementation --------------------------------------------
void CSignalLevelMeter::Update ( CVector<double>& vecdAudio )
void CStereoSignalLevelMeter::Update ( CVector<double>& vecdAudio )
{
// get the vector size
const int iVecSize = vecdAudio.Size();
// get the stereo vector size
const int iStereoVecSize = vecdAudio.Size();
// Get maximum of current block
//
@ -43,15 +43,29 @@ void CSignalLevelMeter::Update ( CVector<double>& vecdAudio )
// special cases but for the average music signals the following code
// should give good results.
//
double dMax = 0.0;
for ( int i = 0; i < iVecSize; i += 3 )
double dMaxL = 0.0;
double dMaxR = 0.0;
for ( int i = 0; i < iStereoVecSize; i += 6 ) // 2 * 3 = 6 -> stereo
{
if ( dMax < vecdAudio[i] )
// left channel
if ( dMaxL < vecdAudio[i] )
{
dMax = vecdAudio[i];
dMaxL = vecdAudio[i];
}
// right channel
if ( dMaxR < vecdAudio[i + 1] )
{
dMaxR = vecdAudio[i + 1];
}
}
dCurLevelL = UpdateCurLevel ( dCurLevelL, dMaxL );
dCurLevelR = UpdateCurLevel ( dCurLevelR, dMaxR );
}
double CStereoSignalLevelMeter::UpdateCurLevel ( double dCurLevel, const double& dMax )
{
// decrease max with time
if ( dCurLevel >= METER_FLY_BACK )
{
@ -66,13 +80,17 @@ void CSignalLevelMeter::Update ( CVector<double>& vecdAudio )
// update current level -> only use maximum
if ( dMax > dCurLevel )
{
dCurLevel = dMax;
return dMax;
}
else
{
return dCurLevel;
}
}
double CSignalLevelMeter::MicLevel()
double CStereoSignalLevelMeter::CalcLogResult ( const double& dLinearLevel )
{
const double dNormMicLevel = dCurLevel / _MAXSHORT;
const double dNormMicLevel = dLinearLevel / _MAXSHORT;
// logarithmic measure
if ( dNormMicLevel > 0 )

View file

@ -356,19 +356,24 @@ public slots:
/* Other Classes **************************************************************/
// Signal Level Meter ----------------------------------------------------------
class CSignalLevelMeter
// Stereo Signal Level Meter ---------------------------------------------------
class CStereoSignalLevelMeter
{
public:
CSignalLevelMeter() : dCurLevel ( 0.0 ) {}
virtual ~CSignalLevelMeter() {}
CStereoSignalLevelMeter() { Reset(); }
virtual ~CStereoSignalLevelMeter() {}
void Update ( CVector<double>& vecdAudio );
double MicLevel();
void Reset() { dCurLevel = 0.0; }
void Update ( CVector<double>& vecdAudio );
double MicLevelLeft() { return CalcLogResult ( dCurLevelL ); }
double MicLevelRight() { return CalcLogResult ( dCurLevelR ); }
void Reset() { dCurLevelL = 0.0; dCurLevelR = 0.0; }
protected:
double dCurLevel;
double CalcLogResult ( const double& dLinearLevel );
double UpdateCurLevel ( double dCurLevel, const double& dMax );
double dCurLevelL;
double dCurLevelR;
};
class CHostAddress
@ -409,7 +414,7 @@ class CAudioReverb
public:
CAudioReverb ( const double rT60 = (double) 5.0 );
void Clear();
void Clear();
double ProcessSample ( const double input );
protected: