diff --git a/linux/sound.h b/linux/sound.h index 8ac06b75..afc91971 100755 --- a/linux/sound.h +++ b/linux/sound.h @@ -1,5 +1,5 @@ /******************************************************************************\ - * Copyright (c) 2004-2005 + * Copyright (c) 2004-2008 * * Author(s): * Volker Fischer, Alexander Kurpiers @@ -53,15 +53,11 @@ public: {} virtual ~CSound() {Close();} - /* Not implemented yet, always return one device and default string */ - int GetNumDev() {return 1;} - void SetOutDev(int iNewDev) {} - void SetInDev(int iNewDev) {} - - /* Return invalid device ID which is the same as using "wave mapper" which - we assume here to be used */ - int GetOutDev() {return 1;} - int GetInDev() {return 1;} + // not implemented yet, always return one device and default string + int GetNumDev() { return 1; } + std::string GetDeviceName ( const int iDiD ) { return "wave mapper"; } + int SetDev ( const int iNewDev ) {} // dummy + int GetDev() { return 0; } #if WITH_SOUND void SetInNumBuf(int iNewNum); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 65cea0e3..e945fa8f 100755 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -108,6 +108,14 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, double ( iCurNetBufSiFactOut * MIN_BLOCK_DURATION_MS), 'f', 2 ) + " ms" ); + // init combo box containing all available sound cards in the system + cbSoundcard->clear(); + for ( int iSndDevIdx = 0; iSndDevIdx < pClient->GetSndInterface()->GetNumDev(); iSndDevIdx++ ) + { + cbSoundcard->addItem ( pClient->GetSndInterface()->GetDeviceName ( iSndDevIdx ).c_str() ); + } + cbSoundcard->setCurrentIndex ( pClient->GetSndInterface()->GetDev() ); + // "OpenChatOnNewMessage" check box if ( pClient->GetOpenChatOnNewMessage() ) { @@ -164,6 +172,10 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, QObject::connect ( cbOpenChatOnNewMessage, SIGNAL ( stateChanged ( int ) ), this, SLOT ( OnOpenChatOnNewMessageStateChanged ( int ) ) ); + // combo boxes + QObject::connect ( cbSoundcard, SIGNAL ( activated ( int ) ), + this, SLOT ( OnSoundCrdSelection ( int ) ) ); + // misc QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ), this, SLOT ( OnPingTimeResult ( int ) ) ); @@ -228,6 +240,23 @@ void CClientSettingsDlg::OnSliderNetBufSiFactOut ( int value ) UpdateDisplay(); } +void CClientSettingsDlg::OnSoundCrdSelection ( int iSndDevIdx ) +{ + try + { + pClient->GetSndInterface()->SetDev ( iSndDevIdx ); + } + catch ( CGenErr generr ) + { + QMessageBox::critical ( 0, APP_NAME, + QString ( "The selected audio device could not be used because " + "of the following error: " ) + generr.GetErrorText(), "Ok", 0 ); + + // recover old selection + cbSoundcard->setCurrentIndex ( pClient->GetSndInterface()->GetDev() ); + } +} + void CClientSettingsDlg::OnOpenChatOnNewMessageStateChanged ( int value ) { pClient->SetOpenChatOnNewMessage ( value == Qt::Checked ); diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 3436c0d9..a41ef2cc 100755 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -83,4 +83,5 @@ public slots: void OnOpenChatOnNewMessageStateChanged ( int value ); void OnAudioCompressionButtonGroupClicked ( QAbstractButton* button ); void OnPingTimeResult ( int iPingTime ); + void OnSoundCrdSelection ( int iSndDevIdx ); }; diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index ed51aeb7..d84904bc 100755 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -5,8 +5,8 @@ 0 0 - 478 - 310 + 506 + 329 @@ -723,6 +723,33 @@ Misc + + + + + + Soundcard + + + + + + + + 0 + 0 + + + + + 153 + 0 + + + + + + @@ -733,7 +760,7 @@ - + 0 @@ -796,7 +823,7 @@ - + Ping Time: @@ -863,7 +890,7 @@ 0 - + Protocol Status: diff --git a/src/global.h b/src/global.h index b6298e48..279d9e16 100755 --- a/src/global.h +++ b/src/global.h @@ -81,6 +81,9 @@ // number of ticks of audio in/out buffer sliders # define AUD_SLIDER_LENGTH 8 +// maximum number of recognized sound cards installed in the system +#define MAX_NUMBER_SOUND_CARDS 10 + // maximum number of internet connections (channels) // if you want to change this paramter, there has to be done code modifications // on other places, too! The code tag "MAX_NUM_CHANNELS_TAG" shows these places diff --git a/src/settings.cpp b/src/settings.cpp index 725bb51b..0913385c 100755 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -77,10 +77,24 @@ void CSettings::ReadIniFile ( const QString& sFileName ) pClient->SetReverbOnLeftChan ( bValue ); } + // sound card selection + // special case with this setting: the sound card initialization depends on this setting + // call, therefore, if no setting file parameter could be retrieved, the sound card is + // initialized with a default setting defined here + if ( GetNumericIniSet ( IniXMLDocument, "client", "auddevidx", 1, MAX_NUMBER_SOUND_CARDS, iValue ) ) + { + pClient->GetSndInterface()->SetDev ( iValue ); + } + else + { + // default setting: use first sound card in system + pClient->GetSndInterface()->SetDev ( 0 ); + } + // sound card in number of buffers if ( GetNumericIniSet ( IniXMLDocument, "client", "audinbuf", 1, AUD_SLIDER_LENGTH, iValue ) ) { - pClient->GetSndInterface()->SetInNumBuf( iValue ); + pClient->GetSndInterface()->SetInNumBuf ( iValue ); } // sound card out number of buffers @@ -156,6 +170,9 @@ void CSettings::WriteIniFile ( const QString& sFileName ) // reverberation channel assignment SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", pClient->IsReverbOnLeftChan() ); + // sound card selection + SetNumericIniSet ( IniXMLDocument, "client", "auddevidx", pClient->GetSndInterface()->GetDev() ); + // sound card in number of buffers SetNumericIniSet ( IniXMLDocument, "client", "audinbuf", pClient->GetSndInterface()->GetInNumBuf() ); diff --git a/windows/sound.cpp b/windows/sound.cpp index 2ebcb663..90ded758 100755 --- a/windows/sound.cpp +++ b/windows/sound.cpp @@ -249,6 +249,150 @@ void CSound::SetOutNumBuf ( int iNewNum ) /******************************************************************************\ * Common * \******************************************************************************/ +void CSound::SetDev ( const int iNewDev ) +{ + // check if an ASIO driver was already initialized + if ( lCurDev >= 0 ) + { + // a device was already been initialized and is used, kill working + // thread and clean up + // set event to ensure that thread leaves the waiting function + if ( m_ASIOEvent != NULL ) + { + SetEvent ( m_ASIOEvent ); + } + + // wait for the thread to terminate + Sleep ( 500 ); + + // stop audio and dispose ASIO buffers + ASIOStop(); + ASIODisposeBuffers(); + + // remove old driver + ASIOExit(); + asioDrivers->removeCurrentDriver(); + + const std::string strErrorMessage = LoadAndInitializeDriver ( iNewDev ); + + if ( !strErrorMessage.empty() ) + { + // loading and initializing the new driver failed, go back to original + // driver and display error message + LoadAndInitializeDriver ( lCurDev ); + + throw CGenErr ( strErrorMessage.c_str() ); + } + } + else + { + // This is the first time a driver is to be initialized, we first try + // to load the selected driver, if this fails, we try to load the first + // available driver in the system. If this fails, too, we throw an error + // that no driver is available -> it does not make sense to start the llcon + // software if no audio hardware is available + const std::string strErrorMessage = LoadAndInitializeDriver ( iNewDev ); + + if ( !strErrorMessage.empty() ) + { + // loading and initializing the new driver failed, try to find at + // least one usable driver + if ( !LoadAndInitializeFirstValidDriver() ) + { + throw CGenErr ( "No usable ASIO audio device (driver) found." ); + } + } + } +} + +std::string CSound::LoadAndInitializeDriver ( const int iDriverIdx ) +{ + // load driver + loadAsioDriver ( cDriverNames[iDriverIdx] ); + if ( ASIOInit ( &driverInfo ) != ASE_OK ) + { + // clean up and return error string + asioDrivers->removeCurrentDriver(); + return "The audio driver could not be initialized."; + } + + // check the number of available channels + long lNumInChan; + long lNumOutChan; + ASIOGetChannels ( &lNumInChan, &lNumOutChan ); + if ( ( lNumInChan < NUM_IN_OUT_CHANNELS ) || + ( lNumOutChan < NUM_IN_OUT_CHANNELS ) ) + { + // clean up and return error string + ASIOExit(); + asioDrivers->removeCurrentDriver(); + return "The audio device does not support the " + "required number of channels."; + } + + // set the sample rate and check if sample rate is supported + ASIOSetSampleRate ( SND_CRD_SAMPLE_RATE ); + + ASIOSampleRate sampleRate; + ASIOGetSampleRate ( &sampleRate ); + if ( sampleRate != SND_CRD_SAMPLE_RATE ) + { + // clean up and return error string + ASIOExit(); + asioDrivers->removeCurrentDriver(); + return "The audio device does not support the " + "required sample rate."; + } + + // query the usable buffer sizes + ASIOGetBufferSize ( &HWBufferInfo.lMinSize, + &HWBufferInfo.lMaxSize, + &HWBufferInfo.lPreferredSize, + &HWBufferInfo.lGranularity ); + + // check wether the driver requires the ASIOOutputReady() optimization + // (can be used by the driver to reduce output latency by one block) + bASIOPostOutput = ( ASIOOutputReady() == ASE_OK ); + + // store ID of selected driver + lCurDev = iDriverIdx; + + return ""; +} + +bool CSound::LoadAndInitializeFirstValidDriver() +{ + // load and initialize first valid ASIO driver + bool bValidDriverDetected = false; + int iCurDriverIdx = 0; + + // try all available drivers in the system ("lNumDevs" devices) + while ( !bValidDriverDetected && iCurDriverIdx < lNumDevs ) + { + if ( loadAsioDriver ( cDriverNames[iCurDriverIdx] ) ) + { + if ( ASIOInit ( &driverInfo ) == ASE_OK ) + { + // initialization was successful + bValidDriverDetected = true; + + // store ID of selected driver + lCurDev = iCurDriverIdx; + } + else + { + // driver could not be loaded, free memory + asioDrivers->removeCurrentDriver(); + } + } + + // try next driver + iCurDriverIdx++; + } + + return bValidDriverDetected; +} + void CSound::InitRecordingAndPlayback ( int iNewBufferSize ) { int i; @@ -429,8 +573,6 @@ void CSound::Close() CSound::CSound() { - int i; - // init number of sound buffers iNewNumSndBufIn = NUM_SOUND_BUFFERS_IN; iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN; @@ -441,85 +583,26 @@ CSound::CSound() m_ASIOEvent = NULL; // get available ASIO driver names in system - char* cDriverNames[MAX_NUMBER_SOUND_CARDS]; - for ( i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ ) + for ( int i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ ) { cDriverNames[i] = new char[32]; } loadAsioDriver ( "dummy" ); // to initialize external object - const long lNumDetDriv = - asioDrivers->getDriverNames ( cDriverNames, MAX_NUMBER_SOUND_CARDS ); - - - // load and initialize first valid ASIO driver - bool bValidDriverDetected = false; - int iCurDriverIdx = 0; - - while ( !bValidDriverDetected && iCurDriverIdx < lNumDetDriv ) - { - if ( loadAsioDriver ( cDriverNames[iCurDriverIdx] ) ) - { - if ( ASIOInit ( &driverInfo ) == ASE_OK ) - { - bValidDriverDetected = true; - } - else - { - // driver could not be loaded, free memory - asioDrivers->removeCurrentDriver(); - } - } - - // try next driver - iCurDriverIdx++; - } + lNumDevs = asioDrivers->getDriverNames ( cDriverNames, MAX_NUMBER_SOUND_CARDS ); // in case we do not have a driver available, throw error - if ( !bValidDriverDetected ) + if ( lNumDevs == 0 ) { - throw CGenErr ( "No suitable ASIO audio device found." ); + throw CGenErr ( "No ASIO audio device (driver) found." ); } + asioDrivers->removeCurrentDriver(); -// TEST we only use one driver for a first try -iNumDevs = 1; -pstrDevices[0] = driverInfo.name; + // init device index with illegal value to show that driver is not initialized + lCurDev = -1; - - // check the number of available channels - long lNumInChan; - long lNumOutChan; - ASIOGetChannels ( &lNumInChan, &lNumOutChan ); - if ( ( lNumInChan < NUM_IN_OUT_CHANNELS ) || - ( lNumOutChan < NUM_IN_OUT_CHANNELS ) ) - { - throw CGenErr ( "The audio device does not support the " - "required number of channels." ); - } - - // query the usable buffer sizes - ASIOGetBufferSize ( &HWBufferInfo.lMinSize, - &HWBufferInfo.lMaxSize, - &HWBufferInfo.lPreferredSize, - &HWBufferInfo.lGranularity ); - - // set the sample rate and check if sample rate is supported - ASIOSetSampleRate ( SND_CRD_SAMPLE_RATE ); - - ASIOSampleRate sampleRate; - ASIOGetSampleRate ( &sampleRate ); - if ( sampleRate != SND_CRD_SAMPLE_RATE ) - { - throw CGenErr ( "The audio device does not support the " - "required sample rate." ); - } - - // check wether the driver requires the ASIOOutputReady() optimization - // (can be used by the driver to reduce output latency by one block) - bASIOPostOutput = ( ASIOOutputReady() == ASE_OK ); - - // set up the asioCallback structure and create the ASIO data buffer + // set up the asioCallback structure asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.asioMessage = &asioMessages; @@ -533,9 +616,9 @@ pstrDevices[0] = driverInfo.name; // create event m_ASIOEvent = CreateEvent ( NULL, FALSE, FALSE, NULL ); - // init flags - bChangParamIn = false; - bChangParamOut = false; + // init flags (initiate init for first run) + bChangParamIn = true; + bChangParamOut = true; } CSound::~CSound() diff --git a/windows/sound.h b/windows/sound.h index 1783c9c1..094e9f44 100755 --- a/windows/sound.h +++ b/windows/sound.h @@ -50,9 +50,6 @@ #define NUM_SOUND_BUFFERS_IN 4 #define NUM_SOUND_BUFFERS_OUT 4 -// maximum number of recognized sound cards installed in the system -#define MAX_NUMBER_SOUND_CARDS 10 - /* Classes ********************************************************************/ #ifdef USE_ASIO_SND_INTERFACE @@ -71,26 +68,36 @@ public: CSound(); virtual ~CSound(); - void InitRecording ( int iNewBufferSize, bool bNewBlocking = true ) { bBlockingRec = bNewBlocking; InitRecordingAndPlayback ( iNewBufferSize ); } - void InitPlayback ( int iNewBufferSize, bool bNewBlocking = false ) { bBlockingPlay = bNewBlocking; InitRecordingAndPlayback ( iNewBufferSize ); } + void InitRecording ( const int iNewBufferSize, const bool bNewBlocking = true ) + { + bBlockingRec = bNewBlocking; + InitRecordingAndPlayback ( iNewBufferSize ); + } + void InitPlayback ( const int iNewBufferSize, const bool bNewBlocking = false ) + { + bBlockingPlay = bNewBlocking; + InitRecordingAndPlayback ( iNewBufferSize ); + } bool Read ( CVector& psData ); bool Write ( CVector& psData ); - int GetNumDev() { return iNumDevs; } - std::string GetDeviceName ( int iDiD ) { return pstrDevices[iDiD]; } - void SetOutDev ( int iNewDev ) {} // not supported - int GetOutDev() { return 0; } // not supported - void SetInDev ( int iNewDev ) {} // not supported - int GetInDev() { return 0; } // not supported - void SetOutNumBuf ( int iNewNum ); + int GetNumDev() { return lNumDevs; } + std::string GetDeviceName ( const int iDiD ) { return cDriverNames[iDiD]; } + + void SetDev ( const int iNewDev ); + int GetDev() { return lCurDev; } + + void SetOutNumBuf ( const int iNewNum ); int GetOutNumBuf(); - void SetInNumBuf ( int iNewNum ); + void SetInNumBuf ( const int iNewNum ); int GetInNumBuf(); void Close(); protected: - void InitRecordingAndPlayback ( int iNewBufferSize ); + bool LoadAndInitializeFirstValidDriver(); + std::string LoadAndInitializeDriver ( const int iIdx ); + void InitRecordingAndPlayback ( const int iNewBufferSize ); // audio hardware buffer info struct sHWBufferInfo @@ -107,8 +114,10 @@ protected: static void sampleRateChanged ( ASIOSampleRate sRate ) {} static long asioMessages ( long selector, long value, void* message, double* opt ); - int iNumDevs; - std::string pstrDevices[MAX_NUMBER_SOUND_CARDS]; + long lNumDevs; + long lCurDev; + char* cDriverNames[MAX_NUMBER_SOUND_CARDS]; + bool bChangParamIn; bool bChangParamOut; @@ -131,6 +140,11 @@ public: int GetNumDev() { return iNumDevs; } std::string GetDeviceName ( int iDiD ) { return pstrDevices[iDiD]; } + + // dummy functions + int SetDev ( const int iNewDev ) {} + int GetDev() { return 0; } + void SetOutDev ( int iNewDev ); int GetOutDev() { return iCurOutDev; } void SetInDev ( int iNewDev );