Remove old resource file.
[jack2.git] / macosx / coreaudio / JackCoreAudioAdapter.cpp
blobe485888a1d9b69b5c3f6aac9a441b09486dc31f8
1 /*
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"
22 #include <unistd.h>
24 #include <CoreServices/CoreServices.h>
26 namespace Jack
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()
45 UInt32 size;
46 Boolean isWritable;
47 int i, deviceNum;
48 OSStatus err;
49 CFStringRef UIname;
51 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable);
52 if (err != noErr) {
53 return err;
56 deviceNum = size / sizeof(AudioDeviceID);
57 AudioDeviceID devices[deviceNum];
59 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices);
60 if (err != noErr) {
61 return err;
64 for (i = 0; i < deviceNum; i++) {
65 char device_name[256];
66 char internal_name[256];
68 size = sizeof(CFStringRef);
69 UIname = NULL;
70 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname);
71 if (err == noErr) {
72 CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding());
73 } else {
74 goto error;
77 size = 256;
78 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name);
79 if (err != noErr) {
80 return err;
83 jack_info("Device name = \'%s\', internal_name = \'%s\' (to be used as -C, -P, or -d parameter)", device_name, internal_name);
86 return noErr;
88 error:
89 if (UIname != NULL) {
90 CFRelease(UIname);
92 return err;
95 static void printError(OSStatus err)
97 switch (err) {
98 case kAudioHardwareNoError:
99 jack_log("error code : kAudioHardwareNoError");
100 break;
101 case kAudioConverterErr_FormatNotSupported:
102 jack_log("error code : kAudioConverterErr_FormatNotSupported");
103 break;
104 case kAudioConverterErr_OperationNotSupported:
105 jack_log("error code : kAudioConverterErr_OperationNotSupported");
106 break;
107 case kAudioConverterErr_PropertyNotSupported:
108 jack_log("error code : kAudioConverterErr_PropertyNotSupported");
109 break;
110 case kAudioConverterErr_InvalidInputSize:
111 jack_log("error code : kAudioConverterErr_InvalidInputSize");
112 break;
113 case kAudioConverterErr_InvalidOutputSize:
114 jack_log("error code : kAudioConverterErr_InvalidOutputSize");
115 break;
116 case kAudioConverterErr_UnspecifiedError:
117 jack_log("error code : kAudioConverterErr_UnspecifiedError");
118 break;
119 case kAudioConverterErr_BadPropertySizeError:
120 jack_log("error code : kAudioConverterErr_BadPropertySizeError");
121 break;
122 case kAudioConverterErr_RequiresPacketDescriptionsError:
123 jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError");
124 break;
125 case kAudioConverterErr_InputSampleRateOutOfRange:
126 jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange");
127 break;
128 case kAudioConverterErr_OutputSampleRateOutOfRange:
129 jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange");
130 break;
131 case kAudioHardwareNotRunningError:
132 jack_log("error code : kAudioHardwareNotRunningError");
133 break;
134 case kAudioHardwareUnknownPropertyError:
135 jack_log("error code : kAudioHardwareUnknownPropertyError");
136 break;
137 case kAudioHardwareIllegalOperationError:
138 jack_log("error code : kAudioHardwareIllegalOperationError");
139 break;
140 case kAudioHardwareBadDeviceError:
141 jack_log("error code : kAudioHardwareBadDeviceError");
142 break;
143 case kAudioHardwareBadStreamError:
144 jack_log("error code : kAudioHardwareBadStreamError");
145 break;
146 case kAudioDeviceUnsupportedFormatError:
147 jack_log("error code : kAudioDeviceUnsupportedFormatError");
148 break;
149 case kAudioDevicePermissionsError:
150 jack_log("error code : kAudioDevicePermissionsError");
151 break;
152 case kAudioHardwareBadObjectError:
153 jack_log("error code : kAudioHardwareBadObjectError");
154 break;
155 case kAudioHardwareUnsupportedOperationError:
156 jack_log("error code : kAudioHardwareUnsupportedOperationError");
157 break;
158 default:
159 jack_log("error code : unknown");
160 break;
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();
173 break;
177 return noErr;
180 OSStatus JackCoreAudioAdapter::SRNotificationCallback(AudioDeviceID inDevice,
181 UInt32 inChannel,
182 Boolean isInput,
183 AudioDevicePropertyID inPropertyID,
184 void* inClientData)
186 JackCoreAudioAdapter* driver = static_cast<JackCoreAudioAdapter*>(inClientData);
188 switch (inPropertyID) {
190 case kAudioDevicePropertyNominalSampleRate: {
191 jack_log("JackCoreAudioAdapter::SRNotificationCallback kAudioDevicePropertyNominalSampleRate");
192 driver->fState = true;
193 break;
197 return noErr;
200 // A better implementation would try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code)
201 OSStatus JackCoreAudioAdapter::DeviceNotificationCallback(AudioDeviceID inDevice,
202 UInt32 inChannel,
203 Boolean isInput,
204 AudioDevicePropertyID inPropertyID,
205 void* inClientData)
208 switch (inPropertyID) {
210 case kAudioDeviceProcessorOverload: {
211 jack_error("JackCoreAudioAdapter::DeviceNotificationCallback kAudioDeviceProcessorOverload");
212 break;
215 case kAudioDevicePropertyStreamConfiguration: {
216 jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration");
217 return kAudioHardwareUnsupportedOperationError;
220 case kAudioDevicePropertyNominalSampleRate: {
221 jack_error("Cannot handle kAudioDevicePropertyNominalSampleRate");
222 return kAudioHardwareUnsupportedOperationError;
226 return noErr;
229 int JackCoreAudioAdapter::AddListeners()
231 OSStatus err = noErr;
233 // Add listeners
234 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this);
235 if (err != noErr) {
236 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload");
237 printError(err);
238 return -1;
241 err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback, this);
242 if (err != noErr) {
243 jack_error("Error calling AudioHardwareAddPropertyListener with kAudioHardwarePropertyDevices");
244 printError(err);
245 return -1;
248 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback, this);
249 if (err != noErr) {
250 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate");
251 printError(err);
252 return -1;
255 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback, this);
256 if (err != noErr) {
257 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning");
258 printError(err);
259 return -1;
262 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this);
263 if (err != noErr) {
264 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration");
265 printError(err);
266 return -1;
269 err = AudioDeviceAddPropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this);
270 if (err != noErr) {
271 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration");
272 printError(err);
273 return -1;
276 return 0;
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,
292 UInt32 inBusNumber,
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);
299 if (err == noErr) {
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);
311 return noErr;
312 } else {
313 return err;
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)
320 const JSList* node;
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];
326 fCaptureUID[0] = 0;
327 fPlaybackUID[0] = 0;
328 fClockDriftCompensate = false;
330 // Default values
331 fCaptureChannels = -1;
332 fPlaybackChannels = -1;
334 SInt32 major;
335 SInt32 minor;
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) {
354 case 'c' :
355 fCaptureChannels = fPlaybackChannels = param->value.ui;
356 break;
358 case 'i':
359 fCaptureChannels = param->value.ui;
360 break;
362 case 'o':
363 fPlaybackChannels = param->value.ui;
364 break;
366 case 'C':
367 fCapturing = true;
368 strncpy(fCaptureUID, param->value.str, 256);
369 break;
371 case 'P':
372 fPlaying = true;
373 strncpy(fPlaybackUID, param->value.str, 256);
374 break;
376 case 'd':
377 strncpy(fCaptureUID, param->value.str, 256);
378 strncpy(fPlaybackUID, param->value.str, 256);
379 break;
381 case 'D':
382 fCapturing = fPlaying = true;
383 break;
385 case 'r':
386 SetAdaptedSampleRate(param->value.ui);
387 break;
389 case 'p':
390 SetAdaptedBufferSize(param->value.ui);
391 break;
393 case 'l':
394 DisplayDeviceNames();
395 break;
397 case 'q':
398 fQuality = param->value.ui;
399 break;
401 case 'g':
402 fRingbufferCurSize = param->value.ui;
403 fAdaptative = false;
404 break;
406 case 's':
407 fClockDriftCompensate = true;
408 break;
412 /* duplex is the default */
413 if (!fCapturing && !fPlaying) {
414 fCapturing = true;
415 fPlaying = true;
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)
454 OSStatus res;
455 UInt32 theSize = sizeof(UInt32);
456 AudioDeviceID inDefault;
457 AudioDeviceID outDefault;
459 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) {
460 return res;
463 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) {
464 return res;
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;
476 } else {
477 *id = inDefault;
478 return noErr;
482 OSStatus JackCoreAudioAdapter::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput)
484 OSStatus err = noErr;
485 UInt32 outSize;
486 Boolean outWritable;
488 channelCount = 0;
489 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable);
490 if (err == noErr) {
491 AudioBufferList bufferList[outSize];
492 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList);
493 if (err == noErr) {
494 for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++) {
495 channelCount += bufferList->mBuffers[i].mNumberChannels;
500 return err;
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) };
509 if (inIUD == NULL) {
510 return kAudioHardwareUnspecifiedError;
511 } else {
512 OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value);
513 CFRelease(inIUD);
514 jack_log("GetDeviceIDFromUID %s %ld", UID, *id);
515 return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res;
519 OSStatus JackCoreAudioAdapter::GetDefaultInputDevice(AudioDeviceID* id)
521 OSStatus res;
522 UInt32 theSize = sizeof(UInt32);
523 AudioDeviceID inDefault;
525 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) {
526 return res;
529 if (inDefault == 0) {
530 jack_error("Error: default input device is 0, please select a correct one !!");
531 return -1;
533 jack_log("GetDefaultInputDevice: input = %ld ", inDefault);
534 *id = inDefault;
535 return noErr;
538 OSStatus JackCoreAudioAdapter::GetDefaultOutputDevice(AudioDeviceID* id)
540 OSStatus res;
541 UInt32 theSize = sizeof(UInt32);
542 AudioDeviceID outDefault;
544 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) {
545 return res;
548 if (outDefault == 0) {
549 jack_error("Error: default output device is 0, please select a correct one !!");
550 return -1;
552 jack_log("GetDefaultOutputDevice: output = %ld", outDefault);
553 *id = outDefault;
554 return noErr;
557 OSStatus JackCoreAudioAdapter::GetDeviceNameFromID(AudioDeviceID id, char* name)
559 UInt32 size = 256;
560 return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name);
563 AudioDeviceID JackCoreAudioAdapter::GetDeviceIDFromName(const char* name)
565 UInt32 size;
566 Boolean isWritable;
567 int i, deviceNum;
569 OSStatus err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable);
570 if (err != noErr) {
571 return -1;
574 deviceNum = size / sizeof(AudioDeviceID);
575 AudioDeviceID devices[deviceNum];
577 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices);
578 if (err != noErr) {
579 return err;
582 for (i = 0; i < deviceNum; i++) {
583 char device_name[256];
584 size = 256;
585 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name);
586 if (err != noErr) {
587 return -1;
588 } else if (strcmp(device_name, name) == 0) {
589 return devices[i];
593 return -1;
596 // Setup
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;
606 // Duplex
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");
617 return -1;
620 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) {
621 jack_error("Cannot get device name from device ID");
622 return -1;
625 } else {
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");
634 return -1;
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");
642 return -1;
646 if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) {
647 return -1;
651 // Capture only
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");
657 return -1;
660 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) {
661 jack_error("Cannot get device name from device ID");
662 return -1;
665 // Playback only
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");
671 return -1;
674 if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) {
675 jack_error("Cannot get device name from device ID");
676 return -1;
679 // Use default driver in duplex mode
680 } else {
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");
692 goto built_in;
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");
700 goto built_in;
704 if (captureID > 0 && playbackID > 0) {
705 if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) {
706 goto built_in;
708 } else {
709 jack_error("Cannot use default input/output");
710 goto built_in;
715 return 0;
717 built_in:
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) {
725 return -1;
727 } else {
728 jack_error("Cannot aggregate built-in input and output");
729 return -1;
732 return 0;
735 int JackCoreAudioAdapter::SetupChannels(bool capturing,
736 bool playing,
737 int& inchannels,
738 int& outchannels,
739 int& in_nChannels,
740 int& out_nChannels,
741 bool strict)
743 OSStatus err = noErr;
745 if (capturing) {
746 err = GetTotalChannels(fDeviceID, in_nChannels, true);
747 if (err != noErr) {
748 jack_error("Cannot get input channel number");
749 printError(err);
750 return -1;
751 } else {
752 jack_log("Max input channels : %d", in_nChannels);
756 if (playing) {
757 err = GetTotalChannels(fDeviceID, out_nChannels, false);
758 if (err != noErr) {
759 jack_error("Cannot get output channel number");
760 printError(err);
761 return -1;
762 } else {
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);
769 if (strict) {
770 return -1;
774 if (outchannels > out_nChannels) {
775 jack_error("This device hasn't required output channels outchannels = %ld out_nChannels = %ld", outchannels, out_nChannels);
776 if (strict) {
777 return -1;
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;
791 return 0;
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);
799 if (err != noErr) {
800 jack_error("Cannot set buffer size %ld", buffer_size);
801 printError(err);
802 return -1;
805 return 0;
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;
816 UInt32 outSize;
817 Float64 sampleRate;
819 // Get sample rate
820 outSize = sizeof(Float64);
821 err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate);
822 if (err != noErr) {
823 jack_error("Cannot get current sample rate");
824 printError(err);
825 return -1;
826 } else {
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);
836 if (err != noErr) {
837 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate");
838 printError(err);
839 return -1;
841 err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &sampleRate);
842 if (err != noErr) {
843 jack_error("Cannot set sample rate = %ld", samplerate);
844 printError(err);
845 return -1;
848 // Waiting for SR change notification
849 int count = 0;
850 while (!fState && count++ < WAIT_COUNTER) {
851 usleep(100000);
852 jack_log("Wait count = %d", count);
855 // Remove SR change notification
856 AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback);
859 return 0;
862 int JackCoreAudioAdapter::SetupBuffers(int inchannels)
864 jack_log("JackCoreAudioAdapter::SetupBuffers: input = %ld", inchannels);
866 // Prepare buffers
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));
874 return 0;
877 void JackCoreAudioAdapter::DisposeBuffers()
879 if (fInputData) {
880 for (int i = 0; i < fCaptureChannels; i++) {
881 free(fInputData->mBuffers[i].mData);
883 free(fInputData);
884 fInputData = 0;
888 int JackCoreAudioAdapter::OpenAUHAL(bool capturing,
889 bool playing,
890 int inchannels,
891 int outchannels,
892 int in_nChannels,
893 int out_nChannels,
894 jack_nframes_t buffer_size,
895 jack_nframes_t samplerate)
897 ComponentResult err1;
898 UInt32 enableIO;
899 AudioStreamBasicDescription srcFormat, dstFormat;
900 AudioDeviceID currAudioDeviceID;
901 UInt32 size;
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...");
907 return -1;
910 // AUHAL
911 ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
912 Component HALOutput = FindNextComponent(NULL, &cd);
914 err1 = OpenAComponent(HALOutput, &fAUHAL);
915 if (err1 != noErr) {
916 jack_error("Error calling OpenAComponent");
917 printError(err1);
918 goto error;
921 err1 = AudioUnitInitialize(fAUHAL);
922 if (err1 != noErr) {
923 jack_error("Cannot initialize AUHAL unit");
924 printError(err1);
925 goto error;
928 // Start I/O
929 if (capturing && inchannels > 0) {
930 enableIO = 1;
931 jack_log("Setup AUHAL input on");
932 } else {
933 enableIO = 0;
934 jack_log("Setup AUHAL input off");
937 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO));
938 if (err1 != noErr) {
939 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input");
940 printError(err1);
941 goto error;
944 if (playing && outchannels > 0) {
945 enableIO = 1;
946 jack_log("Setup AUHAL output on");
947 } else {
948 enableIO = 0;
949 jack_log("Setup AUHAL output off");
952 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
953 if (err1 != noErr) {
954 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output");
955 printError(err1);
956 goto error;
959 size = sizeof(AudioDeviceID);
960 err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size);
961 if (err1 != noErr) {
962 jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice");
963 printError(err1);
964 goto error;
965 } else {
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));
971 if (err1 != noErr) {
972 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice");
973 printError(err1);
974 goto error;
977 // Set buffer size
978 if (capturing && inchannels > 0) {
979 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32));
980 if (err1 != noErr) {
981 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice");
982 printError(err1);
983 goto error;
987 if (playing && outchannels > 0) {
988 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32));
989 if (err1 != noErr) {
990 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice");
991 printError(err1);
992 goto error;
996 // Setup channel map
997 if (capturing && inchannels > 0 && inchannels <= in_nChannels) {
998 SInt32 chanArr[in_nChannels];
999 for (int i = 0; i < in_nChannels; i++) {
1000 chanArr[i] = -1;
1002 for (int i = 0; i < inchannels; i++) {
1003 chanArr[i] = 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");
1008 printError(err1);
1009 goto error;
1013 if (playing && outchannels > 0 && outchannels <= out_nChannels) {
1014 SInt32 chanArr[out_nChannels];
1015 for (int i = 0; i < out_nChannels; i++) {
1016 chanArr[i] = -1;
1018 for (int i = 0; i < outchannels; i++) {
1019 chanArr[i] = 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");
1024 printError(err1);
1025 goto error;
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");
1036 printError(err1);
1037 goto error;
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");
1056 printError(err1);
1057 goto error;
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");
1067 printError(err1);
1068 goto error;
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");
1087 printError(err1);
1088 goto error;
1092 // Setup callbacks
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");
1100 printError(err1);
1101 goto error;
1103 } else {
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");
1110 printError(err1);
1111 goto error;
1115 return 0;
1117 error:
1118 CloseAUHAL();
1119 return -1;
1122 OSStatus JackCoreAudioAdapter::DestroyAggregateDevice()
1124 OSStatus osErr = noErr;
1125 AudioObjectPropertyAddress pluginAOPA;
1126 pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice;
1127 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
1128 pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
1129 UInt32 outDataSize;
1131 osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
1132 if (osErr != noErr) {
1133 jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error");
1134 printError(osErr);
1135 return osErr;
1138 osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID);
1139 if (osErr != noErr) {
1140 jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyData error");
1141 printError(osErr);
1142 return osErr;
1145 return noErr;
1148 static CFStringRef GetDeviceName(AudioDeviceID id)
1150 UInt32 size = sizeof(CFStringRef);
1151 CFStringRef UIname;
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;
1165 if (err != noErr) {
1166 jack_log("Input device does not have subdevices");
1167 captureDeviceIDArray.push_back(captureDeviceID);
1168 } else {
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;
1179 if (err != noErr) {
1180 jack_log("Output device does not have subdevices");
1181 playbackDeviceIDArray.push_back(playbackDeviceID);
1182 } else {
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;
1196 UInt32 outSize;
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");
1219 } else {
1220 // Check clock domain
1221 osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain);
1222 if (osErr != 0) {
1223 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
1224 printError(osErr);
1225 } else {
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");
1239 } else {
1240 // Check clock domain
1241 osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain);
1242 if (osErr != 0) {
1243 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
1244 printError(osErr);
1245 } else {
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");
1279 printError(osErr);
1280 return osErr;
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");
1295 printError(osErr);
1296 return osErr;
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
1315 int value = 1;
1316 CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
1318 SInt32 system;
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....");
1326 } else {
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]);
1342 if (UID) {
1343 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1344 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
1345 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
1346 //CFRelease(UID);
1347 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
1351 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
1352 CFStringRef UID = GetDeviceName(playbackDeviceID[i]);
1353 if (UID) {
1354 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1355 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
1356 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
1357 //CFRelease(UID);
1358 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
1362 // add sub-device clock array for the aggregate device to the dictionary
1363 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock);
1364 } else {
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]);
1380 if (ref == NULL) {
1381 return -1;
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]);
1391 if (ref == NULL) {
1392 return -1;
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;
1407 UInt32 outDataSize;
1409 osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
1410 if (osErr != noErr) {
1411 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyDataSize error");
1412 printError(osErr);
1413 goto error;
1416 osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice);
1417 if (osErr != noErr) {
1418 jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyData error");
1419 printError(osErr);
1420 goto 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");
1438 printError(osErr);
1439 goto 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");
1458 printError(osErr);
1459 goto 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");
1476 printError(osErr);
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");
1488 printError(osErr);
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");
1497 printError(osErr);
1500 } else {
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);
1508 //----------
1509 // Clean up
1510 //----------
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);
1533 return noErr;
1535 error:
1536 DestroyAggregateDevice();
1537 return -1;
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);
1548 if (err != noErr) {
1549 jack_log("Device does not have subdevices");
1550 return false;
1551 } else {
1552 int num_devices = outSize / sizeof(AudioObjectID);
1553 jack_log("Device does has %d subdevices", num_devices);
1554 return true;
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()
1571 #ifdef JACK_MONITOR
1572 fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize);
1573 #endif
1574 AudioOutputUnitStop(fAUHAL);
1575 DisposeBuffers();
1576 CloseAUHAL();
1577 RemoveListeners();
1578 if (fPluginID > 0) {
1579 DestroyAggregateDevice();
1581 return 0;
1584 int JackCoreAudioAdapter::SetSampleRate(jack_nframes_t sample_rate)
1586 JackAudioAdapterInterface::SetHostSampleRate(sample_rate);
1587 Close();
1588 return Open();
1591 int JackCoreAudioAdapter::SetBufferSize(jack_nframes_t buffer_size)
1593 JackAudioAdapterInterface::SetHostBufferSize(buffer_size);
1594 Close();
1595 return Open();
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);
1605 if (err == noErr) {
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);
1613 if (err != noErr) {
1614 jack_error("GetStreamLatencies kAudioDevicePropertyStreams err = %d", err);
1615 return err;
1618 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, &outWritable);
1619 if (err != noErr) {
1620 jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err);
1621 return err;
1624 for (int i = 0; i < stream_count; i++) {
1625 err = AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &outSize2, &streamLatency);
1626 if (err != noErr) {
1627 jack_error("GetStreamLatencies kAudioStreamPropertyLatency err = %d", err);
1628 return err;
1630 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, bufferList);
1631 if (err != noErr) {
1632 jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err);
1633 return err;
1635 // Push 'channel' time the stream latency
1636 for (uint k = 0; k < bufferList->mBuffers[i].mNumberChannels; k++) {
1637 latencies.push_back(streamLatency);
1641 return err;
1644 int JackCoreAudioAdapter::GetLatency(int port_index, bool input)
1646 UInt32 size = sizeof(UInt32);
1647 UInt32 value1 = 0;
1648 UInt32 value2 = 0;
1650 OSStatus err = AudioDeviceGetProperty(fDeviceID, 0, input, kAudioDevicePropertyLatency, &size, &value1);
1651 if (err != noErr) {
1652 jack_log("AudioDeviceGetProperty kAudioDevicePropertyLatency error");
1654 err = AudioDeviceGetProperty(fDeviceID, 0, input, kAudioDevicePropertySafetyOffset, &size, &value2);
1655 if (err != noErr) {
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];
1668 } else {
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];
1678 } else {
1679 // No stream latency
1680 return GetLatency(port_index, false);
1684 } // namespace
1686 #ifdef __cplusplus
1687 extern "C"
1689 #endif
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);
1699 value.i = -1;
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");
1704 value.str[0] = 0;
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);
1708 value.ui = 44100U;
1709 jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL);
1711 value.ui = 512U;
1712 jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL);
1714 value.i = TRUE;
1715 jack_driver_descriptor_add_parameter(desc, &filler, "duplex", 'D', JackDriverParamBool, &value, NULL, "Provide both capture and playback ports", NULL);
1717 value.str[0] = 0;
1718 jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "CoreAudio device name", NULL);
1720 value.i = TRUE;
1721 jack_driver_descriptor_add_parameter(desc, &filler, "list-devices", 'l', JackDriverParamBool, &value, NULL, "Display available CoreAudio devices", NULL);
1723 value.ui = 0;
1724 jack_driver_descriptor_add_parameter(desc, &filler, "quality", 'q', JackDriverParamInt, &value, NULL, "Resample algorithm quality (0 - 4)", NULL);
1726 value.ui = 32768;
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)");
1729 value.i = FALSE;
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");
1732 return desc;
1736 #ifdef __cplusplus
1738 #endif