Sync JackCoreAudioAdapter code with JackCoreAudioDriver.
[jack2.git] / macosx / coreaudio / JackCoreAudioAdapter.cpp
blob2b3f4253973aa4e92ccd256099113cbf0741704a
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;
55 deviceNum = size / sizeof(AudioDeviceID);
56 AudioDeviceID devices[deviceNum];
58 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices);
59 if (err != noErr)
60 return err;
62 for (i = 0; i < deviceNum; i++) {
63 char device_name[256];
64 char internal_name[256];
66 size = sizeof(CFStringRef);
67 UIname = NULL;
68 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname);
69 if (err == noErr) {
70 CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding());
71 } else {
72 goto error;
75 size = 256;
76 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name);
77 if (err != noErr)
78 return err;
80 jack_info("Device name = \'%s\', internal_name = \'%s\' (to be used as -C, -P, or -d parameter)", device_name, internal_name);
83 return noErr;
85 error:
86 if (UIname != NULL)
87 CFRelease(UIname);
88 return err;
91 static void printError(OSStatus err)
93 switch (err) {
94 case kAudioHardwareNoError:
95 jack_log("error code : kAudioHardwareNoError");
96 break;
97 case kAudioConverterErr_FormatNotSupported:
98 jack_log("error code : kAudioConverterErr_FormatNotSupported");
99 break;
100 case kAudioConverterErr_OperationNotSupported:
101 jack_log("error code : kAudioConverterErr_OperationNotSupported");
102 break;
103 case kAudioConverterErr_PropertyNotSupported:
104 jack_log("error code : kAudioConverterErr_PropertyNotSupported");
105 break;
106 case kAudioConverterErr_InvalidInputSize:
107 jack_log("error code : kAudioConverterErr_InvalidInputSize");
108 break;
109 case kAudioConverterErr_InvalidOutputSize:
110 jack_log("error code : kAudioConverterErr_InvalidOutputSize");
111 break;
112 case kAudioConverterErr_UnspecifiedError:
113 jack_log("error code : kAudioConverterErr_UnspecifiedError");
114 break;
115 case kAudioConverterErr_BadPropertySizeError:
116 jack_log("error code : kAudioConverterErr_BadPropertySizeError");
117 break;
118 case kAudioConverterErr_RequiresPacketDescriptionsError:
119 jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError");
120 break;
121 case kAudioConverterErr_InputSampleRateOutOfRange:
122 jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange");
123 break;
124 case kAudioConverterErr_OutputSampleRateOutOfRange:
125 jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange");
126 break;
127 case kAudioHardwareNotRunningError:
128 jack_log("error code : kAudioHardwareNotRunningError");
129 break;
130 case kAudioHardwareUnknownPropertyError:
131 jack_log("error code : kAudioHardwareUnknownPropertyError");
132 break;
133 case kAudioHardwareIllegalOperationError:
134 jack_log("error code : kAudioHardwareIllegalOperationError");
135 break;
136 case kAudioHardwareBadDeviceError:
137 jack_log("error code : kAudioHardwareBadDeviceError");
138 break;
139 case kAudioHardwareBadStreamError:
140 jack_log("error code : kAudioHardwareBadStreamError");
141 break;
142 case kAudioDeviceUnsupportedFormatError:
143 jack_log("error code : kAudioDeviceUnsupportedFormatError");
144 break;
145 case kAudioDevicePermissionsError:
146 jack_log("error code : kAudioDevicePermissionsError");
147 break;
148 case kAudioHardwareBadObjectError:
149 jack_log("error code : kAudioHardwareBadObjectError");
150 break;
151 case kAudioHardwareUnsupportedOperationError:
152 jack_log("error code : kAudioHardwareUnsupportedOperationError");
153 break;
154 default:
155 jack_log("error code : unknown");
156 break;
160 OSStatus JackCoreAudioAdapter::SRNotificationCallback(AudioDeviceID inDevice,
161 UInt32 inChannel,
162 Boolean isInput,
163 AudioDevicePropertyID inPropertyID,
164 void* inClientData)
166 JackCoreAudioAdapter* driver = static_cast<JackCoreAudioAdapter*>(inClientData);
168 switch (inPropertyID) {
170 case kAudioDevicePropertyNominalSampleRate: {
171 jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate");
172 driver->fState = true;
173 break;
177 return noErr;
180 // A better implementation would try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code)
181 OSStatus JackCoreAudioAdapter::DeviceNotificationCallback(AudioDeviceID inDevice,
182 UInt32 inChannel,
183 Boolean isInput,
184 AudioDevicePropertyID inPropertyID,
185 void* inClientData)
188 switch (inPropertyID) {
190 case kAudioDeviceProcessorOverload: {
191 jack_error("JackCoreAudioAdapter::DeviceNotificationCallback kAudioDeviceProcessorOverload");
192 break;
195 case kAudioDevicePropertyStreamConfiguration: {
196 jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration");
197 return kAudioHardwareUnsupportedOperationError;
200 case kAudioDevicePropertyNominalSampleRate: {
201 jack_error("Cannot handle kAudioDevicePropertyNominalSampleRate");
202 return kAudioHardwareUnsupportedOperationError;
206 return noErr;
209 int JackCoreAudioAdapter::AddListeners()
211 OSStatus err = noErr;
213 // Add listeners
214 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this);
215 if (err != noErr) {
216 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload");
217 printError(err);
218 return -1;
221 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioHardwarePropertyDevices, DeviceNotificationCallback, this);
222 if (err != noErr) {
223 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioHardwarePropertyDevices");
224 printError(err);
225 return -1;
228 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback, this);
229 if (err != noErr) {
230 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate");
231 printError(err);
232 return -1;
235 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback, this);
236 if (err != noErr) {
237 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning");
238 printError(err);
239 return -1;
242 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this);
243 if (err != noErr) {
244 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration");
245 printError(err);
246 return -1;
249 err = AudioDeviceAddPropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this);
250 if (err != noErr) {
251 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration");
252 printError(err);
253 return -1;
256 return 0;
259 void JackCoreAudioAdapter::RemoveListeners()
261 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback);
262 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioHardwarePropertyDevices, DeviceNotificationCallback);
263 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback);
264 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback);
265 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback);
266 AudioDeviceRemovePropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback);
269 OSStatus JackCoreAudioAdapter::Render(void *inRefCon,
270 AudioUnitRenderActionFlags *ioActionFlags,
271 const AudioTimeStamp *inTimeStamp,
272 UInt32 inBusNumber,
273 UInt32 inNumberFrames,
274 AudioBufferList *ioData)
276 JackCoreAudioAdapter* adapter = static_cast<JackCoreAudioAdapter*>(inRefCon);
277 AudioUnitRender(adapter->fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, adapter->fInputData);
279 float* inputBuffer[adapter->fCaptureChannels];
280 float* outputBuffer[adapter->fPlaybackChannels];
282 for (int i = 0; i < adapter->fCaptureChannels; i++) {
283 inputBuffer[i] = (float*)adapter->fInputData->mBuffers[i].mData;
285 for (int i = 0; i < adapter->fPlaybackChannels; i++) {
286 outputBuffer[i] = (float*)ioData->mBuffers[i].mData;
289 adapter->PushAndPull((float**)inputBuffer, (float**)outputBuffer, inNumberFrames);
290 return noErr;
293 JackCoreAudioAdapter::JackCoreAudioAdapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params)
294 :JackAudioAdapterInterface(buffer_size, sample_rate), fInputData(0), fCapturing(false), fPlaying(false), fState(false)
296 const JSList* node;
297 const jack_driver_param_t* param;
298 int in_nChannels = 0;
299 int out_nChannels = 0;
300 char captureName[256];
301 char playbackName[256];
302 fCaptureUID[0] = 0;
303 fPlaybackUID[0] = 0;
304 fClockDriftCompensate = false;
306 // Default values
307 fCaptureChannels = -1;
308 fPlaybackChannels = -1;
310 SInt32 major;
311 SInt32 minor;
312 Gestalt(gestaltSystemVersionMajor, &major);
313 Gestalt(gestaltSystemVersionMinor, &minor);
315 // Starting with 10.6 systems, the HAL notification thread is created internally
316 if (major == 10 && minor >= 6) {
317 CFRunLoopRef theRunLoop = NULL;
318 AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
319 OSStatus theError = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
320 if (theError != noErr) {
321 jack_error("JackCoreAudioAdapter::Open kAudioHardwarePropertyRunLoop error");
325 for (node = params; node; node = jack_slist_next(node)) {
326 param = (const jack_driver_param_t*) node->data;
328 switch (param->character) {
330 case 'c' :
331 fCaptureChannels = fPlaybackChannels = param->value.ui;
332 break;
334 case 'i':
335 fCaptureChannels = param->value.ui;
336 break;
338 case 'o':
339 fPlaybackChannels = param->value.ui;
340 break;
342 case 'C':
343 fCapturing = true;
344 strncpy(fCaptureUID, param->value.str, 256);
345 break;
347 case 'P':
348 fPlaying = true;
349 strncpy(fPlaybackUID, param->value.str, 256);
350 break;
352 case 'd':
353 strncpy(fCaptureUID, param->value.str, 256);
354 strncpy(fPlaybackUID, param->value.str, 256);
355 break;
357 case 'D':
358 fCapturing = fPlaying = true;
359 break;
361 case 'r':
362 SetAdaptedSampleRate(param->value.ui);
363 break;
365 case 'p':
366 SetAdaptedBufferSize(param->value.ui);
367 break;
369 case 'l':
370 DisplayDeviceNames();
371 break;
373 case 'q':
374 fQuality = param->value.ui;
375 break;
377 case 'g':
378 fRingbufferCurSize = param->value.ui;
379 fAdaptative = false;
380 break;
382 case 's':
383 fClockDriftCompensate = true;
384 break;
388 /* duplex is the default */
389 if (!fCapturing && !fPlaying) {
390 fCapturing = true;
391 fPlaying = true;
394 if (SetupDevices(fCaptureUID, fPlaybackUID, captureName, playbackName, fAdaptedSampleRate) < 0)
395 throw -1;
397 if (SetupChannels(fCapturing, fPlaying, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, true) < 0)
398 throw -1;
400 if (SetupBufferSize(fAdaptedBufferSize) < 0)
401 throw -1;
403 if (SetupSampleRate(fAdaptedSampleRate) < 0)
404 throw -1;
406 if (OpenAUHAL(fCapturing, fPlaying, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, fAdaptedBufferSize, fAdaptedSampleRate) < 0)
407 throw -1;
409 if (fCapturing && fCaptureChannels > 0)
410 if (SetupBuffers(fCaptureChannels) < 0)
411 throw -1;
413 if (AddListeners() < 0)
414 throw -1;
417 OSStatus JackCoreAudioAdapter::GetDefaultDevice(AudioDeviceID* id)
419 OSStatus res;
420 UInt32 theSize = sizeof(UInt32);
421 AudioDeviceID inDefault;
422 AudioDeviceID outDefault;
424 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr)
425 return res;
427 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr)
428 return res;
430 jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault);
432 // Get the device only if default input and output are the same
433 if (inDefault == outDefault) {
434 *id = inDefault;
435 return noErr;
436 } else {
437 jack_error("Default input and output devices are not the same !!");
438 return kAudioHardwareBadDeviceError;
442 OSStatus JackCoreAudioAdapter::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput)
444 OSStatus err = noErr;
445 UInt32 outSize;
446 Boolean outWritable;
447 AudioBufferList* bufferList = 0;
449 channelCount = 0;
450 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable);
451 if (err == noErr) {
452 bufferList = (AudioBufferList*)malloc(outSize);
453 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList);
454 if (err == noErr) {
455 for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++)
456 channelCount += bufferList->mBuffers[i].mNumberChannels;
459 if (bufferList)
460 free(bufferList);
463 return err;
466 OSStatus JackCoreAudioAdapter::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id)
468 UInt32 size = sizeof(AudioValueTranslation);
469 CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding());
470 AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) };
472 if (inIUD == NULL) {
473 return kAudioHardwareUnspecifiedError;
474 } else {
475 OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value);
476 CFRelease(inIUD);
477 jack_log("GetDeviceIDFromUID %s %ld", UID, *id);
478 return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res;
482 OSStatus JackCoreAudioAdapter::GetDefaultInputDevice(AudioDeviceID* id)
484 OSStatus res;
485 UInt32 theSize = sizeof(UInt32);
486 AudioDeviceID inDefault;
488 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr)
489 return res;
491 jack_log("GetDefaultInputDevice: input = %ld ", inDefault);
492 *id = inDefault;
493 return noErr;
496 OSStatus JackCoreAudioAdapter::GetDefaultOutputDevice(AudioDeviceID* id)
498 OSStatus res;
499 UInt32 theSize = sizeof(UInt32);
500 AudioDeviceID outDefault;
502 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr)
503 return res;
505 jack_log("GetDefaultOutputDevice: output = %ld", outDefault);
506 *id = outDefault;
507 return noErr;
510 OSStatus JackCoreAudioAdapter::GetDeviceNameFromID(AudioDeviceID id, char* name)
512 UInt32 size = 256;
513 return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name);
516 // Setup
517 int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid,
518 const char* playback_driver_uid,
519 char* capture_driver_name,
520 char* playback_driver_name,
521 jack_nframes_t samplerate)
523 capture_driver_name[0] = 0;
524 playback_driver_name[0] = 0;
526 // Duplex
527 if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) {
529 // Same device for capture and playback...
530 if (strcmp(capture_driver_uid, playback_driver_uid) == 0) {
532 if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) {
533 jack_log("Will take default in/out");
534 if (GetDefaultDevice(&fDeviceID) != noErr) {
535 jack_error("Cannot open default device");
536 return -1;
539 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) {
540 jack_error("Cannot get device name from device ID");
541 return -1;
544 } else {
546 // Creates aggregate device
547 AudioDeviceID captureID, playbackID;
548 if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr)
549 return -1;
550 if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr)
551 return -1;
552 if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr)
553 return -1;
556 // Capture only
557 } else if (strcmp(capture_driver_uid, "") != 0) {
558 jack_log("JackCoreAudioAdapter::Open capture only");
559 if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) {
560 if (GetDefaultInputDevice(&fDeviceID) != noErr) {
561 jack_error("Cannot open default device");
562 return -1;
565 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) {
566 jack_error("Cannot get device name from device ID");
567 return -1;
570 // Playback only
571 } else if (strcmp(playback_driver_uid, "") != 0) {
572 jack_log("JackCoreAudioAdapter::Open playback only");
573 if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) {
574 if (GetDefaultOutputDevice(&fDeviceID) != noErr) {
575 jack_error("Cannot open default device");
576 return -1;
579 if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) {
580 jack_error("Cannot get device name from device ID");
581 return -1;
584 // Use default driver in duplex mode
585 } else {
586 jack_log("JackCoreAudioAdapter::Open default driver");
587 if (GetDefaultDevice(&fDeviceID) != noErr) {
588 jack_error("Cannot open default device");
589 return -1;
591 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) {
592 jack_error("Cannot get device name from device ID");
593 return -1;
597 return 0;
600 int JackCoreAudioAdapter::SetupChannels(bool capturing,
601 bool playing,
602 int& inchannels,
603 int& outchannels,
604 int& in_nChannels,
605 int& out_nChannels,
606 bool strict)
608 OSStatus err = noErr;
610 if (capturing) {
611 err = GetTotalChannels(fDeviceID, in_nChannels, true);
612 if (err != noErr) {
613 jack_error("Cannot get input channel number");
614 printError(err);
615 return -1;
616 } else {
617 jack_log("Max input channels : %d", in_nChannels);
621 if (playing) {
622 err = GetTotalChannels(fDeviceID, out_nChannels, false);
623 if (err != noErr) {
624 jack_error("Cannot get output channel number");
625 printError(err);
626 return -1;
627 } else {
628 jack_log("Max output channels : %d", out_nChannels);
632 if (inchannels > in_nChannels) {
633 jack_error("This device hasn't required input channels inchannels = %ld in_nChannels = %ld", inchannels, in_nChannels);
634 if (strict)
635 return -1;
638 if (outchannels > out_nChannels) {
639 jack_error("This device hasn't required output channels outchannels = %ld out_nChannels = %ld", outchannels, out_nChannels);
640 if (strict)
641 return -1;
644 if (inchannels == -1) {
645 jack_log("Setup max in channels = %ld", in_nChannels);
646 inchannels = in_nChannels;
649 if (outchannels == -1) {
650 jack_log("Setup max out channels = %ld", out_nChannels);
651 outchannels = out_nChannels;
654 return 0;
657 int JackCoreAudioAdapter::SetupBufferSize(jack_nframes_t buffer_size)
659 // Setting buffer size
660 UInt32 outSize = sizeof(UInt32);
661 OSStatus err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &buffer_size);
662 if (err != noErr) {
663 jack_error("Cannot set buffer size %ld", buffer_size);
664 printError(err);
665 return -1;
668 return 0;
671 int JackCoreAudioAdapter::SetupSampleRate(jack_nframes_t samplerate)
673 return SetupSampleRateAux(fDeviceID, samplerate);
676 int JackCoreAudioAdapter::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes_t samplerate)
678 OSStatus err = noErr;
679 UInt32 outSize;
680 Float64 sampleRate;
682 // Get sample rate
683 outSize = sizeof(Float64);
684 err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate);
685 if (err != noErr) {
686 jack_error("Cannot get current sample rate");
687 printError(err);
688 return -1;
691 // If needed, set new sample rate
692 if (samplerate != (jack_nframes_t)sampleRate) {
693 sampleRate = (Float64)samplerate;
695 // To get SR change notification
696 err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this);
697 if (err != noErr) {
698 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate");
699 printError(err);
700 return -1;
702 err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &sampleRate);
703 if (err != noErr) {
704 jack_error("Cannot set sample rate = %ld", samplerate);
705 printError(err);
706 return -1;
709 // Waiting for SR change notification
710 int count = 0;
711 while (!fState && count++ < WAIT_COUNTER) {
712 usleep(100000);
713 jack_log("Wait count = %d", count);
716 // Remove SR change notification
717 AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback);
720 return 0;
723 int JackCoreAudioAdapter::SetupBuffers(int inchannels)
725 jack_log("JackCoreAudioAdapter::SetupBuffers: input = %ld", inchannels);
727 // Prepare buffers
728 fInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer));
729 fInputData->mNumberBuffers = inchannels;
730 for (int i = 0; i < fCaptureChannels; i++) {
731 fInputData->mBuffers[i].mNumberChannels = 1;
732 fInputData->mBuffers[i].mDataByteSize = fAdaptedBufferSize * sizeof(float);
733 fInputData->mBuffers[i].mData = malloc(fAdaptedBufferSize * sizeof(float));
735 return 0;
738 void JackCoreAudioAdapter::DisposeBuffers()
740 if (fInputData) {
741 for (int i = 0; i < fCaptureChannels; i++)
742 free(fInputData->mBuffers[i].mData);
743 free(fInputData);
744 fInputData = 0;
748 int JackCoreAudioAdapter::OpenAUHAL(bool capturing,
749 bool playing,
750 int inchannels,
751 int outchannels,
752 int in_nChannels,
753 int out_nChannels,
754 jack_nframes_t buffer_size,
755 jack_nframes_t samplerate)
757 ComponentResult err1;
758 UInt32 enableIO;
759 AudioStreamBasicDescription srcFormat, dstFormat;
760 AudioDeviceID currAudioDeviceID;
761 UInt32 size;
763 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);
765 if (inchannels == 0 && outchannels == 0) {
766 jack_error("No input and output channels...");
767 return -1;
770 // AUHAL
771 ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
772 Component HALOutput = FindNextComponent(NULL, &cd);
774 err1 = OpenAComponent(HALOutput, &fAUHAL);
775 if (err1 != noErr) {
776 jack_error("Error calling OpenAComponent");
777 printError(err1);
778 goto error;
781 err1 = AudioUnitInitialize(fAUHAL);
782 if (err1 != noErr) {
783 jack_error("Cannot initialize AUHAL unit");
784 printError(err1);
785 goto error;
788 // Start I/O
789 if (capturing && inchannels > 0) {
790 enableIO = 1;
791 jack_log("Setup AUHAL input on");
792 } else {
793 enableIO = 0;
794 jack_log("Setup AUHAL input off");
797 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO));
798 if (err1 != noErr) {
799 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input");
800 printError(err1);
801 goto error;
804 if (playing && outchannels > 0) {
805 enableIO = 1;
806 jack_log("Setup AUHAL output on");
807 } else {
808 enableIO = 0;
809 jack_log("Setup AUHAL output off");
812 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
813 if (err1 != noErr) {
814 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output");
815 printError(err1);
816 goto error;
819 size = sizeof(AudioDeviceID);
820 err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size);
821 if (err1 != noErr) {
822 jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice");
823 printError(err1);
824 goto error;
825 } else {
826 jack_log("AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID);
829 // Setup up choosen device, in both input and output cases
830 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID));
831 if (err1 != noErr) {
832 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice");
833 printError(err1);
834 goto error;
837 // Set buffer size
838 if (capturing && inchannels > 0) {
839 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32));
840 if (err1 != noErr) {
841 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice");
842 printError(err1);
843 goto error;
847 if (playing && outchannels > 0) {
848 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32));
849 if (err1 != noErr) {
850 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice");
851 printError(err1);
852 goto error;
856 // Setup channel map
857 if (capturing && inchannels > 0 && inchannels < in_nChannels) {
858 SInt32 chanArr[in_nChannels];
859 for (int i = 0; i < in_nChannels; i++) {
860 chanArr[i] = -1;
862 for (int i = 0; i < inchannels; i++) {
863 chanArr[i] = i;
865 AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_nChannels);
866 if (err1 != noErr) {
867 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 1");
868 printError(err1);
869 goto error;
873 if (playing && outchannels > 0 && outchannels < out_nChannels) {
874 SInt32 chanArr[out_nChannels];
875 for (int i = 0; i < out_nChannels; i++) {
876 chanArr[i] = -1;
878 for (int i = 0; i < outchannels; i++) {
879 chanArr[i] = i;
881 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_nChannels);
882 if (err1 != noErr) {
883 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 0");
884 printError(err1);
885 goto error;
889 // Setup stream converters
890 if (capturing && inchannels > 0) {
892 size = sizeof(AudioStreamBasicDescription);
893 err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &srcFormat, &size);
894 if (err1 != noErr) {
895 jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input");
896 printError(err1);
897 goto error;
899 PrintStreamDesc(&srcFormat);
901 jack_log("Setup AUHAL input stream converter SR = %ld", samplerate);
902 srcFormat.mSampleRate = samplerate;
903 srcFormat.mFormatID = kAudioFormatLinearPCM;
904 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
905 srcFormat.mBytesPerPacket = sizeof(float);
906 srcFormat.mFramesPerPacket = 1;
907 srcFormat.mBytesPerFrame = sizeof(float);
908 srcFormat.mChannelsPerFrame = inchannels;
909 srcFormat.mBitsPerChannel = 32;
910 PrintStreamDesc(&srcFormat);
912 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription));
914 if (err1 != noErr) {
915 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input");
916 printError(err1);
917 goto error;
921 if (playing && outchannels > 0) {
923 size = sizeof(AudioStreamBasicDescription);
924 err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &dstFormat, &size);
925 if (err1 != noErr) {
926 jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output");
927 printError(err1);
928 goto error;
930 PrintStreamDesc(&dstFormat);
932 jack_log("Setup AUHAL output stream converter SR = %ld", samplerate);
933 dstFormat.mSampleRate = samplerate;
934 dstFormat.mFormatID = kAudioFormatLinearPCM;
935 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
936 dstFormat.mBytesPerPacket = sizeof(float);
937 dstFormat.mFramesPerPacket = 1;
938 dstFormat.mBytesPerFrame = sizeof(float);
939 dstFormat.mChannelsPerFrame = outchannels;
940 dstFormat.mBitsPerChannel = 32;
941 PrintStreamDesc(&dstFormat);
943 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription));
945 if (err1 != noErr) {
946 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output");
947 printError(err1);
948 goto error;
952 // Setup callbacks
953 if (inchannels > 0 && outchannels == 0) {
954 AURenderCallbackStruct output;
955 output.inputProc = Render;
956 output.inputProcRefCon = this;
957 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output));
958 if (err1 != noErr) {
959 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1");
960 printError(err1);
961 goto error;
963 } else {
964 AURenderCallbackStruct output;
965 output.inputProc = Render;
966 output.inputProcRefCon = this;
967 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output));
968 if (err1 != noErr) {
969 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0");
970 printError(err1);
971 goto error;
975 return 0;
977 error:
978 CloseAUHAL();
979 return -1;
982 OSStatus JackCoreAudioAdapter::DestroyAggregateDevice()
984 OSStatus osErr = noErr;
985 AudioObjectPropertyAddress pluginAOPA;
986 pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice;
987 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
988 pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
989 UInt32 outDataSize;
991 osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
992 if (osErr != noErr) {
993 jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error");
994 printError(osErr);
995 return osErr;
998 osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID);
999 if (osErr != noErr) {
1000 jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyData error");
1001 printError(osErr);
1002 return osErr;
1005 return noErr;
1008 static CFStringRef GetDeviceName(AudioDeviceID id)
1010 UInt32 size = sizeof(CFStringRef);
1011 CFStringRef UIname;
1012 OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname);
1013 return (err == noErr) ? UIname : NULL;
1016 OSStatus JackCoreAudioAdapter::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice)
1018 OSStatus err = noErr;
1019 AudioObjectID sub_device[32];
1020 UInt32 outSize = sizeof(sub_device);
1022 err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
1023 vector<AudioDeviceID> captureDeviceIDArray;
1025 if (err != noErr) {
1026 jack_log("Input device does not have subdevices");
1027 captureDeviceIDArray.push_back(captureDeviceID);
1028 } else {
1029 int num_devices = outSize / sizeof(AudioObjectID);
1030 jack_log("Input device has %d subdevices", num_devices);
1031 for (int i = 0; i < num_devices; i++) {
1032 captureDeviceIDArray.push_back(sub_device[i]);
1036 err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
1037 vector<AudioDeviceID> playbackDeviceIDArray;
1039 if (err != noErr) {
1040 jack_log("Output device does not have subdevices");
1041 playbackDeviceIDArray.push_back(playbackDeviceID);
1042 } else {
1043 int num_devices = outSize / sizeof(AudioObjectID);
1044 jack_log("Output device has %d subdevices", num_devices);
1045 for (int i = 0; i < num_devices; i++) {
1046 playbackDeviceIDArray.push_back(sub_device[i]);
1050 return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice);
1053 OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> captureDeviceID, vector<AudioDeviceID> playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice)
1055 OSStatus osErr = noErr;
1056 UInt32 outSize;
1057 Boolean outWritable;
1059 // Prepare sub-devices for clock drift compensation
1060 // Workaround for bug in the HAL : until 10.6.2
1061 AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1062 AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1063 UInt32 theQualifierDataSize = sizeof(AudioObjectID);
1064 AudioClassID inClass = kAudioSubDeviceClassID;
1065 void* theQualifierData = &inClass;
1066 UInt32 subDevicesNum = 0;
1068 //---------------------------------------------------------------------------
1069 // Setup SR of both devices otherwise creating AD may fail...
1070 //---------------------------------------------------------------------------
1071 UInt32 keptclockdomain = 0;
1072 UInt32 clockdomain = 0;
1073 outSize = sizeof(UInt32);
1074 bool need_clock_drift_compensation = false;
1076 for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
1077 if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) {
1078 jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of input device");
1079 } else {
1080 // Check clock domain
1081 osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain);
1082 if (osErr != 0) {
1083 jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
1084 printError(osErr);
1085 } else {
1086 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
1087 jack_log("JackCoreAudioDriver::CreateAggregateDevice : input clockdomain = %d", clockdomain);
1088 if (clockdomain != 0 && clockdomain != keptclockdomain) {
1089 jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...");
1090 need_clock_drift_compensation = true;
1096 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
1097 if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) {
1098 jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of output device");
1099 } else {
1100 // Check clock domain
1101 osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain);
1102 if (osErr != 0) {
1103 jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
1104 printError(osErr);
1105 } else {
1106 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
1107 jack_log("JackCoreAudioDriver::CreateAggregateDevice : output clockdomain = %d", clockdomain);
1108 if (clockdomain != 0 && clockdomain != keptclockdomain) {
1109 jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...");
1110 need_clock_drift_compensation = true;
1116 // If no valid clock domain was found, then assume we have to compensate...
1117 if (keptclockdomain == 0) {
1118 need_clock_drift_compensation = true;
1121 //---------------------------------------------------------------------------
1122 // Start to create a new aggregate by getting the base audio hardware plugin
1123 //---------------------------------------------------------------------------
1125 char device_name[256];
1126 for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
1127 GetDeviceNameFromID(captureDeviceID[i], device_name);
1128 jack_info("Separated input = '%s' ", device_name);
1131 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
1132 GetDeviceNameFromID(playbackDeviceID[i], device_name);
1133 jack_info("Separated output = '%s' ", device_name);
1136 osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable);
1137 if (osErr != noErr) {
1138 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error");
1139 printError(osErr);
1140 return osErr;
1143 AudioValueTranslation pluginAVT;
1145 CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio");
1147 pluginAVT.mInputData = &inBundleRef;
1148 pluginAVT.mInputDataSize = sizeof(inBundleRef);
1149 pluginAVT.mOutputData = &fPluginID;
1150 pluginAVT.mOutputDataSize = sizeof(fPluginID);
1152 osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT);
1153 if (osErr != noErr) {
1154 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error");
1155 printError(osErr);
1156 return osErr;
1159 //-------------------------------------------------
1160 // Create a CFDictionary for our aggregate device
1161 //-------------------------------------------------
1163 CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1165 CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex");
1166 CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex");
1168 // add the name of the device to the dictionary
1169 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef);
1171 // add our choice of UID for the aggregate device to the dictionary
1172 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef);
1174 // add a "private aggregate key" to the dictionary
1175 int value = 1;
1176 CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
1178 SInt32 system;
1179 Gestalt(gestaltSystemVersion, &system);
1181 jack_log("JackCoreAudioDriver::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054);
1183 // Starting with 10.5.4 systems, the AD can be internal... (better)
1184 if (system < 0x00001054) {
1185 jack_log("JackCoreAudioDriver::CreateAggregateDevice : public aggregate device....");
1186 } else {
1187 jack_log("JackCoreAudioDriver::CreateAggregateDevice : private aggregate device....");
1188 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef);
1191 // Prepare sub-devices for clock drift compensation
1192 CFMutableArrayRef subDevicesArrayClock = NULL;
1195 if (fClockDriftCompensate) {
1196 if (need_clock_drift_compensation) {
1197 jack_info("Clock drift compensation activated...");
1198 subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1200 for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
1201 CFStringRef UID = GetDeviceName(captureDeviceID[i]);
1202 if (UID) {
1203 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1204 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
1205 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
1206 //CFRelease(UID);
1207 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
1211 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
1212 CFStringRef UID = GetDeviceName(playbackDeviceID[i]);
1213 if (UID) {
1214 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1215 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
1216 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
1217 //CFRelease(UID);
1218 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
1222 // add sub-device clock array for the aggregate device to the dictionary
1223 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock);
1224 } else {
1225 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
1230 //-------------------------------------------------
1231 // Create a CFMutableArray for our sub-device list
1232 //-------------------------------------------------
1234 // we need to append the UID for each device to a CFMutableArray, so create one here
1235 CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1237 vector<CFStringRef> captureDeviceUID;
1238 for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
1239 CFStringRef ref = GetDeviceName(captureDeviceID[i]);
1240 if (ref == NULL)
1241 return -1;
1242 captureDeviceUID.push_back(ref);
1243 // input sub-devices in this example, so append the sub-device's UID to the CFArray
1244 CFArrayAppendValue(subDevicesArray, ref);
1247 vector<CFStringRef> playbackDeviceUID;
1248 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
1249 CFStringRef ref = GetDeviceName(playbackDeviceID[i]);
1250 if (ref == NULL)
1251 return -1;
1252 playbackDeviceUID.push_back(ref);
1253 // output sub-devices in this example, so append the sub-device's UID to the CFArray
1254 CFArrayAppendValue(subDevicesArray, ref);
1257 //-----------------------------------------------------------------------
1258 // Feed the dictionary to the plugin, to create a blank aggregate device
1259 //-----------------------------------------------------------------------
1261 AudioObjectPropertyAddress pluginAOPA;
1262 pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice;
1263 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
1264 pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
1265 UInt32 outDataSize;
1267 osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
1268 if (osErr != noErr) {
1269 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyDataSize error");
1270 printError(osErr);
1271 goto error;
1274 osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice);
1275 if (osErr != noErr) {
1276 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyData error");
1277 printError(osErr);
1278 goto error;
1281 // pause for a bit to make sure that everything completed correctly
1282 // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created
1283 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
1285 //-------------------------
1286 // Set the sub-device list
1287 //-------------------------
1289 pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList;
1290 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
1291 pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
1292 outDataSize = sizeof(CFMutableArrayRef);
1293 osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray);
1294 if (osErr != noErr) {
1295 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error");
1296 printError(osErr);
1297 goto error;
1300 // pause again to give the changes time to take effect
1301 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
1303 //-----------------------
1304 // Set the master device
1305 //-----------------------
1307 // set the master device manually (this is the device which will act as the master clock for the aggregate device)
1308 // pass in the UID of the device you want to use
1309 pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice;
1310 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
1311 pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
1312 outDataSize = sizeof(CFStringRef);
1313 osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); // First apture is master...
1314 if (osErr != noErr) {
1315 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for master device error");
1316 printError(osErr);
1317 goto error;
1320 // pause again to give the changes time to take effect
1321 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
1323 // Prepare sub-devices for clock drift compensation
1324 // Workaround for bug in the HAL : until 10.6.2
1326 if (fClockDriftCompensate) {
1327 if (need_clock_drift_compensation) {
1328 jack_info("Clock drift compensation activated...");
1330 // Get the property data size
1331 osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize);
1332 if (osErr != noErr) {
1333 jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error");
1334 printError(osErr);
1337 // Calculate the number of object IDs
1338 subDevicesNum = outSize / sizeof(AudioObjectID);
1339 jack_info("JackCoreAudioDriver::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum);
1340 AudioObjectID subDevices[subDevicesNum];
1341 outSize = sizeof(subDevices);
1343 osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices);
1344 if (osErr != noErr) {
1345 jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error");
1346 printError(osErr);
1349 // Set kAudioSubDevicePropertyDriftCompensation property...
1350 for (UInt32 index = 0; index < subDevicesNum; ++index) {
1351 UInt32 theDriftCompensationValue = 1;
1352 osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue);
1353 if (osErr != noErr) {
1354 jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error");
1355 printError(osErr);
1358 } else {
1359 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
1363 // pause again to give the changes time to take effect
1364 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
1366 //----------
1367 // Clean up
1368 //----------
1370 // release the private AD key
1371 CFRelease(AggregateDeviceNumberRef);
1373 // release the CF objects we have created - we don't need them any more
1374 CFRelease(aggDeviceDict);
1375 CFRelease(subDevicesArray);
1377 if (subDevicesArrayClock)
1378 CFRelease(subDevicesArrayClock);
1380 // release the device UID
1381 for (UInt32 i = 0; i < captureDeviceUID.size(); i++) {
1382 CFRelease(captureDeviceUID[i]);
1385 for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) {
1386 CFRelease(playbackDeviceUID[i]);
1389 jack_log("New aggregate device %ld", *outAggregateDevice);
1390 return noErr;
1392 error:
1393 DestroyAggregateDevice();
1394 return -1;
1398 bool JackCoreAudioAdapter::IsAggregateDevice(AudioDeviceID device)
1400 OSStatus err = noErr;
1401 AudioObjectID sub_device[32];
1402 UInt32 outSize = sizeof(sub_device);
1403 err = AudioDeviceGetProperty(device, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
1405 if (err != noErr) {
1406 jack_log("Device does not have subdevices");
1407 return false;
1408 } else {
1409 int num_devices = outSize / sizeof(AudioObjectID);
1410 jack_log("Device does has %d subdevices", num_devices);
1411 return true;
1415 void JackCoreAudioAdapter::CloseAUHAL()
1417 AudioUnitUninitialize(fAUHAL);
1418 CloseComponent(fAUHAL);
1421 int JackCoreAudioAdapter::Open()
1423 return (AudioOutputUnitStart(fAUHAL) != noErr) ? -1 : 0;
1426 int JackCoreAudioAdapter::Close()
1428 #ifdef JACK_MONITOR
1429 fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize);
1430 #endif
1431 AudioOutputUnitStop(fAUHAL);
1432 DisposeBuffers();
1433 CloseAUHAL();
1434 RemoveListeners();
1435 if (fPluginID > 0)
1436 DestroyAggregateDevice();
1437 return 0;
1440 int JackCoreAudioAdapter::SetSampleRate ( jack_nframes_t sample_rate ) {
1441 JackAudioAdapterInterface::SetHostSampleRate ( sample_rate );
1442 Close();
1443 return Open();
1446 int JackCoreAudioAdapter::SetBufferSize ( jack_nframes_t buffer_size ) {
1447 JackAudioAdapterInterface::SetHostBufferSize ( buffer_size );
1448 Close();
1449 return Open();
1452 } // namespace
1454 #ifdef __cplusplus
1455 extern "C"
1457 #endif
1459 SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor()
1461 jack_driver_desc_t *desc;
1462 unsigned int i;
1463 desc = (jack_driver_desc_t*)calloc(1, sizeof(jack_driver_desc_t));
1465 strcpy(desc->name, "audioadapter"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1
1466 strcpy(desc->desc, "netjack audio <==> net backend adapter"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1
1468 desc->nparams = 13;
1469 desc->params = (jack_driver_param_desc_t*)calloc(desc->nparams, sizeof(jack_driver_param_desc_t));
1471 i = 0;
1472 strcpy(desc->params[i].name, "channels");
1473 desc->params[i].character = 'c';
1474 desc->params[i].type = JackDriverParamInt;
1475 desc->params[i].value.ui = -1;
1476 strcpy(desc->params[i].short_desc, "Maximum number of channels");
1477 strcpy(desc->params[i].long_desc, "Maximum number of channels. If -1, max possible number of channels will be used");
1479 i++;
1480 strcpy(desc->params[i].name, "inchannels");
1481 desc->params[i].character = 'i';
1482 desc->params[i].type = JackDriverParamInt;
1483 desc->params[i].value.ui = -1;
1484 strcpy(desc->params[i].short_desc, "Maximum number of input channels");
1485 strcpy(desc->params[i].long_desc, "Maximum number of input channels. If -1, max possible number of input channels will be used");
1487 i++;
1488 strcpy(desc->params[i].name, "outchannels");
1489 desc->params[i].character = 'o';
1490 desc->params[i].type = JackDriverParamInt;
1491 desc->params[i].value.ui = -1;
1492 strcpy(desc->params[i].short_desc, "Maximum number of output channels");
1493 strcpy(desc->params[i].long_desc, "Maximum number of output channels. If -1, max possible number of output channels will be used");
1495 i++;
1496 strcpy(desc->params[i].name, "capture");
1497 desc->params[i].character = 'C';
1498 desc->params[i].type = JackDriverParamString;
1499 strcpy(desc->params[i].value.str, "will take default CoreAudio input device");
1500 strcpy(desc->params[i].short_desc, "Provide capture ports. Optionally set CoreAudio device name");
1501 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
1503 i++;
1504 strcpy(desc->params[i].name, "playback");
1505 desc->params[i].character = 'P';
1506 desc->params[i].type = JackDriverParamString;
1507 strcpy(desc->params[i].value.str, "will take default CoreAudio output device");
1508 strcpy(desc->params[i].short_desc, "Provide playback ports. Optionally set CoreAudio device name");
1509 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
1511 i++;
1512 strcpy(desc->params[i].name, "rate");
1513 desc->params[i].character = 'r';
1514 desc->params[i].type = JackDriverParamUInt;
1515 desc->params[i].value.ui = 44100U;
1516 strcpy(desc->params[i].short_desc, "Sample rate");
1517 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
1519 i++;
1520 strcpy(desc->params[i].name, "periodsize");
1521 desc->params[i].character = 'p';
1522 desc->params[i].type = JackDriverParamUInt;
1523 desc->params[i].value.ui = 512U;
1524 strcpy(desc->params[i].short_desc, "Period size");
1525 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
1527 i++;
1528 strcpy(desc->params[i].name, "duplex");
1529 desc->params[i].character = 'D';
1530 desc->params[i].type = JackDriverParamBool;
1531 desc->params[i].value.i = TRUE;
1532 strcpy(desc->params[i].short_desc, "Provide both capture and playback ports");
1533 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
1535 i++;
1536 strcpy(desc->params[i].name, "device");
1537 desc->params[i].character = 'd';
1538 desc->params[i].type = JackDriverParamString;
1539 strcpy(desc->params[i].value.str, "will take default CoreAudio device name");
1540 strcpy(desc->params[i].short_desc, "CoreAudio device name");
1541 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
1543 i++;
1544 strcpy(desc->params[i].name, "list-devices");
1545 desc->params[i].character = 'l';
1546 desc->params[i].type = JackDriverParamBool;
1547 desc->params[i].value.i = TRUE;
1548 strcpy(desc->params[i].short_desc, "Display available CoreAudio devices");
1549 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
1551 i++;
1552 strcpy(desc->params[i].name, "quality");
1553 desc->params[i].character = 'q';
1554 desc->params[i].type = JackDriverParamInt;
1555 desc->params[i].value.ui = 0;
1556 strcpy(desc->params[i].short_desc, "Resample algorithm quality (0 - 4)");
1557 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
1559 i++;
1560 strcpy(desc->params[i].name, "ring-buffer");
1561 desc->params[i].character = 'g';
1562 desc->params[i].type = JackDriverParamInt;
1563 desc->params[i].value.ui = 32768;
1564 strcpy(desc->params[i].short_desc, "Fixed ringbuffer size");
1565 strcpy(desc->params[i].long_desc, "Fixed ringbuffer size (if not set => automatic adaptative)");
1567 i++;
1568 strcpy(desc->params[i].name, "clock-drift");
1569 desc->params[i].character = 's';
1570 desc->params[i].type = JackDriverParamBool;
1571 desc->params[i].value.i = FALSE;
1572 strcpy(desc->params[i].short_desc, "Clock drift compensation");
1573 strcpy(desc->params[i].long_desc, "Whether to compensate clock drift in dynamically created aggregate device");
1575 return desc;
1579 #ifdef __cplusplus
1581 #endif