1 /************************************************************************
3 IMPORTANT NOTE : this file contains two clearly delimited sections :
4 the ARCHITECTURE section (in two parts) and the USER section. Each section
5 is governed by its own copyright and license. Please check individually
6 each section for license and copyright information.
7 *************************************************************************/
9 #ifndef __coreaudio_dsp__
10 #define __coreaudio_dsp__
12 /*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/
14 /************************************************************************
15 FAUST Architecture File
16 Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale
17 ---------------------------------------------------------------------
18 This Architecture section is free software; you can redistribute it
19 and/or modify it under the terms of the GNU General Public License
20 as published by the Free Software Foundation; either version 3 of
21 the License, or (at your option) any later version.
23 This program is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program; If not, see <http://www.gnu.org/licenses/>.
31 EXCEPTION : As a special exception, you may create a larger work
32 that contains this FAUST architecture section and distribute
33 that work under terms of your choice, so long as this FAUST
34 architecture section is not modified.
37 ************************************************************************
38 ************************************************************************/
48 #include <AudioToolbox/AudioConverter.h>
49 #include <CoreAudio/CoreAudio.h>
50 #include <AudioUnit/AudioUnit.h>
51 #include <CoreServices/CoreServices.h>
58 /******************************************************************************
59 *******************************************************************************
63 *******************************************************************************
64 *******************************************************************************/
66 //----------------------------------------------------------------------------
67 // number of physical input and output channels of the CA device
68 //----------------------------------------------------------------------------
73 //----------------------------------------------------------------------------
74 // tables of noninterleaved input and output channels for FAUST
75 //----------------------------------------------------------------------------
77 float* gInChannel
[256];
78 float* gOutChannel
[256];
83 #define WAIT_COUNTER 60
85 typedef UInt8 CAAudioHardwareDeviceSectionID
;
86 #define kAudioDeviceSectionInput ((CAAudioHardwareDeviceSectionID)0x01)
87 #define kAudioDeviceSectionOutput ((CAAudioHardwareDeviceSectionID)0x00)
88 #define kAudioDeviceSectionGlobal ((CAAudioHardwareDeviceSectionID)0x00)
89 #define kAudioDeviceSectionWildcard ((CAAudioHardwareDeviceSectionID)0xFF)
93 class TCoreAudioRenderer
96 AudioBufferList
* fInputData
;
97 AudioDeviceID fDeviceID
;
99 AudioObjectID fPluginID
; // Used for aggregate device
102 OSStatus
GetDefaultDevice(int inChan
, int outChan
, int samplerate
, AudioDeviceID
* id
);
104 OSStatus
CreateAggregateDevice(AudioDeviceID captureDeviceID
, AudioDeviceID playbackDeviceID
, int samplerate
, AudioDeviceID
* outAggregateDevice
);
105 OSStatus
CreateAggregateDeviceAux(vector
<AudioDeviceID
> captureDeviceID
, vector
<AudioDeviceID
> playbackDeviceID
, int samplerate
, AudioDeviceID
* outAggregateDevice
);
106 OSStatus
DestroyAggregateDevice();
108 OSStatus
GetDeviceNameFromID(AudioDeviceID id
, char* name
);
110 int SetupSampleRateAux(AudioDeviceID inDevice
, int samplerate
);
112 static OSStatus
Render(void *inRefCon
,
113 AudioUnitRenderActionFlags
*ioActionFlags
,
114 const AudioTimeStamp
*inTimeStamp
,
116 UInt32 inNumberFrames
,
117 AudioBufferList
*ioData
);
120 static OSStatus
SRNotificationCallback(AudioDeviceID inDevice
,
123 AudioDevicePropertyID inPropertyID
,
129 :fInputData(0),fDeviceID(0),fAUHAL(0),fPluginID(0),fState(false)
131 virtual ~TCoreAudioRenderer()
134 long OpenDefault(long inChan
, long outChan
, long bufferSize
, long sampleRate
);
142 typedef TCoreAudioRenderer
* TCoreAudioRendererPtr
;
144 static void PrintStreamDesc(AudioStreamBasicDescription
*inDesc
)
146 cout
<< "- - - - - - - - - - - - - - - - - - - -" << endl
;
147 cout
<< " Sample Rate: " << inDesc
->mSampleRate
<< endl
;
148 cout
<< " Format ID:%.*s\n" << sizeof(inDesc
->mFormatID
) << (char*)&inDesc
->mFormatID
<< endl
;
149 cout
<< " Format Flags " << inDesc
->mFormatFlags
<< endl
;
150 cout
<< " Bytes per Packet: " << inDesc
->mBytesPerPacket
<< endl
;
151 cout
<< " Frames per Packet: " << inDesc
->mFramesPerPacket
<< endl
;
152 cout
<< " Bytes per Frame: " << inDesc
->mBytesPerFrame
<< endl
;
153 cout
<< " Channels per Frame: "<< inDesc
->mChannelsPerFrame
<< endl
;
154 cout
<< " Bits per Channel: " << inDesc
->mBitsPerChannel
<< endl
;
155 cout
<< "- - - - - - - - - - - - - - - - - - - -" << endl
;
158 static void printError(OSStatus err
)
161 case kAudioHardwareNoError
:
162 printf("error code : kAudioHardwareNoError\n");
164 case kAudioConverterErr_FormatNotSupported
:
165 printf("error code : kAudioConverterErr_FormatNotSupported\n");
167 case kAudioConverterErr_OperationNotSupported
:
168 printf("error code : kAudioConverterErr_OperationNotSupported\n");
170 case kAudioConverterErr_PropertyNotSupported
:
171 printf("error code : kAudioConverterErr_PropertyNotSupported\n");
173 case kAudioConverterErr_InvalidInputSize
:
174 printf("error code : kAudioConverterErr_InvalidInputSize\n");
176 case kAudioConverterErr_InvalidOutputSize
:
177 printf("error code : kAudioConverterErr_InvalidOutputSize\n");
179 case kAudioConverterErr_UnspecifiedError
:
180 printf("error code : kAudioConverterErr_UnspecifiedError\n");
182 case kAudioConverterErr_BadPropertySizeError
:
183 printf("error code : kAudioConverterErr_BadPropertySizeError\n");
185 case kAudioConverterErr_RequiresPacketDescriptionsError
:
186 printf("error code : kAudioConverterErr_RequiresPacketDescriptionsError\n");
188 case kAudioConverterErr_InputSampleRateOutOfRange
:
189 printf("error code : kAudioConverterErr_InputSampleRateOutOfRange\n");
191 case kAudioConverterErr_OutputSampleRateOutOfRange
:
192 printf("error code : kAudioConverterErr_OutputSampleRateOutOfRange\n");
194 case kAudioHardwareNotRunningError
:
195 printf("error code : kAudioHardwareNotRunningError\n");
197 case kAudioHardwareUnknownPropertyError
:
198 printf("error code : kAudioHardwareUnknownPropertyError\n");
200 case kAudioHardwareIllegalOperationError
:
201 printf("error code : kAudioHardwareIllegalOperationError\n");
203 case kAudioHardwareBadDeviceError
:
204 printf("error code : kAudioHardwareBadDeviceError\n");
206 case kAudioHardwareBadStreamError
:
207 printf("error code : kAudioHardwareBadStreamError\n");
209 case kAudioDeviceUnsupportedFormatError
:
210 printf("error code : kAudioDeviceUnsupportedFormatError\n");
212 case kAudioDevicePermissionsError
:
213 printf("error code : kAudioDevicePermissionsError\n");
216 printf("error code : unknown\n");
221 OSStatus
TCoreAudioRenderer::Render(void *inRefCon
,
222 AudioUnitRenderActionFlags
*ioActionFlags
,
223 const AudioTimeStamp
*inTimeStamp
,
225 UInt32 inNumberFrames
,
226 AudioBufferList
*ioData
)
228 TCoreAudioRendererPtr renderer
= (TCoreAudioRendererPtr
)inRefCon
;
229 AudioUnitRender(renderer
->fAUHAL
, ioActionFlags
, inTimeStamp
, 1, inNumberFrames
, renderer
->fInputData
);
230 for (int i
= 0; i
< gDevNumInChans
; i
++) {
231 gInChannel
[i
] = (float*)renderer
->fInputData
->mBuffers
[i
].mData
;
233 for (int i
= 0; i
< gDevNumOutChans
; i
++) {
234 gOutChannel
[i
] = (float*)ioData
->mBuffers
[i
].mData
;
236 gDsp
->compute((int)inNumberFrames
, gInChannel
, gOutChannel
);
240 static CFStringRef
GetDeviceName(AudioDeviceID id
)
242 UInt32 size
= sizeof(CFStringRef
);
244 OSStatus err
= AudioDeviceGetProperty(id
, 0, false, kAudioDevicePropertyDeviceUID
, &size
, &UIname
);
245 return (err
== noErr
) ? UIname
: NULL
;
248 OSStatus
TCoreAudioRenderer::GetDeviceNameFromID(AudioDeviceID id
, char* name
)
251 return AudioDeviceGetProperty(id
, 0, false, kAudioDevicePropertyDeviceName
, &size
, name
);
254 OSStatus
TCoreAudioRenderer::GetDefaultDevice(int inChan
, int outChan
, int samplerate
, AudioDeviceID
* id
)
256 UInt32 theSize
= sizeof(UInt32
);
257 AudioDeviceID inDefault
;
258 AudioDeviceID outDefault
;
261 if ((res
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
,
262 &theSize
, &inDefault
)) != noErr
)
265 if ((res
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
,
266 &theSize
, &outDefault
)) != noErr
)
270 if (inChan
> 0 && outChan
> 0) {
271 // Get the device only if default input and output are the same
272 if (inDefault
== outDefault
) {
276 printf("GetDefaultDevice : input = %ld and output = %ld are not the same, create aggregate device...\n", inDefault
, outDefault
);
277 if (CreateAggregateDevice(inDefault
, outDefault
, samplerate
, id
) != noErr
)
278 return kAudioHardwareBadDeviceError
;
280 } else if (inChan
> 0) {
283 } else if (outChan
> 0) {
287 return kAudioHardwareBadDeviceError
;
293 OSStatus
TCoreAudioRenderer::CreateAggregateDevice(AudioDeviceID captureDeviceID
, AudioDeviceID playbackDeviceID
, int samplerate
, AudioDeviceID
* outAggregateDevice
)
295 OSStatus err
= noErr
;
296 AudioObjectID sub_device
[32];
297 UInt32 outSize
= sizeof(sub_device
);
299 err
= AudioDeviceGetProperty(captureDeviceID
, 0, kAudioDeviceSectionGlobal
, kAudioAggregateDevicePropertyActiveSubDeviceList
, &outSize
, sub_device
);
300 vector
<AudioDeviceID
> captureDeviceIDArray
;
303 printf("Input device does not have subdevices\n");
304 captureDeviceIDArray
.push_back(captureDeviceID
);
306 int num_devices
= outSize
/ sizeof(AudioObjectID
);
307 printf("Input device has %d subdevices\n", num_devices
);
308 for (int i
= 0; i
< num_devices
; i
++) {
309 captureDeviceIDArray
.push_back(sub_device
[i
]);
313 err
= AudioDeviceGetProperty(playbackDeviceID
, 0, kAudioDeviceSectionGlobal
, kAudioAggregateDevicePropertyActiveSubDeviceList
, &outSize
, sub_device
);
314 vector
<AudioDeviceID
> playbackDeviceIDArray
;
317 printf("Output device does not have subdevices\n");
318 playbackDeviceIDArray
.push_back(playbackDeviceID
);
320 int num_devices
= outSize
/ sizeof(AudioObjectID
);
321 printf("Output device has %d subdevices\n", num_devices
);
322 for (int i
= 0; i
< num_devices
; i
++) {
323 playbackDeviceIDArray
.push_back(sub_device
[i
]);
327 return CreateAggregateDeviceAux(captureDeviceIDArray
, playbackDeviceIDArray
, samplerate
, outAggregateDevice
);
331 OSStatus
TCoreAudioRenderer::SRNotificationCallback(AudioDeviceID inDevice
,
332 UInt32
/*inChannel*/,
334 AudioDevicePropertyID inPropertyID
,
337 TCoreAudioRenderer
* driver
= (TCoreAudioRenderer
*)inClientData
;
339 switch (inPropertyID
) {
341 case kAudioDevicePropertyNominalSampleRate
: {
342 printf("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate\n");
343 driver
->fState
= true;
344 // Check new sample rate
346 UInt32 outSize
= sizeof(Float64
);
347 OSStatus err
= AudioDeviceGetProperty(inDevice
, 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyNominalSampleRate
, &outSize
, &sampleRate
);
349 printf("Cannot get current sample rate\n");
352 printf("SRNotificationCallback : checked sample rate = %f\n", sampleRate
);
361 int TCoreAudioRenderer::SetupSampleRateAux(AudioDeviceID inDevice
, int samplerate
)
363 OSStatus err
= noErr
;
368 outSize
= sizeof(Float64
);
369 err
= AudioDeviceGetProperty(inDevice
, 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyNominalSampleRate
, &outSize
, &sampleRate
);
371 printf("Cannot get current sample rate\n");
375 printf("Current sample rate = %f\n", sampleRate
);
378 // If needed, set new sample rate
379 if (samplerate
!= (int)sampleRate
) {
380 sampleRate
= (Float64
)samplerate
;
382 // To get SR change notification
383 err
= AudioDeviceAddPropertyListener(inDevice
, 0, true, kAudioDevicePropertyNominalSampleRate
, SRNotificationCallback
, this);
385 printf("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate\n");
389 err
= AudioDeviceSetProperty(inDevice
, NULL
, 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyNominalSampleRate
, outSize
, &sampleRate
);
391 printf("Cannot set sample rate = %d\n", samplerate
);
396 // Waiting for SR change notification
398 while (!fState
&& count
++ < WAIT_COUNTER
) {
400 printf("Wait count = %d\n", count
);
403 // Check new sample rate
404 outSize
= sizeof(Float64
);
405 err
= AudioDeviceGetProperty(inDevice
, 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyNominalSampleRate
, &outSize
, &sampleRate
);
407 printf("Cannot get current sample rate\n");
410 printf("Checked sample rate = %f\n", sampleRate
);
413 // Remove SR change notification
414 AudioDeviceRemovePropertyListener(inDevice
, 0, true, kAudioDevicePropertyNominalSampleRate
, SRNotificationCallback
);
420 OSStatus
TCoreAudioRenderer::CreateAggregateDeviceAux(vector
<AudioDeviceID
> captureDeviceID
, vector
<AudioDeviceID
> playbackDeviceID
, int samplerate
, AudioDeviceID
* outAggregateDevice
)
422 OSStatus osErr
= noErr
;
426 bool fClockDriftCompensate
= true;
428 // Prepare sub-devices for clock drift compensation
429 // Workaround for bug in the HAL : until 10.6.2
430 AudioObjectPropertyAddress theAddressOwned
= { kAudioObjectPropertyOwnedObjects
, kAudioObjectPropertyScopeGlobal
, kAudioObjectPropertyElementMaster
};
431 AudioObjectPropertyAddress theAddressDrift
= { kAudioSubDevicePropertyDriftCompensation
, kAudioObjectPropertyScopeGlobal
, kAudioObjectPropertyElementMaster
};
432 UInt32 theQualifierDataSize
= sizeof(AudioObjectID
);
433 AudioClassID inClass
= kAudioSubDeviceClassID
;
434 void* theQualifierData
= &inClass
;
435 UInt32 subDevicesNum
= 0;
437 //---------------------------------------------------------------------------
438 // Setup SR of both devices otherwise creating AD may fail...
439 //---------------------------------------------------------------------------
440 UInt32 keptclockdomain
= 0;
441 UInt32 clockdomain
= 0;
442 outSize
= sizeof(UInt32
);
443 bool need_clock_drift_compensation
= false;
445 for (UInt32 i
= 0; i
< captureDeviceID
.size(); i
++) {
446 if (SetupSampleRateAux(captureDeviceID
[i
], samplerate
) < 0) {
447 printf("TCoreAudioRenderer::CreateAggregateDevice : cannot set SR of input device\n");
449 // Check clock domain
450 osErr
= AudioDeviceGetProperty(captureDeviceID
[i
], 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyClockDomain
, &outSize
, &clockdomain
);
452 printf("TCoreAudioRenderer::CreateAggregateDevice : kAudioDevicePropertyClockDomain error\n");
455 keptclockdomain
= (keptclockdomain
== 0) ? clockdomain
: keptclockdomain
;
456 printf("TCoreAudioRenderer::CreateAggregateDevice : input clockdomain = %d\n", clockdomain
);
457 if (clockdomain
!= 0 && clockdomain
!= keptclockdomain
) {
458 printf("TCoreAudioRenderer::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...\n");
459 need_clock_drift_compensation
= true;
465 for (UInt32 i
= 0; i
< playbackDeviceID
.size(); i
++) {
466 if (SetupSampleRateAux(playbackDeviceID
[i
], samplerate
) < 0) {
467 printf("TCoreAudioRenderer::CreateAggregateDevice : cannot set SR of output device\n");
469 // Check clock domain
470 osErr
= AudioDeviceGetProperty(playbackDeviceID
[i
], 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyClockDomain
, &outSize
, &clockdomain
);
472 printf("TCoreAudioRenderer::CreateAggregateDevice : kAudioDevicePropertyClockDomain error\n");
475 keptclockdomain
= (keptclockdomain
== 0) ? clockdomain
: keptclockdomain
;
476 printf("TCoreAudioRenderer::CreateAggregateDevice : output clockdomain = %d", clockdomain
);
477 if (clockdomain
!= 0 && clockdomain
!= keptclockdomain
) {
478 printf("TCoreAudioRenderer::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...\n");
479 need_clock_drift_compensation
= true;
485 // If no valid clock domain was found, then assume we have to compensate...
486 if (keptclockdomain
== 0) {
487 need_clock_drift_compensation
= true;
490 //---------------------------------------------------------------------------
491 // Start to create a new aggregate by getting the base audio hardware plugin
492 //---------------------------------------------------------------------------
494 char device_name
[256];
495 for (UInt32 i
= 0; i
< captureDeviceID
.size(); i
++) {
496 GetDeviceNameFromID(captureDeviceID
[i
], device_name
);
497 printf("Separated input = '%s' \n", device_name
);
500 for (UInt32 i
= 0; i
< playbackDeviceID
.size(); i
++) {
501 GetDeviceNameFromID(playbackDeviceID
[i
], device_name
);
502 printf("Separated output = '%s' \n", device_name
);
505 osErr
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID
, &outSize
, &outWritable
);
506 if (osErr
!= noErr
) {
507 printf("TCoreAudioRenderer::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error\n");
512 AudioValueTranslation pluginAVT
;
514 CFStringRef inBundleRef
= CFSTR("com.apple.audio.CoreAudio");
516 pluginAVT
.mInputData
= &inBundleRef
;
517 pluginAVT
.mInputDataSize
= sizeof(inBundleRef
);
518 pluginAVT
.mOutputData
= &fPluginID
;
519 pluginAVT
.mOutputDataSize
= sizeof(fPluginID
);
521 osErr
= AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID
, &outSize
, &pluginAVT
);
522 if (osErr
!= noErr
) {
523 printf("TCoreAudioRenderer::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error\n");
528 //-------------------------------------------------
529 // Create a CFDictionary for our aggregate device
530 //-------------------------------------------------
532 CFMutableDictionaryRef aggDeviceDict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
534 CFStringRef AggregateDeviceNameRef
= CFSTR("JackDuplex");
535 CFStringRef AggregateDeviceUIDRef
= CFSTR("com.grame.JackDuplex");
537 // add the name of the device to the dictionary
538 CFDictionaryAddValue(aggDeviceDict
, CFSTR(kAudioAggregateDeviceNameKey
), AggregateDeviceNameRef
);
540 // add our choice of UID for the aggregate device to the dictionary
541 CFDictionaryAddValue(aggDeviceDict
, CFSTR(kAudioAggregateDeviceUIDKey
), AggregateDeviceUIDRef
);
543 // add a "private aggregate key" to the dictionary
545 CFNumberRef AggregateDeviceNumberRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &value
);
548 Gestalt(gestaltSystemVersion
, &system
);
550 printf("TCoreAudioRenderer::CreateAggregateDevice : system version = %x limit = %x\n", system
, 0x00001054);
552 // Starting with 10.5.4 systems, the AD can be internal... (better)
553 if (system
< 0x00001054) {
554 printf("TCoreAudioRenderer::CreateAggregateDevice : public aggregate device....\n");
556 printf("TCoreAudioRenderer::CreateAggregateDevice : private aggregate device....\n");
557 CFDictionaryAddValue(aggDeviceDict
, CFSTR(kAudioAggregateDeviceIsPrivateKey
), AggregateDeviceNumberRef
);
560 // Prepare sub-devices for clock drift compensation
561 CFMutableArrayRef subDevicesArrayClock
= NULL
;
564 if (fClockDriftCompensate) {
565 if (need_clock_drift_compensation) {
566 jack_info("Clock drift compensation activated...");
567 subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
569 for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
570 CFStringRef UID = GetDeviceName(captureDeviceID[i]);
572 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
573 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
574 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
576 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
580 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
581 CFStringRef UID = GetDeviceName(playbackDeviceID[i]);
583 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
584 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
585 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
587 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
591 // add sub-device clock array for the aggregate device to the dictionary
592 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock);
594 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
599 //-------------------------------------------------
600 // Create a CFMutableArray for our sub-device list
601 //-------------------------------------------------
603 // we need to append the UID for each device to a CFMutableArray, so create one here
604 CFMutableArrayRef subDevicesArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
606 vector
<CFStringRef
> captureDeviceUID
;
607 for (UInt32 i
= 0; i
< captureDeviceID
.size(); i
++) {
608 CFStringRef ref
= GetDeviceName(captureDeviceID
[i
]);
611 captureDeviceUID
.push_back(ref
);
612 // input sub-devices in this example, so append the sub-device's UID to the CFArray
613 CFArrayAppendValue(subDevicesArray
, ref
);
616 vector
<CFStringRef
> playbackDeviceUID
;
617 for (UInt32 i
= 0; i
< playbackDeviceID
.size(); i
++) {
618 CFStringRef ref
= GetDeviceName(playbackDeviceID
[i
]);
621 playbackDeviceUID
.push_back(ref
);
622 // output sub-devices in this example, so append the sub-device's UID to the CFArray
623 CFArrayAppendValue(subDevicesArray
, ref
);
626 //-----------------------------------------------------------------------
627 // Feed the dictionary to the plugin, to create a blank aggregate device
628 //-----------------------------------------------------------------------
630 AudioObjectPropertyAddress pluginAOPA
;
631 pluginAOPA
.mSelector
= kAudioPlugInCreateAggregateDevice
;
632 pluginAOPA
.mScope
= kAudioObjectPropertyScopeGlobal
;
633 pluginAOPA
.mElement
= kAudioObjectPropertyElementMaster
;
636 osErr
= AudioObjectGetPropertyDataSize(fPluginID
, &pluginAOPA
, 0, NULL
, &outDataSize
);
637 if (osErr
!= noErr
) {
638 printf("TCoreAudioRenderer::CreateAggregateDevice : AudioObjectGetPropertyDataSize error\n");
643 osErr
= AudioObjectGetPropertyData(fPluginID
, &pluginAOPA
, sizeof(aggDeviceDict
), &aggDeviceDict
, &outDataSize
, outAggregateDevice
);
644 if (osErr
!= noErr
) {
645 printf("TCoreAudioRenderer::CreateAggregateDevice : AudioObjectGetPropertyData error\n");
650 // pause for a bit to make sure that everything completed correctly
651 // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created
652 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.1, false);
654 //-------------------------
655 // Set the sub-device list
656 //-------------------------
658 pluginAOPA
.mSelector
= kAudioAggregateDevicePropertyFullSubDeviceList
;
659 pluginAOPA
.mScope
= kAudioObjectPropertyScopeGlobal
;
660 pluginAOPA
.mElement
= kAudioObjectPropertyElementMaster
;
661 outDataSize
= sizeof(CFMutableArrayRef
);
662 osErr
= AudioObjectSetPropertyData(*outAggregateDevice
, &pluginAOPA
, 0, NULL
, outDataSize
, &subDevicesArray
);
663 if (osErr
!= noErr
) {
664 printf("TCoreAudioRenderer::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error\n");
669 // pause again to give the changes time to take effect
670 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.1, false);
672 //-----------------------
673 // Set the master device
674 //-----------------------
676 // set the master device manually (this is the device which will act as the master clock for the aggregate device)
677 // pass in the UID of the device you want to use
678 pluginAOPA
.mSelector
= kAudioAggregateDevicePropertyMasterSubDevice
;
679 pluginAOPA
.mScope
= kAudioObjectPropertyScopeGlobal
;
680 pluginAOPA
.mElement
= kAudioObjectPropertyElementMaster
;
681 outDataSize
= sizeof(CFStringRef
);
682 osErr
= AudioObjectSetPropertyData(*outAggregateDevice
, &pluginAOPA
, 0, NULL
, outDataSize
, &captureDeviceUID
[0]); // First apture is master...
683 if (osErr
!= noErr
) {
684 printf("TCoreAudioRenderer::CreateAggregateDevice : AudioObjectSetPropertyData for master device error\n");
689 // pause again to give the changes time to take effect
690 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.1, false);
692 // Prepare sub-devices for clock drift compensation
693 // Workaround for bug in the HAL : until 10.6.2
695 if (fClockDriftCompensate
) {
696 if (need_clock_drift_compensation
) {
697 printf("Clock drift compensation activated...\n");
699 // Get the property data size
700 osErr
= AudioObjectGetPropertyDataSize(*outAggregateDevice
, &theAddressOwned
, theQualifierDataSize
, theQualifierData
, &outSize
);
701 if (osErr
!= noErr
) {
702 printf("TCoreAudioRenderer::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error\n");
706 // Calculate the number of object IDs
707 subDevicesNum
= outSize
/ sizeof(AudioObjectID
);
708 printf("TCoreAudioRenderer::CreateAggregateDevice clock drift compensation, number of sub-devices = %d\n", subDevicesNum
);
709 AudioObjectID subDevices
[subDevicesNum
];
710 outSize
= sizeof(subDevices
);
712 osErr
= AudioObjectGetPropertyData(*outAggregateDevice
, &theAddressOwned
, theQualifierDataSize
, theQualifierData
, &outSize
, subDevices
);
713 if (osErr
!= noErr
) {
714 printf("TCoreAudioRenderer::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error\n");
718 // Set kAudioSubDevicePropertyDriftCompensation property...
719 for (UInt32 index
= 0; index
< subDevicesNum
; ++index
) {
720 UInt32 theDriftCompensationValue
= 1;
721 osErr
= AudioObjectSetPropertyData(subDevices
[index
], &theAddressDrift
, 0, NULL
, sizeof(UInt32
), &theDriftCompensationValue
);
722 if (osErr
!= noErr
) {
723 printf("TCoreAudioRenderer::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error\n");
728 printf("Clock drift compensation was asked but is not needed (devices use the same clock domain)\n");
732 // pause again to give the changes time to take effect
733 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.1, false);
739 // release the private AD key
740 CFRelease(AggregateDeviceNumberRef
);
742 // release the CF objects we have created - we don't need them any more
743 CFRelease(aggDeviceDict
);
744 CFRelease(subDevicesArray
);
746 if (subDevicesArrayClock
)
747 CFRelease(subDevicesArrayClock
);
749 // release the device UID
750 for (UInt32 i
= 0; i
< captureDeviceUID
.size(); i
++) {
751 CFRelease(captureDeviceUID
[i
]);
754 for (UInt32 i
= 0; i
< playbackDeviceUID
.size(); i
++) {
755 CFRelease(playbackDeviceUID
[i
]);
758 printf("New aggregate device %d\n", *outAggregateDevice
);
762 DestroyAggregateDevice();
766 OSStatus
TCoreAudioRenderer::DestroyAggregateDevice()
768 OSStatus osErr
= noErr
;
769 AudioObjectPropertyAddress pluginAOPA
;
770 pluginAOPA
.mSelector
= kAudioPlugInDestroyAggregateDevice
;
771 pluginAOPA
.mScope
= kAudioObjectPropertyScopeGlobal
;
772 pluginAOPA
.mElement
= kAudioObjectPropertyElementMaster
;
777 osErr
= AudioObjectGetPropertyDataSize(fPluginID
, &pluginAOPA
, 0, NULL
, &outDataSize
);
778 if (osErr
!= noErr
) {
779 printf("TCoreAudioRenderer::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error\n");
784 osErr
= AudioObjectGetPropertyData(fPluginID
, &pluginAOPA
, 0, NULL
, &outDataSize
, &fDeviceID
);
785 if (osErr
!= noErr
) {
786 printf("TCoreAudioRenderer::DestroyAggregateDevice : AudioObjectGetPropertyData error\n");
797 long TCoreAudioRenderer::OpenDefault(long inChan
, long outChan
, long bufferSize
, long samplerate
)
799 OSStatus err
= noErr
;
800 ComponentResult err1
;
804 AudioStreamBasicDescription srcFormat
, dstFormat
, sampleRate
;
805 long in_nChannels
, out_nChannels
;
807 printf("OpenDefault inChan = %ld outChan = %ld bufferSize = %ld samplerate = %ld\n", inChan
, outChan
, bufferSize
, samplerate
);
811 Gestalt(gestaltSystemVersionMajor
, &major
);
812 Gestalt(gestaltSystemVersionMinor
, &minor
);
814 // Starting with 10.6 systems, the HAL notification thread is created internally
815 if (major
== 10 && minor
>= 6) {
816 CFRunLoopRef theRunLoop
= NULL
;
817 AudioObjectPropertyAddress theAddress
= { kAudioHardwarePropertyRunLoop
, kAudioObjectPropertyScopeGlobal
, kAudioObjectPropertyElementMaster
};
818 OSStatus osErr
= AudioObjectSetPropertyData (kAudioObjectSystemObject
, &theAddress
, 0, NULL
, sizeof(CFRunLoopRef
), &theRunLoop
);
819 if (osErr
!= noErr
) {
820 printf("TCoreAudioRenderer::Open kAudioHardwarePropertyRunLoop error\n");
825 if (GetDefaultDevice(inChan
, outChan
, samplerate
,&fDeviceID
) != noErr
) {
826 printf("Cannot open default device\n");
830 // Setting buffer size
831 outSize
= sizeof(UInt32
);
832 err
= AudioDeviceSetProperty(fDeviceID
, NULL
, 0, false, kAudioDevicePropertyBufferFrameSize
, outSize
, &bufferSize
);
834 printf("Cannot set buffer size %ld\n", bufferSize
);
839 // Setting sample rate
840 outSize
= sizeof(AudioStreamBasicDescription
);
841 err
= AudioDeviceGetProperty(fDeviceID
, 0, false, kAudioDevicePropertyStreamFormat
, &outSize
, &sampleRate
);
843 printf("Cannot get current sample rate\n");
848 if (samplerate
!= long(sampleRate
.mSampleRate
)) {
849 sampleRate
.mSampleRate
= (Float64
)(samplerate
);
850 err
= AudioDeviceSetProperty(fDeviceID
, NULL
, 0, false, kAudioDevicePropertyStreamFormat
, outSize
, &sampleRate
);
852 printf("Cannot set sample rate = %ld\n", samplerate
);
859 ComponentDescription cd
= {kAudioUnitType_Output
, kAudioUnitSubType_HALOutput
, kAudioUnitManufacturer_Apple
, 0, 0};
860 Component HALOutput
= FindNextComponent(NULL
, &cd
);
862 err1
= OpenAComponent(HALOutput
, &fAUHAL
);
864 printf("Error calling OpenAComponent\n");
869 err1
= AudioUnitInitialize(fAUHAL
);
871 printf("Cannot initialize AUHAL unit\n");
877 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Output
, 0, &enableIO
, sizeof(enableIO
));
879 printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output\n");
885 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Input
, 1, &enableIO
, sizeof(enableIO
));
887 printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input\n");
892 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_CurrentDevice
, kAudioUnitScope_Global
, 0, &fDeviceID
, sizeof(AudioDeviceID
));
894 printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice\n");
899 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Global
, 1, (UInt32
*)&bufferSize
, sizeof(UInt32
));
901 printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n");
906 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Global
, 0, (UInt32
*)&bufferSize
, sizeof(UInt32
));
908 printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n");
913 err1
= AudioUnitGetPropertyInfo(fAUHAL
, kAudioOutputUnitProperty_ChannelMap
, kAudioUnitScope_Input
, 1, &outSize
, &isWritable
);
915 printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap-INFO 1\n");
919 in_nChannels
= (err1
== noErr
) ? outSize
/ sizeof(SInt32
) : 0;
920 printf("in_nChannels = %ld\n", in_nChannels
);
922 err1
= AudioUnitGetPropertyInfo(fAUHAL
, kAudioOutputUnitProperty_ChannelMap
, kAudioUnitScope_Output
, 0, &outSize
, &isWritable
);
924 printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap-INFO 0\n");
928 out_nChannels
= (err1
== noErr
) ? outSize
/ sizeof(SInt32
) : 0;
929 printf("out_nChannels = %ld\n", out_nChannels
);
932 Just ignore this case : seems to work without any further change...
934 if (outChan > out_nChannels) {
935 printf("This device hasn't required output channels\n");
938 if (inChan > in_nChannels) {
939 printf("This device hasn't required input channels\n");
944 if (outChan
< out_nChannels
) {
945 SInt32 chanArr
[out_nChannels
];
946 for (int i
= 0; i
< out_nChannels
; i
++) {
949 for (int i
= 0; i
< outChan
; i
++) {
952 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_ChannelMap
, kAudioUnitScope_Output
, 0, chanArr
, sizeof(SInt32
) * out_nChannels
);
954 printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 0\n");
959 if (inChan
< in_nChannels
) {
960 SInt32 chanArr
[in_nChannels
];
961 for (int i
= 0; i
< in_nChannels
; i
++) {
964 for (int i
= 0; i
< inChan
; i
++) {
967 AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_ChannelMap
, kAudioUnitScope_Input
, 1, chanArr
, sizeof(SInt32
) * in_nChannels
);
969 printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 1\n");
975 outSize
= sizeof(AudioStreamBasicDescription
);
976 err1
= AudioUnitGetProperty(fAUHAL
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Output
, 1, &srcFormat
, &outSize
);
978 printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n");
981 PrintStreamDesc(&srcFormat
);
983 srcFormat
.mSampleRate
= samplerate
;
984 srcFormat
.mFormatID
= kAudioFormatLinearPCM
;
985 srcFormat
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
| kLinearPCMFormatFlagIsNonInterleaved
;
986 srcFormat
.mBytesPerPacket
= sizeof(float);
987 srcFormat
.mFramesPerPacket
= 1;
988 srcFormat
.mBytesPerFrame
= sizeof(float);
989 srcFormat
.mChannelsPerFrame
= inChan
;
990 srcFormat
.mBitsPerChannel
= 32;
992 PrintStreamDesc(&srcFormat
);
994 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Output
, 1, &srcFormat
, sizeof(AudioStreamBasicDescription
));
996 printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n");
1002 outSize
= sizeof(AudioStreamBasicDescription
);
1003 err1
= AudioUnitGetProperty(fAUHAL
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &dstFormat
, &outSize
);
1004 if (err1
!= noErr
) {
1005 printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n");
1008 PrintStreamDesc(&dstFormat
);
1010 dstFormat
.mSampleRate
= samplerate
;
1011 dstFormat
.mFormatID
= kAudioFormatLinearPCM
;
1012 dstFormat
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
| kLinearPCMFormatFlagIsNonInterleaved
;
1013 dstFormat
.mBytesPerPacket
= sizeof(float);
1014 dstFormat
.mFramesPerPacket
= 1;
1015 dstFormat
.mBytesPerFrame
= sizeof(float);
1016 dstFormat
.mChannelsPerFrame
= outChan
;
1017 dstFormat
.mBitsPerChannel
= 32;
1019 PrintStreamDesc(&dstFormat
);
1021 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &dstFormat
, sizeof(AudioStreamBasicDescription
));
1022 if (err1
!= noErr
) {
1023 printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n");
1028 if (inChan
> 0 && outChan
== 0) {
1029 AURenderCallbackStruct output
;
1030 output
.inputProc
= Render
;
1031 output
.inputProcRefCon
= this;
1032 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_SetInputCallback
, kAudioUnitScope_Global
, 0, &output
, sizeof(output
));
1033 if (err1
!= noErr
) {
1034 printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1\n");
1039 AURenderCallbackStruct output
;
1040 output
.inputProc
= Render
;
1041 output
.inputProcRefCon
= this;
1042 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
, 0, &output
, sizeof(output
));
1043 if (err1
!= noErr
) {
1044 printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0\n");
1050 fInputData
= (AudioBufferList
*)malloc(sizeof(UInt32
) + inChan
* sizeof(AudioBuffer
));
1051 if (fInputData
== 0) {
1052 printf("Cannot allocate memory for input buffers\n");
1055 fInputData
->mNumberBuffers
= inChan
;
1058 for (int i
= 0; i
< inChan
; i
++) {
1059 fInputData
->mBuffers
[i
].mNumberChannels
= 1;
1060 fInputData
->mBuffers
[i
].mData
= malloc(bufferSize
* sizeof(float));
1061 fInputData
->mBuffers
[i
].mDataByteSize
= bufferSize
* sizeof(float);
1067 AudioUnitUninitialize(fAUHAL
);
1068 CloseComponent(fAUHAL
);
1072 long TCoreAudioRenderer::Close()
1074 for (int i
= 0; i
< gDevNumInChans
; i
++) {
1075 free(fInputData
->mBuffers
[i
].mData
);
1078 AudioUnitUninitialize(fAUHAL
);
1079 CloseComponent(fAUHAL
);
1080 DestroyAggregateDevice();
1084 long TCoreAudioRenderer::Start()
1086 OSStatus err
= AudioOutputUnitStart(fAUHAL
);
1089 printf("Error while opening device : device open error \n");
1096 long TCoreAudioRenderer::Stop()
1098 OSStatus err
= AudioOutputUnitStop(fAUHAL
);
1101 printf("Error while closing device : device close error \n");
1110 /******************************************************************************
1111 *******************************************************************************
1113 CORE AUDIO INTERFACE
1115 *******************************************************************************
1116 *******************************************************************************/
1117 class coreaudio
: public audio
{
1119 TCoreAudioRenderer audio_device
;
1120 long fSampleRate
, fFramesPerBuf
;
1123 coreaudio(long srate
, long fpb
) : fSampleRate(srate
), fFramesPerBuf(fpb
) {}
1124 virtual ~coreaudio() {}
1126 virtual bool init(const char* /*name*/, dsp
* DSP
) {
1128 DSP
->init (fSampleRate
);
1129 gDevNumInChans
= DSP
->getNumInputs();
1130 gDevNumOutChans
= DSP
->getNumOutputs();
1131 if (audio_device
.OpenDefault(gDevNumInChans
, gDevNumOutChans
, fFramesPerBuf
, fSampleRate
) < 0) {
1132 printf("Cannot open CoreAudio device\n");
1138 virtual bool start() {
1139 if (audio_device
.Start() < 0) {
1140 printf("Cannot start CoreAudio device\n");
1146 virtual void stop() {
1147 audio_device
.Stop();
1148 audio_device
.Close();
1155 /********************END ARCHITECTURE SECTION (part 2/2)****************/