some more work for moving functions from the Windows sound interface to the sound base class

This commit is contained in:
Volker Fischer 2011-10-18 20:04:56 +00:00
parent a8795c1bf3
commit 8720c40db6
8 changed files with 1200 additions and 1177 deletions

View file

@ -1,3 +1,11 @@
3.2.1
TODO working on bug fix: Windows: driver is changed during connection results
sometimes in lowed noise
3.2.0 3.2.0
- new GUI style of the main window, added switch for selecting the GUI style - new GUI style of the main window, added switch for selecting the GUI style

View file

@ -59,7 +59,7 @@ class CSound : public CSoundBase
{ {
public: public:
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg ) : CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg ) :
CSoundBase ( true, fpNewProcessCallback, arg ), iJACKBufferSizeMono ( 0 ), CSoundBase ( "Jack", true, fpNewProcessCallback, arg ), iJACKBufferSizeMono ( 0 ),
iJACKBufferSizeStero ( 0 ) { OpenJack(); } iJACKBufferSizeStero ( 0 ) { OpenJack(); }
virtual ~CSound() { CloseJack(); } virtual ~CSound() { CloseJack(); }
@ -67,10 +67,6 @@ public:
virtual void Start(); virtual void Start();
virtual void Stop(); virtual void Stop();
// device cannot be set under Linux
virtual int GetNumDev() { return 1; }
virtual QString GetDeviceName ( const int ) { return "Jack"; }
// these variables should be protected but cannot since we want // these variables should be protected but cannot since we want
// to access them from the callback function // to access them from the callback function
CVector<short> vecsTmpAudioSndCrdStereo; CVector<short> vecsTmpAudioSndCrdStereo;

View file

@ -38,17 +38,13 @@ class CSound : public CSoundBase
{ {
public: public:
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg ) : CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg ) :
CSoundBase ( true, fpNewProcessCallback, arg ) { OpenCoreAudio(); } CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg ) { OpenCoreAudio(); }
virtual ~CSound() { CloseCoreAudio(); } virtual ~CSound() { CloseCoreAudio(); }
virtual int Init ( const int iNewPrefMonoBufferSize ); virtual int Init ( const int iNewPrefMonoBufferSize );
virtual void Start(); virtual void Start();
virtual void Stop(); virtual void Stop();
// device cannot be set under MacOS
virtual int GetNumDev() { return 1; }
virtual QString GetDeviceName ( const int ) { return "Core Audio"; }
// these variables should be protected but cannot since we want // these variables should be protected but cannot since we want
// to access them from the callback function // to access them from the callback function
CVector<short> vecsTmpAudioSndCrdStereo; CVector<short> vecsTmpAudioSndCrdStereo;

View file

@ -455,6 +455,18 @@ void CClient::OnSndCrdReinitRequest()
// reinit the driver (we use the currently selected driver) and // reinit the driver (we use the currently selected driver) and
// init client object, too // init client object, too
// TODO possible bug: In ASIO driver if set dev is called, the driver is
// unloaded. See ASIO manual: "Note: A host application has to defer
// processing of these notification to a later "secure" time as the
// driver has finish its processing of the notification. Especially on
// the kAsioResetRequest it is a bad idea to unload the driver during
// the asioMessage callback since the callback has to return back into
// the driver, which is no longer present."
// TODO write separate driver reset function in sound base instead
// of doing setdev with the old driver ID -> avoid unloading driver
Sound.SetDev ( Sound.GetDev() ); Sound.SetDev ( Sound.GetDev() );
Init(); Init();

View file

