diff --git a/linux/sound.cpp b/linux/sound.cpp index 55938010..57ccfd3e 100755 --- a/linux/sound.cpp +++ b/linux/sound.cpp @@ -1,271 +1,271 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - * This code is based on the simple_client example of the Jack audio interface. - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "sound.h" - -#ifdef WITH_SOUND -void CSound::OpenJack() -{ - jack_status_t JackStatus; - - // try to become a client of the JACK server - pJackClient = jack_client_open ( "llcon", JackNullOption, &JackStatus ); - if ( pJackClient == NULL ) - { - throw CGenErr ( tr ( "Jack server not running" ) ); - } - - // tell the JACK server to call "process()" whenever - // there is work to be done - jack_set_process_callback ( pJackClient, process, this ); - - // register a "buffer size changed" callback function - jack_set_buffer_size_callback ( pJackClient, bufferSizeCallback, this ); - - // register shutdown callback function - jack_on_shutdown ( pJackClient, shutdownCallback, this ); - -// TEST check sample rate, if not correct, just fire error -if ( jack_get_sample_rate ( pJackClient ) != SYSTEM_SAMPLE_RATE_HZ ) -{ - throw CGenErr ( tr ( "Jack server sample rate is different from " - "required one" ) ); -} - - // create four ports (two for input, two for output -> stereo) - input_port_left = jack_port_register ( pJackClient, "input left", - JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); - - input_port_right = jack_port_register ( pJackClient, "input right", - JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); - - output_port_left = jack_port_register ( pJackClient, "output left", - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); - - output_port_right = jack_port_register ( pJackClient, "output right", - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); - - if ( ( input_port_left == NULL ) || - ( input_port_right == NULL ) || - ( output_port_left == NULL ) || - ( output_port_right == NULL ) ) - { - throw CGenErr ( tr ( "Jack port registering failed" ) ); - } - - const char** ports; - - // tell the JACK server that we are ready to roll - if ( jack_activate ( pJackClient ) ) - { - throw CGenErr ( tr ( "Cannot activate client" ) ); - } - - // connect the ports, note: you cannot do this before - // the client is activated, because we cannot allow - // connections to be made to clients that are not - // running - if ( ( ports = jack_get_ports ( pJackClient, NULL, NULL, - JackPortIsPhysical | JackPortIsOutput ) ) == NULL ) - { - throw CGenErr ( tr ( "Cannot find any physical capture ports" ) ); - } - - if ( !ports[1] ) - { - throw CGenErr ( tr ( "Cannot find enough physical capture ports" ) ); - } - - if ( jack_connect ( pJackClient, ports[0], jack_port_name ( input_port_left ) ) ) - { - throw CGenErr ( tr ( "Cannot connect input ports" ) ); - } - if ( jack_connect ( pJackClient, ports[1], jack_port_name ( input_port_right ) ) ) - { - throw CGenErr ( tr ( "Cannot connect input ports" ) ); - } - - free ( ports ); - - if ( ( ports = jack_get_ports ( pJackClient, NULL, NULL, - JackPortIsPhysical | JackPortIsInput ) ) == NULL ) - { - throw CGenErr ( tr ( "Cannot find any physical playback ports" ) ); - } - - if ( !ports[1] ) - { - throw CGenErr ( tr ( "Cannot find enough physical playback ports" ) ); - } - - if ( jack_connect ( pJackClient, jack_port_name ( output_port_left ), ports[0] ) ) - { - throw CGenErr ( tr ( "Cannot connect output ports" ) ); - } - if ( jack_connect ( pJackClient, jack_port_name ( output_port_right ), ports[1] ) ) - { - throw CGenErr ( tr ( "Cannot connect output ports" ) ); - } - - free ( ports ); -} - -void CSound::CloseJack() -{ - // deactivate client - jack_deactivate ( pJackClient ); - - // unregister ports - jack_port_unregister ( pJackClient, input_port_left ); - jack_port_unregister ( pJackClient, input_port_right ); - jack_port_unregister ( pJackClient, output_port_left ); - jack_port_unregister ( pJackClient, output_port_right ); - - // close client connection to jack server - jack_client_close ( pJackClient ); -} - -void CSound::Start() -{ - // call base class - CSoundBase::Start(); -} - -void CSound::Stop() -{ - // call base class - CSoundBase::Stop(); -} - -int CSound::Init ( const int /* iNewPrefMonoBufferSize */ ) -{ -// try setting buffer size -// TODO seems not to work! -> no audio after this operation! -//jack_set_buffer_size ( pJackClient, iNewPrefMonoBufferSize ); - - // get actual buffer size - iJACKBufferSizeMono = jack_get_buffer_size ( pJackClient ); - - // init base class - CSoundBase::Init ( iJACKBufferSizeMono ); - - // set internal buffer size value and calculate stereo buffer size - iJACKBufferSizeStero = 2 * iJACKBufferSizeMono; - - // create memory for intermediate audio buffer - vecsTmpAudioSndCrdStereo.Init ( iJACKBufferSizeStero ); - - return iJACKBufferSizeMono; -} - - -// JACK callbacks -------------------------------------------------------------- -int CSound::process ( jack_nframes_t nframes, void* arg ) -{ - CSound* pSound = reinterpret_cast ( arg ); - int i; - - if ( pSound->IsRunning() ) - { - // get input data pointer - jack_default_audio_sample_t* in_left = - (jack_default_audio_sample_t*) jack_port_get_buffer ( - pSound->input_port_left, nframes ); - - jack_default_audio_sample_t* in_right = - (jack_default_audio_sample_t*) jack_port_get_buffer ( - pSound->input_port_right, nframes ); - - // copy input data - for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ ) - { - pSound->vecsTmpAudioSndCrdStereo[2 * i] = - (short) ( in_left[i] * _MAXSHORT ); - - pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = - (short) ( in_right[i] * _MAXSHORT ); - } - - // call processing callback function - pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); - - // get output data pointer - jack_default_audio_sample_t* out_left = - (jack_default_audio_sample_t*) jack_port_get_buffer ( - pSound->output_port_left, nframes ); - - jack_default_audio_sample_t* out_right = - (jack_default_audio_sample_t*) jack_port_get_buffer ( - pSound->output_port_right, nframes ); - - // copy output data - for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ ) - { - out_left[i] = (jack_default_audio_sample_t) - pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT; - - out_right[i] = (jack_default_audio_sample_t) - pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT; - } - } - else - { - // get output data pointer - jack_default_audio_sample_t* out_left = - (jack_default_audio_sample_t*) jack_port_get_buffer ( - pSound->output_port_left, nframes ); - - jack_default_audio_sample_t* out_right = - (jack_default_audio_sample_t*) jack_port_get_buffer ( - pSound->output_port_right, nframes ); - - // clear output data - memset ( out_left, - 0, sizeof ( jack_default_audio_sample_t ) * nframes ); - - memset ( out_right, - 0, sizeof ( jack_default_audio_sample_t ) * nframes ); - } - - return 0; // zero on success, non-zero on error -} - -int CSound::bufferSizeCallback ( jack_nframes_t, void *arg ) -{ - CSound* pSound = reinterpret_cast ( arg ); - - pSound->EmitReinitRequestSignal(); - - return 0; // zero on success, non-zero on error -} - -void CSound::shutdownCallback ( void* ) -{ - // without a Jack server, our software makes no sense to run, throw - // error message - throw CGenErr ( tr ( "Jack server was shut down" ) ); -} -#endif // WITH_SOUND - +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + * This code is based on the simple_client example of the Jack audio interface. + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "sound.h" + +#ifdef WITH_SOUND +void CSound::OpenJack() +{ + jack_status_t JackStatus; + + // try to become a client of the JACK server + pJackClient = jack_client_open ( "llcon", JackNullOption, &JackStatus ); + if ( pJackClient == NULL ) + { + throw CGenErr ( tr ( "Jack server not running" ) ); + } + + // tell the JACK server to call "process()" whenever + // there is work to be done + jack_set_process_callback ( pJackClient, process, this ); + + // register a "buffer size changed" callback function + jack_set_buffer_size_callback ( pJackClient, bufferSizeCallback, this ); + + // register shutdown callback function + jack_on_shutdown ( pJackClient, shutdownCallback, this ); + +// TEST check sample rate, if not correct, just fire error +if ( jack_get_sample_rate ( pJackClient ) != SYSTEM_SAMPLE_RATE_HZ ) +{ + throw CGenErr ( tr ( "Jack server sample rate is different from " + "required one" ) ); +} + + // create four ports (two for input, two for output -> stereo) + input_port_left = jack_port_register ( pJackClient, "input left", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + + input_port_right = jack_port_register ( pJackClient, "input right", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + + output_port_left = jack_port_register ( pJackClient, "output left", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + + output_port_right = jack_port_register ( pJackClient, "output right", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + + if ( ( input_port_left == NULL ) || + ( input_port_right == NULL ) || + ( output_port_left == NULL ) || + ( output_port_right == NULL ) ) + { + throw CGenErr ( tr ( "Jack port registering failed" ) ); + } + + const char** ports; + + // tell the JACK server that we are ready to roll + if ( jack_activate ( pJackClient ) ) + { + throw CGenErr ( tr ( "Cannot activate client" ) ); + } + + // connect the ports, note: you cannot do this before + // the client is activated, because we cannot allow + // connections to be made to clients that are not + // running + if ( ( ports = jack_get_ports ( pJackClient, NULL, NULL, + JackPortIsPhysical | JackPortIsOutput ) ) == NULL ) + { + throw CGenErr ( tr ( "Cannot find any physical capture ports" ) ); + } + + if ( !ports[1] ) + { + throw CGenErr ( tr ( "Cannot find enough physical capture ports" ) ); + } + + if ( jack_connect ( pJackClient, ports[0], jack_port_name ( input_port_left ) ) ) + { + throw CGenErr ( tr ( "Cannot connect input ports" ) ); + } + if ( jack_connect ( pJackClient, ports[1], jack_port_name ( input_port_right ) ) ) + { + throw CGenErr ( tr ( "Cannot connect input ports" ) ); + } + + free ( ports ); + + if ( ( ports = jack_get_ports ( pJackClient, NULL, NULL, + JackPortIsPhysical | JackPortIsInput ) ) == NULL ) + { + throw CGenErr ( tr ( "Cannot find any physical playback ports" ) ); + } + + if ( !ports[1] ) + { + throw CGenErr ( tr ( "Cannot find enough physical playback ports" ) ); + } + + if ( jack_connect ( pJackClient, jack_port_name ( output_port_left ), ports[0] ) ) + { + throw CGenErr ( tr ( "Cannot connect output ports" ) ); + } + if ( jack_connect ( pJackClient, jack_port_name ( output_port_right ), ports[1] ) ) + { + throw CGenErr ( tr ( "Cannot connect output ports" ) ); + } + + free ( ports ); +} + +void CSound::CloseJack() +{ + // deactivate client + jack_deactivate ( pJackClient ); + + // unregister ports + jack_port_unregister ( pJackClient, input_port_left ); + jack_port_unregister ( pJackClient, input_port_right ); + jack_port_unregister ( pJackClient, output_port_left ); + jack_port_unregister ( pJackClient, output_port_right ); + + // close client connection to jack server + jack_client_close ( pJackClient ); +} + +void CSound::Start() +{ + // call base class + CSoundBase::Start(); +} + +void CSound::Stop() +{ + // call base class + CSoundBase::Stop(); +} + +int CSound::Init ( const int /* iNewPrefMonoBufferSize */ ) +{ +// try setting buffer size +// TODO seems not to work! -> no audio after this operation! +//jack_set_buffer_size ( pJackClient, iNewPrefMonoBufferSize ); + + // get actual buffer size + iJACKBufferSizeMono = jack_get_buffer_size ( pJackClient ); + + // init base class + CSoundBase::Init ( iJACKBufferSizeMono ); + + // set internal buffer size value and calculate stereo buffer size + iJACKBufferSizeStero = 2 * iJACKBufferSizeMono; + + // create memory for intermediate audio buffer + vecsTmpAudioSndCrdStereo.Init ( iJACKBufferSizeStero ); + + return iJACKBufferSizeMono; +} + + +// JACK callbacks -------------------------------------------------------------- +int CSound::process ( jack_nframes_t nframes, void* arg ) +{ + CSound* pSound = reinterpret_cast ( arg ); + int i; + + if ( pSound->IsRunning() ) + { + // get input data pointer + jack_default_audio_sample_t* in_left = + (jack_default_audio_sample_t*) jack_port_get_buffer ( + pSound->input_port_left, nframes ); + + jack_default_audio_sample_t* in_right = + (jack_default_audio_sample_t*) jack_port_get_buffer ( + pSound->input_port_right, nframes ); + + // copy input data + for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ ) + { + pSound->vecsTmpAudioSndCrdStereo[2 * i] = + (short) ( in_left[i] * _MAXSHORT ); + + pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = + (short) ( in_right[i] * _MAXSHORT ); + } + + // call processing callback function + pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); + + // get output data pointer + jack_default_audio_sample_t* out_left = + (jack_default_audio_sample_t*) jack_port_get_buffer ( + pSound->output_port_left, nframes ); + + jack_default_audio_sample_t* out_right = + (jack_default_audio_sample_t*) jack_port_get_buffer ( + pSound->output_port_right, nframes ); + + // copy output data + for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ ) + { + out_left[i] = (jack_default_audio_sample_t) + pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT; + + out_right[i] = (jack_default_audio_sample_t) + pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT; + } + } + else + { + // get output data pointer + jack_default_audio_sample_t* out_left = + (jack_default_audio_sample_t*) jack_port_get_buffer ( + pSound->output_port_left, nframes ); + + jack_default_audio_sample_t* out_right = + (jack_default_audio_sample_t*) jack_port_get_buffer ( + pSound->output_port_right, nframes ); + + // clear output data + memset ( out_left, + 0, sizeof ( jack_default_audio_sample_t ) * nframes ); + + memset ( out_right, + 0, sizeof ( jack_default_audio_sample_t ) * nframes ); + } + + return 0; // zero on success, non-zero on error +} + +int CSound::bufferSizeCallback ( jack_nframes_t, void *arg ) +{ + CSound* pSound = reinterpret_cast ( arg ); + + pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART_AND_INIT ); + + return 0; // zero on success, non-zero on error +} + +void CSound::shutdownCallback ( void* ) +{ + // without a Jack server, our software makes no sense to run, throw + // error message + throw CGenErr ( tr ( "Jack server was shut down" ) ); +} +#endif // WITH_SOUND + diff --git a/mac/sound.cpp b/mac/sound.cpp index 3dac89a1..e1cfd5f4 100755 --- a/mac/sound.cpp +++ b/mac/sound.cpp @@ -1,552 +1,548 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "sound.h" - - -/* Implementation *************************************************************/ -CSound::CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), void* arg ) : - CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg ) -{ - // set up stream format - streamFormat.mSampleRate = SYSTEM_SAMPLE_RATE_HZ; - streamFormat.mFormatID = kAudioFormatLinearPCM; - streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger; - streamFormat.mFramesPerPacket = 1; - streamFormat.mBytesPerFrame = 4; - streamFormat.mBytesPerPacket = 4; - streamFormat.mChannelsPerFrame = 2; // stereo - streamFormat.mBitsPerChannel = 16; - - // set up a callback struct for new input data - inputCallbackStruct.inputProc = processInput; - inputCallbackStruct.inputProcRefCon = this; - - // set up a callback struct for new output data - outputCallbackStruct.inputProc = processOutput; - outputCallbackStruct.inputProcRefCon = this; - - // allocate memory for buffer struct - pBufferList = (AudioBufferList*) malloc ( offsetof ( AudioBufferList, - mBuffers[0] ) + sizeof ( AudioBuffer ) ); - - // open the default unit - ComponentDescription desc; - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_HALOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - Component comp = FindNextComponent ( NULL, &desc ); - if ( comp == NULL ) - { - throw CGenErr ( tr ( "No CoreAudio next component found" ) ); - } - - if ( OpenAComponent ( comp, &audioInputUnit ) ) - { - throw CGenErr ( tr ( "CoreAudio creating input component instance failed" ) ); - } - - if ( OpenAComponent ( comp, &audioOutputUnit ) ) - { - throw CGenErr ( tr ( "CoreAudio creating output component instance failed" ) ); - } - - // we enable input and disable output for input component - UInt32 enableIO = 1; - AudioUnitSetProperty ( audioInputUnit, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Input, - 1, // input element - &enableIO, - sizeof ( enableIO ) ); - - enableIO = 0; - AudioUnitSetProperty ( audioInputUnit, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Output, - 0, // output element - &enableIO, - sizeof ( enableIO ) ); - - // set up a callback function for new input data - if ( AudioUnitSetProperty ( audioInputUnit, - kAudioOutputUnitProperty_SetInputCallback, - kAudioUnitScope_Global, - 0, - &inputCallbackStruct, - sizeof ( inputCallbackStruct ) ) ) - { - throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) ); - } - - // set input stream format - if ( AudioUnitSetProperty ( audioInputUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 1, - &streamFormat, - sizeof ( streamFormat ) ) ) - { - throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) ); - } - - - // set up a callback function for new output data - if ( AudioUnitSetProperty ( audioOutputUnit, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Global, - 0, - &outputCallbackStruct, - sizeof ( outputCallbackStruct ) ) ) - { - throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) ); - } - - // ste output stream format - if ( AudioUnitSetProperty ( audioOutputUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 0, - &streamFormat, - sizeof ( streamFormat ) ) ) - { - throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) ); - } - - - // Get available input/output devices -------------------------------------- - UInt32 iPropertySize; - - // first get property size of devices array and allocate memory - AudioHardwareGetPropertyInfo ( kAudioHardwarePropertyDevices, - &iPropertySize, - NULL ); - - AudioDeviceID* audioDevices = (AudioDeviceID*) malloc ( iPropertySize ); - - // now actually query all devices present in the system - AudioHardwareGetProperty ( kAudioHardwarePropertyDevices, - &iPropertySize, - audioDevices ); - - // calculate device count based on size of returned data array - const UInt32 deviceCount = ( iPropertySize / sizeof ( AudioDeviceID ) ); - - // always add system default devices for input and output as first entry - lNumDevs = 0; - strDriverNames[lNumDevs] = "System Default In/Out Devices"; - - iPropertySize = sizeof ( AudioDeviceID ); - if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultInputDevice, - &iPropertySize, - &audioInputDevice[lNumDevs] ) ) - { - throw CGenErr ( tr ( "CoreAudio input AudioHardwareGetProperty call failed. " - "It seems that no sound card is available in the system." ) ); - } - - iPropertySize = sizeof ( AudioDeviceID ); - if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultOutputDevice, - &iPropertySize, - &audioOutputDevice[lNumDevs] ) ) - { - throw CGenErr ( tr ( "CoreAudio output AudioHardwareGetProperty call failed. " - "It seems that no sound card is available in the system." ) ); - } - - lNumDevs++; // next device - - // add detected devices (also check for maximum allowed sound cards!) - // - // we add combined entries for input and output for each device so that we - // do not need two combo boxes in the GUI for input and output (therefore - // all possible combinations are required) - for ( UInt32 i = 0; ( i < deviceCount ) && ( i < MAX_NUMBER_SOUND_CARDS - 1 ); i++ ) - { - for ( UInt32 j = 0; ( j < deviceCount ) && ( j < MAX_NUMBER_SOUND_CARDS - 1 ); j++ ) - { - // get device infos for both current devices - QString strDeviceName_i; - QString strDeviceName_j; - bool bIsInput_i; - bool bIsInput_j; - bool bIsOutput_i; - bool bIsOutput_j; - - GetAudioDeviceInfos ( audioDevices[i], - strDeviceName_i, - bIsInput_i, - bIsOutput_i ); - - GetAudioDeviceInfos ( audioDevices[j], - strDeviceName_j, - bIsInput_j, - bIsOutput_j ); - - // check if i device is input and j device is output - if ( bIsInput_i && bIsOutput_j ) - { - strDriverNames[lNumDevs] = "in: " + - strDeviceName_i + "/out: " + - strDeviceName_j; - - // store audio device IDs - audioInputDevice[lNumDevs] = audioDevices[i]; - audioOutputDevice[lNumDevs] = audioDevices[j]; - - lNumDevs++; // next device - } - } - } - - // init device index as not initialized (invalid) - lCurDev = INVALID_SNC_CARD_DEVICE; -} - -void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID, - QString& strDeviceName, - bool& bIsInput, - bool& bIsOutput ) -{ - // get property name - UInt32 iPropertySize = sizeof ( CFStringRef ); - CFStringRef sPropertyStringValue; - - AudioDeviceGetProperty ( DeviceID, - 0, - false, - kAudioObjectPropertyName, - &iPropertySize, - &sPropertyStringValue ); - - // convert CFString in c-string (quick hack!) and then in QString - char* sC_strPropValue = - (char*) malloc ( CFStringGetLength ( sPropertyStringValue ) + 1 ); - - CFStringGetCString ( sPropertyStringValue, - sC_strPropValue, - CFStringGetLength ( sPropertyStringValue ) + 1, - kCFStringEncodingISOLatin1 ); - - strDeviceName = sC_strPropValue; - - // check if device is input or output or both (is that possible?) - // we do this by trying to set the current device for the audio unit - // with the parameter input and output and then we simply check the - // error/ok result - bIsInput = !AudioUnitSetProperty ( audioInputUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 1, - &DeviceID, - sizeof ( AudioDeviceID ) ); - - bIsOutput = !AudioUnitSetProperty ( audioOutputUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &DeviceID, - sizeof ( AudioDeviceID ) ); -} - -QString CSound::LoadAndInitializeDriver ( int iDriverIdx ) -{ - // set input device - if ( AudioUnitSetProperty ( audioInputUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 1, - &audioInputDevice[iDriverIdx], - sizeof ( AudioDeviceID ) ) ) - { - throw CGenErr ( tr ( "CoreAudio input AudioUnitSetProperty call failed" ) ); - } - - // set output device - if ( AudioUnitSetProperty ( audioOutputUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &audioOutputDevice[iDriverIdx], - sizeof ( AudioDeviceID ) ) ) - { - throw CGenErr ( tr ( "CoreAudio output AudioUnitSetProperty call failed" ) ); - } - - // check device capabilities if it fullfills our requirements - const QString strStat = - CheckDeviceCapabilities ( audioInputUnit, audioOutputUnit ); - - // check if device is capable - if ( strStat.isEmpty() ) - { - // store ID of selected driver if initialization was successful - lCurDev = iDriverIdx; - - // setup callback for xruns (only for input is enough) - AudioDeviceAddPropertyListener ( audioInputDevice[lCurDev], - 0, - true, - kAudioDeviceProcessorOverload, - deviceNotification, - this ); - } - - return strStat; -} - -QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit, - ComponentInstance& NewAudioOutputUnit ) -{ - UInt32 size; - - // check input device sample rate - size = sizeof ( Float64 ); - Float64 inputSampleRate; - AudioUnitGetProperty ( NewAudioInputUnit, - kAudioUnitProperty_SampleRate, - kAudioUnitScope_Input, - 1, - &inputSampleRate, - &size ); - - if ( static_cast ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ ) - { - return QString ( tr ( "Current system audio input device sample " - "rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in " - "Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg ( - static_cast ( inputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ ); - } - - // check output device sample rate - size = sizeof ( Float64 ); - Float64 outputSampleRate; - AudioUnitGetProperty ( NewAudioOutputUnit, - kAudioUnitProperty_SampleRate, - kAudioUnitScope_Output, - 0, - &outputSampleRate, - &size ); - - if ( static_cast ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ ) - { - return QString ( tr ( "Current system audio output device sample " - "rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in " - "Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg ( - static_cast ( outputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ ); - } - - // everything is ok, return empty string for "no error" case - return ""; -} - -void CSound::CloseCoreAudio() -{ - // clean up - AudioUnitUninitialize ( audioInputUnit ); - AudioUnitUninitialize ( audioOutputUnit ); - CloseComponent ( audioInputUnit ); - CloseComponent ( audioOutputUnit ); -} - -void CSound::Start() -{ - // start the rendering - AudioOutputUnitStart ( audioInputUnit ); - AudioOutputUnitStart ( audioOutputUnit ); - - // call base class - CSoundBase::Start(); -} - -void CSound::Stop() -{ - // stop the audio stream - AudioOutputUnitStop ( audioInputUnit ); - AudioOutputUnitStop ( audioOutputUnit ); - - // call base class - CSoundBase::Stop(); -} - -int CSound::Init ( const int iNewPrefMonoBufferSize ) -{ - UInt32 iActualMonoBufferSize; - - // Error message string: in case buffer sizes on input and output cannot be - // set to the same value - const QString strErrBufSize = tr ( "The buffer sizes of the current " - "input and output audio device cannot be set to a common value. Please " - "choose other input/output audio devices in your system settings." ); - - // try to set input buffer size - iActualMonoBufferSize = - SetBufferSize ( audioInputDevice[lCurDev], true, iNewPrefMonoBufferSize ); - - if ( iActualMonoBufferSize != static_cast ( iNewPrefMonoBufferSize ) ) - { - // try to set the input buffer size to the output so that we - // have a matching pair - if ( SetBufferSize ( audioOutputDevice[lCurDev], false, iActualMonoBufferSize ) != - iActualMonoBufferSize ) - { - throw CGenErr ( strErrBufSize ); - } - } - else - { - // try to set output buffer size - if ( SetBufferSize ( audioOutputDevice[lCurDev], false, iNewPrefMonoBufferSize ) != - static_cast ( iNewPrefMonoBufferSize ) ) - { - throw CGenErr ( strErrBufSize ); - } - } - - // store buffer size - iCoreAudioBufferSizeMono = iActualMonoBufferSize; - - // init base class - CSoundBase::Init ( iCoreAudioBufferSizeMono ); - - // set internal buffer size value and calculate stereo buffer size - iCoreAudioBufferSizeStero = 2 * iCoreAudioBufferSizeMono; - - // create memory for intermediate audio buffer - vecsTmpAudioSndCrdStereo.Init ( iCoreAudioBufferSizeStero ); - - // fill audio unit buffer struct - pBufferList->mNumberBuffers = 1; - pBufferList->mBuffers[0].mNumberChannels = 2; // stereo - pBufferList->mBuffers[0].mDataByteSize = iCoreAudioBufferSizeMono * 4; // 2 bytes, 2 channels - pBufferList->mBuffers[0].mData = &vecsTmpAudioSndCrdStereo[0]; - - // initialize units - if ( AudioUnitInitialize ( audioInputUnit ) ) - { - throw CGenErr ( tr ( "Initialization of CoreAudio failed" ) ); - } - - if ( AudioUnitInitialize ( audioOutputUnit ) ) - { - throw CGenErr ( tr ( "Initialization of CoreAudio failed" ) ); - } - - return iCoreAudioBufferSizeMono; -} - -UInt32 CSound::SetBufferSize ( AudioDeviceID& audioDeviceID, - const bool bIsInput, - UInt32 iPrefBufferSize ) -{ - // first set the value - UInt32 iSizeBufValue = sizeof ( UInt32 ); - AudioDeviceSetProperty ( audioDeviceID, - NULL, - 0, - bIsInput, - kAudioDevicePropertyBufferFrameSize, - iSizeBufValue, - &iPrefBufferSize ); - - // read back which value is actually used - UInt32 iActualMonoBufferSize; - AudioDeviceGetProperty ( audioDeviceID, - 0, - bIsInput, - kAudioDevicePropertyBufferFrameSize, - &iSizeBufValue, - &iActualMonoBufferSize ); - - return iActualMonoBufferSize; -} - -OSStatus CSound::deviceNotification ( AudioDeviceID, - UInt32, - Boolean, - AudioDevicePropertyID inPropertyID, - void* inRefCon ) -{ - CSound* pSound = reinterpret_cast ( inRefCon ); - - if ( inPropertyID == kAudioDeviceProcessorOverload ) - { - // xrun handling (it is important to act on xruns under CoreAudio - // since it seems that the xrun situation stays stable for a - // while and would give you a long time bad audio - -// TEST we reuse the signal which was intended only for Windows OS but -// with a modification in the client (see client.cpp) -pSound->EmitReinitRequestSignal(); - - } - - return noErr; -} - -OSStatus CSound::processInput ( void* inRefCon, - AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList* ) -{ - CSound* pSound = reinterpret_cast ( inRefCon ); - - QMutexLocker locker ( &pSound->Mutex ); - - // get the new audio data - AudioUnitRender ( pSound->audioInputUnit, - ioActionFlags, - inTimeStamp, - inBusNumber, - inNumberFrames, - pSound->pBufferList ); - - // call processing callback function - pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); - - return noErr; -} - -OSStatus CSound::processOutput ( void* inRefCon, - AudioUnitRenderActionFlags*, - const AudioTimeStamp*, - UInt32, - UInt32, - AudioBufferList* ioData ) -{ - CSound* pSound = reinterpret_cast ( inRefCon ); - - QMutexLocker locker ( &pSound->Mutex ); - - memcpy ( ioData->mBuffers[0].mData, - &pSound->vecsTmpAudioSndCrdStereo[0], - pSound->pBufferList->mBuffers[0].mDataByteSize); - - return noErr; -} +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "sound.h" + + +/* Implementation *************************************************************/ +CSound::CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), void* arg ) : + CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg ) +{ + // set up stream format + streamFormat.mSampleRate = SYSTEM_SAMPLE_RATE_HZ; + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger; + streamFormat.mFramesPerPacket = 1; + streamFormat.mBytesPerFrame = 4; + streamFormat.mBytesPerPacket = 4; + streamFormat.mChannelsPerFrame = 2; // stereo + streamFormat.mBitsPerChannel = 16; + + // set up a callback struct for new input data + inputCallbackStruct.inputProc = processInput; + inputCallbackStruct.inputProcRefCon = this; + + // set up a callback struct for new output data + outputCallbackStruct.inputProc = processOutput; + outputCallbackStruct.inputProcRefCon = this; + + // allocate memory for buffer struct + pBufferList = (AudioBufferList*) malloc ( offsetof ( AudioBufferList, + mBuffers[0] ) + sizeof ( AudioBuffer ) ); + + // open the default unit + ComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_HALOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + Component comp = FindNextComponent ( NULL, &desc ); + if ( comp == NULL ) + { + throw CGenErr ( tr ( "No CoreAudio next component found" ) ); + } + + if ( OpenAComponent ( comp, &audioInputUnit ) ) + { + throw CGenErr ( tr ( "CoreAudio creating input component instance failed" ) ); + } + + if ( OpenAComponent ( comp, &audioOutputUnit ) ) + { + throw CGenErr ( tr ( "CoreAudio creating output component instance failed" ) ); + } + + // we enable input and disable output for input component + UInt32 enableIO = 1; + AudioUnitSetProperty ( audioInputUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, + 1, // input element + &enableIO, + sizeof ( enableIO ) ); + + enableIO = 0; + AudioUnitSetProperty ( audioInputUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, + 0, // output element + &enableIO, + sizeof ( enableIO ) ); + + // set up a callback function for new input data + if ( AudioUnitSetProperty ( audioInputUnit, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, + 0, + &inputCallbackStruct, + sizeof ( inputCallbackStruct ) ) ) + { + throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) ); + } + + // set input stream format + if ( AudioUnitSetProperty ( audioInputUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &streamFormat, + sizeof ( streamFormat ) ) ) + { + throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) ); + } + + + // set up a callback function for new output data + if ( AudioUnitSetProperty ( audioOutputUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Global, + 0, + &outputCallbackStruct, + sizeof ( outputCallbackStruct ) ) ) + { + throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) ); + } + + // ste output stream format + if ( AudioUnitSetProperty ( audioOutputUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &streamFormat, + sizeof ( streamFormat ) ) ) + { + throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) ); + } + + + // Get available input/output devices -------------------------------------- + UInt32 iPropertySize; + + // first get property size of devices array and allocate memory + AudioHardwareGetPropertyInfo ( kAudioHardwarePropertyDevices, + &iPropertySize, + NULL ); + + AudioDeviceID* audioDevices = (AudioDeviceID*) malloc ( iPropertySize ); + + // now actually query all devices present in the system + AudioHardwareGetProperty ( kAudioHardwarePropertyDevices, + &iPropertySize, + audioDevices ); + + // calculate device count based on size of returned data array + const UInt32 deviceCount = ( iPropertySize / sizeof ( AudioDeviceID ) ); + + // always add system default devices for input and output as first entry + lNumDevs = 0; + strDriverNames[lNumDevs] = "System Default In/Out Devices"; + + iPropertySize = sizeof ( AudioDeviceID ); + if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultInputDevice, + &iPropertySize, + &audioInputDevice[lNumDevs] ) ) + { + throw CGenErr ( tr ( "CoreAudio input AudioHardwareGetProperty call failed. " + "It seems that no sound card is available in the system." ) ); + } + + iPropertySize = sizeof ( AudioDeviceID ); + if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultOutputDevice, + &iPropertySize, + &audioOutputDevice[lNumDevs] ) ) + { + throw CGenErr ( tr ( "CoreAudio output AudioHardwareGetProperty call failed. " + "It seems that no sound card is available in the system." ) ); + } + + lNumDevs++; // next device + + // add detected devices (also check for maximum allowed sound cards!) + // + // we add combined entries for input and output for each device so that we + // do not need two combo boxes in the GUI for input and output (therefore + // all possible combinations are required) + for ( UInt32 i = 0; ( i < deviceCount ) && ( i < MAX_NUMBER_SOUND_CARDS - 1 ); i++ ) + { + for ( UInt32 j = 0; ( j < deviceCount ) && ( j < MAX_NUMBER_SOUND_CARDS - 1 ); j++ ) + { + // get device infos for both current devices + QString strDeviceName_i; + QString strDeviceName_j; + bool bIsInput_i; + bool bIsInput_j; + bool bIsOutput_i; + bool bIsOutput_j; + + GetAudioDeviceInfos ( audioDevices[i], + strDeviceName_i, + bIsInput_i, + bIsOutput_i ); + + GetAudioDeviceInfos ( audioDevices[j], + strDeviceName_j, + bIsInput_j, + bIsOutput_j ); + + // check if i device is input and j device is output + if ( bIsInput_i && bIsOutput_j ) + { + strDriverNames[lNumDevs] = "in: " + + strDeviceName_i + "/out: " + + strDeviceName_j; + + // store audio device IDs + audioInputDevice[lNumDevs] = audioDevices[i]; + audioOutputDevice[lNumDevs] = audioDevices[j]; + + lNumDevs++; // next device + } + } + } + + // init device index as not initialized (invalid) + lCurDev = INVALID_SNC_CARD_DEVICE; +} + +void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID, + QString& strDeviceName, + bool& bIsInput, + bool& bIsOutput ) +{ + // get property name + UInt32 iPropertySize = sizeof ( CFStringRef ); + CFStringRef sPropertyStringValue; + + AudioDeviceGetProperty ( DeviceID, + 0, + false, + kAudioObjectPropertyName, + &iPropertySize, + &sPropertyStringValue ); + + // convert CFString in c-string (quick hack!) and then in QString + char* sC_strPropValue = + (char*) malloc ( CFStringGetLength ( sPropertyStringValue ) + 1 ); + + CFStringGetCString ( sPropertyStringValue, + sC_strPropValue, + CFStringGetLength ( sPropertyStringValue ) + 1, + kCFStringEncodingISOLatin1 ); + + strDeviceName = sC_strPropValue; + + // check if device is input or output or both (is that possible?) + // we do this by trying to set the current device for the audio unit + // with the parameter input and output and then we simply check the + // error/ok result + bIsInput = !AudioUnitSetProperty ( audioInputUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 1, + &DeviceID, + sizeof ( AudioDeviceID ) ); + + bIsOutput = !AudioUnitSetProperty ( audioOutputUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &DeviceID, + sizeof ( AudioDeviceID ) ); +} + +QString CSound::LoadAndInitializeDriver ( int iDriverIdx ) +{ + // set input device + if ( AudioUnitSetProperty ( audioInputUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 1, + &audioInputDevice[iDriverIdx], + sizeof ( AudioDeviceID ) ) ) + { + throw CGenErr ( tr ( "CoreAudio input AudioUnitSetProperty call failed" ) ); + } + + // set output device + if ( AudioUnitSetProperty ( audioOutputUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &audioOutputDevice[iDriverIdx], + sizeof ( AudioDeviceID ) ) ) + { + throw CGenErr ( tr ( "CoreAudio output AudioUnitSetProperty call failed" ) ); + } + + // check device capabilities if it fullfills our requirements + const QString strStat = + CheckDeviceCapabilities ( audioInputUnit, audioOutputUnit ); + + // check if device is capable + if ( strStat.isEmpty() ) + { + // store ID of selected driver if initialization was successful + lCurDev = iDriverIdx; + + // setup callback for xruns (only for input is enough) + AudioDeviceAddPropertyListener ( audioInputDevice[lCurDev], + 0, + true, + kAudioDeviceProcessorOverload, + deviceNotification, + this ); + } + + return strStat; +} + +QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit, + ComponentInstance& NewAudioOutputUnit ) +{ + UInt32 size; + + // check input device sample rate + size = sizeof ( Float64 ); + Float64 inputSampleRate; + AudioUnitGetProperty ( NewAudioInputUnit, + kAudioUnitProperty_SampleRate, + kAudioUnitScope_Input, + 1, + &inputSampleRate, + &size ); + + if ( static_cast ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ ) + { + return QString ( tr ( "Current system audio input device sample " + "rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in " + "Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg ( + static_cast ( inputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ ); + } + + // check output device sample rate + size = sizeof ( Float64 ); + Float64 outputSampleRate; + AudioUnitGetProperty ( NewAudioOutputUnit, + kAudioUnitProperty_SampleRate, + kAudioUnitScope_Output, + 0, + &outputSampleRate, + &size ); + + if ( static_cast ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ ) + { + return QString ( tr ( "Current system audio output device sample " + "rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in " + "Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg ( + static_cast ( outputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ ); + } + + // everything is ok, return empty string for "no error" case + return ""; +} + +void CSound::CloseCoreAudio() +{ + // clean up + AudioUnitUninitialize ( audioInputUnit ); + AudioUnitUninitialize ( audioOutputUnit ); + CloseComponent ( audioInputUnit ); + CloseComponent ( audioOutputUnit ); +} + +void CSound::Start() +{ + // start the rendering + AudioOutputUnitStart ( audioInputUnit ); + AudioOutputUnitStart ( audioOutputUnit ); + + // call base class + CSoundBase::Start(); +} + +void CSound::Stop() +{ + // stop the audio stream + AudioOutputUnitStop ( audioInputUnit ); + AudioOutputUnitStop ( audioOutputUnit ); + + // call base class + CSoundBase::Stop(); +} + +int CSound::Init ( const int iNewPrefMonoBufferSize ) +{ + UInt32 iActualMonoBufferSize; + + // Error message string: in case buffer sizes on input and output cannot be + // set to the same value + const QString strErrBufSize = tr ( "The buffer sizes of the current " + "input and output audio device cannot be set to a common value. Please " + "choose other input/output audio devices in your system settings." ); + + // try to set input buffer size + iActualMonoBufferSize = + SetBufferSize ( audioInputDevice[lCurDev], true, iNewPrefMonoBufferSize ); + + if ( iActualMonoBufferSize != static_cast ( iNewPrefMonoBufferSize ) ) + { + // try to set the input buffer size to the output so that we + // have a matching pair + if ( SetBufferSize ( audioOutputDevice[lCurDev], false, iActualMonoBufferSize ) != + iActualMonoBufferSize ) + { + throw CGenErr ( strErrBufSize ); + } + } + else + { + // try to set output buffer size + if ( SetBufferSize ( audioOutputDevice[lCurDev], false, iNewPrefMonoBufferSize ) != + static_cast ( iNewPrefMonoBufferSize ) ) + { + throw CGenErr ( strErrBufSize ); + } + } + + // store buffer size + iCoreAudioBufferSizeMono = iActualMonoBufferSize; + + // init base class + CSoundBase::Init ( iCoreAudioBufferSizeMono ); + + // set internal buffer size value and calculate stereo buffer size + iCoreAudioBufferSizeStero = 2 * iCoreAudioBufferSizeMono; + + // create memory for intermediate audio buffer + vecsTmpAudioSndCrdStereo.Init ( iCoreAudioBufferSizeStero ); + + // fill audio unit buffer struct + pBufferList->mNumberBuffers = 1; + pBufferList->mBuffers[0].mNumberChannels = 2; // stereo + pBufferList->mBuffers[0].mDataByteSize = iCoreAudioBufferSizeMono * 4; // 2 bytes, 2 channels + pBufferList->mBuffers[0].mData = &vecsTmpAudioSndCrdStereo[0]; + + // initialize units + if ( AudioUnitInitialize ( audioInputUnit ) ) + { + throw CGenErr ( tr ( "Initialization of CoreAudio failed" ) ); + } + + if ( AudioUnitInitialize ( audioOutputUnit ) ) + { + throw CGenErr ( tr ( "Initialization of CoreAudio failed" ) ); + } + + return iCoreAudioBufferSizeMono; +} + +UInt32 CSound::SetBufferSize ( AudioDeviceID& audioDeviceID, + const bool bIsInput, + UInt32 iPrefBufferSize ) +{ + // first set the value + UInt32 iSizeBufValue = sizeof ( UInt32 ); + AudioDeviceSetProperty ( audioDeviceID, + NULL, + 0, + bIsInput, + kAudioDevicePropertyBufferFrameSize, + iSizeBufValue, + &iPrefBufferSize ); + + // read back which value is actually used + UInt32 iActualMonoBufferSize; + AudioDeviceGetProperty ( audioDeviceID, + 0, + bIsInput, + kAudioDevicePropertyBufferFrameSize, + &iSizeBufValue, + &iActualMonoBufferSize ); + + return iActualMonoBufferSize; +} + +OSStatus CSound::deviceNotification ( AudioDeviceID, + UInt32, + Boolean, + AudioDevicePropertyID inPropertyID, + void* inRefCon ) +{ + CSound* pSound = reinterpret_cast ( inRefCon ); + + if ( inPropertyID == kAudioDeviceProcessorOverload ) + { + // xrun handling (it is important to act on xruns under CoreAudio + // since it seems that the xrun situation stays stable for a + // while and would give you a long time bad audio + pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART ); + } + + return noErr; +} + +OSStatus CSound::processInput ( void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ) +{ + CSound* pSound = reinterpret_cast ( inRefCon ); + + QMutexLocker locker ( &pSound->Mutex ); + + // get the new audio data + AudioUnitRender ( pSound->audioInputUnit, + ioActionFlags, + inTimeStamp, + inBusNumber, + inNumberFrames, + pSound->pBufferList ); + + // call processing callback function + pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); + + return noErr; +} + +OSStatus CSound::processOutput ( void* inRefCon, + AudioUnitRenderActionFlags*, + const AudioTimeStamp*, + UInt32, + UInt32, + AudioBufferList* ioData ) +{ + CSound* pSound = reinterpret_cast ( inRefCon ); + + QMutexLocker locker ( &pSound->Mutex ); + + memcpy ( ioData->mBuffers[0].mData, + &pSound->vecsTmpAudioSndCrdStereo[0], + pSound->pBufferList->mBuffers[0].mDataByteSize); + + return noErr; +} diff --git a/src/client.cpp b/src/client.cpp index d5eab442..46077360 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,1020 +1,1009 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "client.h" - - -/* Implementation *************************************************************/ -CClient::CClient ( const quint16 iPortNumber ) : - vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), - strName ( "" ), - Channel ( false ), /* we need a client channel -> "false" */ - iCeltNumCodedBytes ( CELT_NUM_BYTES_MONO_NORMAL_QUALITY ), - bCeltDoHighQuality ( false ), - bUseStereo ( false ), - Socket ( &Channel, &ConnLessProtocol, iPortNumber ), - Sound ( AudioCallback, this ), - iAudioInFader ( AUD_FADER_IN_MIDDLE ), - bReverbOnLeftChan ( false ), - iReverbLevel ( 0 ), - iSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ), - iSndCrdFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ), - bSndCrdConversionBufferRequired ( false ), - iSndCardMonoBlockSizeSamConvBuff ( 0 ), - bFraSiFactPrefSupported ( false ), - bFraSiFactDefSupported ( false ), - bFraSiFactSafeSupported ( false ), - bOpenChatOnNewMessage ( true ), - eGUIDesign ( GD_ORIGINAL ), - strCentralServerAddress ( "" ), - bUseDefaultCentralServerAddress ( true ), - iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ) -{ - // init audio encoder/decoder (mono) - CeltModeMono = celt_mode_create ( - SYSTEM_SAMPLE_RATE_HZ, 1, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); - - CeltEncoderMono = celt_encoder_create ( CeltModeMono ); - CeltDecoderMono = celt_decoder_create ( CeltModeMono ); - -#ifdef USE_LOW_COMPLEXITY_CELT_ENC - // set encoder low complexity - celt_encoder_ctl ( CeltEncoderMono, - CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); -#endif - - // init audio encoder/decoder (stereo) - CeltModeStereo = celt_mode_create ( - SYSTEM_SAMPLE_RATE_HZ, 2, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); - - CeltEncoderStereo = celt_encoder_create ( CeltModeStereo ); - CeltDecoderStereo = celt_decoder_create ( CeltModeStereo ); - -#ifdef USE_LOW_COMPLEXITY_CELT_ENC - // set encoder low complexity - celt_encoder_ctl ( CeltEncoderStereo, - CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); -#endif - - - // Connections ------------------------------------------------------------- - // connection for protocol - QObject::connect ( &Channel, - SIGNAL ( MessReadyForSending ( CVector ) ), - this, SLOT ( OnSendProtMessage ( CVector ) ) ); - - QObject::connect ( &Channel, - SIGNAL ( DetectedCLMessage ( CVector, int ) ), - this, SLOT ( OnDetectedCLMessage ( CVector, int ) ) ); - - QObject::connect ( &Channel, SIGNAL ( ReqJittBufSize() ), - this, SLOT ( OnReqJittBufSize() ) ); - - QObject::connect ( &Channel, SIGNAL ( JittBufSizeChanged ( int ) ), - this, SLOT ( OnJittBufSizeChanged ( int ) ) ); - - QObject::connect ( &Channel, SIGNAL ( ReqChanName() ), - this, SLOT ( OnReqChanName() ) ); - - QObject::connect ( &Channel, - SIGNAL ( ConClientListMesReceived ( CVector ) ), - SIGNAL ( ConClientListMesReceived ( CVector ) ) ); - - QObject::connect ( &Channel, - SIGNAL ( Disconnected() ), - SIGNAL ( Disconnected() ) ); - - QObject::connect ( &Channel, SIGNAL ( NewConnection() ), - this, SLOT ( OnNewConnection() ) ); - - QObject::connect ( &Channel, - SIGNAL ( ChatTextReceived ( QString ) ), - SIGNAL ( ChatTextReceived ( QString ) ) ); - - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLMessReadyForSending ( CHostAddress, CVector ) ), - this, SLOT ( OnSendCLProtMessage ( CHostAddress, CVector ) ) ); - - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ) ); - - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLPingReceived ( CHostAddress, int ) ), - this, SLOT ( OnCLPingReceived ( CHostAddress, int ) ) ); - - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLPingWithNumClientsReceived ( CHostAddress, int, int ) ), - this, SLOT ( OnCLPingWithNumClientsReceived ( CHostAddress, int, int ) ) ); - - QObject::connect ( &Sound, SIGNAL ( ReinitRequest() ), - this, SLOT ( OnSndCrdReinitRequest() ) ); -} - -void CClient::OnSendProtMessage ( CVector vecMessage ) -{ - // the protocol queries me to call the function to send the message - // send it through the network - Socket.SendPacket ( vecMessage, Channel.GetAddress() ); -} - -void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, - CVector vecMessage ) -{ - // the protocol queries me to call the function to send the message - // send it through the network - Socket.SendPacket ( vecMessage, InetAddr ); -} - -void CClient::OnDetectedCLMessage ( CVector vecbyData, - int iNumBytes ) -{ - // this is a special case: we received a connection less message but we are - // in a connection - ConnLessProtocol.ParseConnectionLessMessage ( vecbyData, - iNumBytes, - Channel.GetAddress() ); -} - -void CClient::OnJittBufSizeChanged ( int iNewJitBufSize ) -{ - // we received a jitter buffer size changed message from the server, - // only apply this value if auto jitter buffer size is enabled - if ( GetDoAutoSockBufSize() ) - { - // Note: Do not use the "SetServerSockBufNumFrames" function for setting - // the new server jitter buffer size since then a message would be sent - // to the server which is incorrect. - iServerSockBufNumFrames = iNewJitBufSize; - } -} - -void CClient::OnNewConnection() -{ - // a new connection was successfully initiated, send name and request - // connected clients list - Channel.SetRemoteName ( strName ); - - // We have to send a connected clients list request since it can happen - // that we just had connected to the server and then disconnected but - // the server still thinks that we are connected (the server is still - // waiting for the channel time-out). If we now connect again, we would - // not get the list because the server does not know about a new connection. - // Same problem is with the jitter buffer message. - Channel.CreateReqConnClientsList(); - CreateServerJitterBufferMessage(); -} - -void CClient::CreateServerJitterBufferMessage() -{ - // per definition in the client: if auto jitter buffer is enabled, both, - // the client and server shall use an auto jitter buffer - if ( GetDoAutoSockBufSize() ) - { - // in case auto jitter buffer size is enabled, we have to transmit a - // special value - Channel.CreateJitBufMes ( AUTO_NET_BUF_SIZE_FOR_PROTOCOL ); - } - else - { - Channel.CreateJitBufMes ( GetServerSockBufNumFrames() ); - } -} - -void CClient::OnCLPingReceived ( CHostAddress InetAddr, - int iMs ) -{ - // make sure we are running and the server address is correct - if ( IsRunning() && ( InetAddr == Channel.GetAddress() ) ) - { - // take care of wrap arounds (if wrapping, do not use result) - const int iCurDiff = EvaluatePingMessage ( iMs ); - if ( iCurDiff >= 0 ) - { - emit PingTimeReceived ( iCurDiff ); - } - } -} - -void CClient::OnCLPingWithNumClientsReceived ( CHostAddress InetAddr, - int iMs, - int iNumClients ) -{ - // take care of wrap arounds (if wrapping, do not use result) - const int iCurDiff = EvaluatePingMessage ( iMs ); - if ( iCurDiff >= 0 ) - { - emit CLPingTimeWithNumClientsReceived ( InetAddr, - iCurDiff, - iNumClients ); - } -} - -int CClient::PreparePingMessage() -{ - // transmit the current precise time (in ms) - return PreciseTime.elapsed(); -} - -int CClient::EvaluatePingMessage ( const int iMs ) -{ - // calculate difference between received time in ms and current time in ms - return PreciseTime.elapsed() - iMs; -} - -void CClient::SetDoAutoSockBufSize ( const bool bValue ) -{ - // first, set new value in the channel object - Channel.SetDoAutoSockBufSize ( bValue ); - - // inform the server about the change - CreateServerJitterBufferMessage(); -} - -bool CClient::SetServerAddr ( QString strNAddr ) -{ - CHostAddress HostAddress; - if ( LlconNetwUtil().ParseNetworkAddress ( strNAddr, - HostAddress ) ) - { - // apply address to the channel - Channel.SetAddress ( HostAddress ); - - return true; - } - else - { - return false; // invalid address - } -} - -void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor ) -{ - // first check new input parameter - if ( ( iNewFactor == FRAME_SIZE_FACTOR_PREFERRED ) || - ( iNewFactor == FRAME_SIZE_FACTOR_DEFAULT ) || - ( iNewFactor == FRAME_SIZE_FACTOR_SAFE ) ) - { - // init with new parameter, if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - // set new parameter - iSndCrdPrefFrameSizeFactor = iNewFactor; - - // init with new block size index parameter - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } - } -} - -void CClient::SetCELTHighQuality ( const bool bNCeltHighQualityFlag ) -{ - // init with new parameter, if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - // set new parameter - bCeltDoHighQuality = bNCeltHighQualityFlag; - Init(); - - if ( bWasRunning ) - { - Sound.Start(); - } -} - -void CClient::SetUseStereo ( const bool bNUseStereo ) -{ - // init with new parameter, if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - // set new parameter - bUseStereo = bNUseStereo; - Init(); - - if ( bWasRunning ) - { - Sound.Start(); - } -} - -QString CClient::SetSndCrdDev ( const int iNewDev ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - const QString strReturn = Sound.SetDev ( iNewDev ); - - // init again because the sound card actual buffer size might - // be changed on new device - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } - - return strReturn; -} - -void CClient::SetSndCrdLeftInputChannel ( const int iNewChan ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - Sound.SetLeftInputChannel ( iNewChan ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::SetSndCrdRightInputChannel ( const int iNewChan ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - Sound.SetRightInputChannel ( iNewChan ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::SetSndCrdLeftOutputChannel ( const int iNewChan ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - Sound.SetLeftOutputChannel ( iNewChan ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::SetSndCrdRightOutputChannel ( const int iNewChan ) -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - Sound.SetRightOutputChannel ( iNewChan ); - Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::OnSndCrdReinitRequest() -{ - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - - // reinit the driver (we use the currently selected driver) and - // init client object, too - -// TODO possible bug: In ASIO driver if set dev is called, the driver is -// unloaded. See ASIO manual: "Note: A host application has to defer -// processing of these notification to a later "secure" time as the -// driver has finish its processing of the notification. Especially on -// the kAsioResetRequest it is a bad idea to unload the driver during -// the asioMessage callback since the callback has to return back into -// the driver, which is no longer present." - -// TODO write separate driver reset function in sound base instead -// of doing setdev with the old driver ID -> avoid unloading driver - - Sound.SetDev ( Sound.GetDev() ); - -// This is a test. We reuse the SndCrdReinitRequest which was -// initially only intended for the Windows OS. But for Mac we -// must not call the client Init function to work properly, therefore -// we add the preprocesser check here -// TODO better solution was to introduce a new signal in the sound base -// and a new signal handler in the client -#if !defined ( __APPLE__ ) && !defined ( __MACOSX ) - Init(); -#endif - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } -} - -void CClient::Start() -{ - // init object - Init(); - - // enable channel - Channel.SetEnable ( true ); - - // start audio interface - Sound.Start(); -} - -void CClient::Stop() -{ - // stop audio interface - Sound.Stop(); - - // disable channel - Channel.SetEnable ( false ); - - // wait for approx. 100 ms to make sure no audio packet is still in the - // network queue causing the channel to be reconnected right after having - // received the disconnect message (seems not to gain much, disconnect is - // still not working reliably) - QTime DieTime = QTime::currentTime().addMSecs ( 100 ); - while ( QTime::currentTime() < DieTime ) - { - // exclude user input events because if we use AllEvents, it happens - // that if the user initiates a connection and disconnection quickly - // (e.g. quickly pressing enter five times), the software can get into - // an unknown state - QCoreApplication::processEvents ( - QEventLoop::ExcludeUserInputEvents, 100 ); - } - - // Send disconnect message to server (Since we disable our protocol - // receive mechanism with the next command, we do not evaluate any - // respond from the server, therefore we just hope that the message - // gets its way to the server, if not, the old behaviour time-out - // disconnects the connection anyway). - ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); - - // reset current signal level and LEDs - SignalLevelMeter.Reset(); - PostWinMessage ( MS_RESET_ALL, 0 ); -} - -void CClient::Init() -{ - // check if possible frame size factors are supported - const int iFraSizePreffered = - FRAME_SIZE_FACTOR_PREFERRED * SYSTEM_FRAME_SIZE_SAMPLES; - - bFraSiFactPrefSupported = - ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); - - const int iFraSizeDefault = - FRAME_SIZE_FACTOR_DEFAULT * SYSTEM_FRAME_SIZE_SAMPLES; - - bFraSiFactDefSupported = - ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); - - const int iFraSizeSafe = - FRAME_SIZE_FACTOR_SAFE * SYSTEM_FRAME_SIZE_SAMPLES; - - bFraSiFactSafeSupported = - ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); - - // translate block size index in actual block size - const int iPrefMonoFrameSize = - iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; - - // get actual sound card buffer size using preferred size - iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); - - // Calculate the current sound card frame size factor. In case - // the current mono block size is not a multiple of the system - // frame size, we have to use a sound card conversion buffer. - if ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) || - ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) || - ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) - { - // regular case: one of our predefined buffer sizes is available - iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES; - - // no sound card conversion buffer required - bSndCrdConversionBufferRequired = false; - } - else - { - // An unsupported sound card buffer size is currently used -> we have - // to use a conversion buffer. Per definition we use the smallest buffer - // size as llcon frame size - - // store actual sound card buffer size (stereo) - iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam; - const int iSndCardStereoBlockSizeSamConvBuff = 2 * iMonoBlockSizeSam; - - // overwrite block size by smallest supported llcon buffer size - iSndCrdFrameSizeFactor = FRAME_SIZE_FACTOR_PREFERRED; - iMonoBlockSizeSam = - SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED; - - iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; - - // inits for conversion buffer (the size of the conversion buffer must - // be the sum of input/output sizes which is the worst case fill level) - const int iConBufSize = - iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff; - - SndCrdConversionBufferIn.Init ( iConBufSize ); - SndCrdConversionBufferOut.Init ( iConBufSize ); - vecDataConvBuf.Init ( iStereoBlockSizeSam ); - - // the output conversion buffer must be filled with the inner - // block size for initialization (this is the latency which is - // introduced by the conversion buffer) to avoid buffer underruns - const CVector vZeros ( iStereoBlockSizeSam, 0 ); - SndCrdConversionBufferOut.Put ( vZeros, vZeros.Size() ); - - bSndCrdConversionBufferRequired = true; - } - - // calculate stereo (two channels) buffer size - iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; - - vecsAudioSndCrdMono.Init ( iMonoBlockSizeSam ); - vecdAudioStereo.Init ( iStereoBlockSizeSam ); - - // init reverberation - AudioReverbL.Init ( SYSTEM_SAMPLE_RATE_HZ ); - AudioReverbR.Init ( SYSTEM_SAMPLE_RATE_HZ ); - - // inits for CELT coding - if ( bCeltDoHighQuality ) - { - if ( bUseStereo ) - { - iCeltNumCodedBytes = CELT_NUM_BYTES_STEREO_HIGH_QUALITY; - } - else - { - iCeltNumCodedBytes = CELT_NUM_BYTES_MONO_HIGH_QUALITY; - } - } - else - { - if ( bUseStereo ) - { - iCeltNumCodedBytes = CELT_NUM_BYTES_STEREO_NORMAL_QUALITY; - } - else - { - iCeltNumCodedBytes = CELT_NUM_BYTES_MONO_NORMAL_QUALITY; - } - } - vecCeltData.Init ( iCeltNumCodedBytes ); - - // inits for network and channel - vecbyNetwData.Init ( iCeltNumCodedBytes ); - if ( bUseStereo ) - { - vecsNetwork.Init ( iStereoBlockSizeSam ); - - // set the channel network properties - Channel.SetAudioStreamProperties ( iCeltNumCodedBytes, - iSndCrdFrameSizeFactor, - 2 ); - } - else - { - vecsNetwork.Init ( iMonoBlockSizeSam ); - - // set the channel network properties - Channel.SetAudioStreamProperties ( iCeltNumCodedBytes, - iSndCrdFrameSizeFactor, - 1 ); - } -} - -void CClient::AudioCallback ( CVector& psData, void* arg ) -{ - // get the pointer to the object - CClient* pMyClientObj = reinterpret_cast ( arg ); - - // process audio data - pMyClientObj->ProcessSndCrdAudioData ( psData ); -} - -void CClient::ProcessSndCrdAudioData ( CVector& vecsStereoSndCrd ) -{ - // check if a conversion buffer is required or not - if ( bSndCrdConversionBufferRequired ) - { - // add new sound card block in conversion buffer - SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); - - // process all available blocks of data - while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam ) - { - // get one block of data for processing - SndCrdConversionBufferIn.Get ( vecDataConvBuf ); - - // process audio data - ProcessAudioDataIntern ( vecDataConvBuf ); - - SndCrdConversionBufferOut.Put ( vecDataConvBuf, vecDataConvBuf.Size() ); - } - - // get processed sound card block out of the conversion buffer - SndCrdConversionBufferOut.Get ( vecsStereoSndCrd ); - } - else - { - // regular case: no conversion buffer required - // process audio data - ProcessAudioDataIntern ( vecsStereoSndCrd ); - } -} - -void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) -{ - int i, j; - - // Transmit signal --------------------------------------------------------- - // update stereo signal level meter - SignalLevelMeter.Update ( vecsStereoSndCrd ); - - // convert data from short to double - for ( i = 0; i < iStereoBlockSizeSam; i++ ) - { - vecdAudioStereo[i] = static_cast ( vecsStereoSndCrd[i] ); - } - - // add reverberation effect if activated - if ( iReverbLevel != 0 ) - { - // calculate attenuation amplification factor - const double dRevLev = - static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 2; - - if ( bUseStereo ) - { - // for stereo always apply reverberation effect on both channels - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // left channel - vecdAudioStereo[i] += - dRevLev * AudioReverbL.ProcessSample ( vecdAudioStereo[i] ); - - // right channel - vecdAudioStereo[i + 1] += - dRevLev * AudioReverbR.ProcessSample ( vecdAudioStereo[i + 1] ); - } - } - else - { - if ( bReverbOnLeftChan ) - { - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // left channel - vecdAudioStereo[i] += - dRevLev * AudioReverbL.ProcessSample ( vecdAudioStereo[i] ); - } - } - else - { - for ( i = 1; i < iStereoBlockSizeSam; i += 2 ) - { - // right channel - vecdAudioStereo[i] += - dRevLev * AudioReverbR.ProcessSample ( vecdAudioStereo[i] ); - } - } - } - } - - // mix both signals depending on the fading setting, convert - // from double to short - if ( iAudioInFader == AUD_FADER_IN_MIDDLE ) - { - if ( bUseStereo ) - { - // perform type conversion - for ( i = 0; i < iStereoBlockSizeSam; i++ ) - { - vecsNetwork[i] = Double2Short ( vecdAudioStereo[i] ); - } - } - else - { - // mix channels together - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - vecsNetwork[i] = - Double2Short ( ( vecdAudioStereo[j] + - vecdAudioStereo[j + 1] ) / 2 ); - } - } - } - else - { - if ( bUseStereo ) - { - // stereo - const double dAttFactStereo = static_cast ( - 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 right channel - vecsNetwork[j] = Double2Short ( - vecdAudioStereo[j] ); - - vecsNetwork[j + 1] = Double2Short ( - dAttFactStereo * vecdAudioStereo[j + 1] ); - } - } - else - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel - vecsNetwork[j] = Double2Short ( - dAttFactStereo * vecdAudioStereo[j] ); - - vecsNetwork[j + 1] = Double2Short ( - vecdAudioStereo[j + 1] ); - } - } - } - else - { - // mono - // make sure that in the middle position the two channels are - // amplified by 1/2, if the pan is set to one channel, this - // channel should have an amplification of 1 - const double dAttFactMono = static_cast ( - AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / - AUD_FADER_IN_MIDDLE / 2; - - const double dAmplFactMono = 0.5 + static_cast ( - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / - AUD_FADER_IN_MIDDLE / 2; - - if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on right channel - vecsNetwork[i] = Double2Short ( - dAmplFactMono * vecdAudioStereo[j] + - dAttFactMono * vecdAudioStereo[j + 1] ); - } - } - else - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel - vecsNetwork[i] = Double2Short ( - dAmplFactMono * vecdAudioStereo[j + 1] + - dAttFactMono * vecdAudioStereo[j] ); - } - } - } - } - - for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) - { - if ( bUseStereo ) - { - // encode current audio frame with CELT encoder - celt_encode ( CeltEncoderStereo, - &vecsNetwork[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES], - NULL, - &vecCeltData[0], - iCeltNumCodedBytes ); - } - else - { - // encode current audio frame with CELT encoder - celt_encode ( CeltEncoderMono, - &vecsNetwork[i * SYSTEM_FRAME_SIZE_SAMPLES], - NULL, - &vecCeltData[0], - iCeltNumCodedBytes ); - } - - // send coded audio through the network - Socket.SendPacket ( Channel.PrepSendPacket ( vecCeltData ), - Channel.GetAddress() ); - } - - - // Receive signal ---------------------------------------------------------- - for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) - { - // receive a new block - const bool bReceiveDataOk = - ( Channel.GetData ( vecbyNetwData ) == GS_BUFFER_OK ); - - if ( bReceiveDataOk ) - { - PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN ); - } - else - { - PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED ); - } - - // CELT decoding - if ( bReceiveDataOk ) - { - if ( bUseStereo ) - { - celt_decode ( CeltDecoderStereo, - &vecbyNetwData[0], - iCeltNumCodedBytes, - &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES] ); - } - else - { - celt_decode ( CeltDecoderMono, - &vecbyNetwData[0], - iCeltNumCodedBytes, - &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES] ); - } - } - else - { - // lost packet - if ( bUseStereo ) - { - celt_decode ( CeltDecoderStereo, - NULL, - 0, - &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES] ); - } - else - { - celt_decode ( CeltDecoderMono, - NULL, - 0, - &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES] ); - } - } - } - - -/* -// TEST -// fid=fopen('v.dat','r');x=fread(fid,'int16');fclose(fid); -static FILE* pFileDelay = fopen("c:\\temp\\test2.dat", "wb"); -short sData[2]; -for (i = 0; i < iMonoBlockSizeSam; i++) -{ - sData[0] = (short) vecsAudioSndCrdMono[i]; - fwrite(&sData, size_t(2), size_t(1), pFileDelay); -} -fflush(pFileDelay); -*/ - - - // check if channel is connected - if ( Channel.IsConnected() ) - { - if ( !bUseStereo ) - { - // copy mono data in stereo sound card buffer - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = - vecsAudioSndCrdMono[i]; - } - } - } - else - { - // if not connected, clear data - vecsStereoSndCrd.Reset ( 0 ); - } - - // update socket buffer size - Channel.UpdateSocketBufferSize(); -} - -int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) -{ -/* - For estimating the overall delay, use the following assumptions: - - the mean delay of a cyclic buffer is half the buffer size (since - for the average it is assumed that the buffer is half filled) - - consider the jitter buffer on the server side, too -*/ - // the buffer sizes at client and server divided by 2 (half the buffer - // for the delay) is the total socket buffer size - const double dTotalJitterBufferDelayMs = SYSTEM_BLOCK_DURATION_MS_FLOAT * - static_cast ( GetSockBufNumFrames() + - GetServerSockBufNumFrames() ) / 2; - - // we assume that we have two period sizes for the input and one for the - // output, therefore we have "3 *" instead of "2 *" (for input and output) - // the actual sound card buffer size, also consider delay introduced by - // sound card conversion buffer by using - // "GetSndCrdConvBufAdditionalDelayMonoBlSize" - const double dTotalSoundCardDelayMs = - ( 3 * GetSndCrdActualMonoBlSize() + - GetSndCrdConvBufAdditionalDelayMonoBlSize() ) * - 1000 / SYSTEM_SAMPLE_RATE_HZ; - - // network packets are of the same size as the audio packets per definition - // if no sound card conversion buffer is used - const double dDelayToFillNetworkPacketsMs = - GetSystemMonoBlSize() * 1000 / SYSTEM_SAMPLE_RATE_HZ; - - // CELT additional delay at small frame sizes is half a frame size - const double dAdditionalAudioCodecDelayMs = - SYSTEM_BLOCK_DURATION_MS_FLOAT / 2; - - const double dTotalBufferDelayMs = - dDelayToFillNetworkPacketsMs + - dTotalJitterBufferDelayMs + - dTotalSoundCardDelayMs + - dAdditionalAudioCodecDelayMs; - - return LlconMath::round ( dTotalBufferDelayMs + iPingTimeMs ); -} +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "client.h" + + +/* Implementation *************************************************************/ +CClient::CClient ( const quint16 iPortNumber ) : + vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), + strName ( "" ), + Channel ( false ), /* we need a client channel -> "false" */ + iCeltNumCodedBytes ( CELT_NUM_BYTES_MONO_NORMAL_QUALITY ), + bCeltDoHighQuality ( false ), + bUseStereo ( false ), + Socket ( &Channel, &ConnLessProtocol, iPortNumber ), + Sound ( AudioCallback, this ), + iAudioInFader ( AUD_FADER_IN_MIDDLE ), + bReverbOnLeftChan ( false ), + iReverbLevel ( 0 ), + iSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ), + iSndCrdFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ), + bSndCrdConversionBufferRequired ( false ), + iSndCardMonoBlockSizeSamConvBuff ( 0 ), + bFraSiFactPrefSupported ( false ), + bFraSiFactDefSupported ( false ), + bFraSiFactSafeSupported ( false ), + bOpenChatOnNewMessage ( true ), + eGUIDesign ( GD_ORIGINAL ), + strCentralServerAddress ( "" ), + bUseDefaultCentralServerAddress ( true ), + iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ) +{ + // init audio encoder/decoder (mono) + CeltModeMono = celt_mode_create ( + SYSTEM_SAMPLE_RATE_HZ, 1, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); + + CeltEncoderMono = celt_encoder_create ( CeltModeMono ); + CeltDecoderMono = celt_decoder_create ( CeltModeMono ); + +#ifdef USE_LOW_COMPLEXITY_CELT_ENC + // set encoder low complexity + celt_encoder_ctl ( CeltEncoderMono, + CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); +#endif + + // init audio encoder/decoder (stereo) + CeltModeStereo = celt_mode_create ( + SYSTEM_SAMPLE_RATE_HZ, 2, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); + + CeltEncoderStereo = celt_encoder_create ( CeltModeStereo ); + CeltDecoderStereo = celt_decoder_create ( CeltModeStereo ); + +#ifdef USE_LOW_COMPLEXITY_CELT_ENC + // set encoder low complexity + celt_encoder_ctl ( CeltEncoderStereo, + CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); +#endif + + + // Connections ------------------------------------------------------------- + // connection for protocol + QObject::connect ( &Channel, + SIGNAL ( MessReadyForSending ( CVector ) ), + this, SLOT ( OnSendProtMessage ( CVector ) ) ); + + QObject::connect ( &Channel, + SIGNAL ( DetectedCLMessage ( CVector, int ) ), + this, SLOT ( OnDetectedCLMessage ( CVector, int ) ) ); + + QObject::connect ( &Channel, SIGNAL ( ReqJittBufSize() ), + this, SLOT ( OnReqJittBufSize() ) ); + + QObject::connect ( &Channel, SIGNAL ( JittBufSizeChanged ( int ) ), + this, SLOT ( OnJittBufSizeChanged ( int ) ) ); + + QObject::connect ( &Channel, SIGNAL ( ReqChanName() ), + this, SLOT ( OnReqChanName() ) ); + + QObject::connect ( &Channel, + SIGNAL ( ConClientListMesReceived ( CVector ) ), + SIGNAL ( ConClientListMesReceived ( CVector ) ) ); + + QObject::connect ( &Channel, + SIGNAL ( Disconnected() ), + SIGNAL ( Disconnected() ) ); + + QObject::connect ( &Channel, SIGNAL ( NewConnection() ), + this, SLOT ( OnNewConnection() ) ); + + QObject::connect ( &Channel, + SIGNAL ( ChatTextReceived ( QString ) ), + SIGNAL ( ChatTextReceived ( QString ) ) ); + + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLMessReadyForSending ( CHostAddress, CVector ) ), + this, SLOT ( OnSendCLProtMessage ( CHostAddress, CVector ) ) ); + + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), + SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ) ); + + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLPingReceived ( CHostAddress, int ) ), + this, SLOT ( OnCLPingReceived ( CHostAddress, int ) ) ); + + QObject::connect ( &ConnLessProtocol, + SIGNAL ( CLPingWithNumClientsReceived ( CHostAddress, int, int ) ), + this, SLOT ( OnCLPingWithNumClientsReceived ( CHostAddress, int, int ) ) ); + + QObject::connect ( &Sound, SIGNAL ( ReinitRequest ( ESndCrdResetType ) ), + this, SLOT ( OnSndCrdReinitRequest ( ESndCrdResetType ) ) ); +} + +void CClient::OnSendProtMessage ( CVector vecMessage ) +{ + // the protocol queries me to call the function to send the message + // send it through the network + Socket.SendPacket ( vecMessage, Channel.GetAddress() ); +} + +void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, + CVector vecMessage ) +{ + // the protocol queries me to call the function to send the message + // send it through the network + Socket.SendPacket ( vecMessage, InetAddr ); +} + +void CClient::OnDetectedCLMessage ( CVector vecbyData, + int iNumBytes ) +{ + // this is a special case: we received a connection less message but we are + // in a connection + ConnLessProtocol.ParseConnectionLessMessage ( vecbyData, + iNumBytes, + Channel.GetAddress() ); +} + +void CClient::OnJittBufSizeChanged ( int iNewJitBufSize ) +{ + // we received a jitter buffer size changed message from the server, + // only apply this value if auto jitter buffer size is enabled + if ( GetDoAutoSockBufSize() ) + { + // Note: Do not use the "SetServerSockBufNumFrames" function for setting + // the new server jitter buffer size since then a message would be sent + // to the server which is incorrect. + iServerSockBufNumFrames = iNewJitBufSize; + } +} + +void CClient::OnNewConnection() +{ + // a new connection was successfully initiated, send name and request + // connected clients list + Channel.SetRemoteName ( strName ); + + // We have to send a connected clients list request since it can happen + // that we just had connected to the server and then disconnected but + // the server still thinks that we are connected (the server is still + // waiting for the channel time-out). If we now connect again, we would + // not get the list because the server does not know about a new connection. + // Same problem is with the jitter buffer message. + Channel.CreateReqConnClientsList(); + CreateServerJitterBufferMessage(); +} + +void CClient::CreateServerJitterBufferMessage() +{ + // per definition in the client: if auto jitter buffer is enabled, both, + // the client and server shall use an auto jitter buffer + if ( GetDoAutoSockBufSize() ) + { + // in case auto jitter buffer size is enabled, we have to transmit a + // special value + Channel.CreateJitBufMes ( AUTO_NET_BUF_SIZE_FOR_PROTOCOL ); + } + else + { + Channel.CreateJitBufMes ( GetServerSockBufNumFrames() ); + } +} + +void CClient::OnCLPingReceived ( CHostAddress InetAddr, + int iMs ) +{ + // make sure we are running and the server address is correct + if ( IsRunning() && ( InetAddr == Channel.GetAddress() ) ) + { + // take care of wrap arounds (if wrapping, do not use result) + const int iCurDiff = EvaluatePingMessage ( iMs ); + if ( iCurDiff >= 0 ) + { + emit PingTimeReceived ( iCurDiff ); + } + } +} + +void CClient::OnCLPingWithNumClientsReceived ( CHostAddress InetAddr, + int iMs, + int iNumClients ) +{ + // take care of wrap arounds (if wrapping, do not use result) + const int iCurDiff = EvaluatePingMessage ( iMs ); + if ( iCurDiff >= 0 ) + { + emit CLPingTimeWithNumClientsReceived ( InetAddr, + iCurDiff, + iNumClients ); + } +} + +int CClient::PreparePingMessage() +{ + // transmit the current precise time (in ms) + return PreciseTime.elapsed(); +} + +int CClient::EvaluatePingMessage ( const int iMs ) +{ + // calculate difference between received time in ms and current time in ms + return PreciseTime.elapsed() - iMs; +} + +void CClient::SetDoAutoSockBufSize ( const bool bValue ) +{ + // first, set new value in the channel object + Channel.SetDoAutoSockBufSize ( bValue ); + + // inform the server about the change + CreateServerJitterBufferMessage(); +} + +bool CClient::SetServerAddr ( QString strNAddr ) +{ + CHostAddress HostAddress; + if ( LlconNetwUtil().ParseNetworkAddress ( strNAddr, + HostAddress ) ) + { + // apply address to the channel + Channel.SetAddress ( HostAddress ); + + return true; + } + else + { + return false; // invalid address + } +} + +void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor ) +{ + // first check new input parameter + if ( ( iNewFactor == FRAME_SIZE_FACTOR_PREFERRED ) || + ( iNewFactor == FRAME_SIZE_FACTOR_DEFAULT ) || + ( iNewFactor == FRAME_SIZE_FACTOR_SAFE ) ) + { + // init with new parameter, if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + // set new parameter + iSndCrdPrefFrameSizeFactor = iNewFactor; + + // init with new block size index parameter + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } + } +} + +void CClient::SetCELTHighQuality ( const bool bNCeltHighQualityFlag ) +{ + // init with new parameter, if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + // set new parameter + bCeltDoHighQuality = bNCeltHighQualityFlag; + Init(); + + if ( bWasRunning ) + { + Sound.Start(); + } +} + +void CClient::SetUseStereo ( const bool bNUseStereo ) +{ + // init with new parameter, if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + // set new parameter + bUseStereo = bNUseStereo; + Init(); + + if ( bWasRunning ) + { + Sound.Start(); + } +} + +QString CClient::SetSndCrdDev ( const int iNewDev ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + const QString strReturn = Sound.SetDev ( iNewDev ); + + // init again because the sound card actual buffer size might + // be changed on new device + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } + + return strReturn; +} + +void CClient::SetSndCrdLeftInputChannel ( const int iNewChan ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + Sound.SetLeftInputChannel ( iNewChan ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::SetSndCrdRightInputChannel ( const int iNewChan ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + Sound.SetRightInputChannel ( iNewChan ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::SetSndCrdLeftOutputChannel ( const int iNewChan ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + Sound.SetLeftOutputChannel ( iNewChan ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::SetSndCrdRightOutputChannel ( const int iNewChan ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + Sound.SetRightOutputChannel ( iNewChan ); + Init(); + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::OnSndCrdReinitRequest ( ESndCrdResetType eSndCrdResetType ) +{ + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) + { + Sound.Stop(); + } + + // perform reinit request as indicated by the request type parameter + if ( eSndCrdResetType != RS_ONLY_RESTART ) + { + if ( eSndCrdResetType != RS_ONLY_RESTART_AND_INIT ) + { + // reinit the driver if requested + // (we use the currently selected driver) + Sound.SetDev ( Sound.GetDev() ); + } + + // init client object (must always be performed if the driver + // was changed) + Init(); + } + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } +} + +void CClient::Start() +{ + // init object + Init(); + + // enable channel + Channel.SetEnable ( true ); + + // start audio interface + Sound.Start(); +} + +void CClient::Stop() +{ + // stop audio interface + Sound.Stop(); + + // disable channel + Channel.SetEnable ( false ); + + // wait for approx. 100 ms to make sure no audio packet is still in the + // network queue causing the channel to be reconnected right after having + // received the disconnect message (seems not to gain much, disconnect is + // still not working reliably) + QTime DieTime = QTime::currentTime().addMSecs ( 100 ); + while ( QTime::currentTime() < DieTime ) + { + // exclude user input events because if we use AllEvents, it happens + // that if the user initiates a connection and disconnection quickly + // (e.g. quickly pressing enter five times), the software can get into + // an unknown state + QCoreApplication::processEvents ( + QEventLoop::ExcludeUserInputEvents, 100 ); + } + + // Send disconnect message to server (Since we disable our protocol + // receive mechanism with the next command, we do not evaluate any + // respond from the server, therefore we just hope that the message + // gets its way to the server, if not, the old behaviour time-out + // disconnects the connection anyway). + ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); + + // reset current signal level and LEDs + SignalLevelMeter.Reset(); + PostWinMessage ( MS_RESET_ALL, 0 ); +} + +void CClient::Init() +{ + // check if possible frame size factors are supported + const int iFraSizePreffered = + FRAME_SIZE_FACTOR_PREFERRED * SYSTEM_FRAME_SIZE_SAMPLES; + + bFraSiFactPrefSupported = + ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); + + const int iFraSizeDefault = + FRAME_SIZE_FACTOR_DEFAULT * SYSTEM_FRAME_SIZE_SAMPLES; + + bFraSiFactDefSupported = + ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); + + const int iFraSizeSafe = + FRAME_SIZE_FACTOR_SAFE * SYSTEM_FRAME_SIZE_SAMPLES; + + bFraSiFactSafeSupported = + ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); + + // translate block size index in actual block size + const int iPrefMonoFrameSize = + iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; + + // get actual sound card buffer size using preferred size + iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); + + // Calculate the current sound card frame size factor. In case + // the current mono block size is not a multiple of the system + // frame size, we have to use a sound card conversion buffer. + if ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) || + ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) || + ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) + { + // regular case: one of our predefined buffer sizes is available + iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES; + + // no sound card conversion buffer required + bSndCrdConversionBufferRequired = false; + } + else + { + // An unsupported sound card buffer size is currently used -> we have + // to use a conversion buffer. Per definition we use the smallest buffer + // size as llcon frame size + + // store actual sound card buffer size (stereo) + iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam; + const int iSndCardStereoBlockSizeSamConvBuff = 2 * iMonoBlockSizeSam; + + // overwrite block size by smallest supported llcon buffer size + iSndCrdFrameSizeFactor = FRAME_SIZE_FACTOR_PREFERRED; + iMonoBlockSizeSam = + SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED; + + iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; + + // inits for conversion buffer (the size of the conversion buffer must + // be the sum of input/output sizes which is the worst case fill level) + const int iConBufSize = + iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff; + + SndCrdConversionBufferIn.Init ( iConBufSize ); + SndCrdConversionBufferOut.Init ( iConBufSize ); + vecDataConvBuf.Init ( iStereoBlockSizeSam ); + + // the output conversion buffer must be filled with the inner + // block size for initialization (this is the latency which is + // introduced by the conversion buffer) to avoid buffer underruns + const CVector vZeros ( iStereoBlockSizeSam, 0 ); + SndCrdConversionBufferOut.Put ( vZeros, vZeros.Size() ); + + bSndCrdConversionBufferRequired = true; + } + + // calculate stereo (two channels) buffer size + iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; + + vecsAudioSndCrdMono.Init ( iMonoBlockSizeSam ); + vecdAudioStereo.Init ( iStereoBlockSizeSam ); + + // init reverberation + AudioReverbL.Init ( SYSTEM_SAMPLE_RATE_HZ ); + AudioReverbR.Init ( SYSTEM_SAMPLE_RATE_HZ ); + + // inits for CELT coding + if ( bCeltDoHighQuality ) + { + if ( bUseStereo ) + { + iCeltNumCodedBytes = CELT_NUM_BYTES_STEREO_HIGH_QUALITY; + } + else + { + iCeltNumCodedBytes = CELT_NUM_BYTES_MONO_HIGH_QUALITY; + } + } + else + { + if ( bUseStereo ) + { + iCeltNumCodedBytes = CELT_NUM_BYTES_STEREO_NORMAL_QUALITY; + } + else + { + iCeltNumCodedBytes = CELT_NUM_BYTES_MONO_NORMAL_QUALITY; + } + } + vecCeltData.Init ( iCeltNumCodedBytes ); + + // inits for network and channel + vecbyNetwData.Init ( iCeltNumCodedBytes ); + if ( bUseStereo ) + { + vecsNetwork.Init ( iStereoBlockSizeSam ); + + // set the channel network properties + Channel.SetAudioStreamProperties ( iCeltNumCodedBytes, + iSndCrdFrameSizeFactor, + 2 ); + } + else + { + vecsNetwork.Init ( iMonoBlockSizeSam ); + + // set the channel network properties + Channel.SetAudioStreamProperties ( iCeltNumCodedBytes, + iSndCrdFrameSizeFactor, + 1 ); + } +} + +void CClient::AudioCallback ( CVector& psData, void* arg ) +{ + // get the pointer to the object + CClient* pMyClientObj = reinterpret_cast ( arg ); + + // process audio data + pMyClientObj->ProcessSndCrdAudioData ( psData ); +} + +void CClient::ProcessSndCrdAudioData ( CVector& vecsStereoSndCrd ) +{ + // check if a conversion buffer is required or not + if ( bSndCrdConversionBufferRequired ) + { + // add new sound card block in conversion buffer + SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); + + // process all available blocks of data + while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam ) + { + // get one block of data for processing + SndCrdConversionBufferIn.Get ( vecDataConvBuf ); + + // process audio data + ProcessAudioDataIntern ( vecDataConvBuf ); + + SndCrdConversionBufferOut.Put ( vecDataConvBuf, vecDataConvBuf.Size() ); + } + + // get processed sound card block out of the conversion buffer + SndCrdConversionBufferOut.Get ( vecsStereoSndCrd ); + } + else + { + // regular case: no conversion buffer required + // process audio data + ProcessAudioDataIntern ( vecsStereoSndCrd ); + } +} + +void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) +{ + int i, j; + + // Transmit signal --------------------------------------------------------- + // update stereo signal level meter + SignalLevelMeter.Update ( vecsStereoSndCrd ); + + // convert data from short to double + for ( i = 0; i < iStereoBlockSizeSam; i++ ) + { + vecdAudioStereo[i] = static_cast ( vecsStereoSndCrd[i] ); + } + + // add reverberation effect if activated + if ( iReverbLevel != 0 ) + { + // calculate attenuation amplification factor + const double dRevLev = + static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 2; + + if ( bUseStereo ) + { + // for stereo always apply reverberation effect on both channels + for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) + { + // left channel + vecdAudioStereo[i] += + dRevLev * AudioReverbL.ProcessSample ( vecdAudioStereo[i] ); + + // right channel + vecdAudioStereo[i + 1] += + dRevLev * AudioReverbR.ProcessSample ( vecdAudioStereo[i + 1] ); + } + } + else + { + if ( bReverbOnLeftChan ) + { + for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) + { + // left channel + vecdAudioStereo[i] += + dRevLev * AudioReverbL.ProcessSample ( vecdAudioStereo[i] ); + } + } + else + { + for ( i = 1; i < iStereoBlockSizeSam; i += 2 ) + { + // right channel + vecdAudioStereo[i] += + dRevLev * AudioReverbR.ProcessSample ( vecdAudioStereo[i] ); + } + } + } + } + + // mix both signals depending on the fading setting, convert + // from double to short + if ( iAudioInFader == AUD_FADER_IN_MIDDLE ) + { + if ( bUseStereo ) + { + // perform type conversion + for ( i = 0; i < iStereoBlockSizeSam; i++ ) + { + vecsNetwork[i] = Double2Short ( vecdAudioStereo[i] ); + } + } + else + { + // mix channels together + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + vecsNetwork[i] = + Double2Short ( ( vecdAudioStereo[j] + + vecdAudioStereo[j + 1] ) / 2 ); + } + } + } + else + { + if ( bUseStereo ) + { + // stereo + const double dAttFactStereo = static_cast ( + 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 right channel + vecsNetwork[j] = Double2Short ( + vecdAudioStereo[j] ); + + vecsNetwork[j + 1] = Double2Short ( + dAttFactStereo * vecdAudioStereo[j + 1] ); + } + } + else + { + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // attenuation on left channel + vecsNetwork[j] = Double2Short ( + dAttFactStereo * vecdAudioStereo[j] ); + + vecsNetwork[j + 1] = Double2Short ( + vecdAudioStereo[j + 1] ); + } + } + } + else + { + // mono + // make sure that in the middle position the two channels are + // amplified by 1/2, if the pan is set to one channel, this + // channel should have an amplification of 1 + const double dAttFactMono = static_cast ( + AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / + AUD_FADER_IN_MIDDLE / 2; + + const double dAmplFactMono = 0.5 + static_cast ( + abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / + AUD_FADER_IN_MIDDLE / 2; + + if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) + { + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // attenuation on right channel + vecsNetwork[i] = Double2Short ( + dAmplFactMono * vecdAudioStereo[j] + + dAttFactMono * vecdAudioStereo[j + 1] ); + } + } + else + { + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // attenuation on left channel + vecsNetwork[i] = Double2Short ( + dAmplFactMono * vecdAudioStereo[j + 1] + + dAttFactMono * vecdAudioStereo[j] ); + } + } + } + } + + for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) + { + if ( bUseStereo ) + { + // encode current audio frame with CELT encoder + celt_encode ( CeltEncoderStereo, + &vecsNetwork[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES], + NULL, + &vecCeltData[0], + iCeltNumCodedBytes ); + } + else + { + // encode current audio frame with CELT encoder + celt_encode ( CeltEncoderMono, + &vecsNetwork[i * SYSTEM_FRAME_SIZE_SAMPLES], + NULL, + &vecCeltData[0], + iCeltNumCodedBytes ); + } + + // send coded audio through the network + Socket.SendPacket ( Channel.PrepSendPacket ( vecCeltData ), + Channel.GetAddress() ); + } + + + // Receive signal ---------------------------------------------------------- + for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) + { + // receive a new block + const bool bReceiveDataOk = + ( Channel.GetData ( vecbyNetwData ) == GS_BUFFER_OK ); + + if ( bReceiveDataOk ) + { + PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN ); + } + else + { + PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED ); + } + + // CELT decoding + if ( bReceiveDataOk ) + { + if ( bUseStereo ) + { + celt_decode ( CeltDecoderStereo, + &vecbyNetwData[0], + iCeltNumCodedBytes, + &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES] ); + } + else + { + celt_decode ( CeltDecoderMono, + &vecbyNetwData[0], + iCeltNumCodedBytes, + &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES] ); + } + } + else + { + // lost packet + if ( bUseStereo ) + { + celt_decode ( CeltDecoderStereo, + NULL, + 0, + &vecsStereoSndCrd[i * 2 * SYSTEM_FRAME_SIZE_SAMPLES] ); + } + else + { + celt_decode ( CeltDecoderMono, + NULL, + 0, + &vecsAudioSndCrdMono[i * SYSTEM_FRAME_SIZE_SAMPLES] ); + } + } + } + + +/* +// TEST +// fid=fopen('v.dat','r');x=fread(fid,'int16');fclose(fid); +static FILE* pFileDelay = fopen("c:\\temp\\test2.dat", "wb"); +short sData[2]; +for (i = 0; i < iMonoBlockSizeSam; i++) +{ + sData[0] = (short) vecsAudioSndCrdMono[i]; + fwrite(&sData, size_t(2), size_t(1), pFileDelay); +} +fflush(pFileDelay); +*/ + + + // check if channel is connected + if ( Channel.IsConnected() ) + { + if ( !bUseStereo ) + { + // copy mono data in stereo sound card buffer + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = + vecsAudioSndCrdMono[i]; + } + } + } + else + { + // if not connected, clear data + vecsStereoSndCrd.Reset ( 0 ); + } + + // update socket buffer size + Channel.UpdateSocketBufferSize(); +} + +int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) +{ +/* + For estimating the overall delay, use the following assumptions: + - the mean delay of a cyclic buffer is half the buffer size (since + for the average it is assumed that the buffer is half filled) + - consider the jitter buffer on the server side, too +*/ + // the buffer sizes at client and server divided by 2 (half the buffer + // for the delay) is the total socket buffer size + const double dTotalJitterBufferDelayMs = SYSTEM_BLOCK_DURATION_MS_FLOAT * + static_cast ( GetSockBufNumFrames() + + GetServerSockBufNumFrames() ) / 2; + + // we assume that we have two period sizes for the input and one for the + // output, therefore we have "3 *" instead of "2 *" (for input and output) + // the actual sound card buffer size, also consider delay introduced by + // sound card conversion buffer by using + // "GetSndCrdConvBufAdditionalDelayMonoBlSize" + const double dTotalSoundCardDelayMs = + ( 3 * GetSndCrdActualMonoBlSize() + + GetSndCrdConvBufAdditionalDelayMonoBlSize() ) * + 1000 / SYSTEM_SAMPLE_RATE_HZ; + + // network packets are of the same size as the audio packets per definition + // if no sound card conversion buffer is used + const double dDelayToFillNetworkPacketsMs = + GetSystemMonoBlSize() * 1000 / SYSTEM_SAMPLE_RATE_HZ; + + // CELT additional delay at small frame sizes is half a frame size + const double dAdditionalAudioCodecDelayMs = + SYSTEM_BLOCK_DURATION_MS_FLOAT / 2; + + const double dTotalBufferDelayMs = + dDelayToFillNetworkPacketsMs + + dTotalJitterBufferDelayMs + + dTotalSoundCardDelayMs + + dAdditionalAudioCodecDelayMs; + + return LlconMath::round ( dTotalBufferDelayMs + iPingTimeMs ); +} diff --git a/src/client.h b/src/client.h index 9a5ac1c5..36cf474c 100755 --- a/src/client.h +++ b/src/client.h @@ -335,7 +335,7 @@ public slots: int iMs, int iNumClients ); - void OnSndCrdReinitRequest(); + void OnSndCrdReinitRequest ( ESndCrdResetType eSndCrdResetType ); signals: void ConClientListMesReceived ( CVector vecChanInfo ); diff --git a/src/soundbase.h b/src/soundbase.h index 76f017ea..cae24c9a 100755 --- a/src/soundbase.h +++ b/src/soundbase.h @@ -1,115 +1,126 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#if !defined ( SOUNDBASE_HOIHGEH8_3_4344456456345634565KJIUHF1912__INCLUDED_ ) -#define SOUNDBASE_HOIHGEH8_3_4344456456345634565KJIUHF1912__INCLUDED_ - -#include -#include -#include -#include "global.h" -#include "util.h" - - -/* Classes ********************************************************************/ -class CSoundBase : public QThread -{ - Q_OBJECT - -public: - CSoundBase ( const QString& strNewSystemDriverTechniqueName, - const bool bNewIsCallbackAudioInterface, - void (*fpNewProcessCallback) ( CVector& psData, void* pParg ), - void* pParg ); - - virtual int Init ( const int iNewPrefMonoBufferSize ); - virtual void Start(); - virtual void Stop(); - - // device selection - virtual int GetNumDev() { return lNumDevs; } - virtual QString GetDeviceName ( const int iDiD ) { return strDriverNames[iDiD]; } - virtual QString SetDev ( const int ); - virtual int GetDev() { return lCurDev; } - - virtual int GetNumInputChannels() { return 2; } - virtual QString GetInputChannelName ( const int ) { return "Default"; } - virtual void SetLeftInputChannel ( const int ) {} - virtual void SetRightInputChannel ( const int ) {} - virtual int GetLeftInputChannel() { return 0; } - virtual int GetRightInputChannel() { return 1; } - - virtual int GetNumOutputChannels() { return 2; } - virtual QString GetOutputChannelName ( const int ) { return "Default"; } - virtual void SetLeftOutputChannel ( const int ) {} - virtual void SetRightOutputChannel ( const int ) {} - virtual int GetLeftOutputChannel() { return 0; } - virtual int GetRightOutputChannel() { return 1; } - - virtual void OpenDriverSetup() {} - - bool IsRunning() const { return bRun; } - - // TODO this should be protected but since it is used - // in a callback function it has to be public -> better solution - void EmitReinitRequestSignal() { emit ReinitRequest(); } - -protected: - // driver handling - virtual QString LoadAndInitializeDriver ( int ) { return ""; } - virtual void UnloadCurrentDriver() {} - QVector LoadAndInitializeFirstValidDriver(); - - // function pointer to callback function - void (*fpProcessCallback) ( CVector& psData, void* arg ); - void* pProcessCallbackArg; - - // callback function call for derived classes - void ProcessCallback ( CVector& psData ) - { - (*fpProcessCallback) ( psData, pProcessCallbackArg ); - } - - // these functions should be overwritten by derived class for - // non callback based audio interfaces - virtual bool Read ( CVector& ) { printf ( "no sound!" ); return false; } - virtual bool Write ( CVector& ) { printf ( "no sound!" ); return false; } - - void run(); - bool bRun; - - bool bIsCallbackAudioInterface; - QString strSystemDriverTechniqueName; - - CVector vecsAudioSndCrdStereo; - - long lNumDevs; - long lCurDev; - QString strDriverNames[MAX_NUMBER_SOUND_CARDS]; - -signals: - void ReinitRequest(); -}; - -#endif /* !defined ( SOUNDBASE_HOIHGEH8_3_4344456456345634565KJIUHF1912__INCLUDED_ ) */ +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#if !defined ( SOUNDBASE_HOIHGEH8_3_4344456456345634565KJIUHF1912__INCLUDED_ ) +#define SOUNDBASE_HOIHGEH8_3_4344456456345634565KJIUHF1912__INCLUDED_ + +#include +#include +#include +#include "global.h" +#include "util.h" + + +// TODO better solution with enum definition +// problem: in signals it seems not to work to use CSoundBase::ESndCrdResetType +enum ESndCrdResetType +{ + RS_ONLY_RESTART, + RS_ONLY_RESTART_AND_INIT, + RS_RELOAD_RESTART_AND_INIT +}; + + +/* Classes ********************************************************************/ +class CSoundBase : public QThread +{ + Q_OBJECT + +public: + CSoundBase ( const QString& strNewSystemDriverTechniqueName, + const bool bNewIsCallbackAudioInterface, + void (*fpNewProcessCallback) ( CVector& psData, void* pParg ), + void* pParg ); + + virtual int Init ( const int iNewPrefMonoBufferSize ); + virtual void Start(); + virtual void Stop(); + + // device selection + virtual int GetNumDev() { return lNumDevs; } + virtual QString GetDeviceName ( const int iDiD ) { return strDriverNames[iDiD]; } + virtual QString SetDev ( const int ); + virtual int GetDev() { return lCurDev; } + + virtual int GetNumInputChannels() { return 2; } + virtual QString GetInputChannelName ( const int ) { return "Default"; } + virtual void SetLeftInputChannel ( const int ) {} + virtual void SetRightInputChannel ( const int ) {} + virtual int GetLeftInputChannel() { return 0; } + virtual int GetRightInputChannel() { return 1; } + + virtual int GetNumOutputChannels() { return 2; } + virtual QString GetOutputChannelName ( const int ) { return "Default"; } + virtual void SetLeftOutputChannel ( const int ) {} + virtual void SetRightOutputChannel ( const int ) {} + virtual int GetLeftOutputChannel() { return 0; } + virtual int GetRightOutputChannel() { return 1; } + + virtual void OpenDriverSetup() {} + + bool IsRunning() const { return bRun; } + + // TODO this should be protected but since it is used + // in a callback function it has to be public -> better solution + void EmitReinitRequestSignal ( const ESndCrdResetType eSndCrdResetType ) + { emit ReinitRequest ( eSndCrdResetType ); } + +protected: + // driver handling + virtual QString LoadAndInitializeDriver ( int ) { return ""; } + virtual void UnloadCurrentDriver() {} + QVector LoadAndInitializeFirstValidDriver(); + + // function pointer to callback function + void (*fpProcessCallback) ( CVector& psData, void* arg ); + void* pProcessCallbackArg; + + // callback function call for derived classes + void ProcessCallback ( CVector& psData ) + { + (*fpProcessCallback) ( psData, pProcessCallbackArg ); + } + + // these functions should be overwritten by derived class for + // non callback based audio interfaces + virtual bool Read ( CVector& ) { printf ( "no sound!" ); return false; } + virtual bool Write ( CVector& ) { printf ( "no sound!" ); return false; } + + void run(); + bool bRun; + + bool bIsCallbackAudioInterface; + QString strSystemDriverTechniqueName; + + CVector vecsAudioSndCrdStereo; + + long lNumDevs; + long lCurDev; + QString strDriverNames[MAX_NUMBER_SOUND_CARDS]; + +signals: + void ReinitRequest ( ESndCrdResetType eSndCrdResetType ); +}; + +#endif /* !defined ( SOUNDBASE_HOIHGEH8_3_4344456456345634565KJIUHF1912__INCLUDED_ ) */ diff --git a/windows/sound.cpp b/windows/sound.cpp index 779d3e6e..125b7f58 100755 --- a/windows/sound.cpp +++ b/windows/sound.cpp @@ -959,6 +959,7 @@ long CSound::asioMessages ( long selector, double* ) { long ret = 0; + switch ( selector ) { case kAsioEngineVersion: @@ -968,26 +969,16 @@ long CSound::asioMessages ( long selector, // both messages might be send if the buffer size changes case kAsioBufferSizeChange: + pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART_AND_INIT ); + ret = 1L; // 1L if request is accepted or 0 otherwise + break; + case kAsioResetRequest: - - -// TODO implement separate ASIO reset function in base class! - -// kAsioBufferSizeChange: -// Informs the application that the driver has a new preferred buffer size. -// -// kAsioResetRequest: -// Requests a driver reset. If accepted, this will close the driver -// (ASIOExit() ) and re-open it again (ASIOInit() etc.) at the next -// "safe" time (usually during the application IDLE time). Some -// drivers need to reconfigure for instance when the sample rate -// changes, or some basic changes have been made in ASIOControlPanel(). - - - pSound->EmitReinitRequestSignal(); + pSound->EmitReinitRequestSignal ( RS_RELOAD_RESTART_AND_INIT ); ret = 1L; // 1L if request is accepted or 0 otherwise break; } + return ret; }