From 71598931e2136375af2a28e5b869748fda36a302 Mon Sep 17 00:00:00 2001 From: Volker Fischer Date: Sat, 21 Feb 2009 17:37:15 +0000 Subject: [PATCH] some cleanup and preperations for callback based audio interface --- src/client.cpp | 340 +++++++++++++++++++++-------------------- src/client.h | 6 +- src/llconclientdlg.cpp | 36 ++--- 3 files changed, 199 insertions(+), 183 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 8487ee07..7918fe8c 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -170,6 +170,101 @@ void CClient::OnProtocolStatus ( bool bOk ) } } +void CClient::Start() +{ + // init object + try + { + Init(); + } + catch ( CGenErr generr ) + { + // TODO better error management -> should be catched in main thread + // problem: how to catch errors in a different thread...? + + // quick hack solution + QMessageBox::critical ( 0, APP_NAME, generr.GetErrorText(), "Quit", 0 ); + exit ( 0 ); + } + + // enable channel + Channel.SetEnable ( true ); + + // start the audio working thread with hightest possible priority + start ( QThread::TimeCriticalPriority ); +} + +void CClient::Stop() +{ + // set flag so that thread can leave the main loop + bRun = false; + + // give thread some time to terminate + wait ( 5000 ); + + // disable channel + Channel.SetEnable ( false ); + + // disable sound interface + Sound.Close(); + + // reset current signal level and LEDs + SignalLevelMeter.Reset(); + PostWinMessage ( MS_RESET_ALL, 0 ); +} + +void CClient::run() +{ + // Set thread priority (The working thread should have a higher + // priority than the GUI) +#ifdef _WIN32 + SetThreadPriority ( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ); +#else +/* + // set the process to realtime privs, taken from + // "http://www.gardena.net/benno/linux/audio" but does not seem to work, + // maybe a problem with user rights + struct sched_param schp; + memset ( &schp, 0, sizeof ( schp ) ); + schp.sched_priority = sched_get_priority_max ( SCHED_FIFO ); + sched_setscheduler ( 0, SCHED_FIFO, &schp ); +*/ +#endif + + // main loop of working thread + bRun = true; + while ( bRun ) + { + // get audio from sound card (blocking function) + if ( Sound.Read ( vecsAudioSndCrdStereo ) ) + { + PostWinMessage ( MS_SOUND_IN, MUL_COL_LED_RED ); + } + else + { + PostWinMessage ( MS_SOUND_IN, MUL_COL_LED_GREEN ); + } + + // process audio data + ProcessAudioData ( vecsAudioSndCrdStereo ); + + // play the new block + if ( Sound.Write ( vecsAudioSndCrdStereo ) ) + { + PostWinMessage ( MS_SOUND_OUT, MUL_COL_LED_RED ); + } + else + { + PostWinMessage ( MS_SOUND_OUT, MUL_COL_LED_GREEN ); + } + } +} + + + + + + void CClient::Init() { // set block size (in samples) @@ -205,149 +300,98 @@ void CClient::Init() AudioReverb.Clear(); } -void CClient::run() +void CClient::ProcessAudioData ( CVector& vecsStereoSndCrd ) { int i, j; - // Set thread priority (The working thread should have a higher - // priority than the GUI) -#ifdef _WIN32 - SetThreadPriority ( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ); -#else -/* - // set the process to realtime privs, taken from - // "http://www.gardena.net/benno/linux/audio" but does not seem to work, - // maybe a problem with user rights - struct sched_param schp; - memset ( &schp, 0, sizeof ( schp ) ); - schp.sched_priority = sched_get_priority_max ( SCHED_FIFO ); - sched_setscheduler ( 0, SCHED_FIFO, &schp ); -*/ -#endif - - // init object - try + // convert data from short to double + for ( i = 0; i < iSndCrdStereoBlockSizeSam; i++ ) { - Init(); - } - catch ( CGenErr generr ) - { - // TODO better error management -> should be catched in main thread - // problem: how to catch errors in a different thread...? - - // quick hack solution - QMessageBox::critical ( 0, APP_NAME, generr.GetErrorText(), "Quit", 0 ); - exit ( 0 ); + vecdAudioSndCrdStereo[i] = (double) vecsStereoSndCrd[i]; } + // resample data for each channel seaparately + ResampleObjDown.ResampleStereo ( vecdAudioSndCrdStereo, vecdAudioStereo ); - // runtime phase ----------------------------------------------------------- - // enable channel - Channel.SetEnable ( true ); + // update stereo signal level meter + SignalLevelMeter.Update ( vecdAudioStereo ); - bRun = true; - - // main loop of working thread - while ( bRun ) + // add reverberation effect if activated + if ( iReverbLevel != 0 ) { - // get audio from sound card (blocking function) - if ( Sound.Read ( vecsAudioSndCrdStereo ) ) + // calculate attenuation amplification factor + const double dRevLev = (double) iReverbLevel / AUD_REVERB_MAX / 2; + + if ( bReverbOnLeftChan ) { - PostWinMessage ( MS_SOUND_IN, MUL_COL_LED_RED ); + for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) + { + // left channel + vecdAudioStereo[i] += + dRevLev * AudioReverb.ProcessSample ( vecdAudioStereo[i] ); + } } else { - PostWinMessage ( MS_SOUND_IN, MUL_COL_LED_GREEN ); - } - - // convert data from short to double - for ( i = 0; i < iSndCrdStereoBlockSizeSam; i++ ) - { - vecdAudioSndCrdStereo[i] = (double) vecsAudioSndCrdStereo[i]; - } - - // resample data for each channel seaparately - ResampleObjDown.ResampleStereo ( vecdAudioSndCrdStereo, vecdAudioStereo ); - - // update stereo signal level meter - SignalLevelMeter.Update ( vecdAudioStereo ); - - // add reverberation effect if activated - if ( iReverbLevel != 0 ) - { - // calculate attenuation amplification factor - const double dRevLev = (double) iReverbLevel / AUD_REVERB_MAX / 2; - - if ( bReverbOnLeftChan ) + for ( i = 1; i < iStereoBlockSizeSam; i += 2 ) { - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // left channel - vecdAudioStereo[i] += - dRevLev * AudioReverb.ProcessSample ( vecdAudioStereo[i] ); - } - } - else - { - for ( i = 1; i < iStereoBlockSizeSam; i += 2 ) - { - // right channel - vecdAudioStereo[i] += - dRevLev * AudioReverb.ProcessSample ( vecdAudioStereo[i] ); - } + // right channel + vecdAudioStereo[i] += + dRevLev * AudioReverb.ProcessSample ( vecdAudioStereo[i] ); } } + } - // mix both signals depending on the fading setting, convert - // from double to short - if ( iAudioInFader == AUD_FADER_IN_MIDDLE ) + // mix both signals depending on the fading setting, convert + // from double to short + if ( iAudioInFader == AUD_FADER_IN_MIDDLE ) + { + // just mix channels together + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + vecsNetwork[i] = + Double2Short ( vecdAudioStereo[j] + vecdAudioStereo[j + 1] ); + } + } + else + { + const double dAttFact = + (double) ( AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / + AUD_FADER_IN_MIDDLE; + + if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) { - // just mix channels together for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { + // attenuation on right channel vecsNetwork[i] = - Double2Short ( vecdAudioStereo[j] + vecdAudioStereo[j + 1] ); + Double2Short ( vecdAudioStereo[j] + dAttFact * vecdAudioStereo[j + 1] ); } } else { - const double dAttFact = - (double) ( AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / - AUD_FADER_IN_MIDDLE; - - if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on right channel - vecsNetwork[i] = - Double2Short ( vecdAudioStereo[j] + dAttFact * vecdAudioStereo[j + 1] ); - } - } - else - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel - vecsNetwork[i] = - Double2Short ( vecdAudioStereo[j + 1] + dAttFact * vecdAudioStereo[j] ); - } + // attenuation on left channel + vecsNetwork[i] = + Double2Short ( vecdAudioStereo[j + 1] + dAttFact * vecdAudioStereo[j] ); } } + } - // send it through the network - Socket.SendPacket ( Channel.PrepSendPacket ( vecsNetwork ), - Channel.GetAddress() ); + // send it through the network + Socket.SendPacket ( Channel.PrepSendPacket ( vecsNetwork ), + Channel.GetAddress() ); - // receive a new block - if ( Channel.GetData ( vecdNetwData ) == GS_BUFFER_OK ) - { - PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN ); - } - else - { - PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED ); - } + // receive a new block + if ( Channel.GetData ( vecdNetwData ) == GS_BUFFER_OK ) + { + PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN ); + } + else + { + PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED ); + } /* // TEST @@ -356,65 +400,35 @@ static FILE* pFileDelay = fopen("v.dat", "wb"); short sData[2]; for (i = 0; i < iMonoBlockSizeSam; i++) { - sData[0] = (short) vecdNetwData[i]; - fwrite(&sData, size_t(2), size_t(1), pFileDelay); +sData[0] = (short) vecdNetwData[i]; +fwrite(&sData, size_t(2), size_t(1), pFileDelay); } fflush(pFileDelay); */ - // check if channel is connected - if ( Channel.IsConnected() ) - { - // resample data - ResampleObjUp.ResampleMono ( vecdNetwData, vecdAudioSndCrdMono ); + // check if channel is connected + if ( Channel.IsConnected() ) + { + // resample data + ResampleObjUp.ResampleMono ( vecdNetwData, vecdAudioSndCrdMono ); - // convert data from double to short type and copy mono - // received data in both sound card channels - for ( i = 0, j = 0; i < iSndCrdMonoBlockSizeSam; i++, j += 2 ) - { - vecsAudioSndCrdStereo[j] = vecsAudioSndCrdStereo[j + 1] = - Double2Short ( vecdAudioSndCrdMono[i] ); - } - } - else + // convert data from double to short type and copy mono + // received data in both sound card channels + for ( i = 0, j = 0; i < iSndCrdMonoBlockSizeSam; i++, j += 2 ) { - // if not connected, clear data - vecsAudioSndCrdStereo.Reset ( 0 ); + vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = + Double2Short ( vecdAudioSndCrdMono[i] ); } - - // play the new block - if ( Sound.Write ( vecsAudioSndCrdStereo ) ) - { - PostWinMessage ( MS_SOUND_OUT, MUL_COL_LED_RED ); - } - else - { - PostWinMessage ( MS_SOUND_OUT, MUL_COL_LED_GREEN ); - } - - // update response time measurement and socket buffer size - UpdateTimeResponseMeasurement(); - UpdateSocketBufferSize(); + } + else + { + // if not connected, clear data + vecsStereoSndCrd.Reset ( 0 ); } - // disable channel - Channel.SetEnable ( false ); - - // disable sound interface - Sound.Close(); - - // reset current signal level and LEDs - SignalLevelMeter.Reset(); - PostWinMessage ( MS_RESET_ALL, 0 ); -} - -bool CClient::Stop() -{ - // set flag so that thread can leave the main loop - bRun = false; - - // give thread some time to terminate, return status - return wait ( 5000 ); + // update response time measurement and socket buffer size + UpdateTimeResponseMeasurement(); + UpdateSocketBufferSize(); } void CClient::UpdateTimeResponseMeasurement() diff --git a/src/client.h b/src/client.h index 911c8c3b..e5a2a178 100755 --- a/src/client.h +++ b/src/client.h @@ -66,8 +66,8 @@ public: CClient ( const quint16 iPortNumber ); virtual ~CClient() {} - void Init(); - bool Stop(); + void Start(); + void Stop(); bool IsRunning() { return bRun; } bool SetServerAddr ( QString strNAddr ); double MicLevelL() { return SignalLevelMeter.MicLevelLeft(); } @@ -149,7 +149,9 @@ public: QString strName; protected: + void Init(); virtual void run(); + void ProcessAudioData ( CVector& vecsStereoSndCrd ); void UpdateTimeResponseMeasurement(); void UpdateSocketBufferSize(); diff --git a/src/llconclientdlg.cpp b/src/llconclientdlg.cpp index 49925d8b..9197d305 100755 --- a/src/llconclientdlg.cpp +++ b/src/llconclientdlg.cpp @@ -267,28 +267,12 @@ void CLlconClientDlg::OnSliderAudInFader ( int value ) void CLlconClientDlg::OnConnectDisconBut() { // start/stop client, set button text - if ( pClient->IsRunning() ) - { - pClient->Stop(); - PushButtonConnect->setText ( CON_BUT_CONNECTTEXT ); - - // stop timer for level meter bars and reset them - TimerSigMet.stop(); - ProgressBarInputLevelL->setValue ( 0 ); - ProgressBarInputLevelR->setValue ( 0 ); - - // immediately update status bar - OnTimerStatus(); - - // clear mixer board (remove all faders) - MainMixerBoard->HideAll(); - } - else + if ( !pClient->IsRunning() ) { // set address and check if address is valid if ( pClient->SetServerAddr ( LineEditServerAddr->text() ) ) { - pClient->start ( QThread::TimeCriticalPriority ); + pClient->Start(); PushButtonConnect->setText ( CON_BUT_DISCONNECTTEXT ); @@ -305,6 +289,22 @@ void CLlconClientDlg::OnConnectDisconBut() TextLabelStatus->setText ( tr ( "invalid address" ) ); } } + else + { + pClient->Stop(); + PushButtonConnect->setText ( CON_BUT_CONNECTTEXT ); + + // stop timer for level meter bars and reset them + TimerSigMet.stop(); + ProgressBarInputLevelL->setValue ( 0 ); + ProgressBarInputLevelR->setValue ( 0 ); + + // immediately update status bar + OnTimerStatus(); + + // clear mixer board (remove all faders) + MainMixerBoard->HideAll(); + } } void CLlconClientDlg::OnOpenGeneralSettings()