add a raw CoreMidi data debug mode
[ardour.git] / libs / backends / coreaudio / coreaudio_pcmio_aggregate.cc
1 /*
2  * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
3  * Copyright (C) 2004-2008 Grame
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include <vector>
21
22 void
23 CoreAudioPCM::destroy_aggregate_device ()
24 {
25         if (_aggregate_plugin_id == 0) {
26                 return;
27         }
28
29         OSStatus err;
30
31         AudioObjectPropertyAddress property_address;
32         property_address.mSelector = kAudioPlugInDestroyAggregateDevice;
33         property_address.mScope = kAudioObjectPropertyScopeGlobal;
34         property_address.mElement = kAudioObjectPropertyElementMaster;
35         UInt32 outDataSize = 0;
36
37         err = AudioObjectGetPropertyDataSize(_aggregate_plugin_id, &property_address, 0, NULL, &outDataSize);
38         if (err != noErr) {
39                 fprintf(stderr, "DestroyAggregateDevice : AudioObjectGetPropertyDataSize error\n");
40                 return;
41         }
42
43         err = AudioObjectGetPropertyData(_aggregate_plugin_id, &property_address, 0, NULL, &outDataSize, &_aggregate_device_id);
44         if (err != noErr) {
45                 fprintf(stderr, "DestroyAggregateDevice : AudioObjectGetPropertyData error\n");
46                 return;
47         }
48 #ifndef NDEBUG
49         printf("DestroyAggregateDevice : OK (plugin: %u device:%u)\n",
50                         (unsigned int)_aggregate_plugin_id,
51                         (unsigned int)_aggregate_device_id);
52 #endif
53 }
54
55 int
56 CoreAudioPCM::create_aggregate_device (
57                 AudioDeviceID input_device_id,
58                 AudioDeviceID output_device_id,
59                 uint32_t sample_rate,
60                 AudioDeviceID* created_device)
61 {
62         OSStatus err;
63         AudioObjectID sub_device[32];
64         UInt32 size = sizeof(sub_device);
65
66         /* look up sub-devices */
67         err = GetPropertyWrapper (input_device_id, 0, 0, kAudioAggregateDevicePropertyActiveSubDeviceList, &size, sub_device);
68         std::vector<AudioDeviceID> input_device_ids;
69
70         if (err != noErr) {
71                 input_device_ids.push_back(input_device_id);
72         } else {
73                 uint32_t num_devices = size / sizeof(AudioObjectID);
74                 for (uint32_t i = 0; i < num_devices; ++i) {
75                         input_device_ids.push_back(sub_device[i]);
76                 }
77         }
78
79         size = sizeof(sub_device);
80         err = GetPropertyWrapper (output_device_id, 0, 0, kAudioAggregateDevicePropertyActiveSubDeviceList, &size, sub_device);
81         std::vector<AudioDeviceID> output_device_ids;
82
83         if (err != noErr) {
84                 output_device_ids.push_back(output_device_id);
85         } else {
86                 uint32_t num_devices = size / sizeof(AudioObjectID);
87                 for (uint32_t i = 0; i < num_devices; ++i) {
88                         output_device_ids.push_back(sub_device[i]);
89                 }
90         }
91
92         //---------------------------------------------------------------------------
93         // Setup SR of both devices otherwise creating AD may fail...
94         //---------------------------------------------------------------------------
95         UInt32 keptclockdomain = 0;
96         UInt32 clockdomain = 0;
97         size = sizeof(UInt32);
98         bool need_clock_drift_compensation = false;
99
100         for (size_t i = 0; i < input_device_ids.size(); ++i) {
101                 set_device_sample_rate_id(input_device_ids[i], sample_rate, true);
102
103                 // Check clock domain
104                 err = GetPropertyWrapper (input_device_ids[i], 0, 0, kAudioDevicePropertyClockDomain, &size, &clockdomain);
105                 if (err == noErr) {
106                         keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
107                         if (clockdomain != 0 && clockdomain != keptclockdomain) {
108 #ifndef NDEBUG
109                                 printf("AggregateDevice: devices do not share the same clock.\n");
110 #endif
111                                 need_clock_drift_compensation = true;
112                         }
113                 }
114         }
115
116         for (UInt32 i = 0; i < output_device_ids.size(); i++) {
117                 set_device_sample_rate_id(output_device_ids[i], sample_rate, true);
118
119                 // Check clock domain
120                 err = GetPropertyWrapper (output_device_ids[i], 0, 0, kAudioDevicePropertyClockDomain, &size, &clockdomain);
121                 if (err == noErr) {
122                         keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
123                         if (clockdomain != 0 && clockdomain != keptclockdomain) {
124 #ifndef NDEBUG
125                                 printf("AggregateDevice: devices do not share the same clock.\n");
126 #endif
127                                 need_clock_drift_compensation = true;
128                         }
129                 }
130         }
131
132         // If no valid clock domain was found, then assume we have to compensate...
133         if (keptclockdomain == 0) {
134                 need_clock_drift_compensation = true;
135         }
136
137         //---------------------------------------------------------------------------
138         // Start to create a new aggregate by getting the base audio hardware plugin
139         //---------------------------------------------------------------------------
140
141 #ifndef NDEBUG
142         char device_name[256];
143         for (size_t i = 0; i < input_device_ids.size(); ++i) {
144                 GetDeviceNameFromID(input_device_ids[i], device_name);
145                 printf("Separated input = '%s'\n", device_name);
146         }
147
148         for (size_t i = 0; i < output_device_ids.size(); ++i) {
149                 GetDeviceNameFromID(output_device_ids[i], device_name);
150                 printf("Separated output = '%s'\n", device_name);
151         }
152 #endif
153
154         err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyPlugInForBundleID, &size);
155         if (err != noErr) {
156                 fprintf(stderr, "AggregateDevice: AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error\n");
157                 return -1;
158         }
159
160         AudioValueTranslation pluginAVT;
161
162         CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio");
163
164         pluginAVT.mInputData = &inBundleRef;
165         pluginAVT.mInputDataSize = sizeof(inBundleRef);
166         pluginAVT.mOutputData = &_aggregate_plugin_id;
167         pluginAVT.mOutputDataSize = sizeof(AudioDeviceID);
168
169         err = GetHardwarePropertyWrapper (kAudioHardwarePropertyPlugInForBundleID, &size, &pluginAVT);
170         if (err != noErr) {
171                 fprintf(stderr, "AggregateDevice: AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error\n");
172                 return -1;
173         }
174
175         //-------------------------------------------------
176         // Create a CFDictionary for our aggregate device
177         //-------------------------------------------------
178
179         CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
180
181         CFStringRef AggregateDeviceNameRef = CFSTR("ArdourDuplex");
182         CFStringRef AggregateDeviceUIDRef = CFSTR("com.ardour.CoreAudio");
183         CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef);
184         CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef);
185
186         // hide from list
187         int value = 1;
188         CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
189         CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef);
190
191         //-------------------------------------------------
192         // Create a CFMutableArray for our sub-device list
193         //-------------------------------------------------
194
195         // we need to append the UID for each device to a CFMutableArray, so create one here
196         CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
197
198         std::vector<CFStringRef> captureDeviceUID;
199         for (UInt32 i = 0; i < input_device_ids.size(); i++) {
200                 CFStringRef ref = GetDeviceName(input_device_ids[i]);
201                 if (ref == NULL) {
202                         return -1;
203                 }
204                 captureDeviceUID.push_back(ref);
205                 CFArrayAppendValue(subDevicesArray, ref);
206         }
207
208         std::vector<CFStringRef> playbackDeviceUID;
209         for (UInt32 i = 0; i < output_device_ids.size(); i++) {
210                 CFStringRef ref = GetDeviceName(output_device_ids[i]);
211                 if (ref == NULL) {
212                         return -1;
213                 }
214                 playbackDeviceUID.push_back(ref);
215                 CFArrayAppendValue(subDevicesArray, ref);
216         }
217
218         //-----------------------------------------------------------------------
219         // Feed the dictionary to the plugin, to create a blank aggregate device
220         //-----------------------------------------------------------------------
221
222         AudioObjectPropertyAddress pluginAOPA;
223         pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice;
224         pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
225         pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
226         UInt32 outDataSize = 0;
227
228         err = AudioObjectGetPropertyDataSize(_aggregate_plugin_id, &pluginAOPA, 0, NULL, &outDataSize);
229         if (err != noErr) {
230                 char *rv = (char*)&err;
231                 fprintf(stderr, "AggregateDevice: AudioObjectGetPropertyDataSize error '%c%c%c%c' 0x%08x\n", rv[0], rv[1], rv[2], rv[3], err);
232                 goto error;
233         }
234
235         err = AudioObjectGetPropertyData(_aggregate_plugin_id, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, created_device);
236         if (err != noErr) {
237                 char *rv = (char*)&err;
238                 fprintf(stderr, "AggregateDevice: AudioObjectGetPropertyData error '%c%c%c%c' 0x%08x\n", rv[0], rv[1], rv[2], rv[3], err);
239                 goto error;
240         }
241
242         // pause for a bit to make sure that everything completed correctly
243         // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created
244         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
245
246         //-------------------------
247         // Set the sub-device list
248         //-------------------------
249
250         pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList;
251         pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
252         pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
253         outDataSize = sizeof(CFMutableArrayRef);
254         err = AudioObjectSetPropertyData(*created_device, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray);
255         if (err != noErr) {
256                 fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for sub-device list error\n");
257                 goto error;
258         }
259
260         // pause again to give the changes time to take effect
261         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
262
263         //-----------------------
264         // Set the master device
265         //-----------------------
266
267         // set the master device manually (this is the device which will act as the master clock for the aggregate device)
268         // pass in the UID of the device you want to use
269         pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice;
270         pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
271         pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
272         outDataSize = sizeof(CFStringRef);
273         err = AudioObjectSetPropertyData(*created_device, &pluginAOPA, 0, NULL, outDataSize, &playbackDeviceUID[0]);
274         if (err != noErr) {
275                 fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for playback-master device error\n");
276                 // try playback
277                 err = AudioObjectSetPropertyData(*created_device, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]);
278         }
279         if (err != noErr) {
280                 fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for capture-master device error\n");
281                 goto error;
282         }
283
284         // pause again to give the changes time to take effect
285         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
286
287         // Prepare sub-devices for clock drift compensation
288         // Workaround for bug in the HAL : until 10.6.2
289         if (need_clock_drift_compensation) {
290
291                 AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
292                 AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
293                 UInt32 theQualifierDataSize = sizeof(AudioObjectID);
294                 AudioClassID inClass = kAudioSubDeviceClassID;
295                 void* theQualifierData = &inClass;
296                 UInt32 subDevicesNum = 0;
297
298 #ifndef NDEBUG
299                 printf("Clock drift compensation activated...\n");
300 #endif
301
302                 // Get the property data size
303                 err = AudioObjectGetPropertyDataSize(*created_device, &theAddressOwned, theQualifierDataSize, theQualifierData, &size);
304                 if (err != noErr) {
305                         fprintf(stderr, "AggregateDevice: kAudioObjectPropertyOwnedObjects error\n");
306                 }
307
308                 //      Calculate the number of object IDs
309                 subDevicesNum = size / sizeof(AudioObjectID);
310 #ifndef NDEBUG
311                 printf("AggregateDevice: clock drift compensation, number of sub-devices = %u\n", (unsigned int)subDevicesNum);
312 #endif
313                 AudioObjectID subDevices[subDevicesNum];
314                 size = sizeof(subDevices);
315
316                 err = AudioObjectGetPropertyData(*created_device, &theAddressOwned, theQualifierDataSize, theQualifierData, &size, subDevices);
317                 if (err != noErr) {
318                         fprintf(stderr, "AggregateDevice: kAudioObjectPropertyOwnedObjects error\n");
319                 }
320
321                 // Set kAudioSubDevicePropertyDriftCompensation property...
322                 for (UInt32 index = 0; index < subDevicesNum; ++index) {
323                         UInt32 theDriftCompensationValue = 1;
324                         err = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue);
325                         if (err != noErr) {
326                                 fprintf(stderr, "AggregateDevice: kAudioSubDevicePropertyDriftCompensation error\n");
327                         }
328                 }
329         }
330
331         // pause again to give the changes time to take effect
332         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
333
334         //----------
335         // Clean up
336         //----------
337
338         // release the private AD key
339         CFRelease(AggregateDeviceNumberRef);
340
341         // release the CF objects we have created - we don't need them any more
342         CFRelease(aggDeviceDict);
343         CFRelease(subDevicesArray);
344
345         // release the device UID
346         for (size_t i = 0; i < captureDeviceUID.size(); ++i) {
347                 CFRelease(captureDeviceUID[i]);
348         }
349
350         for (size_t i = 0; i < playbackDeviceUID.size(); ++i) {
351                 CFRelease(playbackDeviceUID[i]);
352         }
353
354 #ifndef NDEBUG
355         printf("AggregateDevice: new aggregate device %u\n", (unsigned int)*created_device);
356 #endif
357         return 0;
358
359 error:
360         destroy_aggregate_device();
361         return -1;
362 }