new feature: soundcard selection

This commit is contained in:
Volker Fischer 2008-10-30 23:23:26 +00:00
parent d72746279d
commit 1c2535db16
8 changed files with 274 additions and 104 deletions

View file

@ -1,5 +1,5 @@
/******************************************************************************\ /******************************************************************************\
* Copyright (c) 2004-2005 * Copyright (c) 2004-2008
* *
* Author(s): * Author(s):
* Volker Fischer, Alexander Kurpiers * Volker Fischer, Alexander Kurpiers
@ -53,15 +53,11 @@ public:
{} {}
virtual ~CSound() {Close();} virtual ~CSound() {Close();}
/* Not implemented yet, always return one device and default string */ // not implemented yet, always return one device and default string
int GetNumDev() {return 1;} int GetNumDev() { return 1; }
void SetOutDev(int iNewDev) {} std::string GetDeviceName ( const int iDiD ) { return "wave mapper"; }
void SetInDev(int iNewDev) {} int SetDev ( const int iNewDev ) {} // dummy
int GetDev() { return 0; }
/* 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;}
#if WITH_SOUND #if WITH_SOUND
void SetInNumBuf(int iNewNum); void SetInNumBuf(int iNewNum);

View file

@ -108,6 +108,14 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
double ( iCurNetBufSiFactOut * MIN_BLOCK_DURATION_MS), 'f', 2 ) + double ( iCurNetBufSiFactOut * MIN_BLOCK_DURATION_MS), 'f', 2 ) +
" ms" ); " 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 // "OpenChatOnNewMessage" check box
if ( pClient->GetOpenChatOnNewMessage() ) if ( pClient->GetOpenChatOnNewMessage() )
{ {
@ -164,6 +172,10 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
QObject::connect ( cbOpenChatOnNewMessage, SIGNAL ( stateChanged ( int ) ), QObject::connect ( cbOpenChatOnNewMessage, SIGNAL ( stateChanged ( int ) ),
this, SLOT ( OnOpenChatOnNewMessageStateChanged ( int ) ) ); this, SLOT ( OnOpenChatOnNewMessageStateChanged ( int ) ) );
// combo boxes
QObject::connect ( cbSoundcard, SIGNAL ( activated ( int ) ),
this, SLOT ( OnSoundCrdSelection ( int ) ) );
// misc // misc
QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ), QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ),
this, SLOT ( OnPingTimeResult ( int ) ) ); this, SLOT ( OnPingTimeResult ( int ) ) );
@ -228,6 +240,23 @@ void CClientSettingsDlg::OnSliderNetBufSiFactOut ( int value )
UpdateDisplay(); 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 ) void CClientSettingsDlg::OnOpenChatOnNewMessageStateChanged ( int value )
{ {
pClient->SetOpenChatOnNewMessage ( value == Qt::Checked ); pClient->SetOpenChatOnNewMessage ( value == Qt::Checked );

View file

@ -83,4 +83,5 @@ public slots:
void OnOpenChatOnNewMessageStateChanged ( int value ); void OnOpenChatOnNewMessageStateChanged ( int value );
void OnAudioCompressionButtonGroupClicked ( QAbstractButton* button ); void OnAudioCompressionButtonGroupClicked ( QAbstractButton* button );
void OnPingTimeResult ( int iPingTime ); void OnPingTimeResult ( int iPingTime );
void OnSoundCrdSelection ( int iSndDevIdx );
}; };

View file

@ -5,8 +5,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>478</width> <width>506</width>
<height>310</height> <height>329</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle" >
@ -723,6 +723,33 @@
<string>Misc</string> <string>Misc</string>
</property> </property>
<layout class="QVBoxLayout" > <layout class="QVBoxLayout" >
<item>
<layout class="QVBoxLayout" >
<item>
<widget class="QLabel" name="lSoundcard" >
<property name="text" >
<string>Soundcard</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbSoundcard" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Minimum" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>153</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
<item> <item>
@ -733,7 +760,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label" > <widget class="QLabel" name="lOpenChat" >
<property name="sizePolicy" > <property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" > <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -796,7 +823,7 @@
<item> <item>
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
<item> <item>
<widget class="QLabel" name="TextLabel1_3" > <widget class="QLabel" name="lPingTime" >
<property name="text" > <property name="text" >
<string>Ping Time:</string> <string>Ping Time:</string>
</property> </property>
@ -863,7 +890,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="TextLabel1_2" > <widget class="QLabel" name="lProtocolStat" >
<property name="text" > <property name="text" >
<string>Protocol Status:</string> <string>Protocol Status:</string>
</property> </property>

View file

@ -81,6 +81,9 @@
// number of ticks of audio in/out buffer sliders // number of ticks of audio in/out buffer sliders
# define AUD_SLIDER_LENGTH 8 # 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) // maximum number of internet connections (channels)
// if you want to change this paramter, there has to be done code modifications // 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 // on other places, too! The code tag "MAX_NUM_CHANNELS_TAG" shows these places

View file

@ -77,10 +77,24 @@ void CSettings::ReadIniFile ( const QString& sFileName )
pClient->SetReverbOnLeftChan ( bValue ); 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 // sound card in number of buffers
if ( GetNumericIniSet ( IniXMLDocument, "client", "audinbuf", 1, AUD_SLIDER_LENGTH, iValue ) ) if ( GetNumericIniSet ( IniXMLDocument, "client", "audinbuf", 1, AUD_SLIDER_LENGTH, iValue ) )
{ {
pClient->GetSndInterface()->SetInNumBuf( iValue ); pClient->GetSndInterface()->SetInNumBuf ( iValue );
} }
// sound card out number of buffers // sound card out number of buffers
@ -156,6 +170,9 @@ void CSettings::WriteIniFile ( const QString& sFileName )
// reverberation channel assignment // reverberation channel assignment
SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", pClient->IsReverbOnLeftChan() ); SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", pClient->IsReverbOnLeftChan() );
// sound card selection
SetNumericIniSet ( IniXMLDocument, "client", "auddevidx", pClient->GetSndInterface()->GetDev() );
// sound card in number of buffers // sound card in number of buffers
SetNumericIniSet ( IniXMLDocument, "client", "audinbuf", pClient->GetSndInterface()->GetInNumBuf() ); SetNumericIniSet ( IniXMLDocument, "client", "audinbuf", pClient->GetSndInterface()->GetInNumBuf() );

View file

@ -249,6 +249,150 @@ void CSound::SetOutNumBuf ( int iNewNum )
/******************************************************************************\ /******************************************************************************\
* Common * * 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 ) void CSound::InitRecordingAndPlayback ( int iNewBufferSize )
{ {
int i; int i;
@ -429,8 +573,6 @@ void CSound::Close()
CSound::CSound() CSound::CSound()
{ {
int i;
// init number of sound buffers // init number of sound buffers
iNewNumSndBufIn = NUM_SOUND_BUFFERS_IN; iNewNumSndBufIn = NUM_SOUND_BUFFERS_IN;
iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN; iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN;
@ -441,85 +583,26 @@ CSound::CSound()
m_ASIOEvent = NULL; m_ASIOEvent = NULL;
// get available ASIO driver names in system // get available ASIO driver names in system
char* cDriverNames[MAX_NUMBER_SOUND_CARDS]; for ( int i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ )
for ( i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ )
{ {
cDriverNames[i] = new char[32]; cDriverNames[i] = new char[32];
} }
loadAsioDriver ( "dummy" ); // to initialize external object loadAsioDriver ( "dummy" ); // to initialize external object
const long lNumDetDriv = lNumDevs = asioDrivers->getDriverNames ( cDriverNames, MAX_NUMBER_SOUND_CARDS );
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++;
}
// in case we do not have a driver available, throw error // 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 // init device index with illegal value to show that driver is not initialized
iNumDevs = 1; lCurDev = -1;
pstrDevices[0] = driverInfo.name;
// set up the asioCallback structure
// 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
asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.bufferSwitch = &bufferSwitch;
asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.sampleRateDidChange = &sampleRateChanged;
asioCallbacks.asioMessage = &asioMessages; asioCallbacks.asioMessage = &asioMessages;
@ -533,9 +616,9 @@ pstrDevices[0] = driverInfo.name;
// create event // create event
m_ASIOEvent = CreateEvent ( NULL, FALSE, FALSE, NULL ); m_ASIOEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
// init flags // init flags (initiate init for first run)
bChangParamIn = false; bChangParamIn = true;
bChangParamOut = false; bChangParamOut = true;
} }
CSound::~CSound() CSound::~CSound()

View file

@ -50,9 +50,6 @@
#define NUM_SOUND_BUFFERS_IN 4 #define NUM_SOUND_BUFFERS_IN 4
#define NUM_SOUND_BUFFERS_OUT 4 #define NUM_SOUND_BUFFERS_OUT 4
// maximum number of recognized sound cards installed in the system
#define MAX_NUMBER_SOUND_CARDS 10
/* Classes ********************************************************************/ /* Classes ********************************************************************/
#ifdef USE_ASIO_SND_INTERFACE #ifdef USE_ASIO_SND_INTERFACE
@ -71,26 +68,36 @@ public:
CSound(); CSound();
virtual ~CSound(); virtual ~CSound();
void InitRecording ( int iNewBufferSize, bool bNewBlocking = true ) { bBlockingRec = bNewBlocking; InitRecordingAndPlayback ( iNewBufferSize ); } void InitRecording ( const int iNewBufferSize, const bool bNewBlocking = true )
void InitPlayback ( int iNewBufferSize, bool bNewBlocking = false ) { bBlockingPlay = bNewBlocking; InitRecordingAndPlayback ( iNewBufferSize ); } {
bBlockingRec = bNewBlocking;
InitRecordingAndPlayback ( iNewBufferSize );
}
void InitPlayback ( const int iNewBufferSize, const bool bNewBlocking = false )
{
bBlockingPlay = bNewBlocking;
InitRecordingAndPlayback ( iNewBufferSize );
}
bool Read ( CVector<short>& psData ); bool Read ( CVector<short>& psData );
bool Write ( CVector<short>& psData ); bool Write ( CVector<short>& psData );
int GetNumDev() { return iNumDevs; } int GetNumDev() { return lNumDevs; }
std::string GetDeviceName ( int iDiD ) { return pstrDevices[iDiD]; } std::string GetDeviceName ( const int iDiD ) { return cDriverNames[iDiD]; }
void SetOutDev ( int iNewDev ) {} // not supported
int GetOutDev() { return 0; } // not supported void SetDev ( const int iNewDev );
void SetInDev ( int iNewDev ) {} // not supported int GetDev() { return lCurDev; }
int GetInDev() { return 0; } // not supported
void SetOutNumBuf ( int iNewNum ); void SetOutNumBuf ( const int iNewNum );
int GetOutNumBuf(); int GetOutNumBuf();
void SetInNumBuf ( int iNewNum ); void SetInNumBuf ( const int iNewNum );
int GetInNumBuf(); int GetInNumBuf();
void Close(); void Close();
protected: protected:
void InitRecordingAndPlayback ( int iNewBufferSize ); bool LoadAndInitializeFirstValidDriver();
std::string LoadAndInitializeDriver ( const int iIdx );
void InitRecordingAndPlayback ( const int iNewBufferSize );
// audio hardware buffer info // audio hardware buffer info
struct sHWBufferInfo struct sHWBufferInfo
@ -107,8 +114,10 @@ protected:
static void sampleRateChanged ( ASIOSampleRate sRate ) {} static void sampleRateChanged ( ASIOSampleRate sRate ) {}
static long asioMessages ( long selector, long value, void* message, double* opt ); static long asioMessages ( long selector, long value, void* message, double* opt );
int iNumDevs; long lNumDevs;
std::string pstrDevices[MAX_NUMBER_SOUND_CARDS]; long lCurDev;
char* cDriverNames[MAX_NUMBER_SOUND_CARDS];
bool bChangParamIn; bool bChangParamIn;
bool bChangParamOut; bool bChangParamOut;
@ -131,6 +140,11 @@ public:
int GetNumDev() { return iNumDevs; } int GetNumDev() { return iNumDevs; }
std::string GetDeviceName ( int iDiD ) { return pstrDevices[iDiD]; } std::string GetDeviceName ( int iDiD ) { return pstrDevices[iDiD]; }
// dummy functions
int SetDev ( const int iNewDev ) {}
int GetDev() { return 0; }
void SetOutDev ( int iNewDev ); void SetOutDev ( int iNewDev );
int GetOutDev() { return iCurOutDev; } int GetOutDev() { return iCurOutDev; }
void SetInDev ( int iNewDev ); void SetInDev ( int iNewDev );