@ -26,16 +26,18 @@
/* Implementation *************************************************************/ /* Implementation *************************************************************/
CSoundBase::CSoundBase ( const bool bNewIsCallbackAudioInterface, CSoundBase::CSoundBase ( const QString& strNewSystemDriverTechniqueName,
const bool bNewIsCallbackAudioInterface,
void (*fpNewProcessCallback) ( CVector<int16_t>& psData, void* pParg ), void (*fpNewProcessCallback) ( CVector<int16_t>& psData, void* pParg ),
void* pParg ) : void* pParg ) :
fpProcessCallback ( fpNewProcessCallback ), fpProcessCallback ( fpNewProcessCallback ),
pProcessCallbackArg ( pParg ), bRun ( false ), pProcessCallbackArg ( pParg ), bRun ( false ),
bIsCallbackAudioInterface ( bNewIsCallbackAudioInterface ) bIsCallbackAudioInterface ( bNewIsCallbackAudioInterface ),
strSystemDriverTechniqueName ( strNewSystemDriverTechniqueName )
{ {
// initializations for the sound card names (default) // initializations for the sound card names (default)
lNumDevs = 1; lNumDevs = 1;
strDriverNames[0] = "Default"; strDriverNames[0] = strSystemDriverTechniqueName;
// set current device // set current device
lCurDev = 0; // default device lCurDev = 0; // default device
@ -123,3 +125,139 @@ void CSoundBase::run()
} }
} }
} }
/******************************************************************************\
* Device handling *
\******************************************************************************/
QString CSoundBase::SetDev ( const int iNewDev )
{
QString strReturn = ""; // init with no error
bool bTryLoadAnyDriver = false;
// check if an ASIO driver was already initialized
if ( lCurDev >= 0 )
{
// a device was already been initialized and is used, first clean up
// driver
UnloadCurrentDriver();
const QString strErrorMessage = LoadAndInitializeDriver ( iNewDev );
if ( !strErrorMessage.isEmpty() )
{
if ( iNewDev != lCurDev )
{
// loading and initializing the new driver failed, go back to
// original driver and display error message
LoadAndInitializeDriver ( lCurDev );
}
else
{
// the same driver is used but the driver properties seems to
// have changed so that they are not compatible to our
// software anymore
QMessageBox::critical (
0, APP_NAME, QString ( tr ( "The audio driver properties "
"have changed to a state which is incompatible to this "
"software. The selected audio device could not be used "
"because of the following error: <b>" ) ) +
strErrorMessage +
QString ( tr ( "</b><br><br>Please restart the software." ) ),
"Close", 0 );
_exit ( 0 );
}
// store error return message
strReturn = strErrorMessage;
}
}
else
{
if ( iNewDev != INVALID_SNC_CARD_DEVICE )
{
// 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.
if ( !LoadAndInitializeDriver ( iNewDev ).isEmpty() )
{
// loading and initializing the new driver failed, try to find
// at least one usable driver
bTryLoadAnyDriver = true;
}
}
else
{
// try to find one usable driver (select the first valid driver)
bTryLoadAnyDriver = true;
}
}
if ( bTryLoadAnyDriver )
{
// try to load and initialize any valid driver
QVector<QString> vsErrorList =
LoadAndInitializeFirstValidDriver();
if ( !vsErrorList.isEmpty() )
{
// create error message with all details
QString sErrorMessage = tr ( "<b>No usable " ) +
strSystemDriverTechniqueName + tr ( " audio device "
"(driver) found.</b><br><br>"
"In the following there is a list of all available drivers "
"with the associated error message:<ul>" );
for ( int i = 0; i < lNumDevs; i++ )
{
sErrorMessage += "<li><b>" + GetDeviceName ( i ) + "</b>: " +
vsErrorList[i] + "</li>";
}
sErrorMessage += "</ul>";
throw CGenErr ( sErrorMessage );
}
}
return strReturn;
}
QVector<QString> CSoundBase::LoadAndInitializeFirstValidDriver()
{
QVector<QString> vsErrorList;
// 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 ) )
{
// try to load and initialize current driver, store error message
const QString strCurError =
LoadAndInitializeDriver ( iCurDriverIdx );
vsErrorList.append ( strCurError );
if ( strCurError.isEmpty() )
{
// initialization was successful
bValidDriverDetected = true;
// store ID of selected driver
lCurDev = iCurDriverIdx;
// empty error list shows that init was successful
vsErrorList.clear();
}
// try next driver
iCurDriverIdx++;
}
return vsErrorList;
}

View file

