2 Copyright (C) 2008 Grame
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "JackCoreAudioAdapter.h"
21 #include "JackError.h"
24 #include <CoreServices/CoreServices.h>
29 static void PrintStreamDesc(AudioStreamBasicDescription
*inDesc
)
31 jack_log("- - - - - - - - - - - - - - - - - - - -");
32 jack_log(" Sample Rate:%f", inDesc
->mSampleRate
);
33 jack_log(" Format ID:%.*s", (int) sizeof(inDesc
->mFormatID
), (char*)&inDesc
->mFormatID
);
34 jack_log(" Format Flags:%lX", inDesc
->mFormatFlags
);
35 jack_log(" Bytes per Packet:%ld", inDesc
->mBytesPerPacket
);
36 jack_log(" Frames per Packet:%ld", inDesc
->mFramesPerPacket
);
37 jack_log(" Bytes per Frame:%ld", inDesc
->mBytesPerFrame
);
38 jack_log(" Channels per Frame:%ld", inDesc
->mChannelsPerFrame
);
39 jack_log(" Bits per Channel:%ld", inDesc
->mBitsPerChannel
);
40 jack_log("- - - - - - - - - - - - - - - - - - - -");
43 static OSStatus
DisplayDeviceNames()
51 err
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
, &size
, &isWritable
);
56 deviceNum
= size
/ sizeof(AudioDeviceID
);
57 AudioDeviceID devices
[deviceNum
];
59 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDevices
, &size
, devices
);
64 for (i
= 0; i
< deviceNum
; i
++) {
65 char device_name
[256];
66 char internal_name
[256];
68 size
= sizeof(CFStringRef
);
70 err
= AudioDeviceGetProperty(devices
[i
], 0, false, kAudioDevicePropertyDeviceUID
, &size
, &UIname
);
72 CFStringGetCString(UIname
, internal_name
, 256, CFStringGetSystemEncoding());
78 err
= AudioDeviceGetProperty(devices
[i
], 0, false, kAudioDevicePropertyDeviceName
, &size
, device_name
);
83 jack_info("Device name = \'%s\', internal_name = \'%s\' (to be used as -C, -P, or -d parameter)", device_name
, internal_name
);
95 static void printError(OSStatus err
)
98 case kAudioHardwareNoError
:
99 jack_log("error code : kAudioHardwareNoError");
101 case kAudioConverterErr_FormatNotSupported
:
102 jack_log("error code : kAudioConverterErr_FormatNotSupported");
104 case kAudioConverterErr_OperationNotSupported
:
105 jack_log("error code : kAudioConverterErr_OperationNotSupported");
107 case kAudioConverterErr_PropertyNotSupported
:
108 jack_log("error code : kAudioConverterErr_PropertyNotSupported");
110 case kAudioConverterErr_InvalidInputSize
:
111 jack_log("error code : kAudioConverterErr_InvalidInputSize");
113 case kAudioConverterErr_InvalidOutputSize
:
114 jack_log("error code : kAudioConverterErr_InvalidOutputSize");
116 case kAudioConverterErr_UnspecifiedError
:
117 jack_log("error code : kAudioConverterErr_UnspecifiedError");
119 case kAudioConverterErr_BadPropertySizeError
:
120 jack_log("error code : kAudioConverterErr_BadPropertySizeError");
122 case kAudioConverterErr_RequiresPacketDescriptionsError
:
123 jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError");
125 case kAudioConverterErr_InputSampleRateOutOfRange
:
126 jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange");
128 case kAudioConverterErr_OutputSampleRateOutOfRange
:
129 jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange");
131 case kAudioHardwareNotRunningError
:
132 jack_log("error code : kAudioHardwareNotRunningError");
134 case kAudioHardwareUnknownPropertyError
:
135 jack_log("error code : kAudioHardwareUnknownPropertyError");
137 case kAudioHardwareIllegalOperationError
:
138 jack_log("error code : kAudioHardwareIllegalOperationError");
140 case kAudioHardwareBadDeviceError
:
141 jack_log("error code : kAudioHardwareBadDeviceError");
143 case kAudioHardwareBadStreamError
:
144 jack_log("error code : kAudioHardwareBadStreamError");
146 case kAudioDeviceUnsupportedFormatError
:
147 jack_log("error code : kAudioDeviceUnsupportedFormatError");
149 case kAudioDevicePermissionsError
:
150 jack_log("error code : kAudioDevicePermissionsError");
152 case kAudioHardwareBadObjectError
:
153 jack_log("error code : kAudioHardwareBadObjectError");
155 case kAudioHardwareUnsupportedOperationError
:
156 jack_log("error code : kAudioHardwareUnsupportedOperationError");
159 jack_log("error code : unknown");
164 OSStatus
JackCoreAudioAdapter::AudioHardwareNotificationCallback(AudioHardwarePropertyID inPropertyID
, void* inClientData
)
166 JackCoreAudioAdapter
* driver
= (JackCoreAudioAdapter
*)inClientData
;
168 switch (inPropertyID
) {
170 case kAudioHardwarePropertyDevices
: {
171 jack_log("JackCoreAudioAdapter::AudioHardwareNotificationCallback kAudioHardwarePropertyDevices");
172 DisplayDeviceNames();
180 OSStatus
JackCoreAudioAdapter::SRNotificationCallback(AudioDeviceID inDevice
,
183 AudioDevicePropertyID inPropertyID
,
186 JackCoreAudioAdapter
* driver
= static_cast<JackCoreAudioAdapter
*>(inClientData
);
188 switch (inPropertyID
) {
190 case kAudioDevicePropertyNominalSampleRate
: {
191 jack_log("JackCoreAudioAdapter::SRNotificationCallback kAudioDevicePropertyNominalSampleRate");
192 driver
->fState
= true;
200 // A better implementation would try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code)
201 OSStatus
JackCoreAudioAdapter::DeviceNotificationCallback(AudioDeviceID inDevice
,
204 AudioDevicePropertyID inPropertyID
,
208 switch (inPropertyID
) {
210 case kAudioDeviceProcessorOverload
: {
211 jack_error("JackCoreAudioAdapter::DeviceNotificationCallback kAudioDeviceProcessorOverload");
215 case kAudioDevicePropertyStreamConfiguration
: {
216 jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration");
217 return kAudioHardwareUnsupportedOperationError
;
220 case kAudioDevicePropertyNominalSampleRate
: {
221 jack_error("Cannot handle kAudioDevicePropertyNominalSampleRate");
222 return kAudioHardwareUnsupportedOperationError
;
229 int JackCoreAudioAdapter::AddListeners()
231 OSStatus err
= noErr
;
234 err
= AudioDeviceAddPropertyListener(fDeviceID
, 0, true, kAudioDeviceProcessorOverload
, DeviceNotificationCallback
, this);
236 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload");
241 err
= AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices
, AudioHardwareNotificationCallback
, this);
243 jack_error("Error calling AudioHardwareAddPropertyListener with kAudioHardwarePropertyDevices");
248 err
= AudioDeviceAddPropertyListener(fDeviceID
, 0, true, kAudioDevicePropertyNominalSampleRate
, DeviceNotificationCallback
, this);
250 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate");
255 err
= AudioDeviceAddPropertyListener(fDeviceID
, 0, true, kAudioDevicePropertyDeviceIsRunning
, DeviceNotificationCallback
, this);
257 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning");
262 err
= AudioDeviceAddPropertyListener(fDeviceID
, 0, true, kAudioDevicePropertyStreamConfiguration
, DeviceNotificationCallback
, this);
264 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration");
269 err
= AudioDeviceAddPropertyListener(fDeviceID
, 0, false, kAudioDevicePropertyStreamConfiguration
, DeviceNotificationCallback
, this);
271 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration");
279 void JackCoreAudioAdapter::RemoveListeners()
281 AudioDeviceRemovePropertyListener(fDeviceID
, 0, true, kAudioDeviceProcessorOverload
, DeviceNotificationCallback
);
282 AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices
, AudioHardwareNotificationCallback
);
283 AudioDeviceRemovePropertyListener(fDeviceID
, 0, true, kAudioDevicePropertyNominalSampleRate
, DeviceNotificationCallback
);
284 AudioDeviceRemovePropertyListener(fDeviceID
, 0, true, kAudioDevicePropertyDeviceIsRunning
, DeviceNotificationCallback
);
285 AudioDeviceRemovePropertyListener(fDeviceID
, 0, true, kAudioDevicePropertyStreamConfiguration
, DeviceNotificationCallback
);
286 AudioDeviceRemovePropertyListener(fDeviceID
, 0, false, kAudioDevicePropertyStreamConfiguration
, DeviceNotificationCallback
);
289 OSStatus
JackCoreAudioAdapter::Render(void *inRefCon
,
290 AudioUnitRenderActionFlags
*ioActionFlags
,
291 const AudioTimeStamp
*inTimeStamp
,
293 UInt32 inNumberFrames
,
294 AudioBufferList
*ioData
)
296 JackCoreAudioAdapter
* adapter
= static_cast<JackCoreAudioAdapter
*>(inRefCon
);
297 OSStatus err
= AudioUnitRender(adapter
->fAUHAL
, ioActionFlags
, inTimeStamp
, 1, inNumberFrames
, adapter
->fInputData
);
300 jack_default_audio_sample_t
* inputBuffer
[adapter
->fCaptureChannels
];
301 jack_default_audio_sample_t
* outputBuffer
[adapter
->fPlaybackChannels
];
303 for (int i
= 0; i
< adapter
->fCaptureChannels
; i
++) {
304 inputBuffer
[i
] = (jack_default_audio_sample_t
*)adapter
->fInputData
->mBuffers
[i
].mData
;
306 for (int i
= 0; i
< adapter
->fPlaybackChannels
; i
++) {
307 outputBuffer
[i
] = (jack_default_audio_sample_t
*)ioData
->mBuffers
[i
].mData
;
310 adapter
->PushAndPull((jack_default_audio_sample_t
**)inputBuffer
, (jack_default_audio_sample_t
**)outputBuffer
, inNumberFrames
);
317 JackCoreAudioAdapter::JackCoreAudioAdapter(jack_nframes_t buffer_size
, jack_nframes_t sample_rate
, const JSList
* params
)
318 :JackAudioAdapterInterface(buffer_size
, sample_rate
), fInputData(0), fCapturing(false), fPlaying(false), fState(false)
321 const jack_driver_param_t
* param
;
322 int in_nChannels
= 0;
323 int out_nChannels
= 0;
324 char captureName
[256];
325 char playbackName
[256];
328 fClockDriftCompensate
= false;
331 fCaptureChannels
= -1;
332 fPlaybackChannels
= -1;
336 Gestalt(gestaltSystemVersionMajor
, &major
);
337 Gestalt(gestaltSystemVersionMinor
, &minor
);
339 // Starting with 10.6 systems, the HAL notification thread is created internally
340 if (major
== 10 && minor
>= 6) {
341 CFRunLoopRef theRunLoop
= NULL
;
342 AudioObjectPropertyAddress theAddress
= { kAudioHardwarePropertyRunLoop
, kAudioObjectPropertyScopeGlobal
, kAudioObjectPropertyElementMaster
};
343 OSStatus theError
= AudioObjectSetPropertyData (kAudioObjectSystemObject
, &theAddress
, 0, NULL
, sizeof(CFRunLoopRef
), &theRunLoop
);
344 if (theError
!= noErr
) {
345 jack_error("JackCoreAudioAdapter::Open kAudioHardwarePropertyRunLoop error");
349 for (node
= params
; node
; node
= jack_slist_next(node
)) {
350 param
= (const jack_driver_param_t
*) node
->data
;
352 switch (param
->character
) {
355 fCaptureChannels
= fPlaybackChannels
= param
->value
.ui
;
359 fCaptureChannels
= param
->value
.ui
;
363 fPlaybackChannels
= param
->value
.ui
;
368 strncpy(fCaptureUID
, param
->value
.str
, 256);
373 strncpy(fPlaybackUID
, param
->value
.str
, 256);
377 strncpy(fCaptureUID
, param
->value
.str
, 256);
378 strncpy(fPlaybackUID
, param
->value
.str
, 256);
382 fCapturing
= fPlaying
= true;
386 SetAdaptedSampleRate(param
->value
.ui
);
390 SetAdaptedBufferSize(param
->value
.ui
);
394 DisplayDeviceNames();
398 fQuality
= param
->value
.ui
;
402 fRingbufferCurSize
= param
->value
.ui
;
407 fClockDriftCompensate
= true;
412 /* duplex is the default */
413 if (!fCapturing
&& !fPlaying
) {
418 if (SetupDevices(fCaptureUID
, fPlaybackUID
, captureName
, playbackName
, fAdaptedSampleRate
) < 0) {
419 throw std::bad_alloc();
422 if (SetupChannels(fCapturing
, fPlaying
, fCaptureChannels
, fPlaybackChannels
, in_nChannels
, out_nChannels
, true) < 0) {
423 throw std::bad_alloc();
426 if (SetupBufferSize(fAdaptedBufferSize
) < 0) {
427 throw std::bad_alloc();
430 if (SetupSampleRate(fAdaptedSampleRate
) < 0) {
431 throw std::bad_alloc();
434 if (OpenAUHAL(fCapturing
, fPlaying
, fCaptureChannels
, fPlaybackChannels
, in_nChannels
, out_nChannels
, fAdaptedBufferSize
, fAdaptedSampleRate
) < 0) {
435 throw std::bad_alloc();
438 if (fCapturing
&& fCaptureChannels
> 0) {
439 if (SetupBuffers(fCaptureChannels
) < 0) {
440 throw std::bad_alloc();
444 if (AddListeners() < 0) {
445 throw std::bad_alloc();
448 GetStreamLatencies(fDeviceID
, true, fInputLatencies
);
449 GetStreamLatencies(fDeviceID
, false, fOutputLatencies
);
452 OSStatus
JackCoreAudioAdapter::GetDefaultDevice(AudioDeviceID
* id
)
455 UInt32 theSize
= sizeof(UInt32
);
456 AudioDeviceID inDefault
;
457 AudioDeviceID outDefault
;
459 if ((res
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
, &theSize
, &inDefault
)) != noErr
) {
463 if ((res
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
, &theSize
, &outDefault
)) != noErr
) {
467 jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault
, outDefault
);
469 // Get the device only if default input and output are the same
470 if (inDefault
!= outDefault
) {
471 jack_error("Default input and output devices are not the same !!");
472 return kAudioHardwareBadDeviceError
;
473 } else if (inDefault
== 0) {
474 jack_error("Default input and output devices are null !!");
475 return kAudioHardwareBadDeviceError
;
482 OSStatus
JackCoreAudioAdapter::GetTotalChannels(AudioDeviceID device
, int& channelCount
, bool isInput
)
484 OSStatus err
= noErr
;
489 err
= AudioDeviceGetPropertyInfo(device
, 0, isInput
, kAudioDevicePropertyStreamConfiguration
, &outSize
, &outWritable
);
491 AudioBufferList bufferList
[outSize
];
492 err
= AudioDeviceGetProperty(device
, 0, isInput
, kAudioDevicePropertyStreamConfiguration
, &outSize
, bufferList
);
494 for (unsigned int i
= 0; i
< bufferList
->mNumberBuffers
; i
++) {
495 channelCount
+= bufferList
->mBuffers
[i
].mNumberChannels
;
503 OSStatus
JackCoreAudioAdapter::GetDeviceIDFromUID(const char* UID
, AudioDeviceID
* id
)
505 UInt32 size
= sizeof(AudioValueTranslation
);
506 CFStringRef inIUD
= CFStringCreateWithCString(NULL
, UID
, CFStringGetSystemEncoding());
507 AudioValueTranslation value
= { &inIUD
, sizeof(CFStringRef
), id
, sizeof(AudioDeviceID
) };
510 return kAudioHardwareUnspecifiedError
;
512 OSStatus res
= AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID
, &size
, &value
);
514 jack_log("GetDeviceIDFromUID %s %ld", UID
, *id
);
515 return (*id
== kAudioDeviceUnknown
) ? kAudioHardwareBadDeviceError
: res
;
519 OSStatus
JackCoreAudioAdapter::GetDefaultInputDevice(AudioDeviceID
* id
)
522 UInt32 theSize
= sizeof(UInt32
);
523 AudioDeviceID inDefault
;
525 if ((res
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
, &theSize
, &inDefault
)) != noErr
) {
529 if (inDefault
== 0) {
530 jack_error("Error: default input device is 0, please select a correct one !!");
533 jack_log("GetDefaultInputDevice: input = %ld ", inDefault
);
538 OSStatus
JackCoreAudioAdapter::GetDefaultOutputDevice(AudioDeviceID
* id
)
541 UInt32 theSize
= sizeof(UInt32
);
542 AudioDeviceID outDefault
;
544 if ((res
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
, &theSize
, &outDefault
)) != noErr
) {
548 if (outDefault
== 0) {
549 jack_error("Error: default output device is 0, please select a correct one !!");
552 jack_log("GetDefaultOutputDevice: output = %ld", outDefault
);
557 OSStatus
JackCoreAudioAdapter::GetDeviceNameFromID(AudioDeviceID id
, char* name
)
560 return AudioDeviceGetProperty(id
, 0, false, kAudioDevicePropertyDeviceName
, &size
, name
);
563 AudioDeviceID
JackCoreAudioAdapter::GetDeviceIDFromName(const char* name
)
569 OSStatus err
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
, &size
, &isWritable
);
574 deviceNum
= size
/ sizeof(AudioDeviceID
);
575 AudioDeviceID devices
[deviceNum
];
577 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDevices
, &size
, devices
);
582 for (i
= 0; i
< deviceNum
; i
++) {
583 char device_name
[256];
585 err
= AudioDeviceGetProperty(devices
[i
], 0, false, kAudioDevicePropertyDeviceName
, &size
, device_name
);
588 } else if (strcmp(device_name
, name
) == 0) {
597 int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid
,
598 const char* playback_driver_uid
,
599 char* capture_driver_name
,
600 char* playback_driver_name
,
601 jack_nframes_t samplerate
)
603 capture_driver_name
[0] = 0;
604 playback_driver_name
[0] = 0;
607 if (strcmp(capture_driver_uid
, "") != 0 && strcmp(playback_driver_uid
, "") != 0) {
608 jack_log("JackCoreAudioDriver::Open duplex");
610 // Same device for capture and playback...
611 if (strcmp(capture_driver_uid
, playback_driver_uid
) == 0) {
613 if (GetDeviceIDFromUID(playback_driver_uid
, &fDeviceID
) != noErr
) {
614 jack_log("Will take default in/out");
615 if (GetDefaultDevice(&fDeviceID
) != noErr
) {
616 jack_error("Cannot open default device");
620 if (GetDeviceNameFromID(fDeviceID
, capture_driver_name
) != noErr
|| GetDeviceNameFromID(fDeviceID
, playback_driver_name
) != noErr
) {
621 jack_error("Cannot get device name from device ID");
627 // Creates aggregate device
628 AudioDeviceID captureID
, playbackID
;
630 if (GetDeviceIDFromUID(capture_driver_uid
, &captureID
) != noErr
) {
631 jack_log("Will take default input");
632 if (GetDefaultInputDevice(&captureID
) != noErr
) {
633 jack_error("Cannot open default input device");
638 if (GetDeviceIDFromUID(playback_driver_uid
, &playbackID
) != noErr
) {
639 jack_log("Will take default output");
640 if (GetDefaultOutputDevice(&playbackID
) != noErr
) {
641 jack_error("Cannot open default output device");
646 if (CreateAggregateDevice(captureID
, playbackID
, samplerate
, &fDeviceID
) != noErr
) {
652 } else if (strcmp(capture_driver_uid
, "") != 0) {
653 jack_log("JackCoreAudioAdapter::Open capture only");
654 if (GetDeviceIDFromUID(capture_driver_uid
, &fDeviceID
) != noErr
) {
655 if (GetDefaultInputDevice(&fDeviceID
) != noErr
) {
656 jack_error("Cannot open default input device");
660 if (GetDeviceNameFromID(fDeviceID
, capture_driver_name
) != noErr
) {
661 jack_error("Cannot get device name from device ID");
666 } else if (strcmp(playback_driver_uid
, "") != 0) {
667 jack_log("JackCoreAudioAdapter::Open playback only");
668 if (GetDeviceIDFromUID(playback_driver_uid
, &fDeviceID
) != noErr
) {
669 if (GetDefaultOutputDevice(&fDeviceID
) != noErr
) {
670 jack_error("Cannot open default output device");
674 if (GetDeviceNameFromID(fDeviceID
, playback_driver_name
) != noErr
) {
675 jack_error("Cannot get device name from device ID");
679 // Use default driver in duplex mode
681 jack_log("JackCoreAudioAdapter::Open default driver");
682 if (GetDefaultDevice(&fDeviceID
) != noErr
) {
683 jack_error("Cannot open default device in duplex mode, so aggregate default input and default output");
685 // Creates aggregate device
686 AudioDeviceID captureID
= -1, playbackID
= -1;
688 if (GetDeviceIDFromUID(capture_driver_uid
, &captureID
) != noErr
) {
689 jack_log("Will take default input");
690 if (GetDefaultInputDevice(&captureID
) != noErr
) {
691 jack_error("Cannot open default input device");
696 if (GetDeviceIDFromUID(playback_driver_uid
, &playbackID
) != noErr
) {
697 jack_log("Will take default output");
698 if (GetDefaultOutputDevice(&playbackID
) != noErr
) {
699 jack_error("Cannot open default output device");
704 if (captureID
> 0 && playbackID
> 0) {
705 if (CreateAggregateDevice(captureID
, playbackID
, samplerate
, &fDeviceID
) != noErr
) {
709 jack_error("Cannot use default input/output");
719 // Aggregate built-in input and output
720 AudioDeviceID captureID
= GetDeviceIDFromName("Built-in Input");
721 AudioDeviceID playbackID
= GetDeviceIDFromName("Built-in Output");
723 if (captureID
> 0 && playbackID
> 0) {
724 if (CreateAggregateDevice(captureID
, playbackID
, samplerate
, &fDeviceID
) != noErr
) {
728 jack_error("Cannot aggregate built-in input and output");
735 int JackCoreAudioAdapter::SetupChannels(bool capturing
,
743 OSStatus err
= noErr
;
746 err
= GetTotalChannels(fDeviceID
, in_nChannels
, true);
748 jack_error("Cannot get input channel number");
752 jack_log("Max input channels : %d", in_nChannels
);
757 err
= GetTotalChannels(fDeviceID
, out_nChannels
, false);
759 jack_error("Cannot get output channel number");
763 jack_log("Max output channels : %d", out_nChannels
);
767 if (inchannels
> in_nChannels
) {
768 jack_error("This device hasn't required input channels inchannels = %ld in_nChannels = %ld", inchannels
, in_nChannels
);
774 if (outchannels
> out_nChannels
) {
775 jack_error("This device hasn't required output channels outchannels = %ld out_nChannels = %ld", outchannels
, out_nChannels
);
781 if (inchannels
== -1) {
782 jack_log("Setup max in channels = %ld", in_nChannels
);
783 inchannels
= in_nChannels
;
786 if (outchannels
== -1) {
787 jack_log("Setup max out channels = %ld", out_nChannels
);
788 outchannels
= out_nChannels
;
794 int JackCoreAudioAdapter::SetupBufferSize(jack_nframes_t buffer_size
)
796 // Setting buffer size
797 UInt32 outSize
= sizeof(UInt32
);
798 OSStatus err
= AudioDeviceSetProperty(fDeviceID
, NULL
, 0, false, kAudioDevicePropertyBufferFrameSize
, outSize
, &buffer_size
);
800 jack_error("Cannot set buffer size %ld", buffer_size
);
808 int JackCoreAudioAdapter::SetupSampleRate(jack_nframes_t samplerate
)
810 return SetupSampleRateAux(fDeviceID
, samplerate
);
813 int JackCoreAudioAdapter::SetupSampleRateAux(AudioDeviceID inDevice
, jack_nframes_t samplerate
)
815 OSStatus err
= noErr
;
820 outSize
= sizeof(Float64
);
821 err
= AudioDeviceGetProperty(inDevice
, 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyNominalSampleRate
, &outSize
, &sampleRate
);
823 jack_error("Cannot get current sample rate");
827 jack_log("Current sample rate = %f", sampleRate
);
830 // If needed, set new sample rate
831 if (samplerate
!= (jack_nframes_t
)sampleRate
) {
832 sampleRate
= (Float64
)samplerate
;
834 // To get SR change notification
835 err
= AudioDeviceAddPropertyListener(inDevice
, 0, true, kAudioDevicePropertyNominalSampleRate
, SRNotificationCallback
, this);
837 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate");
841 err
= AudioDeviceSetProperty(inDevice
, NULL
, 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyNominalSampleRate
, outSize
, &sampleRate
);
843 jack_error("Cannot set sample rate = %ld", samplerate
);
848 // Waiting for SR change notification
850 while (!fState
&& count
++ < WAIT_COUNTER
) {
852 jack_log("Wait count = %d", count
);
855 // Remove SR change notification
856 AudioDeviceRemovePropertyListener(inDevice
, 0, true, kAudioDevicePropertyNominalSampleRate
, SRNotificationCallback
);
862 int JackCoreAudioAdapter::SetupBuffers(int inchannels
)
864 jack_log("JackCoreAudioAdapter::SetupBuffers: input = %ld", inchannels
);
867 fInputData
= (AudioBufferList
*)malloc(sizeof(UInt32
) + inchannels
* sizeof(AudioBuffer
));
868 fInputData
->mNumberBuffers
= inchannels
;
869 for (int i
= 0; i
< fCaptureChannels
; i
++) {
870 fInputData
->mBuffers
[i
].mNumberChannels
= 1;
871 fInputData
->mBuffers
[i
].mDataByteSize
= fAdaptedBufferSize
* sizeof(jack_default_audio_sample_t
);
872 fInputData
->mBuffers
[i
].mData
= malloc(fAdaptedBufferSize
* sizeof(jack_default_audio_sample_t
));
877 void JackCoreAudioAdapter::DisposeBuffers()
880 for (int i
= 0; i
< fCaptureChannels
; i
++) {
881 free(fInputData
->mBuffers
[i
].mData
);
888 int JackCoreAudioAdapter::OpenAUHAL(bool capturing
,
894 jack_nframes_t buffer_size
,
895 jack_nframes_t samplerate
)
897 ComponentResult err1
;
899 AudioStreamBasicDescription srcFormat
, dstFormat
;
900 AudioDeviceID currAudioDeviceID
;
903 jack_log("OpenAUHAL capturing = %d playing = %d inchannels = %d outchannels = %d in_nChannels = %d out_nChannels = %d", capturing
, playing
, inchannels
, outchannels
, in_nChannels
, out_nChannels
);
905 if (inchannels
== 0 && outchannels
== 0) {
906 jack_error("No input and output channels...");
911 ComponentDescription cd
= {kAudioUnitType_Output
, kAudioUnitSubType_HALOutput
, kAudioUnitManufacturer_Apple
, 0, 0};
912 Component HALOutput
= FindNextComponent(NULL
, &cd
);
914 err1
= OpenAComponent(HALOutput
, &fAUHAL
);
916 jack_error("Error calling OpenAComponent");
921 err1
= AudioUnitInitialize(fAUHAL
);
923 jack_error("Cannot initialize AUHAL unit");
929 if (capturing
&& inchannels
> 0) {
931 jack_log("Setup AUHAL input on");
934 jack_log("Setup AUHAL input off");
937 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Input
, 1, &enableIO
, sizeof(enableIO
));
939 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input");
944 if (playing
&& outchannels
> 0) {
946 jack_log("Setup AUHAL output on");
949 jack_log("Setup AUHAL output off");
952 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Output
, 0, &enableIO
, sizeof(enableIO
));
954 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output");
959 size
= sizeof(AudioDeviceID
);
960 err1
= AudioUnitGetProperty(fAUHAL
, kAudioOutputUnitProperty_CurrentDevice
, kAudioUnitScope_Global
, 0, &currAudioDeviceID
, &size
);
962 jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice");
966 jack_log("AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID
);
969 // Setup up choosen device, in both input and output cases
970 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_CurrentDevice
, kAudioUnitScope_Global
, 0, &fDeviceID
, sizeof(AudioDeviceID
));
972 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice");
978 if (capturing
&& inchannels
> 0) {
979 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Global
, 1, (UInt32
*)&buffer_size
, sizeof(UInt32
));
981 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice");
987 if (playing
&& outchannels
> 0) {
988 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Global
, 0, (UInt32
*)&buffer_size
, sizeof(UInt32
));
990 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice");
997 if (capturing
&& inchannels
> 0 && inchannels
<= in_nChannels
) {
998 SInt32 chanArr
[in_nChannels
];
999 for (int i
= 0; i
< in_nChannels
; i
++) {
1002 for (int i
= 0; i
< inchannels
; i
++) {
1005 AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_ChannelMap
, kAudioUnitScope_Input
, 1, chanArr
, sizeof(SInt32
) * in_nChannels
);
1006 if (err1
!= noErr
) {
1007 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 1");
1013 if (playing
&& outchannels
> 0 && outchannels
<= out_nChannels
) {
1014 SInt32 chanArr
[out_nChannels
];
1015 for (int i
= 0; i
< out_nChannels
; i
++) {
1018 for (int i
= 0; i
< outchannels
; i
++) {
1021 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_ChannelMap
, kAudioUnitScope_Output
, 0, chanArr
, sizeof(SInt32
) * out_nChannels
);
1022 if (err1
!= noErr
) {
1023 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 0");
1029 // Setup stream converters
1030 if (capturing
&& inchannels
> 0) {
1032 size
= sizeof(AudioStreamBasicDescription
);
1033 err1
= AudioUnitGetProperty(fAUHAL
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Output
, 0, &srcFormat
, &size
);
1034 if (err1
!= noErr
) {
1035 jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input");
1039 PrintStreamDesc(&srcFormat
);
1041 jack_log("Setup AUHAL input stream converter SR = %ld", samplerate
);
1042 srcFormat
.mSampleRate
= samplerate
;
1043 srcFormat
.mFormatID
= kAudioFormatLinearPCM
;
1044 srcFormat
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
| kLinearPCMFormatFlagIsNonInterleaved
;
1045 srcFormat
.mBytesPerPacket
= sizeof(jack_default_audio_sample_t
);
1046 srcFormat
.mFramesPerPacket
= 1;
1047 srcFormat
.mBytesPerFrame
= sizeof(jack_default_audio_sample_t
);
1048 srcFormat
.mChannelsPerFrame
= inchannels
;
1049 srcFormat
.mBitsPerChannel
= 32;
1050 PrintStreamDesc(&srcFormat
);
1052 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Output
, 1, &srcFormat
, sizeof(AudioStreamBasicDescription
));
1054 if (err1
!= noErr
) {
1055 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input");
1061 if (playing
&& outchannels
> 0) {
1063 size
= sizeof(AudioStreamBasicDescription
);
1064 err1
= AudioUnitGetProperty(fAUHAL
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 1, &dstFormat
, &size
);
1065 if (err1
!= noErr
) {
1066 jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output");
1070 PrintStreamDesc(&dstFormat
);
1072 jack_log("Setup AUHAL output stream converter SR = %ld", samplerate
);
1073 dstFormat
.mSampleRate
= samplerate
;
1074 dstFormat
.mFormatID
= kAudioFormatLinearPCM
;
1075 dstFormat
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
| kLinearPCMFormatFlagIsNonInterleaved
;
1076 dstFormat
.mBytesPerPacket
= sizeof(jack_default_audio_sample_t
);
1077 dstFormat
.mFramesPerPacket
= 1;
1078 dstFormat
.mBytesPerFrame
= sizeof(jack_default_audio_sample_t
);
1079 dstFormat
.mChannelsPerFrame
= outchannels
;
1080 dstFormat
.mBitsPerChannel
= 32;
1081 PrintStreamDesc(&dstFormat
);
1083 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &dstFormat
, sizeof(AudioStreamBasicDescription
));
1085 if (err1
!= noErr
) {
1086 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output");
1093 if (inchannels
> 0 && outchannels
== 0) {
1094 AURenderCallbackStruct output
;
1095 output
.inputProc
= Render
;
1096 output
.inputProcRefCon
= this;
1097 err1
= AudioUnitSetProperty(fAUHAL
, kAudioOutputUnitProperty_SetInputCallback
, kAudioUnitScope_Global
, 0, &output
, sizeof(output
));
1098 if (err1
!= noErr
) {
1099 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1");
1104 AURenderCallbackStruct output
;
1105 output
.inputProc
= Render
;
1106 output
.inputProcRefCon
= this;
1107 err1
= AudioUnitSetProperty(fAUHAL
, kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
, 0, &output
, sizeof(output
));
1108 if (err1
!= noErr
) {
1109 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0");
1122 OSStatus
JackCoreAudioAdapter::DestroyAggregateDevice()
1124 OSStatus osErr
= noErr
;
1125 AudioObjectPropertyAddress pluginAOPA
;
1126 pluginAOPA
.mSelector
= kAudioPlugInDestroyAggregateDevice
;
1127 pluginAOPA
.mScope
= kAudioObjectPropertyScopeGlobal
;
1128 pluginAOPA
.mElement
= kAudioObjectPropertyElementMaster
;
1131 osErr
= AudioObjectGetPropertyDataSize(fPluginID
, &pluginAOPA
, 0, NULL
, &outDataSize
);
1132 if (osErr
!= noErr
) {
1133 jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error");
1138 osErr
= AudioObjectGetPropertyData(fPluginID
, &pluginAOPA
, 0, NULL
, &outDataSize
, &fDeviceID
);
1139 if (osErr
!= noErr
) {
1140 jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyData error");
1148 static CFStringRef
GetDeviceName(AudioDeviceID id
)
1150 UInt32 size
= sizeof(CFStringRef
);
1152 OSStatus err
= AudioDeviceGetProperty(id
, 0, false, kAudioDevicePropertyDeviceUID
, &size
, &UIname
);
1153 return (err
== noErr
) ? UIname
: NULL
;
1156 OSStatus
JackCoreAudioAdapter::CreateAggregateDevice(AudioDeviceID captureDeviceID
, AudioDeviceID playbackDeviceID
, jack_nframes_t samplerate
, AudioDeviceID
* outAggregateDevice
)
1158 OSStatus err
= noErr
;
1159 AudioObjectID sub_device
[32];
1160 UInt32 outSize
= sizeof(sub_device
);
1162 err
= AudioDeviceGetProperty(captureDeviceID
, 0, kAudioDeviceSectionGlobal
, kAudioAggregateDevicePropertyActiveSubDeviceList
, &outSize
, sub_device
);
1163 vector
<AudioDeviceID
> captureDeviceIDArray
;
1166 jack_log("Input device does not have subdevices");
1167 captureDeviceIDArray
.push_back(captureDeviceID
);
1169 int num_devices
= outSize
/ sizeof(AudioObjectID
);
1170 jack_log("Input device has %d subdevices", num_devices
);
1171 for (int i
= 0; i
< num_devices
; i
++) {
1172 captureDeviceIDArray
.push_back(sub_device
[i
]);
1176 err
= AudioDeviceGetProperty(playbackDeviceID
, 0, kAudioDeviceSectionGlobal
, kAudioAggregateDevicePropertyActiveSubDeviceList
, &outSize
, sub_device
);
1177 vector
<AudioDeviceID
> playbackDeviceIDArray
;
1180 jack_log("Output device does not have subdevices");
1181 playbackDeviceIDArray
.push_back(playbackDeviceID
);
1183 int num_devices
= outSize
/ sizeof(AudioObjectID
);
1184 jack_log("Output device has %d subdevices", num_devices
);
1185 for (int i
= 0; i
< num_devices
; i
++) {
1186 playbackDeviceIDArray
.push_back(sub_device
[i
]);
1190 return CreateAggregateDeviceAux(captureDeviceIDArray
, playbackDeviceIDArray
, samplerate
, outAggregateDevice
);
1193 OSStatus
JackCoreAudioAdapter::CreateAggregateDeviceAux(vector
<AudioDeviceID
> captureDeviceID
, vector
<AudioDeviceID
> playbackDeviceID
, jack_nframes_t samplerate
, AudioDeviceID
* outAggregateDevice
)
1195 OSStatus osErr
= noErr
;
1197 Boolean outWritable
;
1199 // Prepare sub-devices for clock drift compensation
1200 // Workaround for bug in the HAL : until 10.6.2
1201 AudioObjectPropertyAddress theAddressOwned
= { kAudioObjectPropertyOwnedObjects
, kAudioObjectPropertyScopeGlobal
, kAudioObjectPropertyElementMaster
};
1202 AudioObjectPropertyAddress theAddressDrift
= { kAudioSubDevicePropertyDriftCompensation
, kAudioObjectPropertyScopeGlobal
, kAudioObjectPropertyElementMaster
};
1203 UInt32 theQualifierDataSize
= sizeof(AudioObjectID
);
1204 AudioClassID inClass
= kAudioSubDeviceClassID
;
1205 void* theQualifierData
= &inClass
;
1206 UInt32 subDevicesNum
= 0;
1208 //---------------------------------------------------------------------------
1209 // Setup SR of both devices otherwise creating AD may fail...
1210 //---------------------------------------------------------------------------
1211 UInt32 keptclockdomain
= 0;
1212 UInt32 clockdomain
= 0;
1213 outSize
= sizeof(UInt32
);
1214 bool need_clock_drift_compensation
= false;
1216 for (UInt32 i
= 0; i
< captureDeviceID
.size(); i
++) {
1217 if (SetupSampleRateAux(captureDeviceID
[i
], samplerate
) < 0) {
1218 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : cannot set SR of input device");
1220 // Check clock domain
1221 osErr
= AudioDeviceGetProperty(captureDeviceID
[i
], 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyClockDomain
, &outSize
, &clockdomain
);
1223 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
1226 keptclockdomain
= (keptclockdomain
== 0) ? clockdomain
: keptclockdomain
;
1227 jack_log("JackCoreAudioAdapter::CreateAggregateDevice : input clockdomain = %d", clockdomain
);
1228 if (clockdomain
!= 0 && clockdomain
!= keptclockdomain
) {
1229 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...");
1230 need_clock_drift_compensation
= true;
1236 for (UInt32 i
= 0; i
< playbackDeviceID
.size(); i
++) {
1237 if (SetupSampleRateAux(playbackDeviceID
[i
], samplerate
) < 0) {
1238 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : cannot set SR of output device");
1240 // Check clock domain
1241 osErr
= AudioDeviceGetProperty(playbackDeviceID
[i
], 0, kAudioDeviceSectionGlobal
, kAudioDevicePropertyClockDomain
, &outSize
, &clockdomain
);
1243 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
1246 keptclockdomain
= (keptclockdomain
== 0) ? clockdomain
: keptclockdomain
;
1247 jack_log("JackCoreAudioAdapter::CreateAggregateDevice : output clockdomain = %d", clockdomain
);
1248 if (clockdomain
!= 0 && clockdomain
!= keptclockdomain
) {
1249 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...");
1250 need_clock_drift_compensation
= true;
1256 // If no valid clock domain was found, then assume we have to compensate...
1257 if (keptclockdomain
== 0) {
1258 need_clock_drift_compensation
= true;
1261 //---------------------------------------------------------------------------
1262 // Start to create a new aggregate by getting the base audio hardware plugin
1263 //---------------------------------------------------------------------------
1265 char device_name
[256];
1266 for (UInt32 i
= 0; i
< captureDeviceID
.size(); i
++) {
1267 GetDeviceNameFromID(captureDeviceID
[i
], device_name
);
1268 jack_info("Separated input = '%s' ", device_name
);
1271 for (UInt32 i
= 0; i
< playbackDeviceID
.size(); i
++) {
1272 GetDeviceNameFromID(playbackDeviceID
[i
], device_name
);
1273 jack_info("Separated output = '%s' ", device_name
);
1276 osErr
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID
, &outSize
, &outWritable
);
1277 if (osErr
!= noErr
) {
1278 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error");
1283 AudioValueTranslation pluginAVT
;
1285 CFStringRef inBundleRef
= CFSTR("com.apple.audio.CoreAudio");
1287 pluginAVT
.mInputData
= &inBundleRef
;
1288 pluginAVT
.mInputDataSize
= sizeof(inBundleRef
);
1289 pluginAVT
.mOutputData
= &fPluginID
;
1290 pluginAVT
.mOutputDataSize
= sizeof(fPluginID
);
1292 osErr
= AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID
, &outSize
, &pluginAVT
);
1293 if (osErr
!= noErr
) {
1294 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error");
1299 //-------------------------------------------------
1300 // Create a CFDictionary for our aggregate device
1301 //-------------------------------------------------
1303 CFMutableDictionaryRef aggDeviceDict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1305 CFStringRef AggregateDeviceNameRef
= CFSTR("JackDuplex");
1306 CFStringRef AggregateDeviceUIDRef
= CFSTR("com.grame.JackDuplex");
1308 // add the name of the device to the dictionary
1309 CFDictionaryAddValue(aggDeviceDict
, CFSTR(kAudioAggregateDeviceNameKey
), AggregateDeviceNameRef
);
1311 // add our choice of UID for the aggregate device to the dictionary
1312 CFDictionaryAddValue(aggDeviceDict
, CFSTR(kAudioAggregateDeviceUIDKey
), AggregateDeviceUIDRef
);
1314 // add a "private aggregate key" to the dictionary
1316 CFNumberRef AggregateDeviceNumberRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &value
);
1319 Gestalt(gestaltSystemVersion
, &system
);
1321 jack_log("JackCoreAudioAdapter::CreateAggregateDevice : system version = %x limit = %x", system
, 0x00001054);
1323 // Starting with 10.5.4 systems, the AD can be internal... (better)
1324 if (system
< 0x00001054) {
1325 jack_log("JackCoreAudioAdapter::CreateAggregateDevice : public aggregate device....");
1327 jack_log("JackCoreAudioAdapter::CreateAggregateDevice : private aggregate device....");
1328 CFDictionaryAddValue(aggDeviceDict
, CFSTR(kAudioAggregateDeviceIsPrivateKey
), AggregateDeviceNumberRef
);
1331 // Prepare sub-devices for clock drift compensation
1332 CFMutableArrayRef subDevicesArrayClock
= NULL
;
1335 if (fClockDriftCompensate) {
1336 if (need_clock_drift_compensation) {
1337 jack_info("Clock drift compensation activated...");
1338 subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1340 for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
1341 CFStringRef UID = GetDeviceName(captureDeviceID[i]);
1343 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1344 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
1345 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
1347 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
1351 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
1352 CFStringRef UID = GetDeviceName(playbackDeviceID[i]);
1354 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1355 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
1356 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
1358 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
1362 // add sub-device clock array for the aggregate device to the dictionary
1363 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock);
1365 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
1370 //-------------------------------------------------
1371 // Create a CFMutableArray for our sub-device list
1372 //-------------------------------------------------
1374 // we need to append the UID for each device to a CFMutableArray, so create one here
1375 CFMutableArrayRef subDevicesArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1377 vector
<CFStringRef
> captureDeviceUID
;
1378 for (UInt32 i
= 0; i
< captureDeviceID
.size(); i
++) {
1379 CFStringRef ref
= GetDeviceName(captureDeviceID
[i
]);
1383 captureDeviceUID
.push_back(ref
);
1384 // input sub-devices in this example, so append the sub-device's UID to the CFArray
1385 CFArrayAppendValue(subDevicesArray
, ref
);
1388 vector
<CFStringRef
> playbackDeviceUID
;
1389 for (UInt32 i
= 0; i
< playbackDeviceID
.size(); i
++) {
1390 CFStringRef ref
= GetDeviceName(playbackDeviceID
[i
]);
1394 playbackDeviceUID
.push_back(ref
);
1395 // output sub-devices in this example, so append the sub-device's UID to the CFArray
1396 CFArrayAppendValue(subDevicesArray
, ref
);
1399 //-----------------------------------------------------------------------
1400 // Feed the dictionary to the plugin, to create a blank aggregate device
1401 //-----------------------------------------------------------------------
1403 AudioObjectPropertyAddress pluginAOPA
;
1404 pluginAOPA
.mSelector
= kAudioPlugInCreateAggregateDevice
;
1405 pluginAOPA
.mScope
= kAudioObjectPropertyScopeGlobal
;
1406 pluginAOPA
.mElement
= kAudioObjectPropertyElementMaster
;
1409 osErr
= AudioObjectGetPropertyDataSize(fPluginID
, &pluginAOPA
, 0, NULL
, &outDataSize
);
1410 if (osErr
!= noErr
) {
1411 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyDataSize error");
1416 osErr
= AudioObjectGetPropertyData(fPluginID
, &pluginAOPA
, sizeof(aggDeviceDict
), &aggDeviceDict
, &outDataSize
, outAggregateDevice
);
1417 if (osErr
!= noErr
) {
1418 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyData error");
1423 // pause for a bit to make sure that everything completed correctly
1424 // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created
1425 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.1, false);
1427 //-------------------------
1428 // Set the sub-device list
1429 //-------------------------
1431 pluginAOPA
.mSelector
= kAudioAggregateDevicePropertyFullSubDeviceList
;
1432 pluginAOPA
.mScope
= kAudioObjectPropertyScopeGlobal
;
1433 pluginAOPA
.mElement
= kAudioObjectPropertyElementMaster
;
1434 outDataSize
= sizeof(CFMutableArrayRef
);
1435 osErr
= AudioObjectSetPropertyData(*outAggregateDevice
, &pluginAOPA
, 0, NULL
, outDataSize
, &subDevicesArray
);
1436 if (osErr
!= noErr
) {
1437 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error");
1442 // pause again to give the changes time to take effect
1443 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.1, false);
1445 //-----------------------
1446 // Set the master device
1447 //-----------------------
1449 // set the master device manually (this is the device which will act as the master clock for the aggregate device)
1450 // pass in the UID of the device you want to use
1451 pluginAOPA
.mSelector
= kAudioAggregateDevicePropertyMasterSubDevice
;
1452 pluginAOPA
.mScope
= kAudioObjectPropertyScopeGlobal
;
1453 pluginAOPA
.mElement
= kAudioObjectPropertyElementMaster
;
1454 outDataSize
= sizeof(CFStringRef
);
1455 osErr
= AudioObjectSetPropertyData(*outAggregateDevice
, &pluginAOPA
, 0, NULL
, outDataSize
, &captureDeviceUID
[0]); // First apture is master...
1456 if (osErr
!= noErr
) {
1457 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for master device error");
1462 // pause again to give the changes time to take effect
1463 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.1, false);
1465 // Prepare sub-devices for clock drift compensation
1466 // Workaround for bug in the HAL : until 10.6.2
1468 if (fClockDriftCompensate
) {
1469 if (need_clock_drift_compensation
) {
1470 jack_info("Clock drift compensation activated...");
1472 // Get the property data size
1473 osErr
= AudioObjectGetPropertyDataSize(*outAggregateDevice
, &theAddressOwned
, theQualifierDataSize
, theQualifierData
, &outSize
);
1474 if (osErr
!= noErr
) {
1475 jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error");
1479 // Calculate the number of object IDs
1480 subDevicesNum
= outSize
/ sizeof(AudioObjectID
);
1481 jack_info("JackCoreAudioAdapter::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum
);
1482 AudioObjectID subDevices
[subDevicesNum
];
1483 outSize
= sizeof(subDevices
);
1485 osErr
= AudioObjectGetPropertyData(*outAggregateDevice
, &theAddressOwned
, theQualifierDataSize
, theQualifierData
, &outSize
, subDevices
);
1486 if (osErr
!= noErr
) {
1487 jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error");
1491 // Set kAudioSubDevicePropertyDriftCompensation property...
1492 for (UInt32 index
= 0; index
< subDevicesNum
; ++index
) {
1493 UInt32 theDriftCompensationValue
= 1;
1494 osErr
= AudioObjectSetPropertyData(subDevices
[index
], &theAddressDrift
, 0, NULL
, sizeof(UInt32
), &theDriftCompensationValue
);
1495 if (osErr
!= noErr
) {
1496 jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error");
1501 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
1505 // pause again to give the changes time to take effect
1506 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.1, false);
1512 // release the private AD key
1513 CFRelease(AggregateDeviceNumberRef
);
1515 // release the CF objects we have created - we don't need them any more
1516 CFRelease(aggDeviceDict
);
1517 CFRelease(subDevicesArray
);
1519 if (subDevicesArrayClock
) {
1520 CFRelease(subDevicesArrayClock
);
1523 // release the device UID
1524 for (UInt32 i
= 0; i
< captureDeviceUID
.size(); i
++) {
1525 CFRelease(captureDeviceUID
[i
]);
1528 for (UInt32 i
= 0; i
< playbackDeviceUID
.size(); i
++) {
1529 CFRelease(playbackDeviceUID
[i
]);
1532 jack_log("New aggregate device %ld", *outAggregateDevice
);
1536 DestroyAggregateDevice();
1541 bool JackCoreAudioAdapter::IsAggregateDevice(AudioDeviceID device
)
1543 OSStatus err
= noErr
;
1544 AudioObjectID sub_device
[32];
1545 UInt32 outSize
= sizeof(sub_device
);
1546 err
= AudioDeviceGetProperty(device
, 0, kAudioDeviceSectionGlobal
, kAudioAggregateDevicePropertyActiveSubDeviceList
, &outSize
, sub_device
);
1549 jack_log("Device does not have subdevices");
1552 int num_devices
= outSize
/ sizeof(AudioObjectID
);
1553 jack_log("Device does has %d subdevices", num_devices
);
1558 void JackCoreAudioAdapter::CloseAUHAL()
1560 AudioUnitUninitialize(fAUHAL
);
1561 CloseComponent(fAUHAL
);
1564 int JackCoreAudioAdapter::Open()
1566 return (AudioOutputUnitStart(fAUHAL
) != noErr
) ? -1 : 0;
1569 int JackCoreAudioAdapter::Close()
1572 fTable
.Save(fHostBufferSize
, fHostSampleRate
, fAdaptedSampleRate
, fAdaptedBufferSize
);
1574 AudioOutputUnitStop(fAUHAL
);
1578 if (fPluginID
> 0) {
1579 DestroyAggregateDevice();
1584 int JackCoreAudioAdapter::SetSampleRate(jack_nframes_t sample_rate
)
1586 JackAudioAdapterInterface::SetHostSampleRate(sample_rate
);
1591 int JackCoreAudioAdapter::SetBufferSize(jack_nframes_t buffer_size
)
1593 JackAudioAdapterInterface::SetHostBufferSize(buffer_size
);
1598 OSStatus
JackCoreAudioAdapter::GetStreamLatencies(AudioDeviceID device
, bool isInput
, vector
<int>& latencies
)
1600 OSStatus err
= noErr
;
1601 UInt32 outSize1
, outSize2
, outSize3
;
1602 Boolean outWritable
;
1604 err
= AudioDeviceGetPropertyInfo(device
, 0, isInput
, kAudioDevicePropertyStreams
, &outSize1
, &outWritable
);
1606 int stream_count
= outSize1
/ sizeof(UInt32
);
1607 AudioStreamID streamIDs
[stream_count
];
1608 AudioBufferList bufferList
[stream_count
];
1609 UInt32 streamLatency
;
1610 outSize2
= sizeof(UInt32
);
1612 err
= AudioDeviceGetProperty(device
, 0, isInput
, kAudioDevicePropertyStreams
, &outSize1
, streamIDs
);
1614 jack_error("GetStreamLatencies kAudioDevicePropertyStreams err = %d", err
);
1618 err
= AudioDeviceGetPropertyInfo(device
, 0, isInput
, kAudioDevicePropertyStreamConfiguration
, &outSize3
, &outWritable
);
1620 jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err
);
1624 for (int i
= 0; i
< stream_count
; i
++) {
1625 err
= AudioStreamGetProperty(streamIDs
[i
], 0, kAudioStreamPropertyLatency
, &outSize2
, &streamLatency
);
1627 jack_error("GetStreamLatencies kAudioStreamPropertyLatency err = %d", err
);
1630 err
= AudioDeviceGetProperty(device
, 0, isInput
, kAudioDevicePropertyStreamConfiguration
, &outSize3
, bufferList
);
1632 jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err
);
1635 // Push 'channel' time the stream latency
1636 for (uint k
= 0; k
< bufferList
->mBuffers
[i
].mNumberChannels
; k
++) {
1637 latencies
.push_back(streamLatency
);
1644 int JackCoreAudioAdapter::GetLatency(int port_index
, bool input
)
1646 UInt32 size
= sizeof(UInt32
);
1650 OSStatus err
= AudioDeviceGetProperty(fDeviceID
, 0, input
, kAudioDevicePropertyLatency
, &size
, &value1
);
1652 jack_log("AudioDeviceGetProperty kAudioDevicePropertyLatency error");
1654 err
= AudioDeviceGetProperty(fDeviceID
, 0, input
, kAudioDevicePropertySafetyOffset
, &size
, &value2
);
1656 jack_log("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error");
1659 // TODO : add stream latency
1661 return value1
+ value2
+ fAdaptedBufferSize
;
1664 int JackCoreAudioAdapter::GetInputLatency(int port_index
)
1666 if (port_index
< int(fInputLatencies
.size())) {
1667 return GetLatency(port_index
, true) + fInputLatencies
[port_index
];
1669 // No stream latency
1670 return GetLatency(port_index
, true);
1674 int JackCoreAudioAdapter::GetOutputLatency(int port_index
)
1676 if (port_index
< int(fOutputLatencies
.size())) {
1677 return GetLatency(port_index
, false) + fOutputLatencies
[port_index
];
1679 // No stream latency
1680 return GetLatency(port_index
, false);
1691 SERVER_EXPORT jack_driver_desc_t
* jack_get_descriptor()
1693 jack_driver_desc_t
* desc
;
1694 jack_driver_desc_filler_t filler
;
1695 jack_driver_param_value_t value
;
1697 desc
= jack_driver_descriptor_construct("audioadapter", JackDriverNone
, "netjack audio <==> net backend adapter", &filler
);
1700 jack_driver_descriptor_add_parameter(desc
, &filler
, "channels", 'c', JackDriverParamInt
, &value
, NULL
, "Maximum number of channels", "Maximum number of channels. If -1, max possible number of channels will be used");
1701 jack_driver_descriptor_add_parameter(desc
, &filler
, "in-channels", 'i', JackDriverParamInt
, &value
, NULL
, "Maximum number of input channels", "Maximum number of input channels. If -1, max possible number of input channels will be used");
1702 jack_driver_descriptor_add_parameter(desc
, &filler
, "out-channels", 'o', JackDriverParamInt
, &value
, NULL
, "Maximum number of output channels", "Maximum number of output channels. If -1, max possible number of output channels will be used");
1705 jack_driver_descriptor_add_parameter(desc
, &filler
, "capture", 'C', JackDriverParamString
, &value
, NULL
, "Input CoreAudio device name", NULL
);
1706 jack_driver_descriptor_add_parameter(desc
, &filler
, "playback", 'P', JackDriverParamString
, &value
, NULL
, "Output CoreAudio device name", NULL
);
1709 jack_driver_descriptor_add_parameter(desc
, &filler
, "rate", 'r', JackDriverParamUInt
, &value
, NULL
, "Sample rate", NULL
);
1712 jack_driver_descriptor_add_parameter(desc
, &filler
, "period", 'p', JackDriverParamUInt
, &value
, NULL
, "Frames per period", NULL
);
1715 jack_driver_descriptor_add_parameter(desc
, &filler
, "duplex", 'D', JackDriverParamBool
, &value
, NULL
, "Provide both capture and playback ports", NULL
);
1718 jack_driver_descriptor_add_parameter(desc
, &filler
, "device", 'd', JackDriverParamString
, &value
, NULL
, "CoreAudio device name", NULL
);
1721 jack_driver_descriptor_add_parameter(desc
, &filler
, "list-devices", 'l', JackDriverParamBool
, &value
, NULL
, "Display available CoreAudio devices", NULL
);
1724 jack_driver_descriptor_add_parameter(desc
, &filler
, "quality", 'q', JackDriverParamInt
, &value
, NULL
, "Resample algorithm quality (0 - 4)", NULL
);
1727 jack_driver_descriptor_add_parameter(desc
, &filler
, "ring-buffer", 'g', JackDriverParamInt
, &value
, NULL
, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)");
1730 jack_driver_descriptor_add_parameter(desc
, &filler
, "clock-drift", 's', JackDriverParamBool
, &value
, NULL
, "Clock drift compensation", "Whether to compensate clock drift in dynamically created aggregate device");