support for selecting audio device for Mac

This commit is contained in:
Volker Fischer 2012-01-22 10:26:34 +00:00
parent 02c0bcb1ac
commit e31c59e0f2
2 changed files with 133 additions and 151 deletions

View file

@ -92,6 +92,51 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
&enableIO, &enableIO,
sizeof ( enableIO ) ); sizeof ( enableIO ) );
// set up a callback function for new input data
if ( AudioUnitSetProperty ( audioInputUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&inputCallbackStruct,
sizeof ( inputCallbackStruct ) ) )
{
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
}
// set input stream format
if ( AudioUnitSetProperty ( audioInputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
1,
&streamFormat,
sizeof ( streamFormat ) ) )
{
throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) );
}
// set up a callback function for new output data
if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
0,
&outputCallbackStruct,
sizeof ( outputCallbackStruct ) ) )
{
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
}
// ste output stream format
if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof ( streamFormat ) ) )
{
throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) );
}
// Get available input/output devices -------------------------------------- // Get available input/output devices --------------------------------------
UInt32 iPropertySize; UInt32 iPropertySize;
@ -114,8 +159,27 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
// always add system default devices for input and output as first entry // always add system default devices for input and output as first entry
lNumDevs = 0; lNumDevs = 0;
strDriverNames[lNumDevs] = "System Default In/Out Devices"; strDriverNames[lNumDevs] = "System Default In/Out Devices";
lNumDevs++;
/* iPropertySize = sizeof ( AudioDeviceID );
if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultInputDevice,
&iPropertySize,
&audioInputDevice[lNumDevs] ) )
{
throw CGenErr ( tr ( "CoreAudio input AudioHardwareGetProperty call failed. "
"It seems that no sound card is available in the system." ) );
}
iPropertySize = sizeof ( AudioDeviceID );
if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultOutputDevice,
&iPropertySize,
&audioOutputDevice[lNumDevs] ) )
{
throw CGenErr ( tr ( "CoreAudio output AudioHardwareGetProperty call failed. "
"It seems that no sound card is available in the system." ) );
}
lNumDevs++; // next device
// add detected devices (also check for maximum allowed sound cards!) // add detected devices (also check for maximum allowed sound cards!)
// //
// we add combined entries for input and output for each device so that we // we add combined entries for input and output for each device so that we
@ -126,20 +190,20 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
for ( UInt32 j = 0; ( j < deviceCount ) && ( j < MAX_NUMBER_SOUND_CARDS - 1 ); j++ ) for ( UInt32 j = 0; ( j < deviceCount ) && ( j < MAX_NUMBER_SOUND_CARDS - 1 ); j++ )
{ {
// get device infos for both current devices // get device infos for both current devices
QString strDevicName_i; QString strDeviceName_i;
QString strDeviceName_j;
bool bIsInput_i; bool bIsInput_i;
bool bIsOutput_i;
QString strDevicName_j;
bool bIsInput_j; bool bIsInput_j;
bool bIsOutput_i;
bool bIsOutput_j; bool bIsOutput_j;
GetAudioDeviceInfos ( audioDevices[i], GetAudioDeviceInfos ( audioDevices[i],
strDevicName_i, strDeviceName_i,
bIsInput_i, bIsInput_i,
bIsOutput_i ); bIsOutput_i );
GetAudioDeviceInfos ( audioDevices[j], GetAudioDeviceInfos ( audioDevices[j],
strDevicName_j, strDeviceName_j,
bIsInput_j, bIsInput_j,
bIsOutput_j ); bIsOutput_j );
@ -147,15 +211,17 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
if ( bIsInput_i && bIsOutput_j ) if ( bIsInput_i && bIsOutput_j )
{ {
strDriverNames[lNumDevs] = "in: " + strDriverNames[lNumDevs] = "in: " +
strDevicName_i + "/out: " + strDeviceName_i + "/out: " +
strDevicName_j; strDeviceName_j;
lNumDevs++; // store audio device IDs
audioInputDevice[lNumDevs] = audioDevices[i];
audioOutputDevice[lNumDevs] = audioDevices[j];
lNumDevs++; // next device
} }
} }
} }
*/
OpenCoreAudio();
// init device index as not initialized (invalid) // init device index as not initialized (invalid)
lCurDev = INVALID_SNC_CARD_DEVICE; lCurDev = INVALID_SNC_CARD_DEVICE;
@ -173,13 +239,13 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
AudioDeviceGetProperty ( DeviceID, AudioDeviceGetProperty ( DeviceID,
0, 0,
false, false,
kAudioObjectPropertyName, //kAudioObjectPropertyManufacturer, kAudioObjectPropertyName,
&iPropertySize, &iPropertySize,
&sPropertyStringValue ); &sPropertyStringValue );
// convert CFString in c-string (quick hack!) and then in QString // convert CFString in c-string (quick hack!) and then in QString
char* sC_strPropValue = char* sC_strPropValue =
(char*) malloc ( CFStringGetLength ( sPropertyStringValue ) + 1 ); (char*) malloc ( CFStringGetLength ( sPropertyStringValue ) + 1 );
CFStringGetCString ( sPropertyStringValue, CFStringGetCString ( sPropertyStringValue,
sC_strPropValue, sC_strPropValue,
@ -189,35 +255,51 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
strDeviceName = sC_strPropValue; strDeviceName = sC_strPropValue;
// check if device is input or output or both (is that possible?) // check if device is input or output or both (is that possible?)
// we do this by trying to set the current device for the audio unit
// with the parameter input and output and then we simply check the
// error/ok result
bIsInput = !AudioUnitSetProperty ( audioInputUnit, bIsInput = !AudioUnitSetProperty ( audioInputUnit,
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, kAudioUnitScope_Global,
1, 1,
&DeviceID, &DeviceID,
sizeof ( audioInputDevice ) ); sizeof ( AudioDeviceID ) );
bIsOutput = !AudioUnitSetProperty ( audioOutputUnit, bIsOutput = !AudioUnitSetProperty ( audioOutputUnit,
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, kAudioUnitScope_Global,
0, 0,
&DeviceID, &DeviceID,
sizeof ( audioOutputDevice ) ); sizeof ( AudioDeviceID ) );
} }
QString CSound::LoadAndInitializeDriver ( int iDriverIdx ) QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
{ {
/* // set input device
// load driver if ( AudioUnitSetProperty ( audioInputUnit,
loadAsioDriver ( cDriverNames[iDriverIdx] ); kAudioOutputUnitProperty_CurrentDevice,
if ( ASIOInit ( &driverInfo ) != ASE_OK ) kAudioUnitScope_Global,
1,
&audioInputDevice[iDriverIdx],
sizeof ( AudioDeviceID ) ) )
{ {
// clean up and return error string throw CGenErr ( tr ( "CoreAudio input AudioUnitSetProperty call failed" ) );
asioDrivers->removeCurrentDriver(); }
return tr ( "The audio driver could not be initialized." );
// set output device
if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&audioOutputDevice[iDriverIdx],
sizeof ( AudioDeviceID ) ) )
{
throw CGenErr ( tr ( "CoreAudio output AudioUnitSetProperty call failed" ) );
} }
// check device capabilities if it fullfills our requirements // check device capabilities if it fullfills our requirements
const QString strStat = CheckDeviceCapabilities(); const QString strStat =
CheckDeviceCapabilities ( audioInputUnit, audioOutputUnit );
// check if device is capable // check if device is capable
if ( strStat.isEmpty() ) if ( strStat.isEmpty() )
@ -225,115 +307,8 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
// store ID of selected driver if initialization was successful // store ID of selected driver if initialization was successful
lCurDev = iDriverIdx; lCurDev = iDriverIdx;
} }
else
{
// driver cannot be used, clean up
asioDrivers->removeCurrentDriver();
}
return strStat; return strStat;
*/
return ""; // TEST
}
void CSound::OpenCoreAudio()
{
UInt32 size;
// set input device
size = sizeof ( AudioDeviceID );
if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultInputDevice,
&size,
&audioInputDevice ) )
{
throw CGenErr ( tr ( "CoreAudio input AudioHardwareGetProperty call failed" ) );
}
if ( AudioUnitSetProperty ( audioInputUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
1,
&audioInputDevice,
sizeof ( audioInputDevice ) ) )
{
throw CGenErr ( tr ( "CoreAudio input AudioUnitSetProperty call failed" ) );
}
// set up a callback function for new input data
if ( AudioUnitSetProperty ( audioInputUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&inputCallbackStruct,
sizeof ( inputCallbackStruct ) ) )
{
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
}
// set output device
size = sizeof ( AudioDeviceID );
if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultOutputDevice,
&size,
&audioOutputDevice ) )
{
throw CGenErr ( tr ( "CoreAudio output AudioHardwareGetProperty call failed" ) );
}
if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&audioOutputDevice,
sizeof ( audioOutputDevice ) ) )
{
throw CGenErr ( tr ( "CoreAudio output AudioUnitSetProperty call failed" ) );
}
// set up a callback function for new output data
if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
0,
&outputCallbackStruct,
sizeof ( outputCallbackStruct ) ) )
{
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
}
// our output
if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(streamFormat) ) )
{
throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) );
}
// our input
if ( AudioUnitSetProperty ( audioInputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
1,
&streamFormat,
sizeof(streamFormat) ) )
{
throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) );
}
// check device capabilities if it fullfills our requirements
const QString strStat = CheckDeviceCapabilities ( audioInputUnit, audioOutputUnit );
// check if device is capable
if ( strStat.isEmpty() )
{
// TODO
}
else
{
throw CGenErr ( strStat );
}
} }
QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit, QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit,
@ -414,20 +389,21 @@ int CSound::Init ( const int iNewPrefMonoBufferSize )
{ {
UInt32 iActualMonoBufferSize; UInt32 iActualMonoBufferSize;
// Error message string: in case buffer sizes on input and output cannot be set to the same value // Error message string: in case buffer sizes on input and output cannot be
// set to the same value
const QString strErrBufSize = tr ( "The buffer sizes of the current " const QString strErrBufSize = tr ( "The buffer sizes of the current "
"input and output audio device cannot be set to a common value. Please " "input and output audio device cannot be set to a common value. Please "
"choose other input/output audio devices in your system settings." ); "choose other input/output audio devices in your system settings." );
// try to set input buffer size // try to set input buffer size
iActualMonoBufferSize = iActualMonoBufferSize =
SetBufferSize ( audioInputDevice, true, iNewPrefMonoBufferSize ); SetBufferSize ( audioInputDevice[lCurDev], true, iNewPrefMonoBufferSize );
if ( iActualMonoBufferSize != static_cast<UInt32> ( iNewPrefMonoBufferSize ) ) if ( iActualMonoBufferSize != static_cast<UInt32> ( iNewPrefMonoBufferSize ) )
{ {
// try to set the input buffer size to the output so that we // try to set the input buffer size to the output so that we
// have a matching pair // have a matching pair
if ( SetBufferSize ( audioOutputDevice, false, iActualMonoBufferSize ) != if ( SetBufferSize ( audioOutputDevice[lCurDev], false, iActualMonoBufferSize ) !=
iActualMonoBufferSize ) iActualMonoBufferSize )
{ {
throw CGenErr ( strErrBufSize ); throw CGenErr ( strErrBufSize );
@ -436,7 +412,7 @@ int CSound::Init ( const int iNewPrefMonoBufferSize )
else else
{ {
// try to set output buffer size // try to set output buffer size
if ( SetBufferSize ( audioOutputDevice, false, iNewPrefMonoBufferSize ) != if ( SetBufferSize ( audioOutputDevice[lCurDev], false, iNewPrefMonoBufferSize ) !=
static_cast<UInt32> ( iNewPrefMonoBufferSize ) ) static_cast<UInt32> ( iNewPrefMonoBufferSize ) )
{ {
throw CGenErr ( strErrBufSize ); throw CGenErr ( strErrBufSize );
@ -456,10 +432,10 @@ int CSound::Init ( const int iNewPrefMonoBufferSize )
vecsTmpAudioSndCrdStereo.Init ( iCoreAudioBufferSizeStero ); vecsTmpAudioSndCrdStereo.Init ( iCoreAudioBufferSizeStero );
// fill audio unit buffer struct // fill audio unit buffer struct
pBufferList->mNumberBuffers = 1; pBufferList->mNumberBuffers = 1;
pBufferList->mBuffers[0].mNumberChannels = 2; // stereo pBufferList->mBuffers[0].mNumberChannels = 2; // stereo
pBufferList->mBuffers[0].mDataByteSize = iCoreAudioBufferSizeMono * 4; // 2 bytes, 2 channels pBufferList->mBuffers[0].mDataByteSize = iCoreAudioBufferSizeMono * 4; // 2 bytes, 2 channels
pBufferList->mBuffers[0].mData = &vecsTmpAudioSndCrdStereo[0]; pBufferList->mBuffers[0].mData = &vecsTmpAudioSndCrdStereo[0];
// initialize unit // initialize unit
if ( AudioUnitInitialize ( audioInputUnit ) ) if ( AudioUnitInitialize ( audioInputUnit ) )
@ -526,12 +502,12 @@ OSStatus CSound::processInput ( void* inRefCon,
return noErr; return noErr;
} }
OSStatus CSound::processOutput ( void* inRefCon, OSStatus CSound::processOutput ( void* inRefCon,
AudioUnitRenderActionFlags*, AudioUnitRenderActionFlags*,
const AudioTimeStamp*, const AudioTimeStamp*,
UInt32, UInt32,
UInt32, UInt32,
AudioBufferList* ioData ) AudioBufferList* ioData )
{ {
CSound* pSound = reinterpret_cast<CSound*> ( inRefCon ); CSound* pSound = reinterpret_cast<CSound*> ( inRefCon );

View file

@ -52,10 +52,10 @@ public:
protected: protected:
virtual QString LoadAndInitializeDriver ( int iIdx ); virtual QString LoadAndInitializeDriver ( int iIdx );
QString CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit, QString CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit,
ComponentInstance& NewAudioOutputUnit ); ComponentInstance& NewAudioOutputUnit );
void OpenCoreAudio();
void CloseCoreAudio(); void CloseCoreAudio();
UInt32 SetBufferSize ( AudioDeviceID& audioDeviceID, UInt32 SetBufferSize ( AudioDeviceID& audioDeviceID,
@ -68,13 +68,19 @@ protected:
bool& bIsOutput ); bool& bIsOutput );
// callbacks // callbacks
static OSStatus processInput ( void* inRefCon,AudioUnitRenderActionFlags* ioActionFlags, static OSStatus processInput ( void* inRefCon,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioUnitRenderActionFlags* ioActionFlags,
AudioBufferList* ); const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* );
static OSStatus processOutput ( void* inRefCon,AudioUnitRenderActionFlags*, static OSStatus processOutput ( void* inRefCon,
const AudioTimeStamp*, UInt32, UInt32, AudioUnitRenderActionFlags*,
AudioBufferList* ioData ); const AudioTimeStamp*,
UInt32,
UInt32,
AudioBufferList* ioData );
AudioStreamBasicDescription streamFormat; AudioStreamBasicDescription streamFormat;
@ -82,13 +88,13 @@ protected:
AURenderCallbackStruct outputCallbackStruct; AURenderCallbackStruct outputCallbackStruct;
ComponentInstance audioInputUnit; ComponentInstance audioInputUnit;
AudioDeviceID audioInputDevice; AudioDeviceID audioInputDevice[MAX_NUMBER_SOUND_CARDS];
ComponentInstance audioOutputUnit; ComponentInstance audioOutputUnit;
AudioDeviceID audioOutputDevice; AudioDeviceID audioOutputDevice[MAX_NUMBER_SOUND_CARDS];
AudioBufferList* pBufferList; AudioBufferList* pBufferList;
QMutex Mutex; QMutex Mutex;
}; };
#endif // !defined(_SOUND_H__9518A621345F78_363456876UZGSDF82CF549__INCLUDED_) #endif // !defined(_SOUND_H__9518A621345F78_363456876UZGSDF82CF549__INCLUDED_)