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 );