JackCoreAudioDriver.cpp 87.6 KB
Newer Older
sletz's avatar
sletz committed
1
/*
sletz's avatar
sletz committed
2
Copyright (C) 2004-2008 Grame
sletz's avatar
sletz committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackCoreAudioDriver.h"
#include "JackEngineControl.h"
#include "JackMachThread.h"
#include "JackGraphManager.h"
#include "JackError.h"
#include "JackClientControl.h"
#include "JackDriverLoader.h"
#include "JackGlobals.h"
28
#include "JackTools.h"
29
#include "JackCompilerDeps.h"
30
#include "JackLockedEngine.h"
sletz's avatar
sletz committed
31

32
#include <sstream>
sletz's avatar
sletz committed
33
#include <iostream>
sletz's avatar
sletz committed
34
#include <CoreServices/CoreServices.h>
35
#include <CoreFoundation/CFNumber.h>
sletz's avatar
sletz committed
36
37
38
39

namespace Jack
{

sletz's avatar
sletz committed
40
static void Print4CharCode(const char* msg, long c)
41
42
43
{
    UInt32 __4CC_number = (c);
    char __4CC_string[5];
sletz's avatar
sletz committed
44
    *((SInt32*)__4CC_string) = EndianU32_NtoB(__4CC_number);
45
46
47
48
    __4CC_string[4] = 0;
    jack_log("%s'%s'", (msg), __4CC_string);
}

49
50
51
52
53
54
55
56
57
58
59
static void PrintStreamDesc(AudioStreamBasicDescription *inDesc)
{
    jack_log("- - - - - - - - - - - - - - - - - - - -");
    jack_log("  Sample Rate:%f", inDesc->mSampleRate);
    jack_log("  Format ID:%.*s", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
    jack_log("  Format Flags:%lX", inDesc->mFormatFlags);
    jack_log("  Bytes per Packet:%ld", inDesc->mBytesPerPacket);
    jack_log("  Frames per Packet:%ld", inDesc->mFramesPerPacket);
    jack_log("  Bytes per Frame:%ld", inDesc->mBytesPerFrame);
    jack_log("  Channels per Frame:%ld", inDesc->mChannelsPerFrame);
    jack_log("  Bits per Channel:%ld", inDesc->mBitsPerChannel);
sletz's avatar
sletz committed
60
    jack_log("- - - - - - - - - - - - - - - - - - - -");
61
62
}

sletz's avatar
sletz committed
63
64
65
66
static void printError(OSStatus err)
{
    switch (err) {
        case kAudioHardwareNoError:
sletz's avatar
sletz committed
67
            jack_log("error code : kAudioHardwareNoError");
sletz's avatar
sletz committed
68
69
            break;
        case kAudioConverterErr_FormatNotSupported:
sletz's avatar
sletz committed
70
            jack_log("error code : kAudioConverterErr_FormatNotSupported");
sletz's avatar
sletz committed
71
72
            break;
        case kAudioConverterErr_OperationNotSupported:
sletz's avatar
sletz committed
73
            jack_log("error code : kAudioConverterErr_OperationNotSupported");
sletz's avatar
sletz committed
74
75
            break;
        case kAudioConverterErr_PropertyNotSupported:
sletz's avatar
sletz committed
76
            jack_log("error code : kAudioConverterErr_PropertyNotSupported");
sletz's avatar
sletz committed
77
78
            break;
        case kAudioConverterErr_InvalidInputSize:
sletz's avatar
sletz committed
79
            jack_log("error code : kAudioConverterErr_InvalidInputSize");
sletz's avatar
sletz committed
80
81
            break;
        case kAudioConverterErr_InvalidOutputSize:
sletz's avatar
sletz committed
82
            jack_log("error code : kAudioConverterErr_InvalidOutputSize");
sletz's avatar
sletz committed
83
84
            break;
        case kAudioConverterErr_UnspecifiedError:
sletz's avatar
sletz committed
85
            jack_log("error code : kAudioConverterErr_UnspecifiedError");
sletz's avatar
sletz committed
86
87
            break;
        case kAudioConverterErr_BadPropertySizeError:
sletz's avatar
sletz committed
88
            jack_log("error code : kAudioConverterErr_BadPropertySizeError");
sletz's avatar
sletz committed
89
90
            break;
        case kAudioConverterErr_RequiresPacketDescriptionsError:
sletz's avatar
sletz committed
91
            jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError");
sletz's avatar
sletz committed
92
93
            break;
        case kAudioConverterErr_InputSampleRateOutOfRange:
sletz's avatar
sletz committed
94
            jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange");
sletz's avatar
sletz committed
95
96
            break;
        case kAudioConverterErr_OutputSampleRateOutOfRange:
sletz's avatar
sletz committed
97
            jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange");
sletz's avatar
sletz committed
98
99
            break;
        case kAudioHardwareNotRunningError:
sletz's avatar
sletz committed
100
            jack_log("error code : kAudioHardwareNotRunningError");
sletz's avatar
sletz committed
101
102
            break;
        case kAudioHardwareUnknownPropertyError:
sletz's avatar
sletz committed
103
            jack_log("error code : kAudioHardwareUnknownPropertyError");
sletz's avatar
sletz committed
104
105
            break;
        case kAudioHardwareIllegalOperationError:
sletz's avatar
sletz committed
106
            jack_log("error code : kAudioHardwareIllegalOperationError");
sletz's avatar
sletz committed
107
108
            break;
        case kAudioHardwareBadDeviceError:
sletz's avatar
sletz committed
109
            jack_log("error code : kAudioHardwareBadDeviceError");
sletz's avatar
sletz committed
110
111
            break;
        case kAudioHardwareBadStreamError:
sletz's avatar
sletz committed
112
            jack_log("error code : kAudioHardwareBadStreamError");
sletz's avatar
sletz committed
113
114
            break;
        case kAudioDeviceUnsupportedFormatError:
sletz's avatar
sletz committed
115
            jack_log("error code : kAudioDeviceUnsupportedFormatError");
sletz's avatar
sletz committed
116
117
            break;
        case kAudioDevicePermissionsError:
sletz's avatar
sletz committed
118
            jack_log("error code : kAudioDevicePermissionsError");
119
            break;
sletz's avatar
sletz committed
120
        case kAudioHardwareBadObjectError:
sletz's avatar
sletz committed
121
            jack_log("error code : kAudioHardwareBadObjectError");
122
            break;
sletz's avatar
sletz committed
123
        case kAudioHardwareUnsupportedOperationError:
sletz's avatar
sletz committed
124
            jack_log("error code : kAudioHardwareUnsupportedOperationError");
sletz's avatar
sletz committed
125
126
            break;
        default:
127
            Print4CharCode("error code : unknown", err);
sletz's avatar
sletz committed
128
129
130
131
132
133
134
135
136
137
138
139
140
            break;
    }
}

static OSStatus DisplayDeviceNames()
{
    UInt32 size;
    Boolean isWritable;
    int i, deviceNum;
    OSStatus err;
    CFStringRef UIname;

    err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable);
141
    if (err != noErr) {
sletz's avatar
sletz committed
142
        return err;
143
    }
sletz's avatar
sletz committed
144
145
146
147
148

    deviceNum = size / sizeof(AudioDeviceID);
    AudioDeviceID devices[deviceNum];

    err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices);
149
    if (err != noErr) {
sletz's avatar
sletz committed
150
        return err;
151
    }
sletz's avatar
sletz committed
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

    for (i = 0; i < deviceNum; i++) {
        char device_name[256];
        char internal_name[256];

        size = sizeof(CFStringRef);
        UIname = NULL;
        err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname);
        if (err == noErr) {
            CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding());
        } else {
            goto error;
        }

        size = 256;
        err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name);
168
        if (err != noErr) {
sletz's avatar
sletz committed
169
            return err;
170
        }
sletz's avatar
sletz committed
171

172
        jack_info("Device name = \'%s\', internal name = \'%s\' (to be used as -C, -P, or -d parameter)", device_name, internal_name);
sletz's avatar
sletz committed
173
174
175
176
177
    }

    return noErr;

error:
178
    if (UIname != NULL) {
sletz's avatar
sletz committed
179
        CFRelease(UIname);
180
    }
sletz's avatar
sletz committed
181
182
183
    return err;
}

184
185
186
187
188
189
190
191
static CFStringRef GetDeviceName(AudioDeviceID id)
{
    UInt32 size = sizeof(CFStringRef);
    CFStringRef UIname;
    OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname);
    return (err == noErr) ? UIname : NULL;
}

sletz's avatar
sletz committed
192
static void ParseChannelList(const string& list, vector<int>& result)
193
194
195
196
197
198
199
200
201
202
203
204
205
{
    stringstream ss(list);
    string token;
	int chan;

    while (ss >> token) {
        istringstream ins;
        ins.str(token);
        ins >> chan;
        result.push_back(chan);
    }
}

sletz's avatar
sletz committed
206
207
208
209
210
211
212
OSStatus JackCoreAudioDriver::Render(void *inRefCon,
                                     AudioUnitRenderActionFlags *ioActionFlags,
                                     const AudioTimeStamp *inTimeStamp,
                                     UInt32 inBusNumber,
                                     UInt32 inNumberFrames,
                                     AudioBufferList *ioData)
{
sletz's avatar
sletz committed
213
214
215
216
    JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inRefCon;
    driver->fActionFags = ioActionFlags;
    driver->fCurrentTime = (AudioTimeStamp *)inTimeStamp;
    driver->fDriverOutputData = ioData;
sletz's avatar
sletz committed
217

218
    // Setup threaded based log function et get RT thread parameters once...
219
    if (set_threaded_log_function()) {
sletz's avatar
sletz committed
220

221
222
        jack_log("set_threaded_log_function");
        JackMachThread::GetParams(pthread_self(), &driver->fEngineControl->fPeriod, &driver->fEngineControl->fComputation, &driver->fEngineControl->fConstraint);
sletz's avatar
sletz committed
223

224
225
226
227
228
        if (driver->fComputationGrain > 0) {
            jack_log("JackCoreAudioDriver::Render : RT thread computation setup to %d percent of period", int(driver->fComputationGrain * 100));
            driver->fEngineControl->fComputation = driver->fEngineControl->fPeriod * driver->fComputationGrain;
        }
    }
229

230
231
    // Signal waiting start function...
    driver->fState = true;
232

sletz's avatar
sletz committed
233
    driver->CycleTakeBeginTime();
234

235
236
237
238
239
240
241
242
243
    if (driver->Process() < 0) {
        jack_error("Process error, stopping driver.");
        driver->NotifyFailure(JackBackendError, "Process error, stopping driver.");    // Message length limited to JACK_MESSAGE_SIZE
        driver->Stop();
        kill(JackTools::GetPID(), SIGINT);
        return kAudioHardwareUnsupportedOperationError;
    } else {
        return noErr;
    }
sletz's avatar
sletz committed
244
245
246
247
}

int JackCoreAudioDriver::Read()
{
248
    if (fCaptureChannels > 0)  { // Calling AudioUnitRender with no input returns a '????' error (callback setting issue ??), so hack to avoid it here...
249
        return (AudioUnitRender(fAUHAL, fActionFags, fCurrentTime, 1, fEngineControl->fBufferSize, fJackInputData) == noErr) ? 0 : -1;
250
251
252
    } else {
        return 0;
    }
sletz's avatar
sletz committed
253
254
255
256
257
258
}

int JackCoreAudioDriver::Write()
{
    for (int i = 0; i < fPlaybackChannels; i++) {
        if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) {
259
260
261
            jack_default_audio_sample_t* buffer = GetOutputBuffer(i);
            int size = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize;
            memcpy((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, buffer, size);
sletz's avatar
sletz committed
262
            // Monitor ports
263
            if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0) {
sletz's avatar
sletz committed
264
                memcpy(GetMonitorBuffer(i), buffer, size);
265
            }
sletz's avatar
sletz committed
266
        } else {
267
            memset((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, 0, sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize);
sletz's avatar
sletz committed
268
269
270
271
272
        }
    }
    return 0;
}

273
OSStatus JackCoreAudioDriver::SRNotificationCallback(AudioDeviceID inDevice,
274
275
276
277
                                                    UInt32 inChannel,
                                                    Boolean	isInput,
                                                    AudioDevicePropertyID inPropertyID,
                                                    void* inClientData)
278
{
sletz's avatar
sletz committed
279
280
281
282
283
    JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData;

    switch (inPropertyID) {

        case kAudioDevicePropertyNominalSampleRate: {
sletz's avatar
sletz committed
284
            jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate");
285
            // Check new sample rate
286
            Float64 tmp_sample_rate;
287
            UInt32 outSize =  sizeof(Float64);
288
            OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate);
289
290
291
292
            if (err != noErr) {
                jack_error("Cannot get current sample rate");
                printError(err);
            } else {
293
                jack_log("SRNotificationCallback : checked sample rate = %f", tmp_sample_rate);
294
            }
295
            driver->fState = true;
sletz's avatar
sletz committed
296
297
298
299
300
            break;
        }
    }

    return noErr;
301
}
302

303
304
305
306
307
308
309
OSStatus JackCoreAudioDriver::BSNotificationCallback(AudioDeviceID inDevice,
                                                     UInt32 inChannel,
                                                     Boolean	isInput,
                                                     AudioDevicePropertyID inPropertyID,
                                                     void* inClientData)
{
    JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData;
310

311
    switch (inPropertyID) {
312

313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
        case kAudioDevicePropertyBufferFrameSize: {
            jack_log("JackCoreAudioDriver::BSNotificationCallback kAudioDevicePropertyBufferFrameSize");
            // Check new buffer size
            UInt32 tmp_buffer_size;
            UInt32 outSize =  sizeof(UInt32);
            OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size);
            if (err != noErr) {
                jack_error("Cannot get current buffer size");
                printError(err);
            } else {
                jack_log("BSNotificationCallback : checked buffer size = %d", tmp_buffer_size);
            }
            driver->fState = true;
            break;
        }
    }
329

330
    return noErr;
331
}
332

sletz's avatar
sletz committed
333
// A better implementation would possibly try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code)
sletz's avatar
sletz committed
334
OSStatus JackCoreAudioDriver::DeviceNotificationCallback(AudioDeviceID inDevice,
335
336
337
338
                                                        UInt32 inChannel,
                                                        Boolean	isInput,
                                                        AudioDevicePropertyID inPropertyID,
                                                        void* inClientData)
sletz's avatar
sletz committed
339
340
{
    JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData;
sletz's avatar
sletz committed
341

sletz's avatar
sletz committed
342
    switch (inPropertyID) {
sletz's avatar
sletz committed
343

344
345
346
347
348
349
350
351
        case kAudioDevicePropertyDeviceIsRunning: {
            UInt32 isrunning = 0;
            UInt32 outsize = sizeof(UInt32);
            if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceIsRunning, &outsize, &isrunning) == noErr) {
                jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceIsRunning = %d", isrunning);
            }
            break;
        }
sletz's avatar
sletz committed
352

sletz's avatar
sletz committed
353
        case kAudioDeviceProcessorOverload: {
354
            jack_error("JackCoreAudioDriver::DeviceNotificationCallback kAudioDeviceProcessorOverload");
355
            jack_time_t cur_time = GetMicroSeconds();
sletz's avatar
sletz committed
356
            driver->NotifyXRun(cur_time, float(cur_time - driver->fBeginDateUst));   // Better this value than nothing...
sletz's avatar
sletz committed
357
            break;
358
        }
sletz's avatar
sletz committed
359

sletz's avatar
sletz committed
360
        case kAudioDevicePropertyStreamConfiguration: {
361
            jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration : server will quit...");
362
            driver->NotifyFailure(JackBackendError, "Another application has changed the device configuration.");   // Message length limited to JACK_MESSAGE_SIZE
363
            driver->CloseAUHAL();
364
            kill(JackTools::GetPID(), SIGINT);
365
            return kAudioHardwareUnsupportedOperationError;
366
        }
sletz's avatar
sletz committed
367

368
        case kAudioDevicePropertyNominalSampleRate: {
369
            Float64 sample_rate = 0;
370
            UInt32 outsize = sizeof(Float64);
371
            OSStatus err = AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outsize, &sample_rate);
372
            if (err != noErr) {
373
                return kAudioHardwareUnsupportedOperationError;
374
            }
375

376
377
378
            char device_name[256];
            const char* digidesign_name = "Digidesign";
            driver->GetDeviceNameFromID(driver->fDeviceID, device_name);
379

380
            if (sample_rate != driver->fEngineControl->fSampleRate) {
381

382
               // Digidesign hardware, so "special" code : change the SR again here
383
               if (strncmp(device_name, digidesign_name, 10) == 0) {
sletz's avatar
sletz committed
384

385
                    jack_log("Digidesign HW = %s", device_name);
sletz's avatar
sletz committed
386

387
                    // Set sample rate again...
388
389
                    sample_rate = driver->fEngineControl->fSampleRate;
                    err = AudioDeviceSetProperty(driver->fDeviceID, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outsize, &sample_rate);
390
                    if (err != noErr) {
391
                        jack_error("Cannot set sample rate = %f", sample_rate);
392
393
                        printError(err);
                    } else {
394
                        jack_log("Set sample rate = %f", sample_rate);
395
                    }
sletz's avatar
sletz committed
396

397
398
                    // Check new sample rate again...
                    outsize = sizeof(Float64);
399
                    err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outsize, &sample_rate);
400
401
402
403
                    if (err != noErr) {
                        jack_error("Cannot get current sample rate");
                        printError(err);
                    } else {
404
                        jack_log("Checked sample rate = %f", sample_rate);
405
406
                    }
                    return noErr;
sletz's avatar
sletz committed
407

408
409
410
411
412
413
                } else {
                    driver->NotifyFailure(JackBackendError, "Another application has changed the sample rate.");    // Message length limited to JACK_MESSAGE_SIZE
                    driver->CloseAUHAL();
                    kill(JackTools::GetPID(), SIGINT);
                    return kAudioHardwareUnsupportedOperationError;
                }
414
415
            }
        }
sletz's avatar
sletz committed
416

sletz's avatar
sletz committed
417
    }
sletz's avatar
sletz committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
    return noErr;
}

OSStatus JackCoreAudioDriver::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id)
{
    UInt32 size = sizeof(AudioValueTranslation);
    CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding());
    AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) };

    if (inIUD == NULL) {
        return kAudioHardwareUnspecifiedError;
    } else {
        OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value);
        CFRelease(inIUD);
432
        jack_log("GetDeviceIDFromUID %s %ld", UID, *id);
sletz's avatar
sletz committed
433
434
435
436
437
438
439
440
441
442
443
        return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res;
    }
}

OSStatus JackCoreAudioDriver::GetDefaultDevice(AudioDeviceID* id)
{
    OSStatus res;
    UInt32 theSize = sizeof(UInt32);
    AudioDeviceID inDefault;
    AudioDeviceID outDefault;

444
    if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) {
sletz's avatar
sletz committed
445
        return res;
446
    }
sletz's avatar
sletz committed
447

448
    if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) {
sletz's avatar
sletz committed
449
        return res;
450
    }
sletz's avatar
sletz committed
451

sletz's avatar
sletz committed
452
    jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault);
sletz's avatar
sletz committed
453

454
    // Get the device only if default input and output are the same
Stephane Letz's avatar
Stephane Letz committed
455
    if (inDefault != outDefault) {
sletz's avatar
sletz committed
456
457
        jack_error("Default input and output devices are not the same !!");
        return kAudioHardwareBadDeviceError;
Stephane Letz's avatar
Stephane Letz committed
458
459
460
461
462
463
    } else if (inDefault == 0) {
        jack_error("Default input and output devices are null !!");
        return kAudioHardwareBadDeviceError;
    } else {
        *id = inDefault;
        return noErr;
sletz's avatar
sletz committed
464
465
466
467
468
469
470
471
472
    }
}

OSStatus JackCoreAudioDriver::GetDefaultInputDevice(AudioDeviceID* id)
{
    OSStatus res;
    UInt32 theSize = sizeof(UInt32);
    AudioDeviceID inDefault;

473
    if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) {
sletz's avatar
sletz committed
474
        return res;
475
    }
sletz's avatar
sletz committed
476

sletz's avatar
sletz committed
477
478
479
480
    if (inDefault == 0) {
        jack_error("Error : input device is 0, please select a correct one !!");
        return -1;
    }
sletz's avatar
sletz committed
481
    jack_log("GetDefaultInputDevice: input = %ld ", inDefault);
sletz's avatar
sletz committed
482
483
484
485
486
487
488
489
490
491
    *id = inDefault;
    return noErr;
}

OSStatus JackCoreAudioDriver::GetDefaultOutputDevice(AudioDeviceID* id)
{
    OSStatus res;
    UInt32 theSize = sizeof(UInt32);
    AudioDeviceID outDefault;

492
    if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) {
sletz's avatar
sletz committed
493
        return res;
494
    }
sletz's avatar
sletz committed
495

sletz's avatar
sletz committed
496
497
498
499
    if (outDefault == 0) {
        jack_error("Error : output device is 0, please select a correct one !!");
        return -1;
    }
sletz's avatar
sletz committed
500
    jack_log("GetDefaultOutputDevice: output = %ld", outDefault);
sletz's avatar
sletz committed
501
502
503
504
505
506
507
508
509
510
    *id = outDefault;
    return noErr;
}

OSStatus JackCoreAudioDriver::GetDeviceNameFromID(AudioDeviceID id, char* name)
{
    UInt32 size = 256;
    return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name);
}

511
OSStatus JackCoreAudioDriver::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput)
sletz's avatar
sletz committed
512
{
513
    OSStatus err = noErr;
sletz's avatar
sletz committed
514
515
    UInt32	outSize;
    Boolean	outWritable;
sletz's avatar
sletz committed
516

517
    channelCount = 0;
sletz's avatar
sletz committed
518
519
    err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable);
    if (err == noErr) {
520
        AudioBufferList bufferList[outSize];
sletz's avatar
sletz committed
521
522
        err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList);
        if (err == noErr) {
523
            for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++) {
524
                channelCount += bufferList->mBuffers[i].mNumberChannels;
525
            }
sletz's avatar
sletz committed
526
527
528
529
530
        }
    }
    return err;
}

sletz's avatar
sletz committed
531
JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table)
sletz's avatar
sletz committed
532
533
534
535
536
        : JackAudioDriver(name, alias, engine, table),
        fJackInputData(NULL),
        fDriverOutputData(NULL),
        fPluginID(0),
        fState(false),
537
538
        fHogged(false),
        fIOUsage(1.f),
nedko's avatar
nedko committed
539
540
        fComputationGrain(-1.f),
        fClockDriftCompensate(false)
sletz's avatar
Cleanup    
sletz committed
541
{}
sletz's avatar
sletz committed
542
543

JackCoreAudioDriver::~JackCoreAudioDriver()
sletz's avatar
Cleanup    
sletz committed
544
{}
545

sletz's avatar
sletz committed
546
OSStatus JackCoreAudioDriver::DestroyAggregateDevice()
547
548
549
550
551
552
553
{
    OSStatus osErr = noErr;
    AudioObjectPropertyAddress pluginAOPA;
    pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice;
    pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
    pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
    UInt32 outDataSize;
sletz's avatar
sletz committed
554

555
    if (fPluginID > 0) {
sletz's avatar
sletz committed
556

557
558
559
560
561
562
        osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
        if (osErr != noErr) {
            jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error");
            printError(osErr);
            return osErr;
        }
sletz's avatar
sletz committed
563

564
565
566
567
568
569
        osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID);
        if (osErr != noErr) {
            jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyData error");
            printError(osErr);
            return osErr;
        }
sletz's avatar
sletz committed
570

571
    }
sletz's avatar
sletz committed
572

573
574
    return noErr;
}
sletz's avatar
sletz committed
575
576

OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice)
577
{
578
579
580
    OSStatus err = noErr;
    AudioObjectID sub_device[32];
    UInt32 outSize = sizeof(sub_device);
sletz's avatar
sletz committed
581

582
583
    err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
    vector<AudioDeviceID> captureDeviceIDArray;
sletz's avatar
sletz committed
584

585
586
587
588
589
590
591
592
593
594
    if (err != noErr) {
        jack_log("Input device does not have subdevices");
        captureDeviceIDArray.push_back(captureDeviceID);
    } else {
        int num_devices = outSize / sizeof(AudioObjectID);
        jack_log("Input device has %d subdevices", num_devices);
        for (int i = 0; i < num_devices; i++) {
            captureDeviceIDArray.push_back(sub_device[i]);
        }
    }
sletz's avatar
sletz committed
595
596

    err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
597
    vector<AudioDeviceID> playbackDeviceIDArray;
sletz's avatar
sletz committed
598

599
600
601
602
603
604
605
606
607
    if (err != noErr) {
        jack_log("Output device does not have subdevices");
        playbackDeviceIDArray.push_back(playbackDeviceID);
    } else {
        int num_devices = outSize / sizeof(AudioObjectID);
        jack_log("Output device has %d subdevices", num_devices);
        for (int i = 0; i < num_devices; i++) {
            playbackDeviceIDArray.push_back(sub_device[i]);
        }
608
    }
sletz's avatar
sletz committed
609

610
611
612
    return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice);
}

sletz's avatar
sletz committed
613
OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> captureDeviceID, vector<AudioDeviceID> playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice)
614
615
616
617
{
    OSStatus osErr = noErr;
    UInt32 outSize;
    Boolean outWritable;
sletz's avatar
sletz committed
618

sletz's avatar
sletz committed
619
    // Prepare sub-devices for clock drift compensation
620
621
622
623
624
    // Workaround for bug in the HAL : until 10.6.2
    AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
    AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
    UInt32 theQualifierDataSize = sizeof(AudioObjectID);
    AudioClassID inClass = kAudioSubDeviceClassID;
sletz's avatar
sletz committed
625
    void* theQualifierData = &inClass;
626
    UInt32 subDevicesNum = 0;
sletz's avatar
sletz committed
627

628
629
630
    //---------------------------------------------------------------------------
    // Setup SR of both devices otherwise creating AD may fail...
    //---------------------------------------------------------------------------
631
632
633
    UInt32 keptclockdomain = 0;
    UInt32 clockdomain = 0;
    outSize = sizeof(UInt32);
634
    bool need_clock_drift_compensation = false;
sletz's avatar
sletz committed
635

636
637
638
    for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
        if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) {
            jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of input device");
639
640
        } else  {
            // Check clock domain
sletz's avatar
sletz committed
641
            osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain);
642
643
644
645
            if (osErr != 0) {
                jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
                printError(osErr);
            } else {
sletz's avatar
sletz committed
646
                keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
647
648
649
                jack_log("JackCoreAudioDriver::CreateAggregateDevice : input clockdomain = %d", clockdomain);
                if (clockdomain != 0 && clockdomain != keptclockdomain) {
                    jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...");
650
                    need_clock_drift_compensation = true;
651
652
                }
            }
653
        }
654
    }
sletz's avatar
sletz committed
655

656
657
658
    for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
        if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) {
            jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of output device");
659
660
        } else {
            // Check clock domain
sletz's avatar
sletz committed
661
            osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain);
662
663
664
665
            if (osErr != 0) {
                jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
                printError(osErr);
            } else {
sletz's avatar
sletz committed
666
                keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
667
668
669
                jack_log("JackCoreAudioDriver::CreateAggregateDevice : output clockdomain = %d", clockdomain);
                if (clockdomain != 0 && clockdomain != keptclockdomain) {
                    jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...");
670
                    need_clock_drift_compensation = true;
671
672
                }
            }
673
        }
674
    }
sletz's avatar
sletz committed
675

676
677
678
679
    // If no valid clock domain was found, then assume we have to compensate...
    if (keptclockdomain == 0) {
        need_clock_drift_compensation = true;
    }
680

681
    //---------------------------------------------------------------------------
682
    // Start to create a new aggregate by getting the base audio hardware plugin
sletz's avatar
sletz committed
683
    //---------------------------------------------------------------------------
sletz's avatar
sletz committed
684

685
686
687
688
689
    char device_name[256];
    for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
        GetDeviceNameFromID(captureDeviceID[i], device_name);
        jack_info("Separated input = '%s' ", device_name);
    }
sletz's avatar
sletz committed
690

691
692
693
694
    for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
        GetDeviceNameFromID(playbackDeviceID[i], device_name);
        jack_info("Separated output = '%s' ", device_name);
    }
sletz's avatar
sletz committed
695

696
    osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable);
697
698
699
    if (osErr != noErr) {
        jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error");
        printError(osErr);
700
        return osErr;
701
    }
702
703
704
705

    AudioValueTranslation pluginAVT;

    CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio");
sletz's avatar
sletz committed
706

707
708
    pluginAVT.mInputData = &inBundleRef;
    pluginAVT.mInputDataSize = sizeof(inBundleRef);
709
710
    pluginAVT.mOutputData = &fPluginID;
    pluginAVT.mOutputDataSize = sizeof(fPluginID);
711
712

    osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT);
713
714
715
    if (osErr != noErr) {
        jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error");
        printError(osErr);
716
        return osErr;
717
    }
718

719
    //-------------------------------------------------
720
    // Create a CFDictionary for our aggregate device
721
    //-------------------------------------------------
722
723
724
725
726

    CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex");
    CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex");
sletz's avatar
sletz committed
727

728
729
730
731
732
    // add the name of the device to the dictionary
    CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef);

    // add our choice of UID for the aggregate device to the dictionary
    CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef);
sletz's avatar
sletz committed
733

734
735
736
    // add a "private aggregate key" to the dictionary
    int value = 1;
    CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
sletz's avatar
sletz committed
737

738
739
    SInt32 system;
    Gestalt(gestaltSystemVersion, &system);
sletz's avatar
sletz committed
740

741
    jack_log("JackCoreAudioDriver::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054);
sletz's avatar
sletz committed
742

743
    // Starting with 10.5.4 systems, the AD can be internal... (better)
744
745
746
747
    if (system < 0x00001054) {
        jack_log("JackCoreAudioDriver::CreateAggregateDevice : public aggregate device....");
    } else {
        jack_log("JackCoreAudioDriver::CreateAggregateDevice : private aggregate device....");
748
749
        CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef);
    }
sletz's avatar
sletz committed
750

751
    // Prepare sub-devices for clock drift compensation
752
    CFMutableArrayRef subDevicesArrayClock = NULL;
sletz's avatar
sletz committed
753

754
755
756
757
    /*
    if (fClockDriftCompensate) {
        if (need_clock_drift_compensation) {
            jack_info("Clock drift compensation activated...");
758
            subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
sletz's avatar
sletz committed
759

760
            for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
761
762
763
764
765
766
767
                CFStringRef UID = GetDeviceName(captureDeviceID[i]);
                if (UID) {
                    CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
                    CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
                    CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
                    //CFRelease(UID);
                    CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
768
769
                }
            }
sletz's avatar
sletz committed
770

771
772
773
774
775
776
777
778
            for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
                CFStringRef UID = GetDeviceName(playbackDeviceID[i]);
                if (UID) {
                    CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
                    CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
                    CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
                    //CFRelease(UID);
                    CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
779
780
                }
            }
sletz's avatar
sletz committed
781

782
783
            // add sub-device clock array for the aggregate device to the dictionary
            CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock);
784
        } else {
785
            jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
786
787
        }
    }
788
    */
