added some test code, added auto setting of the sample rate

This commit is contained in:
Volker Fischer 2015-11-15 19:21:24 +00:00
parent 2607d43af5
commit 6a55eb3e7e
2 changed files with 137 additions and 25 deletions

View File

@ -29,6 +29,25 @@
CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg ) : CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg ) :
CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg ) CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg )
{ {
// Apple Mailing Lists: Subject: GUI Apps should set kAudioHardwarePropertyRunLoop
// in the HAL, From: Jeff Moore, Date: Fri, 6 Dec 2002
// Most GUI applciations have several threads on which they receive
// notifications already, so the having the HAL's thread around is wasteful.
// Here is what you should do: On the thread you want the HAL to use for
// notifications (for most apps, this will be the main thread), add the
// following lines of code:
// tell the HAL to use the current thread as it's run loop
CFRunLoopRef theRunLoop = CFRunLoopGetCurrent();
AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
AudioObjectSetPropertyData ( kAudioObjectSystemObject,
&property,
0,
NULL,
sizeof ( CFRunLoopRef ),
&theRunLoop );
// set up stream format // set up stream format
streamFormat.mSampleRate = SYSTEM_SAMPLE_RATE_HZ; streamFormat.mSampleRate = SYSTEM_SAMPLE_RATE_HZ;
streamFormat.mFormatID = kAudioFormatLinearPCM; streamFormat.mFormatID = kAudioFormatLinearPCM;
@ -114,7 +133,6 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) ); throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) );
} }
// set up a callback function for new output data // set up a callback function for new output data
if ( AudioUnitSetProperty ( audioOutputUnit, if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioUnitProperty_SetRenderCallback, kAudioUnitProperty_SetRenderCallback,
@ -126,7 +144,7 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) ); throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
} }
// ste output stream format // set output stream format
if ( AudioUnitSetProperty ( audioOutputUnit, if ( AudioUnitSetProperty ( audioOutputUnit,
kAudioUnitProperty_StreamFormat, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, kAudioUnitScope_Input,
@ -157,8 +175,6 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
AudioDeviceID* audioDevices = (AudioDeviceID*) malloc ( iPropertySize ); AudioDeviceID* audioDevices = (AudioDeviceID*) malloc ( iPropertySize );
// now actually query all devices present in the system // now actually query all devices present in the system
stPropertyAddress.mSelector = kAudioHardwarePropertyDevices;
AudioObjectGetPropertyData ( kAudioObjectSystemObject, AudioObjectGetPropertyData ( kAudioObjectSystemObject,
&stPropertyAddress, &stPropertyAddress,
0, 0,
@ -248,7 +264,8 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
} }
// init device index as not initialized (invalid) // init device index as not initialized (invalid)
lCurDev = INVALID_SNC_CARD_DEVICE; lCurDev = INVALID_SNC_CARD_DEVICE;
CurrentAudioInputDeviceID = 0;
} }
void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID, void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
@ -343,14 +360,14 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
} }
// check device capabilities if it fullfills our requirements // check device capabilities if it fullfills our requirements
const QString strStat = const QString strStat = CheckDeviceCapabilities ( iDriverIdx );
CheckDeviceCapabilities ( audioInputUnit, audioOutputUnit );
// check if device is capable // check if device is capable
if ( strStat.isEmpty() ) if ( strStat.isEmpty() )
{ {
// store ID of selected driver if initialization was successful // store ID of selected driver if initialization was successful
lCurDev = iDriverIdx; lCurDev = iDriverIdx;
CurrentAudioInputDeviceID = audioInputDevice[iDriverIdx];
// TODO why is only the input enough...? // TODO why is only the input enough...?
@ -369,15 +386,21 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
return strStat; return strStat;
} }
QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit, QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
ComponentInstance& NewAudioOutputUnit )
{ {
UInt32 size; UInt32 size;
const Float64 fSystemSampleRate = (Float64) SYSTEM_SAMPLE_RATE_HZ;
AudioObjectPropertyAddress stSRatePropertyAddress;
stSRatePropertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
stSRatePropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
stSRatePropertyAddress.mElement = kAudioObjectPropertyElementMaster;
// check input device sample rate // check input device sample rate
size = sizeof ( Float64 ); size = sizeof ( Float64 );
Float64 inputSampleRate; Float64 inputSampleRate;
AudioUnitGetProperty ( NewAudioInputUnit,
AudioUnitGetProperty ( audioInputUnit,
kAudioUnitProperty_SampleRate, kAudioUnitProperty_SampleRate,
kAudioUnitScope_Input, kAudioUnitScope_Input,
1, 1,
@ -386,16 +409,25 @@ QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit,
if ( static_cast<int> ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ ) if ( static_cast<int> ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
{ {
return QString ( tr ( "Current system audio input device sample " // try to change the sample rate
"rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in " if ( AudioObjectSetPropertyData ( audioInputDevice[iDriverIdx],
"Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg ( &stSRatePropertyAddress,
static_cast<int> ( inputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ ); 0,
NULL,
sizeof ( Float64 ),
&fSystemSampleRate ) != noErr )
{
return QString ( tr ( "Current system audio input device sample "
"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 (
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 ( NewAudioOutputUnit, AudioUnitGetProperty ( audioOutputUnit,
kAudioUnitProperty_SampleRate, kAudioUnitProperty_SampleRate,
kAudioUnitScope_Output, kAudioUnitScope_Output,
0, 0,
@ -404,10 +436,19 @@ QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit,
if ( static_cast<int> ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ ) if ( static_cast<int> ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
{ {
return QString ( tr ( "Current system audio output device sample " // try to change the sample rate
"rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in " if ( AudioObjectSetPropertyData ( audioOutputDevice[iDriverIdx],
"Applications->Utilities and try to set a sample rate of %2 Hz." ) ).arg ( &stSRatePropertyAddress,
static_cast<int> ( outputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ ); 0,
NULL,
sizeof ( Float64 ),
&fSystemSampleRate ) != noErr )
{
return QString ( tr ( "Current system audio output device sample "
"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 (
static_cast<int> ( outputSampleRate ) ).arg ( SYSTEM_SAMPLE_RATE_HZ );
}
} }
// everything is ok, return empty string for "no error" case // everything is ok, return empty string for "no error" case
@ -506,6 +547,24 @@ int CSound::Init ( const int iNewPrefMonoBufferSize )
throw CGenErr ( tr ( "Initialization of CoreAudio failed" ) ); throw CGenErr ( tr ( "Initialization of CoreAudio failed" ) );
} }
/*
// TEST
AudioDeviceIOProcID testIn;
AudioDeviceCreateIOProcID ( audioInputDevice[lCurDev],
callbackIO,
this,
&testIn );
AudioDeviceIOProcID testOut;
AudioDeviceCreateIOProcID ( audioOutputDevice[lCurDev],
callbackIO,
this,
&testOut );
*/
return iCoreAudioBufferSizeMono; return iCoreAudioBufferSizeMono;
} }
@ -557,15 +616,57 @@ OSStatus CSound::deviceNotification ( AudioDeviceID,
if ( inAddresses->mSelector == kAudioDeviceProcessorOverload ) if ( inAddresses->mSelector == kAudioDeviceProcessorOverload )
{ {
// TODO: Do we need this anymore? If not, we can completely remove this function...
/*
// xrun handling (it is important to act on xruns under CoreAudio // xrun handling (it is important to act on xruns under CoreAudio
// since it seems that the xrun situation stays stable for a // since it seems that the xrun situation stays stable for a
// while and would give you a long time bad audio) // while and would give you a long time bad audio)
pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART ); pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART );
*/
} }
return noErr; return noErr;
} }
/*
// TEST
OSStatus CSound::callbackIO ( AudioDeviceID inDevice,
const AudioTimeStamp*,
const AudioBufferList* inInputData,
const AudioTimeStamp*,
AudioBufferList* outOutputData,
const AudioTimeStamp*,
void* inRefCon )
{
CSound* pSound = static_cast<CSound*> ( inRefCon );
if ( inDevice == pSound->CurrentAudioInputDeviceID )
{
// audio input callback, copy data first
memcpy ( &pSound->vecsTmpAudioSndCrdStereo[0],
inInputData->mBuffers[0].mData,
inInputData->mBuffers[0].mDataByteSize );
// call processing callback function
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
}
else
{
// audio output callback, simply copy data
memcpy ( outOutputData->mBuffers[0].mData,
&pSound->vecsTmpAudioSndCrdStereo[0],
outOutputData->mBuffers[0].mDataByteSize );
}
return kAudioHardwareNoError;
}
*/
OSStatus CSound::processInput ( void* inRefCon, OSStatus CSound::processInput ( void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, const AudioTimeStamp* inTimeStamp,
@ -603,8 +704,8 @@ OSStatus CSound::processOutput ( void* inRefCon,
QMutexLocker locker ( &pSound->Mutex ); QMutexLocker locker ( &pSound->Mutex );
memcpy ( ioData->mBuffers[0].mData, memcpy ( ioData->mBuffers[0].mData,
&pSound->vecsTmpAudioSndCrdStereo[0], &pSound->vecsTmpAudioSndCrdStereo[0],
pSound->pBufferList->mBuffers[0].mDataByteSize); pSound->pBufferList->mBuffers[0].mDataByteSize );
return noErr; return noErr;
} }

View File

@ -50,12 +50,12 @@ public:
CVector<short> vecsTmpAudioSndCrdStereo; CVector<short> vecsTmpAudioSndCrdStereo;
int iCoreAudioBufferSizeMono; int iCoreAudioBufferSizeMono;
int iCoreAudioBufferSizeStereo; int iCoreAudioBufferSizeStereo;
AudioDeviceID CurrentAudioInputDeviceID;
protected: protected:
virtual QString LoadAndInitializeDriver ( int iIdx ); virtual QString LoadAndInitializeDriver ( int iIdx );
QString CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit, QString CheckDeviceCapabilities ( const int iDriverIdx );
ComponentInstance& NewAudioOutputUnit );
void CloseCoreAudio(); void CloseCoreAudio();
@ -74,6 +74,17 @@ protected:
const AudioObjectPropertyAddress* inAddresses, const AudioObjectPropertyAddress* inAddresses,
void* inRefCon ); void* inRefCon );
/*
// TEST
static OSStatus callbackIO ( AudioDeviceID inDevice,
const AudioTimeStamp*,
const AudioBufferList* inInputData,
const AudioTimeStamp*,
AudioBufferList* outOutputData,
const AudioTimeStamp*,
void* inRefCon );
*/
static OSStatus processInput ( void* inRefCon, static OSStatus processInput ( void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, const AudioTimeStamp* inTimeStamp,