some work on Mac audio device selection, not yet finished

This commit is contained in:
Volker Fischer 2012-01-21 18:21:36 +00:00
parent 73f408e401
commit ae3a8b09de
5 changed files with 239 additions and 63 deletions

View file

@ -26,10 +26,30 @@
/* Implementation *************************************************************/ /* Implementation *************************************************************/
void CSound::OpenCoreAudio() CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg ) :
CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg )
{ {
UInt32 size; // set up stream format
ComponentResult err; streamFormat.mSampleRate = SYSTEM_SAMPLE_RATE_HZ;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger;
streamFormat.mFramesPerPacket = 1;
streamFormat.mBytesPerFrame = 4;
streamFormat.mBytesPerPacket = 4;
streamFormat.mChannelsPerFrame = 2; // stereo
streamFormat.mBitsPerChannel = 16;
// set up a callback struct for new input data
inputCallbackStruct.inputProc = processInput;
inputCallbackStruct.inputProcRefCon = this;
// set up a callback struct for new output data
outputCallbackStruct.inputProc = processOutput;
outputCallbackStruct.inputProcRefCon = this;
// allocate memory for buffer struct
pBufferList = (AudioBufferList*) malloc ( offsetof ( AudioBufferList,
mBuffers[0] ) + sizeof ( AudioBuffer ) );
// open the default unit // open the default unit
ComponentDescription desc; ComponentDescription desc;
@ -57,7 +77,7 @@ void CSound::OpenCoreAudio()
// we enable input and disable output for input component // we enable input and disable output for input component
UInt32 enableIO = 1; UInt32 enableIO = 1;
err = AudioUnitSetProperty ( audioInputUnit, AudioUnitSetProperty ( audioInputUnit,
kAudioOutputUnitProperty_EnableIO, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, kAudioUnitScope_Input,
1, // input element 1, // input element
@ -65,13 +85,160 @@ void CSound::OpenCoreAudio()
sizeof ( enableIO ) ); sizeof ( enableIO ) );
enableIO = 0; enableIO = 0;
err = AudioUnitSetProperty ( audioInputUnit, AudioUnitSetProperty ( audioInputUnit,
kAudioOutputUnitProperty_EnableIO, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output, kAudioUnitScope_Output,
0, // output element 0, // output element
&enableIO, &enableIO,
sizeof ( enableIO ) ); sizeof ( enableIO ) );
// Get available input/output devices --------------------------------------
UInt32 iPropertySize;
// first get property size of devices array and allocate memory
AudioHardwareGetPropertyInfo ( kAudioHardwarePropertyDevices,
&iPropertySize,
NULL );
AudioDeviceID* audioDevices = (AudioDeviceID*) malloc ( iPropertySize );
// now actually query all devices present in the system
AudioHardwareGetProperty ( kAudioHardwarePropertyDevices,
&iPropertySize,
audioDevices );
// calculate device count based on size of returned data array
const UInt32 deviceCount = ( iPropertySize / sizeof ( AudioDeviceID ) );
// always add system default devices for input and output as first entry
lNumDevs = 0;
strDriverNames[lNumDevs] = "System Default Device";
lNumDevs++;
/*
// add detected devices (also check for maximum allowed sound cards!)
for ( UInt32 i = 0; ( i < deviceCount ) && ( i < MAX_NUMBER_SOUND_CARDS - 1 ); i++ )
{
// TODO
//for ( UInt32 j = 0; ( j < deviceCount ) && ( j < MAX_NUMBER_SOUND_CARDS - 1 ); j++ )
//{
// if ( i != j )
// {
//
// }
//}
QString strDevicName;
bool bIsInput;
bool bIsOutput;
GetAudioDeviceInfos ( audioDevices[i],
strDevicName,
bIsInput,
bIsOutput );
// TEST
if ( bIsInput && bIsOutput )
strDriverNames[lNumDevs] = "in/out";
else if ( !bIsInput && !bIsOutput )
strDriverNames[lNumDevs] = "";
else if ( bIsInput )
strDriverNames[lNumDevs] = "in";
else if ( bIsOutput )
strDriverNames[lNumDevs] = "out";
lNumDevs++;
}
*/
OpenCoreAudio();
// init device index as not initialized (invalid)
lCurDev = INVALID_SNC_CARD_DEVICE;
}
void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
QString& strDeviceName,
bool& bIsInput,
bool& bIsOutput )
{
// get property name
UInt32 iPropertySize = sizeof ( CFStringRef );
CFStringRef sPropertyStringValue;
AudioDeviceGetProperty ( DeviceID,
0,
false,
kAudioObjectPropertyName, //kAudioObjectPropertyManufacturer,
&iPropertySize,
&sPropertyStringValue );
// convert CFString in c-string (quick hack!) and then in QString
char* sC_strPropValue =
(char*) malloc ( CFStringGetLength ( sPropertyStringValue ) + 1 );
CFStringGetCString ( sPropertyStringValue,
sC_strPropValue,
CFStringGetLength ( sPropertyStringValue ) + 1,
kCFStringEncodingISOLatin1 );
strDeviceName = sC_strPropValue;
// check if device is input or output or both (is that possible?)
bIsInput = !AudioUnitSetProperty ( audioInputUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
1,
&DeviceID,
sizeof ( audioInputDevice ) );
bIsOutput = !AudioUnitSetProperty ( audioOutputUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&DeviceID,
sizeof ( audioOutputDevice ) );
}
QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
{
/*
// load driver
loadAsioDriver ( cDriverNames[iDriverIdx] );
if ( ASIOInit ( &driverInfo ) != ASE_OK )
{
// clean up and return error string
asioDrivers->removeCurrentDriver();
return tr ( "The audio driver could not be initialized." );
}
// check device capabilities if it fullfills our requirements
const QString strStat = CheckDeviceCapabilities();
// check if device is capable
if ( strStat.isEmpty() )
{
// store ID of selected driver if initialization was successful
lCurDev = iDriverIdx;
}
else
{
// driver cannot be used, clean up
asioDrivers->removeCurrentDriver();
}
return strStat;
*/
return ""; // TEST
}
void CSound::OpenCoreAudio()
{
UInt32 size;
// set input device // set input device
size = sizeof ( AudioDeviceID ); size = sizeof ( AudioDeviceID );
if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultInputDevice, if ( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultInputDevice,
@ -92,16 +259,12 @@ void CSound::OpenCoreAudio()
} }
// set up a callback function for new input data // set up a callback function for new input data
AURenderCallbackStruct input;
input.inputProc = processInput;
input.inputProcRefCon = this;
if ( AudioUnitSetProperty ( audioInputUnit, if ( AudioUnitSetProperty ( audioInputUnit,
kAudioOutputUnitProperty_SetInputCallback, kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global, kAudioUnitScope_Global,
0, 0,
&input, &inputCallbackStruct,
sizeof ( input ) ) ) sizeof ( inputCallbackStruct ) ) )
{ {
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) ); throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
} }
@ -126,31 +289,16 @@ void CSound::OpenCoreAudio()
} }
// set up a callback function for new output data // set up a callback function for new output data
AURenderCallbackStruct output;
output.inputProc = processOutput;
output.inputProcRefCon = this;
if ( AudioUnitSetProperty ( audioOutputUnit, if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioUnitProperty_SetRenderCallback, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global, kAudioUnitScope_Global,
0, 0,
&output, &outputCallbackStruct,
sizeof ( output ) ) ) sizeof ( outputCallbackStruct ) ) )
{ {
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) ); throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
} }
// set up stream format
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = SYSTEM_SAMPLE_RATE_HZ;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger;
streamFormat.mFramesPerPacket = 1;
streamFormat.mBytesPerFrame = 4;
streamFormat.mBytesPerPacket = 4;
streamFormat.mChannelsPerFrame = 2; // stereo
streamFormat.mBitsPerChannel = 16;
// our output // our output
if ( AudioUnitSetProperty ( audioOutputUnit, if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioUnitProperty_StreamFormat, kAudioUnitProperty_StreamFormat,
@ -173,10 +321,29 @@ void CSound::OpenCoreAudio()
throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) ); 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,
ComponentInstance& NewAudioOutputUnit )
{
UInt32 size;
// check input device sample rate // check input device sample rate
size = sizeof ( Float64 ); size = sizeof ( Float64 );
Float64 inputSampleRate; Float64 inputSampleRate;
AudioUnitGetProperty ( audioInputUnit, AudioUnitGetProperty ( NewAudioInputUnit,
kAudioUnitProperty_SampleRate, kAudioUnitProperty_SampleRate,
kAudioUnitScope_Input, kAudioUnitScope_Input,
1, 1,
@ -185,16 +352,16 @@ void CSound::OpenCoreAudio()
if ( static_cast<int> ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ ) if ( static_cast<int> ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
{ {
throw CGenErr ( QString ( tr ( "Current system audio input device sample " return QString ( tr ( "Current system audio input device sample "
"rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in " "rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in "
"Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg ( "Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg (
static_cast<int> ( inputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ ) ); static_cast<int> ( inputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ );
} }
// check output device sample rate // check output device sample rate
size = sizeof ( Float64 ); size = sizeof ( Float64 );
Float64 outputSampleRate; Float64 outputSampleRate;
AudioUnitGetProperty ( audioOutputUnit, AudioUnitGetProperty ( NewAudioOutputUnit,
kAudioUnitProperty_SampleRate, kAudioUnitProperty_SampleRate,
kAudioUnitScope_Output, kAudioUnitScope_Output,
0, 0,
@ -203,15 +370,14 @@ void CSound::OpenCoreAudio()
if ( static_cast<int> ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ ) if ( static_cast<int> ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
{ {
throw CGenErr ( QString ( tr ( "Current system audio output device sample " return QString ( tr ( "Current system audio output device sample "
"rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in " "rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in "
"Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg ( "Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg (
static_cast<int> ( outputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ ) ); static_cast<int> ( outputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ );
} }
// allocate memory for buffer struct // everything is ok, return empty string for "no error" case
pBufferList = (AudioBufferList*) malloc ( offsetof ( AudioBufferList, return "";
mBuffers[0] ) + sizeof ( AudioBuffer ) );
} }
void CSound::CloseCoreAudio() void CSound::CloseCoreAudio()

View file

@ -37,8 +37,7 @@
class CSound : public CSoundBase 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 ( "CoreAudio", true, fpNewProcessCallback, arg ) { OpenCoreAudio(); }
virtual ~CSound() { CloseCoreAudio(); } virtual ~CSound() { CloseCoreAudio(); }
virtual int Init ( const int iNewPrefMonoBufferSize ); virtual int Init ( const int iNewPrefMonoBufferSize );
@ -52,11 +51,22 @@ public:
int iCoreAudioBufferSizeStero; int iCoreAudioBufferSizeStero;
protected: protected:
virtual QString LoadAndInitializeDriver ( int iIdx );
QString CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit,
ComponentInstance& NewAudioOutputUnit );
void OpenCoreAudio(); void OpenCoreAudio();
void CloseCoreAudio(); void CloseCoreAudio();
UInt32 SetBufferSize ( AudioDeviceID& audioDeviceID, const bool bIsInput,
UInt32 SetBufferSize ( AudioDeviceID& audioDeviceID,
const bool bIsInput,
UInt32 iPrefBufferSize ); UInt32 iPrefBufferSize );
void GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
QString& strDeviceName,
bool& bIsInput,
bool& bIsOutput );
// callbacks // callbacks
static OSStatus processInput ( void* inRefCon,AudioUnitRenderActionFlags* ioActionFlags, static OSStatus processInput ( void* inRefCon,AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
@ -66,6 +76,11 @@ protected:
const AudioTimeStamp*, UInt32, UInt32, const AudioTimeStamp*, UInt32, UInt32,
AudioBufferList* ioData ); AudioBufferList* ioData );
AudioStreamBasicDescription streamFormat;
AURenderCallbackStruct inputCallbackStruct;
AURenderCallbackStruct outputCallbackStruct;
ComponentInstance audioInputUnit; ComponentInstance audioInputUnit;
AudioDeviceID audioInputDevice; AudioDeviceID audioInputDevice;
ComponentInstance audioOutputUnit; ComponentInstance audioOutputUnit;

View file

@ -263,11 +263,6 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
butDriverSetup->hide(); butDriverSetup->hide();
#endif #endif
// set sound card selection to read-only for MacOS/Linux
#ifndef _WIN32
cbxSoundcard->setEnabled ( false );
#endif
// init delay and other information controls // init delay and other information controls
ledOverallDelay->SetUpdateTime ( 2 * PING_UPDATE_TIME_MS ); ledOverallDelay->SetUpdateTime ( 2 * PING_UPDATE_TIME_MS );
ledOverallDelay->Reset(); ledOverallDelay->Reset();

View file

@ -123,7 +123,7 @@ LED bar: lbr
// maximum number of recognized sound cards installed in the system, // maximum number of recognized sound cards installed in the system,
// definition for "no device" // definition for "no device"
#define MAX_NUMBER_SOUND_CARDS 10 #define MAX_NUMBER_SOUND_CARDS 30
#define INVALID_SNC_CARD_DEVICE -1 #define INVALID_SNC_CARD_DEVICE -1
// define the maximum number of audio channel for input/output we can store // define the maximum number of audio channel for input/output we can store

View file

@ -77,7 +77,7 @@ public:
protected: protected:
// driver handling // driver handling
virtual QString LoadAndInitializeDriver ( int ) { return ""; }; virtual QString LoadAndInitializeDriver ( int ) { return ""; }
virtual void UnloadCurrentDriver() {} virtual void UnloadCurrentDriver() {}
QVector<QString> LoadAndInitializeFirstValidDriver(); QVector<QString> LoadAndInitializeFirstValidDriver();