sletz's avatar
sletz committed
789

790
791
792
    //-------------------------------------------------
    // Create a CFMutableArray for our sub-device list
    //-------------------------------------------------
sletz's avatar
sletz committed
793

794
795
    // we need to append the UID for each device to a CFMutableArray, so create one here
    CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
sletz's avatar
sletz committed
796

797
798
799
    vector<CFStringRef> captureDeviceUID;
    for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
        CFStringRef ref = GetDeviceName(captureDeviceID[i]);
800
        if (ref == NULL) {
801
            return -1;
802
        }
803
804
805
806
        captureDeviceUID.push_back(ref);
        // input sub-devices in this example, so append the sub-device's UID to the CFArray
        CFArrayAppendValue(subDevicesArray, ref);
   }
sletz's avatar
sletz committed
807

808
809
810
    vector<CFStringRef> playbackDeviceUID;
    for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
        CFStringRef ref = GetDeviceName(playbackDeviceID[i]);
811
        if (ref == NULL) {
812
            return -1;
813
        }
814
815
816
817
        playbackDeviceUID.push_back(ref);
        // output sub-devices in this example, so append the sub-device's UID to the CFArray
        CFArrayAppendValue(subDevicesArray, ref);
    }
sletz's avatar
sletz committed
818

819
820
821
    //-----------------------------------------------------------------------
    // Feed the dictionary to the plugin, to create a blank aggregate device
    //-----------------------------------------------------------------------