@ -27,6 +27,7 @@
#include <qthread.h> #include <qthread.h>
#include <qstring.h> #include <qstring.h>
#include <qmessagebox.h>
#include "global.h" #include "global.h"
#include "util.h" #include "util.h"
@ -37,7 +38,8 @@ class CSoundBase : public QThread
Q_OBJECT Q_OBJECT
public: public:
CSoundBase ( const bool bNewIsCallbackAudioInterface, CSoundBase ( const QString& strNewSystemDriverTechniqueName,
const bool bNewIsCallbackAudioInterface,
void (*fpNewProcessCallback) ( CVector<int16_t>& psData, void* pParg ), void (*fpNewProcessCallback) ( CVector<int16_t>& psData, void* pParg ),
void* pParg ); void* pParg );
@ -48,9 +50,7 @@ public:
// device selection // device selection
virtual int GetNumDev() { return lNumDevs; } virtual int GetNumDev() { return lNumDevs; }
virtual QString GetDeviceName ( const int iDiD ) { return strDriverNames[iDiD]; } virtual QString GetDeviceName ( const int iDiD ) { return strDriverNames[iDiD]; }
virtual QString SetDev ( const int );
// dummy implementations in base class
virtual QString SetDev ( const int ) { return ""; }
virtual int GetDev() { return lCurDev; } virtual int GetDev() { return lCurDev; }
virtual int GetNumInputChannels() { return 2; } virtual int GetNumInputChannels() { return 2; }
@ -76,6 +76,11 @@ public:
void EmitReinitRequestSignal() { emit ReinitRequest(); } void EmitReinitRequestSignal() { emit ReinitRequest(); }
protected: protected:
// driver handling
virtual QString LoadAndInitializeDriver ( int ) { return ""; };
virtual void UnloadCurrentDriver() {}
QVector<QString> LoadAndInitializeFirstValidDriver();
// function pointer to callback function // function pointer to callback function
void (*fpProcessCallback) ( CVector<int16_t>& psData, void* arg ); void (*fpProcessCallback) ( CVector<int16_t>& psData, void* arg );
void* pProcessCallbackArg; void* pProcessCallbackArg;
@ -93,7 +98,9 @@ protected:
void run(); void run();
bool bRun; bool bRun;
bool bIsCallbackAudioInterface; bool bIsCallbackAudioInterface;
QString strSystemDriverTechniqueName;
CVector<int16_t> vecsAudioSndCrdStereo; CVector<int16_t> vecsAudioSndCrdStereo;

View file

