some fixes for selecting different ASIO soundcards

This commit is contained in:
Volker Fischer 2008-10-31 20:27:55 +00:00
parent 1c2535db16
commit 8d77d11ff9
7 changed files with 275 additions and 236 deletions

View file

@ -13,13 +13,10 @@
#ifdef WITH_SOUND #ifdef WITH_SOUND
/* Wave in ********************************************************************/ /* Wave in ********************************************************************/
void CSound::InitRecording(int iNewBufferSize, bool bNewBlocking) void CSound::InitRecording ( const bool bNewBlocking )
{ {
int err; int err;
/* set internal buffer size for read */
iBufferSizeIn = iNewBufferSize / NUM_IN_OUT_CHANNELS; /* mono size */
/* if recording device was already open, close it first */ /* if recording device was already open, close it first */
if ( rhandle != NULL ) if ( rhandle != NULL )
{ {
@ -112,12 +109,12 @@ void CSound::InitRecording(int iNewBufferSize, bool bNewBlocking)
qDebug ( "alsa init record done" ); qDebug ( "alsa init record done" );
} }
bool CSound::Read(CVector<short>& psData) bool CSound::Read ( CVector<short>& psData )
{ {
int ret; int ret;
/* Check if device must be opened or reinitialized */ /* Check if device must be opened or reinitialized */
if (bChangParamIn == true) if ( bChangParamIn == true )
{ {
InitRecording ( iBufferSizeIn * NUM_IN_OUT_CHANNELS ); InitRecording ( iBufferSizeIn * NUM_IN_OUT_CHANNELS );
@ -125,7 +122,7 @@ bool CSound::Read(CVector<short>& psData)
bChangParamIn = false; bChangParamIn = false;
} }
ret = snd_pcm_readi(rhandle, &psData[0], iBufferSizeIn); ret = snd_pcm_readi ( rhandle, &psData[0], iBufferSizeIn );
if ( ret < 0 ) if ( ret < 0 )
{ {
@ -143,7 +140,7 @@ bool CSound::Read(CVector<short>& psData)
ret = snd_pcm_start ( rhandle ); ret = snd_pcm_start ( rhandle );
if (ret < 0) if ( ret < 0 )
{ {
qDebug ( "Can't recover from underrun, start failed: %s", snd_strerror ( ret ) ); qDebug ( "Can't recover from underrun, start failed: %s", snd_strerror ( ret ) );
} }
@ -204,13 +201,10 @@ void CSound::SetInNumBuf ( int iNewNum )
/* Wave out *******************************************************************/ /* Wave out *******************************************************************/
void CSound::InitPlayback ( int iNewBufferSize, bool bNewBlocking ) void CSound::InitPlayback ( const bool bNewBlocking )
{ {
int err; int err;
// save buffer size
iBufferSizeOut = iNewBufferSize / NUM_IN_OUT_CHANNELS; // mono size
// if playback device was already open, close it first // if playback device was already open, close it first
if ( phandle != NULL ) if ( phandle != NULL )
{ {
@ -219,7 +213,7 @@ void CSound::InitPlayback ( int iNewBufferSize, bool bNewBlocking )
// playback device (either "hw:0,0" or "plughw:0,0") // playback device (either "hw:0,0" or "plughw:0,0")
if ( err = snd_pcm_open ( &phandle, "hw:0,0", if ( err = snd_pcm_open ( &phandle, "hw:0,0",
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) != 0) SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) != 0 )
{ {
qDebug ( "open error: %s", snd_strerror ( err ) ); qDebug ( "open error: %s", snd_strerror ( err ) );
} }
@ -241,16 +235,16 @@ void CSound::InitPlayback ( int iNewBufferSize, bool bNewBlocking )
bool CSound::Write ( CVector<short>& psData ) bool CSound::Write ( CVector<short>& psData )
{ {
int size = iBufferSizeOut; int size = iBufferSizeOut;
int start = 0; int start = 0;
int ret; int ret;
/* Check if device must be opened or reinitialized */ // check if device must be opened or reinitialized
if ( bChangParamOut == true ) if ( bChangParamOut == true )
{ {
InitPlayback ( iBufferSizeOut * NUM_IN_OUT_CHANNELS ); InitPlayback ( iBufferSizeOut * NUM_IN_OUT_CHANNELS );
/* Reset flag */ // reset flag
bChangParamOut = false; bChangParamOut = false;
} }
@ -262,7 +256,7 @@ bool CSound::Write ( CVector<short>& psData )
{ {
if ( ret == -EPIPE ) if ( ret == -EPIPE )
{ {
/* under-run */ // under-run
qDebug ( "wunderrun" ); qDebug ( "wunderrun" );
ret = snd_pcm_prepare ( phandle ); ret = snd_pcm_prepare ( phandle );
@ -286,7 +280,7 @@ bool CSound::Write ( CVector<short>& psData )
{ {
qDebug("wstrpipe"); qDebug("wstrpipe");
/* wait until the suspend flag is released */ // wait until the suspend flag is released
while ( (ret = snd_pcm_resume ( phandle ) ) == -EAGAIN ) while ( (ret = snd_pcm_resume ( phandle ) ) == -EAGAIN )
{ {
sleep(1); sleep(1);
@ -317,29 +311,29 @@ bool CSound::Write ( CVector<short>& psData )
return false; return false;
} }
void CSound::SetOutNumBuf(int iNewNum) void CSound::SetOutNumBuf ( int iNewNum )
{ {
/* check new parameter */ // check new parameter
if ( ( iNewNum >= MAX_SND_BUF_OUT ) || ( iNewNum < 1 ) ) if ( ( iNewNum >= MAX_SND_BUF_OUT ) || ( iNewNum < 1 ) )
{ {
iNewNum = NUM_PERIOD_BLOCKS_OUT; iNewNum = NUM_PERIOD_BLOCKS_OUT;
} }
/* Change only if parameter is different */ // change only if parameter is different
if ( iNewNum != iCurPeriodSizeOut ) if ( iNewNum != iCurPeriodSizeOut )
{ {
iCurPeriodSizeOut = iNewNum; iCurPeriodSizeOut = iNewNum;
bChangParamOut = true; bChangParamOut = true;
} }
} }
/* common **********************************************************************/ /* common **********************************************************************/
bool CSound::SetHWParams(snd_pcm_t* handle, const int iBufferSizeIn, bool CSound::SetHWParams ( snd_pcm_t* handle, const int iBufferSizeIn,
const int iNumPeriodBlocks) const int iNumPeriodBlocks )
{ {
int err; int err;
snd_pcm_hw_params_t* hwparams; snd_pcm_hw_params_t* hwparams;
// allocate an invalid snd_pcm_hw_params_t using standard malloc // allocate an invalid snd_pcm_hw_params_t using standard malloc
if ( err = snd_pcm_hw_params_malloc ( &hwparams ) < 0 ) if ( err = snd_pcm_hw_params_malloc ( &hwparams ) < 0 )
@ -417,31 +411,31 @@ bool CSound::SetHWParams(snd_pcm_t* handle, const int iBufferSizeIn,
} }
/* check period and buffer size */ // check period and buffer size
snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t buffer_size;
if (err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0) { if ( err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size ) < 0 ) {
qDebug("Unable to get buffer size for playback: %s\n", snd_strerror(err)); qDebug ( "Unable to get buffer size for playback: %s\n", snd_strerror ( err ) );
} }
qDebug("buffer size: %d (desired: %d)", buffer_size, iBufferSizeIn * iNumPeriodBlocks); qDebug ( "buffer size: %d (desired: %d)", buffer_size, iBufferSizeIn * iNumPeriodBlocks );
snd_pcm_uframes_t period_size; snd_pcm_uframes_t period_size;
err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0); err = snd_pcm_hw_params_get_period_size ( hwparams, &period_size, 0 );
if (err < 0) if ( err < 0 )
{ {
qDebug("Unable to get period size for playback: %s\n", snd_strerror(err)); qDebug ( "Unable to get period size for playback: %s\n", snd_strerror ( err ) );
} }
qDebug("frame size: %d (desired: %d)", period_size, iBufferSizeIn); qDebug ( "frame size: %d (desired: %d)", period_size, iBufferSizeIn );
/* clean-up */ // clean-up
snd_pcm_hw_params_free ( hwparams ); snd_pcm_hw_params_free ( hwparams );
return false; return false;
} }
void CSound::Close () void CSound::Close()
{ {
/* read */ // read
if ( rhandle != NULL ) if ( rhandle != NULL )
{ {
snd_pcm_close ( rhandle ); snd_pcm_close ( rhandle );
@ -449,7 +443,7 @@ void CSound::Close ()
rhandle = NULL; rhandle = NULL;
/* playback */ // playback
if ( phandle != NULL ) if ( phandle != NULL )
{ {
snd_pcm_close ( phandle ); snd_pcm_close ( phandle );

View file

@ -31,9 +31,9 @@
/* Definitions ****************************************************************/ /* Definitions ****************************************************************/
#define NUM_IN_OUT_CHANNELS 2 /* always stereo */ #define NUM_IN_OUT_CHANNELS 2 // always stereo
/* the number of periods is critical for latency */ // the number of periods is critical for latency
#define NUM_PERIOD_BLOCKS_IN 2 #define NUM_PERIOD_BLOCKS_IN 2
#define NUM_PERIOD_BLOCKS_OUT 2 #define NUM_PERIOD_BLOCKS_OUT 2
@ -44,14 +44,18 @@
class CSound class CSound
{ {
public: public:
CSound() CSound ( const int iNewBufferSizeStereo )
#if WITH_SOUND #if WITH_SOUND
: rhandle(NULL), phandle(NULL), iCurPeriodSizeIn(NUM_PERIOD_BLOCKS_IN), : rhandle ( NULL ), phandle ( NULL ), iCurPeriodSizeIn ( NUM_PERIOD_BLOCKS_IN ),
iCurPeriodSizeOut(NUM_PERIOD_BLOCKS_OUT), bChangParamIn(true), iCurPeriodSizeOut ( NUM_PERIOD_BLOCKS_OUT ), bChangParamIn ( true ),
bChangParamOut(true) bChangParamOut ( true )
#endif #endif
{} {
virtual ~CSound() {Close();} // set internal buffer size for read and write
iBufferSizeIn = iNewBufferSizeStereo / NUM_IN_OUT_CHANNELS; // mono size
iBufferSizeOut = iNewBufferSizeStereo / NUM_IN_OUT_CHANNELS; // mono size
}
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; }
@ -60,14 +64,14 @@ public:
int GetDev() { return 0; } int GetDev() { return 0; }
#if WITH_SOUND #if WITH_SOUND
void SetInNumBuf(int iNewNum); void SetInNumBuf ( int iNewNum );
int GetInNumBuf() {return iCurPeriodSizeIn;} int GetInNumBuf() { return iCurPeriodSizeIn; }
void SetOutNumBuf(int iNewNum); void SetOutNumBuf ( int iNewNum );
int GetOutNumBuf() {return iCurPeriodSizeOut;} int GetOutNumBuf() { return iCurPeriodSizeOut; }
void InitRecording(int iNewBufferSize, bool bNewBlocking = true); void InitRecording ( const bool bNewBlocking = true );
void InitPlayback(int iNewBufferSize, bool bNewBlocking = false); void InitPlayback ( const bool bNewBlocking = false );
bool Read(CVector<short>& psData); bool Read ( CVector<short>& psData );
bool Write(CVector<short>& psData); bool Write ( CVector<short>& psData );
void Close(); void Close();
@ -75,8 +79,8 @@ protected:
snd_pcm_t* rhandle; snd_pcm_t* rhandle;
snd_pcm_t* phandle; snd_pcm_t* phandle;
bool SetHWParams(snd_pcm_t* handle, const int iBufferSizeIn, bool SetHWParams ( snd_pcm_t* handle, const int iBufferSizeIn,
const int iNumPeriodBlocks); const int iNumPeriodBlocks );
int iBufferSizeOut; int iBufferSizeOut;
int iBufferSizeIn; int iBufferSizeIn;
@ -85,15 +89,15 @@ protected:
bool bChangParamOut; bool bChangParamOut;
int iCurPeriodSizeOut; int iCurPeriodSizeOut;
#else #else
/* Dummy definitions */ // dummy definitions
void SetInNumBuf(int iNewNum) {} void SetInNumBuf ( int iNewNum ) {}
int GetInNumBuf() {return 1;} int GetInNumBuf() { return 1; }
void SetOutNumBuf(int iNewNum) {} void SetOutNumBuf ( int iNewNum ) {}
int GetOutNumBuf() {return 1;} int GetOutNumBuf() { return 1; }
void InitRecording(int iNewBufferSize, bool bNewBlocking = true) {printf("no sound!");} void InitRecording ( const bool bNewBlocking = true ) { printf ( "no sound!" ); }
void InitPlayback(int iNewBufferSize, bool bNewBlocking = false) {printf("no sound!");} void InitPlayback ( const bool bNewBlocking = false ) { printf ( "no sound!" ); }
bool Read(CVector<short>& psData) {printf("no sound!"); return false;} bool Read ( CVector<short>& psData ) { printf ( "no sound!" ); return false; }
bool Write(CVector<short>& psData) {printf("no sound!"); return false;} bool Write ( CVector<short>& psData ) { printf ( "no sound!" ); return false; }
void Close() {} void Close() {}
#endif #endif
}; };

View file

@ -27,6 +27,8 @@
/* Implementation *************************************************************/ /* Implementation *************************************************************/
CClient::CClient ( const quint16 iPortNumber ) : bRun ( false ), CClient::CClient ( const quint16 iPortNumber ) : bRun ( false ),
iSndCrdBlockSizeSam ( MIN_SND_CRD_BLOCK_SIZE_SAMPLES ),
Sound ( MIN_SND_CRD_BLOCK_SIZE_SAMPLES * 2 /* stereo */ ),
Socket ( &Channel, iPortNumber ), Socket ( &Channel, iPortNumber ),
iAudioInFader ( AUD_FADER_IN_MAX / 2 ), iAudioInFader ( AUD_FADER_IN_MAX / 2 ),
iReverbLevel ( AUD_REVERB_MAX / 6 ), iReverbLevel ( AUD_REVERB_MAX / 6 ),
@ -168,9 +170,8 @@ void CClient::OnProtocolStatus ( bool bOk )
void CClient::Init() void CClient::Init()
{ {
// set block sizes (in samples) // set block size (in samples)
iBlockSizeSam = MIN_BLOCK_SIZE_SAMPLES; iBlockSizeSam = MIN_BLOCK_SIZE_SAMPLES;
iSndCrdBlockSizeSam = MIN_SND_CRD_BLOCK_SIZE_SAMPLES;
vecsAudioSndCrd.Init ( iSndCrdBlockSizeSam * 2 ); // stereo vecsAudioSndCrd.Init ( iSndCrdBlockSizeSam * 2 ); // stereo
vecdAudioSndCrdL.Init ( iSndCrdBlockSizeSam ); vecdAudioSndCrdL.Init ( iSndCrdBlockSizeSam );
@ -179,8 +180,8 @@ void CClient::Init()
vecdAudioL.Init ( iBlockSizeSam ); vecdAudioL.Init ( iBlockSizeSam );
vecdAudioR.Init ( iBlockSizeSam ); vecdAudioR.Init ( iBlockSizeSam );
Sound.InitRecording ( iSndCrdBlockSizeSam * 2 ); // stereo Sound.InitRecording();
Sound.InitPlayback ( iSndCrdBlockSizeSam * 2 ); // stereo Sound.InitPlayback();
// resample objects are always initialized with the input block size // resample objects are always initialized with the input block size
// record // record
@ -325,7 +326,7 @@ void CClient::run()
// send it through the network // send it through the network
Socket.SendPacket ( Channel.PrepSendPacket ( vecsNetwork ), Socket.SendPacket ( Channel.PrepSendPacket ( vecsNetwork ),
Channel.GetAddress () ); Channel.GetAddress() );
// receive a new block // receive a new block
if ( Channel.GetData ( vecdNetwData ) == GS_BUFFER_OK ) if ( Channel.GetData ( vecdNetwData ) == GS_BUFFER_OK )

View file

@ -250,7 +250,8 @@ void CClientSettingsDlg::OnSoundCrdSelection ( int iSndDevIdx )
{ {
QMessageBox::critical ( 0, APP_NAME, QMessageBox::critical ( 0, APP_NAME,
QString ( "The selected audio device could not be used because " QString ( "The selected audio device could not be used because "
"of the following error: " ) + generr.GetErrorText(), "Ok", 0 ); "of the following error: " ) + generr.GetErrorText() +
QString ( " The previous driver will be selected." ), "Ok", 0 );
// recover old selection // recover old selection
cbSoundcard->setCurrentIndex ( pClient->GetSndInterface()->GetDev() ); cbSoundcard->setCurrentIndex ( pClient->GetSndInterface()->GetDev() );

View file

@ -138,6 +138,11 @@ int main ( int argc, char** argv )
exit ( 1 ); exit ( 1 );
} }
#ifdef _WIN32
// Set application priority class -> high priority
SetPriorityClass ( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
#endif
// Application object // Application object
QApplication app ( argc, argv, bUseGUI ); QApplication app ( argc, argv, bUseGUI );

View file

@ -87,8 +87,8 @@ bool CSound::Read ( CVector<short>& psData )
// check if device must be opened or reinitialized // check if device must be opened or reinitialized
if ( bChangParamIn ) if ( bChangParamIn )
{ {
// reinit sound interface (init recording requires stereo buffer size) // reinit sound interface
InitRecordingAndPlayback ( iBufferSizeStereo ); InitRecordingAndPlayback();
// reset flag // reset flag
bChangParamIn = false; bChangParamIn = false;
@ -188,8 +188,8 @@ bool CSound::Write ( CVector<short>& psData )
// check if device must be opened or reinitialized // check if device must be opened or reinitialized
if ( bChangParamOut ) if ( bChangParamOut )
{ {
// reinit sound interface (init recording requires stereo buffer size) // reinit sound interface
InitRecordingAndPlayback ( iBufferSizeStereo ); InitRecordingAndPlayback();
// reset flag // reset flag
bChangParamOut = false; bChangParamOut = false;
@ -256,6 +256,9 @@ void CSound::SetDev ( const int iNewDev )
{ {
// a device was already been initialized and is used, kill working // a device was already been initialized and is used, kill working
// thread and clean up // thread and clean up
// stop driver
ASIOStop();
// set event to ensure that thread leaves the waiting function // set event to ensure that thread leaves the waiting function
if ( m_ASIOEvent != NULL ) if ( m_ASIOEvent != NULL )
{ {
@ -265,8 +268,7 @@ void CSound::SetDev ( const int iNewDev )
// wait for the thread to terminate // wait for the thread to terminate
Sleep ( 500 ); Sleep ( 500 );
// stop audio and dispose ASIO buffers // dispose ASIO buffers
ASIOStop();
ASIODisposeBuffers(); ASIODisposeBuffers();
// remove old driver // remove old driver
@ -280,9 +282,11 @@ void CSound::SetDev ( const int iNewDev )
// loading and initializing the new driver failed, go back to original // loading and initializing the new driver failed, go back to original
// driver and display error message // driver and display error message
LoadAndInitializeDriver ( lCurDev ); LoadAndInitializeDriver ( lCurDev );
InitRecordingAndPlayback();
throw CGenErr ( strErrorMessage.c_str() ); throw CGenErr ( strErrorMessage.c_str() );
} }
InitRecordingAndPlayback();
} }
else else
{ {
@ -305,8 +309,15 @@ void CSound::SetDev ( const int iNewDev )
} }
} }
std::string CSound::LoadAndInitializeDriver ( const int iDriverIdx ) std::string CSound::LoadAndInitializeDriver ( int iDriverIdx )
{ {
// first check and correct input parameter
if ( iDriverIdx >= lNumDevs )
{
// we assume here that at least one driver is in the system
iDriverIdx = 0;
}
// load driver // load driver
loadAsioDriver ( cDriverNames[iDriverIdx] ); loadAsioDriver ( cDriverNames[iDriverIdx] );
if ( ASIOInit ( &driverInfo ) != ASE_OK ) if ( ASIOInit ( &driverInfo ) != ASE_OK )
@ -316,6 +327,62 @@ std::string CSound::LoadAndInitializeDriver ( const int iDriverIdx )
return "The audio driver could not be initialized."; return "The audio driver could not be initialized.";
} }
const std::string strStat = GetAndCheckSoundCardProperties();
// store ID of selected driver if initialization was successful
if ( strStat.empty() )
{
lCurDev = iDriverIdx;
}
return strStat;
}
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 )
{
if ( GetAndCheckSoundCardProperties().empty() )
{
// initialization was successful
bValidDriverDetected = true;
// store ID of selected driver
lCurDev = iCurDriverIdx;
}
else
{
// driver could not be loaded, free memory
asioDrivers->removeCurrentDriver();
}
}
else
{
// driver could not be loaded, free memory
asioDrivers->removeCurrentDriver();
}
}
// try next driver
iCurDriverIdx++;
}
return bValidDriverDetected;
}
std::string CSound::GetAndCheckSoundCardProperties()
{
int i;
// check the number of available channels // check the number of available channels
long lNumInChan; long lNumInChan;
long lNumOutChan; long lNumOutChan;
@ -350,165 +417,125 @@ std::string CSound::LoadAndInitializeDriver ( const int iDriverIdx )
&HWBufferInfo.lPreferredSize, &HWBufferInfo.lPreferredSize,
&HWBufferInfo.lGranularity ); &HWBufferInfo.lGranularity );
// calculate "nearest" buffer size and set internal parameter accordingly
// first check minimum and maximum values
if ( iBufferSizeMono < HWBufferInfo.lMinSize )
{
iASIOBufferSizeMono = HWBufferInfo.lMinSize;
}
else
{
if ( iBufferSizeMono > HWBufferInfo.lMaxSize )
{
iASIOBufferSizeMono = HWBufferInfo.lMaxSize;
}
else
{
// initialization
int iTrialBufSize = HWBufferInfo.lMinSize;
int iLastTrialBufSize = HWBufferInfo.lMinSize;
bool bSizeFound = false;
// test loop
while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) )
{
if ( iTrialBufSize >= iBufferSizeMono )
{
// test which buffer size fits better: the old one or the
// current one
if ( ( iTrialBufSize - iBufferSizeMono ) <
( iBufferSizeMono - iLastTrialBufSize ) )
{
iBufferSizeMono = iTrialBufSize;
}
else
{
iBufferSizeMono = iLastTrialBufSize;
}
// exit while loop
bSizeFound = true;
}
if ( !bSizeFound )
{
// store old trial buffer size
iLastTrialBufSize = iTrialBufSize;
// increment trial buffer size (check for special case first)
if ( HWBufferInfo.lGranularity == -1 )
{
// special case: buffer sizes are a power of 2
iTrialBufSize *= 2;
}
else
{
iTrialBufSize += HWBufferInfo.lGranularity;
}
}
}
// set ASIO buffer size
iASIOBufferSizeMono = iTrialBufSize;
}
}
// prepare input channels
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
{
bufferInfos[i].isInput = ASIOTrue;
bufferInfos[i].channelNum = i;
bufferInfos[i].buffers[0] = 0;
bufferInfos[i].buffers[1] = 0;
}
// prepare output channels
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
{
bufferInfos[NUM_IN_OUT_CHANNELS + i].isInput = ASIOFalse;
bufferInfos[NUM_IN_OUT_CHANNELS + i].channelNum = i;
bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[0] = 0;
bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[1] = 0;
}
// create and activate ASIO buffers (buffer size in samples)
ASIOCreateBuffers ( bufferInfos, 2 /* in/out */ * NUM_IN_OUT_CHANNELS /* stereo */,
iASIOBufferSizeMono, &asioCallbacks );
// now get some buffer details
for ( i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ )
{
channelInfos[i].channel = bufferInfos[i].channelNum;
channelInfos[i].isInput = bufferInfos[i].isInput;
ASIOGetChannelInfo ( &channelInfos[i] );
// only 16/24/32 LSB is supported
if ( ( channelInfos[i].type != ASIOSTInt16LSB ) &&
( channelInfos[i].type != ASIOSTInt24LSB ) &&
( channelInfos[i].type != ASIOSTInt32LSB ) )
{
// clean up and return error string
ASIODisposeBuffers();
ASIOExit();
asioDrivers->removeCurrentDriver();
return "Required audio sample format not available (16/24/32 bit LSB).";
}
}
// check wether the driver requires the ASIOOutputReady() optimization // check wether the driver requires the ASIOOutputReady() optimization
// (can be used by the driver to reduce output latency by one block) // (can be used by the driver to reduce output latency by one block)
bASIOPostOutput = ( ASIOOutputReady() == ASE_OK ); bASIOPostOutput = ( ASIOOutputReady() == ASE_OK );
// store ID of selected driver
lCurDev = iDriverIdx;
return ""; return "";
} }
bool CSound::LoadAndInitializeFirstValidDriver() void CSound::InitRecordingAndPlayback()
{ {
// 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;
// first, stop audio and dispose ASIO buffers // first, stop audio and dispose ASIO buffers
ASIOStop(); ASIOStop();
ASIODisposeBuffers();
ASIOMutex.lock(); // get mutex lock ASIOMutex.lock(); // get mutex lock
{ {
// set internal buffer size value and calculate mono buffer size
iBufferSizeStereo = iNewBufferSize;
iBufferSizeMono = iBufferSizeStereo / 2;
// calculate "nearest" buffer size and set internal parameter accordingly
// first check minimum and maximum values
if ( iBufferSizeMono < HWBufferInfo.lMinSize )
{
iASIOBufferSizeMono = HWBufferInfo.lMinSize;
}
else
{
if ( iBufferSizeMono > HWBufferInfo.lMaxSize )
{
iASIOBufferSizeMono = HWBufferInfo.lMaxSize;
}
else
{
// initialization
int iTrialBufSize = HWBufferInfo.lMinSize;
int iLastTrialBufSize = HWBufferInfo.lMinSize;
bool bSizeFound = false;
// test loop
while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) )
{
if ( iTrialBufSize >= iBufferSizeMono )
{
// test which buffer size fits better: the old one or the
// current one
if ( ( iTrialBufSize - iBufferSizeMono ) <
( iBufferSizeMono - iLastTrialBufSize ) )
{
iBufferSizeMono = iTrialBufSize;
}
else
{
iBufferSizeMono = iLastTrialBufSize;
}
// exit while loop
bSizeFound = true;
}
if ( !bSizeFound )
{
// store old trial buffer size
iLastTrialBufSize = iTrialBufSize;
// increment trial buffer size (check for special case first)
if ( HWBufferInfo.lGranularity == -1 )
{
// special case: buffer sizes are a power of 2
iTrialBufSize *= 2;
}
else
{
iTrialBufSize += HWBufferInfo.lGranularity;
}
}
}
// set ASIO buffer size
iASIOBufferSizeMono = iTrialBufSize;
}
}
// prepare input channels
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
{
bufferInfos[i].isInput = ASIOTrue;
bufferInfos[i].channelNum = i;
bufferInfos[i].buffers[0] = 0;
bufferInfos[i].buffers[1] = 0;
}
// prepare output channels
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
{
bufferInfos[NUM_IN_OUT_CHANNELS + i].isInput = ASIOFalse;
bufferInfos[NUM_IN_OUT_CHANNELS + i].channelNum = i;
bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[0] = 0;
bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[1] = 0;
}
// create and activate ASIO buffers (buffer size in samples)
ASIOCreateBuffers ( bufferInfos, 2 /* in/out */ * NUM_IN_OUT_CHANNELS /* stereo */,
iASIOBufferSizeMono, &asioCallbacks );
// now get some buffer details
for ( i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ )
{
channelInfos[i].channel = bufferInfos[i].channelNum;
channelInfos[i].isInput = bufferInfos[i].isInput;
ASIOGetChannelInfo ( &channelInfos[i] );
// only 16 bit is supported
if ( ( channelInfos[i].type != ASIOSTInt16LSB ) &&
( channelInfos[i].type != ASIOSTInt24LSB ) &&
( channelInfos[i].type != ASIOSTInt32LSB ) )
{
throw CGenErr ( "Required audio sample format not available (16/24/32 bit LSB)." );
}
}
// Our buffer management ----------------------------------------------- // Our buffer management -----------------------------------------------
// store new buffer number values // store new buffer number values
iCurNumSndBufIn = iNewNumSndBufIn; iCurNumSndBufIn = iNewNumSndBufIn;
@ -537,7 +564,7 @@ void CSound::InitRecordingAndPlayback ( int iNewBufferSize )
psPlayBuffer = new short[iCurNumSndBufOut * iBufferSizeStereo]; psPlayBuffer = new short[iCurNumSndBufOut * iBufferSizeStereo];
// clear new buffer // clear new buffer
for ( i = 0; i < iCurNumSndBufOut * iBufferSizeStereo; i++ ) for ( int i = 0; i < iCurNumSndBufOut * iBufferSizeStereo; i++ )
{ {
psPlayBuffer[i] = 0; psPlayBuffer[i] = 0;
} }
@ -553,6 +580,9 @@ void CSound::InitRecordingAndPlayback ( int iNewBufferSize )
void CSound::Close() void CSound::Close()
{ {
// stop driver
ASIOStop();
// set event to ensure that thread leaves the waiting function // set event to ensure that thread leaves the waiting function
if ( m_ASIOEvent != NULL ) if ( m_ASIOEvent != NULL )
{ {
@ -562,8 +592,7 @@ void CSound::Close()
// wait for the thread to terminate // wait for the thread to terminate
Sleep ( 500 ); Sleep ( 500 );
// stop audio and dispose ASIO buffers // dispose ASIO buffers
ASIOStop();
ASIODisposeBuffers(); ASIODisposeBuffers();
// set flag to open devices the next time it is initialized // set flag to open devices the next time it is initialized
@ -571,8 +600,12 @@ void CSound::Close()
bChangParamOut = true; bChangParamOut = true;
} }
CSound::CSound() CSound::CSound ( const int iNewBufferSizeStereo )
{ {
// set internal buffer size value and calculate mono buffer size
iBufferSizeStereo = iNewBufferSizeStereo;
iBufferSizeMono = iBufferSizeStereo / 2;
// 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;

View file

@ -65,18 +65,18 @@
class CSound class CSound
{ {
public: public:
CSound(); CSound ( const int iNewBufferSizeStereo );
virtual ~CSound(); virtual ~CSound();
void InitRecording ( const int iNewBufferSize, const bool bNewBlocking = true ) void InitRecording ( const bool bNewBlocking = true )
{ {
bBlockingRec = bNewBlocking; bBlockingRec = bNewBlocking;
InitRecordingAndPlayback ( iNewBufferSize ); InitRecordingAndPlayback();
} }
void InitPlayback ( const int iNewBufferSize, const bool bNewBlocking = false ) void InitPlayback ( const bool bNewBlocking = false )
{ {
bBlockingPlay = bNewBlocking; bBlockingPlay = bNewBlocking;
InitRecordingAndPlayback ( iNewBufferSize ); InitRecordingAndPlayback();
} }
bool Read ( CVector<short>& psData ); bool Read ( CVector<short>& psData );
bool Write ( CVector<short>& psData ); bool Write ( CVector<short>& psData );
@ -96,8 +96,9 @@ public:
protected: protected:
bool LoadAndInitializeFirstValidDriver(); bool LoadAndInitializeFirstValidDriver();
std::string LoadAndInitializeDriver ( const int iIdx ); std::string LoadAndInitializeDriver ( int iIdx );
void InitRecordingAndPlayback ( const int iNewBufferSize ); std::string GetAndCheckSoundCardProperties();
void InitRecordingAndPlayback();
// audio hardware buffer info // audio hardware buffer info
struct sHWBufferInfo struct sHWBufferInfo