sletz's avatar
sletz committed
822

823
824
825
826
827
828
    AudioObjectPropertyAddress pluginAOPA;
    pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice;
    pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
    pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
    UInt32 outDataSize;

829
    osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
830
831
832
    if (osErr != noErr) {
        jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyDataSize error");
        printError(osErr);
833
        goto error;
834
    }
sletz's avatar
sletz committed
835

836
    osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice);
837
838
839
    if (osErr != noErr) {
        jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyData error");
        printError(osErr);
840
        goto error;
841
    }
842
843
844
845
846
847
848
849
850
851
852
853
854
855

    // pause for a bit to make sure that everything completed correctly
    // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);

    //-------------------------
    // Set the sub-device list
    //-------------------------

    pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList;
    pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
    pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
    outDataSize = sizeof(CFMutableArrayRef);
    osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray);
856
857
858
    if (osErr != noErr) {
        jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error");
        printError(osErr);
859
        goto error;
860
    }
sletz's avatar
sletz committed
861

862
863
    // pause again to give the changes time to take effect
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
sletz's avatar
sletz committed
864

865
866
867
868
869
870
871
872
873
874
    //-----------------------
    // Set the master device
    //-----------------------

    // set the master device manually (this is the device which will act as the master clock for the aggregate device)
    // pass in the UID of the device you want to use
    pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice;
    pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
    pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
    outDataSize = sizeof(CFStringRef);