@ -40,143 +40,6 @@ CSound* pSound;
/******************************************************************************\ /******************************************************************************\
* Common * * Common *
\******************************************************************************/ \******************************************************************************/
QString CSound::SetDev ( const int iNewDev )
{
QString strReturn = ""; // init with no error
bool bTryLoadAnyDriver = false;
// check if an ASIO driver was already initialized
if ( lCurDev >= 0 )
{
// a device was already been initialized and is used, first clean up
// dispose ASIO buffers
ASIODisposeBuffers();
// remove old driver
ASIOExit();
asioDrivers->removeCurrentDriver();
const QString strErrorMessage = LoadAndInitializeDriver ( iNewDev );
if ( !strErrorMessage.isEmpty() )
{
if ( iNewDev != lCurDev )
{
// loading and initializing the new driver failed, go back to
// original driver and display error message
LoadAndInitializeDriver ( lCurDev );
}
else
{
// the same driver is used but the driver properties seems to
// have changed so that they are not compatible to our
// software anymore
QMessageBox::critical (
0, APP_NAME, QString ( tr ( "The audio driver properties "
"have changed to a state which is incompatible to this "
"software. The selected audio device could not be used "
"because of the following error: <b>" ) ) +
strErrorMessage +
QString ( tr ( "</b><br><br>Please restart the software." ) ),
"Close", 0 );
_exit ( 0 );
}
// store error return message
strReturn = strErrorMessage;
}
Init ( iASIOBufferSizeStereo );
}
else
{
if ( iNewDev != INVALID_SNC_CARD_DEVICE )
{
// 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.
if ( !LoadAndInitializeDriver ( iNewDev ).isEmpty() )
{
// loading and initializing the new driver failed, try to find
// at least one usable driver
bTryLoadAnyDriver = true;
}
}
else
{
// try to find one usable driver (select the first valid driver)
bTryLoadAnyDriver = true;
}
}
if ( bTryLoadAnyDriver )
{
// try to load and initialize any valid driver
QVector<QString> vsErrorList =
LoadAndInitializeFirstValidDriver();
if ( !vsErrorList.isEmpty() )
{
// create error message with all details
QString sErrorMessage = tr ( "<b>No usable ASIO audio device "
"(driver) found.</b><br><br>"
"In the following there is a list of all available drivers "
"with the associated error message:<ul>" );
for ( int i = 0; i < lNumDevs; i++ )
{
sErrorMessage += "<li><b>" + GetDeviceName ( i ) + "</b>: " +
vsErrorList[i] + "</li>";
}
sErrorMessage += "</ul>";
throw CGenErr ( sErrorMessage );
}
}
return strReturn;
}
QVector<QString> CSound::LoadAndInitializeFirstValidDriver()
{
QVector<QString> vsErrorList;
// 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 ) )
{
// try to load and initialize current driver, store error message
const QString strCurError =
LoadAndInitializeDriver ( iCurDriverIdx );
vsErrorList.append ( strCurError );
if ( strCurError.isEmpty() )
{
// initialization was successful
bValidDriverDetected = true;
// store ID of selected driver
lCurDev = iCurDriverIdx;
// empty error list shows that init was successful
vsErrorList.clear();
}
// try next driver
iCurDriverIdx++;
}
return vsErrorList;
}
QString CSound::LoadAndInitializeDriver ( int iDriverIdx ) QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
{ {
// first check and correct input parameter // first check and correct input parameter
@ -212,6 +75,15 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
return strStat; return strStat;
} }
void CSound::UnloadCurrentDriver()
{
// cleanup ASIO stuff
ASIOStop();
ASIODisposeBuffers();
ASIOExit();
asioDrivers->removeCurrentDriver();
}
QString CSound::CheckDeviceCapabilities() QString CSound::CheckDeviceCapabilities()
{ {
// This function checks if our required input/output channel // This function checks if our required input/output channel
@ -525,7 +397,7 @@ void CSound::Stop()
} }
CSound::CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ), void* arg ) : CSound::CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ), void* arg ) :
CSoundBase ( true, fpNewCallback, arg ), CSoundBase ( "ASIO", true, fpNewCallback, arg ),
vSelectedInputChannels ( NUM_IN_OUT_CHANNELS ), vSelectedInputChannels ( NUM_IN_OUT_CHANNELS ),
vSelectedOutputChannels ( NUM_IN_OUT_CHANNELS ), vSelectedOutputChannels ( NUM_IN_OUT_CHANNELS ),
lNumInChan ( 0 ), lNumInChan ( 0 ),
@ -589,14 +461,6 @@ void CSound::ResetChannelMapping()
vSelectedOutputChannels[1] = 1; vSelectedOutputChannels[1] = 1;
} }
CSound::~CSound()
{
// cleanup ASIO stuff
ASIOStop();
ASIODisposeBuffers();
ASIOExit();
asioDrivers->removeCurrentDriver();
}
// ASIO callbacks ------------------------------------------------------------- // ASIO callbacks -------------------------------------------------------------
ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime*, ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime*,
@ -1104,6 +968,11 @@ long CSound::asioMessages ( long selector,
// both messages might be send if the buffer size changes // both messages might be send if the buffer size changes
case kAsioBufferSizeChange: case kAsioBufferSizeChange:
case kAsioResetRequest: case kAsioResetRequest:
// TODO implement separate ASIO reset function in base class!
pSound->EmitReinitRequestSignal(); pSound->EmitReinitRequestSignal();
ret = 1L; // 1L if request is accepted or 0 otherwise ret = 1L; // 1L if request is accepted or 0 otherwise
break; break;

View file

@ -49,7 +49,7 @@ class CSound : public CSoundBase
{ {
public: public:
CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ), void* arg ); CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ), void* arg );
virtual ~CSound(); virtual ~CSound() { UnloadCurrentDriver(); }
virtual int Init ( const int iNewPrefMonoBufferSize ); virtual int Init ( const int iNewPrefMonoBufferSize );
virtual void Start(); virtual void Start();
@ -57,9 +57,6 @@ public:
virtual void OpenDriverSetup() { ASIOControlPanel(); } virtual void OpenDriverSetup() { ASIOControlPanel(); }
// device selection
virtual QString SetDev ( const int iNewDev );
// channel selection // channel selection
virtual int GetNumInputChannels() { return static_cast<int> ( lNumInChan ); } virtual int GetNumInputChannels() { return static_cast<int> ( lNumInChan ); }
virtual QString GetInputChannelName ( const int iDiD ) { return channelInfosInput[iDiD].name; } virtual QString GetInputChannelName ( const int iDiD ) { return channelInfosInput[iDiD].name; }
@ -76,8 +73,8 @@ public:
virtual int GetRightOutputChannel() { return vSelectedOutputChannels[1]; } virtual int GetRightOutputChannel() { return vSelectedOutputChannels[1]; }
protected: protected:
QVector<QString> LoadAndInitializeFirstValidDriver(); virtual QString LoadAndInitializeDriver ( int iIdx );
QString LoadAndInitializeDriver ( int iIdx ); virtual void UnloadCurrentDriver();
int GetActualBufferSize ( const int iDesiredBufferSizeMono ); int GetActualBufferSize ( const int iDesiredBufferSizeMono );
QString CheckDeviceCapabilities(); QString CheckDeviceCapabilities();
bool CheckSampleTypeSupported ( const ASIOSampleType SamType ); bool CheckSampleTypeSupported ( const ASIOSampleType SamType );