some cleanup and preperations for callback based audio interface
This commit is contained in:
parent
5917ead5e6
commit
71598931e2
3 changed files with 199 additions and 183 deletions
340
src/client.cpp
340
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()
|
void CClient::Init()
|
||||||
{
|
{
|
||||||
// set block size (in samples)
|
// set block size (in samples)
|
||||||
|
@ -205,149 +300,98 @@ void CClient::Init()
|
||||||
AudioReverb.Clear();
|
AudioReverb.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::run()
|
void CClient::ProcessAudioData ( CVector<short>& vecsStereoSndCrd )
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
// Set thread priority (The working thread should have a higher
|
// convert data from short to double
|
||||||
// priority than the GUI)
|
for ( i = 0; i < iSndCrdStereoBlockSizeSam; i++ )
|
||||||
#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
|
|
||||||
{
|
{
|
||||||
Init();
|
vecdAudioSndCrdStereo[i] = (double) vecsStereoSndCrd[i];
|
||||||
}
|
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resample data for each channel seaparately
|
||||||
|
ResampleObjDown.ResampleStereo ( vecdAudioSndCrdStereo, vecdAudioStereo );
|
||||||
|
|
||||||
// runtime phase -----------------------------------------------------------
|
// update stereo signal level meter
|
||||||
// enable channel
|
SignalLevelMeter.Update ( vecdAudioStereo );
|
||||||
Channel.SetEnable ( true );
|
|
||||||
|
|
||||||
bRun = true;
|
// add reverberation effect if activated
|
||||||
|
if ( iReverbLevel != 0 )
|
||||||
// main loop of working thread
|
|
||||||
while ( bRun )
|
|
||||||
{
|
{
|
||||||
// get audio from sound card (blocking function)
|
// calculate attenuation amplification factor
|
||||||
if ( Sound.Read ( vecsAudioSndCrdStereo ) )
|
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
|
else
|
||||||
{
|
{
|
||||||
PostWinMessage ( MS_SOUND_IN, MUL_COL_LED_GREEN );
|
for ( i = 1; i < iStereoBlockSizeSam; i += 2 )
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = 0; i < iStereoBlockSizeSam; i += 2 )
|
// right channel
|
||||||
{
|
vecdAudioStereo[i] +=
|
||||||
// left channel
|
dRevLev * AudioReverb.ProcessSample ( vecdAudioStereo[i] );
|
||||||
vecdAudioStereo[i] +=
|
|
||||||
dRevLev * AudioReverb.ProcessSample ( vecdAudioStereo[i] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for ( i = 1; i < iStereoBlockSizeSam; i += 2 )
|
|
||||||
{
|
|
||||||
// right channel
|
|
||||||
vecdAudioStereo[i] +=
|
|
||||||
dRevLev * AudioReverb.ProcessSample ( vecdAudioStereo[i] );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// mix both signals depending on the fading setting, convert
|
// mix both signals depending on the fading setting, convert
|
||||||
// from double to short
|
// from double to short
|
||||||
if ( 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 )
|
||||||
|
{
|
||||||
|
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 )
|
for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 )
|
||||||
{
|
{
|
||||||
|
// attenuation on right channel
|
||||||
vecsNetwork[i] =
|
vecsNetwork[i] =
|
||||||
Double2Short ( vecdAudioStereo[j] + vecdAudioStereo[j + 1] );
|
Double2Short ( vecdAudioStereo[j] + dAttFact * vecdAudioStereo[j + 1] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const double dAttFact =
|
for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 )
|
||||||
(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 )
|
// attenuation on left channel
|
||||||
{
|
vecsNetwork[i] =
|
||||||
// attenuation on right channel
|
Double2Short ( vecdAudioStereo[j + 1] + dAttFact * vecdAudioStereo[j] );
|
||||||
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] );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// send it through the network
|
// send it through the network
|
||||||
Socket.SendPacket ( Channel.PrepSendPacket ( vecsNetwork ),
|
Socket.SendPacket ( Channel.PrepSendPacket ( vecsNetwork ),
|
||||||
Channel.GetAddress() );
|
Channel.GetAddress() );
|
||||||
|
|
||||||
// receive a new block
|
// receive a new block
|
||||||
if ( Channel.GetData ( vecdNetwData ) == GS_BUFFER_OK )
|
if ( Channel.GetData ( vecdNetwData ) == GS_BUFFER_OK )
|
||||||
{
|
{
|
||||||
PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN );
|
PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED );
|
PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TEST
|
// TEST
|
||||||
|
@ -356,65 +400,35 @@ static FILE* pFileDelay = fopen("v.dat", "wb");
|
||||||
short sData[2];
|
short sData[2];
|
||||||
for (i = 0; i < iMonoBlockSizeSam; i++)
|
for (i = 0; i < iMonoBlockSizeSam; i++)
|
||||||
{
|
{
|
||||||
sData[0] = (short) vecdNetwData[i];
|
sData[0] = (short) vecdNetwData[i];
|
||||||
fwrite(&sData, size_t(2), size_t(1), pFileDelay);
|
fwrite(&sData, size_t(2), size_t(1), pFileDelay);
|
||||||
}
|
}
|
||||||
fflush(pFileDelay);
|
fflush(pFileDelay);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// check if channel is connected
|
// check if channel is connected
|
||||||
if ( Channel.IsConnected() )
|
if ( Channel.IsConnected() )
|
||||||
{
|
{
|
||||||
// resample data
|
// resample data
|
||||||
ResampleObjUp.ResampleMono ( vecdNetwData, vecdAudioSndCrdMono );
|
ResampleObjUp.ResampleMono ( vecdNetwData, vecdAudioSndCrdMono );
|
||||||
|
|
||||||
// convert data from double to short type and copy mono
|
// convert data from double to short type and copy mono
|
||||||
// received data in both sound card channels
|
// received data in both sound card channels
|
||||||
for ( i = 0, j = 0; i < iSndCrdMonoBlockSizeSam; i++, j += 2 )
|
for ( i = 0, j = 0; i < iSndCrdMonoBlockSizeSam; i++, j += 2 )
|
||||||
{
|
|
||||||
vecsAudioSndCrdStereo[j] = vecsAudioSndCrdStereo[j + 1] =
|
|
||||||
Double2Short ( vecdAudioSndCrdMono[i] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// if not connected, clear data
|
vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] =
|
||||||
vecsAudioSndCrdStereo.Reset ( 0 );
|
Double2Short ( vecdAudioSndCrdMono[i] );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// play the new block
|
else
|
||||||
if ( Sound.Write ( vecsAudioSndCrdStereo ) )
|
{
|
||||||
{
|
// if not connected, clear data
|
||||||
PostWinMessage ( MS_SOUND_OUT, MUL_COL_LED_RED );
|
vecsStereoSndCrd.Reset ( 0 );
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PostWinMessage ( MS_SOUND_OUT, MUL_COL_LED_GREEN );
|
|
||||||
}
|
|
||||||
|
|
||||||
// update response time measurement and socket buffer size
|
|
||||||
UpdateTimeResponseMeasurement();
|
|
||||||
UpdateSocketBufferSize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable channel
|
// update response time measurement and socket buffer size
|
||||||
Channel.SetEnable ( false );
|
UpdateTimeResponseMeasurement();
|
||||||
|
UpdateSocketBufferSize();
|
||||||
// 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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::UpdateTimeResponseMeasurement()
|
void CClient::UpdateTimeResponseMeasurement()
|
||||||
|
|
|
@ -66,8 +66,8 @@ public:
|
||||||
CClient ( const quint16 iPortNumber );
|
CClient ( const quint16 iPortNumber );
|
||||||
virtual ~CClient() {}
|
virtual ~CClient() {}
|
||||||
|
|
||||||
void Init();
|
void Start();
|
||||||
bool Stop();
|
void Stop();
|
||||||
bool IsRunning() { return bRun; }
|
bool IsRunning() { return bRun; }
|
||||||
bool SetServerAddr ( QString strNAddr );
|
bool SetServerAddr ( QString strNAddr );
|
||||||
double MicLevelL() { return SignalLevelMeter.MicLevelLeft(); }
|
double MicLevelL() { return SignalLevelMeter.MicLevelLeft(); }
|
||||||
|
@ -149,7 +149,9 @@ public:
|
||||||
QString strName;
|
QString strName;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void Init();
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
void ProcessAudioData ( CVector<short>& vecsStereoSndCrd );
|
||||||
void UpdateTimeResponseMeasurement();
|
void UpdateTimeResponseMeasurement();
|
||||||
void UpdateSocketBufferSize();
|
void UpdateSocketBufferSize();
|
||||||
|
|
||||||
|
|
|
@ -267,28 +267,12 @@ void CLlconClientDlg::OnSliderAudInFader ( int value )
|
||||||
void CLlconClientDlg::OnConnectDisconBut()
|
void CLlconClientDlg::OnConnectDisconBut()
|
||||||
{
|
{
|
||||||
// start/stop client, set button text
|
// start/stop client, set button text
|
||||||
if ( pClient->IsRunning() )
|
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
|
|
||||||
{
|
{
|
||||||
// set address and check if address is valid
|
// set address and check if address is valid
|
||||||
if ( pClient->SetServerAddr ( LineEditServerAddr->text() ) )
|
if ( pClient->SetServerAddr ( LineEditServerAddr->text() ) )
|
||||||
{
|
{
|
||||||
pClient->start ( QThread::TimeCriticalPriority );
|
pClient->Start();
|
||||||
|
|
||||||
PushButtonConnect->setText ( CON_BUT_DISCONNECTTEXT );
|
PushButtonConnect->setText ( CON_BUT_DISCONNECTTEXT );
|
||||||
|
|
||||||
|
@ -305,6 +289,22 @@ void CLlconClientDlg::OnConnectDisconBut()
|
||||||
TextLabelStatus->setText ( tr ( "invalid address" ) );
|
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()
|
void CLlconClientDlg::OnOpenGeneralSettings()
|
||||||
|
|
Loading…
Reference in a new issue