added some test code, added auto setting of the sample rate
This commit is contained in:
parent
2607d43af5
commit
6a55eb3e7e
2 changed files with 137 additions and 25 deletions
121
mac/sound.cpp
121
mac/sound.cpp
|
@ -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,
|
||||||
|
@ -249,6 +265,7 @@ 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,
|
||||||
|
@ -385,17 +408,26 @@ QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit,
|
||||||
&size );
|
&size );
|
||||||
|
|
||||||
if ( static_cast<int> ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
|
if ( static_cast<int> ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
|
||||||
|
{
|
||||||
|
// try to change the sample rate
|
||||||
|
if ( AudioObjectSetPropertyData ( audioInputDevice[iDriverIdx],
|
||||||
|
&stSRatePropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
sizeof ( Float64 ),
|
||||||
|
&fSystemSampleRate ) != noErr )
|
||||||
{
|
{
|
||||||
return 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 ( NewAudioOutputUnit,
|
AudioUnitGetProperty ( audioOutputUnit,
|
||||||
kAudioUnitProperty_SampleRate,
|
kAudioUnitProperty_SampleRate,
|
||||||
kAudioUnitScope_Output,
|
kAudioUnitScope_Output,
|
||||||
0,
|
0,
|
||||||
|
@ -403,12 +435,21 @@ QString CSound::CheckDeviceCapabilities ( ComponentInstance& NewAudioInputUnit,
|
||||||
&size );
|
&size );
|
||||||
|
|
||||||
if ( static_cast<int> ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
|
if ( static_cast<int> ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
|
||||||
|
{
|
||||||
|
// try to change the sample rate
|
||||||
|
if ( AudioObjectSetPropertyData ( audioOutputDevice[iDriverIdx],
|
||||||
|
&stSRatePropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
sizeof ( Float64 ),
|
||||||
|
&fSystemSampleRate ) != noErr )
|
||||||
{
|
{
|
||||||
return 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 );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// everything is ok, return empty string for "no error" case
|
// everything is ok, return empty string for "no error" case
|
||||||
return "";
|
return "";
|
||||||
|
@ -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,
|
||||||
|
|
15
mac/sound.h
15
mac/sound.h
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue