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

View file

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

View file

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

View file

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

View file

@ -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

View file

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

View file

@ -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()

View file

@ -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<short>& psData );
bool Write ( CVector<short>& 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 );