875
    osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]);  // First apture is master...
876
877
878
    if (osErr != noErr) {
        jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for master device error");
        printError(osErr);
879
        goto error;
880
    }
sletz's avatar
sletz committed
881

882
883
    // pause again to give the changes time to take effect
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
sletz's avatar
sletz committed
884

885
886
    // Prepare sub-devices for clock drift compensation
    // Workaround for bug in the HAL : until 10.6.2
sletz's avatar
sletz committed
887

888
889
890
    if (fClockDriftCompensate) {
        if (need_clock_drift_compensation) {
            jack_info("Clock drift compensation activated...");
sletz's avatar
sletz committed
891

892
            // Get the property data size
sletz's avatar
sletz committed
893
            osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize);
894
895
896
897
            if (osErr != noErr) {
                jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error");
                printError(osErr);
            }
sletz's avatar
sletz committed
898

899
            //	Calculate the number of object IDs
sletz's avatar
sletz committed
900
            subDevicesNum = outSize / sizeof(AudioObjectID);
901
902
            jack_info("JackCoreAudioDriver::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum);
            AudioObjectID subDevices[subDevicesNum];
sletz's avatar
sletz committed
903
            outSize = sizeof(subDevices);
sletz's avatar
sletz committed
904

sletz's avatar
sletz committed
905
            osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices);
906
907
908
909
            if (osErr != noErr) {
                jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error");
                printError(osErr);
            }
sletz's avatar
sletz committed
910

911
912
913
914
915
916
917
918
919
920
            // Set kAudioSubDevicePropertyDriftCompensation property...
            for (UInt32 index = 0; index < subDevicesNum; ++index) {
                UInt32 theDriftCompensationValue = 1;
                osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue);
                if (osErr != noErr) {
                    jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error");
                    printError(osErr);
                }
            }
        } else {
921
            jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
922
        }
sletz's avatar
sletz committed
923
924
    }

925
926
    // pause again to give the changes time to take effect
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
sletz's avatar
sletz committed
927

928
929
930
    //----------
    // Clean up
    //----------
sletz's avatar
sletz committed
931

932
    // release the private AD key
933
    CFRelease(AggregateDeviceNumberRef);