2 * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
3 * Copyright (C) 2004-2008 Grame
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.
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.
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.
23 CoreAudioPCM::destroy_aggregate_device ()
25 if (_aggregate_plugin_id == 0) {
31 AudioObjectPropertyAddress property_address;
32 property_address.mSelector = kAudioPlugInDestroyAggregateDevice;
33 property_address.mScope = kAudioObjectPropertyScopeGlobal;
34 property_address.mElement = kAudioObjectPropertyElementMaster;
35 UInt32 outDataSize = 0;
37 err = AudioObjectGetPropertyDataSize(_aggregate_plugin_id, &property_address, 0, NULL, &outDataSize);
39 fprintf(stderr, "DestroyAggregateDevice : AudioObjectGetPropertyDataSize error\n");
43 err = AudioObjectGetPropertyData(_aggregate_plugin_id, &property_address, 0, NULL, &outDataSize, &_aggregate_device_id);
45 fprintf(stderr, "DestroyAggregateDevice : AudioObjectGetPropertyData error\n");
49 printf("DestroyAggregateDevice : OK (plugin: %u device:%u)\n",
50 (unsigned int)_aggregate_plugin_id,
51 (unsigned int)_aggregate_device_id);
56 CoreAudioPCM::create_aggregate_device (
57 AudioDeviceID input_device_id,
58 AudioDeviceID output_device_id,
60 AudioDeviceID* created_device)
63 AudioObjectID sub_device[32];
64 UInt32 size = sizeof(sub_device);
66 /* look up sub-devices */
67 err = GetPropertyWrapper (input_device_id, 0, 0, kAudioAggregateDevicePropertyActiveSubDeviceList, &size, sub_device);
68 std::vector<AudioDeviceID> input_device_ids;
71 input_device_ids.push_back(input_device_id);
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]);
79 size = sizeof(sub_device);
80 err = GetPropertyWrapper (output_device_id, 0, 0, kAudioAggregateDevicePropertyActiveSubDeviceList, &size, sub_device);
81 std::vector<AudioDeviceID> output_device_ids;
84 output_device_ids.push_back(output_device_id);
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]);
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;
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);
103 // Check clock domain
104 err = GetPropertyWrapper (input_device_ids[i], 0, 0, kAudioDevicePropertyClockDomain, &size, &clockdomain);
106 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
107 if (clockdomain != 0 && clockdomain != keptclockdomain) {
109 printf("AggregateDevice: devices do not share the same clock.\n");
111 need_clock_drift_compensation = true;
116 for (UInt32 i = 0; i < output_device_ids.size(); i++) {
117 set_device_sample_rate_id(output_device_ids[i], sample_rate, true);
119 // Check clock domain
120 err = GetPropertyWrapper (output_device_ids[i], 0, 0, kAudioDevicePropertyClockDomain, &size, &clockdomain);
122 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain;
123 if (clockdomain != 0 && clockdomain != keptclockdomain) {
125 printf("AggregateDevice: devices do not share the same clock.\n");
127 need_clock_drift_compensation = true;
132 // If no valid clock domain was found, then assume we have to compensate...
133 if (keptclockdomain == 0) {
134 need_clock_drift_compensation = true;
137 //---------------------------------------------------------------------------
138 // Start to create a new aggregate by getting the base audio hardware plugin
139 //---------------------------------------------------------------------------
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);
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);
154 err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyPlugInForBundleID, &size);
156 fprintf(stderr, "AggregateDevice: AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error\n");
160 AudioValueTranslation pluginAVT;
162 CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio");
164 pluginAVT.mInputData = &inBundleRef;
165 pluginAVT.mInputDataSize = sizeof(inBundleRef);
166 pluginAVT.mOutputData = &_aggregate_plugin_id;
167 pluginAVT.mOutputDataSize = sizeof(AudioDeviceID);
169 err = GetHardwarePropertyWrapper (kAudioHardwarePropertyPlugInForBundleID, &size, &pluginAVT);
171 fprintf(stderr, "AggregateDevice: AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error\n");
175 //-------------------------------------------------
176 // Create a CFDictionary for our aggregate device
177 //-------------------------------------------------
179 CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
181 CFStringRef AggregateDeviceNameRef = CFSTR("ArdourDuplex");
182 CFStringRef AggregateDeviceUIDRef = CFSTR("com.ardour.CoreAudio");
183 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef);
184 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef);
188 CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
189 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef);
191 //-------------------------------------------------
192 // Create a CFMutableArray for our sub-device list
193 //-------------------------------------------------
195 // we need to append the UID for each device to a CFMutableArray, so create one here
196 CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
198 std::vector<CFStringRef> captureDeviceUID;
199 for (UInt32 i = 0; i < input_device_ids.size(); i++) {
200 CFStringRef ref = GetDeviceName(input_device_ids[i]);
204 captureDeviceUID.push_back(ref);
205 CFArrayAppendValue(subDevicesArray, ref);
208 std::vector<CFStringRef> playbackDeviceUID;
209 for (UInt32 i = 0; i < output_device_ids.size(); i++) {
210 CFStringRef ref = GetDeviceName(output_device_ids[i]);
214 playbackDeviceUID.push_back(ref);
215 CFArrayAppendValue(subDevicesArray, ref);
218 //-----------------------------------------------------------------------
219 // Feed the dictionary to the plugin, to create a blank aggregate device
220 //-----------------------------------------------------------------------
222 AudioObjectPropertyAddress pluginAOPA;
223 pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice;
224 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
225 pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
226 UInt32 outDataSize = 0;
228 err = AudioObjectGetPropertyDataSize(_aggregate_plugin_id, &pluginAOPA, 0, NULL, &outDataSize);
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);
235 err = AudioObjectGetPropertyData(_aggregate_plugin_id, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, created_device);
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);
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);
246 //-------------------------
247 // Set the sub-device list
248 //-------------------------
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);
256 fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for sub-device list error\n");
260 // pause again to give the changes time to take effect
261 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
263 //-----------------------
264 // Set the master device
265 //-----------------------
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]);
275 fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for playback-master device error\n");
277 err = AudioObjectSetPropertyData(*created_device, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]);
280 fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for capture-master device error\n");
284 // pause again to give the changes time to take effect
285 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
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) {
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;
299 printf("Clock drift compensation activated...\n");
302 // Get the property data size
303 err = AudioObjectGetPropertyDataSize(*created_device, &theAddressOwned, theQualifierDataSize, theQualifierData, &size);
305 fprintf(stderr, "AggregateDevice: kAudioObjectPropertyOwnedObjects error\n");
308 // Calculate the number of object IDs
309 subDevicesNum = size / sizeof(AudioObjectID);
311 printf("AggregateDevice: clock drift compensation, number of sub-devices = %u\n", (unsigned int)subDevicesNum);
313 AudioObjectID subDevices[subDevicesNum];
314 size = sizeof(subDevices);
316 err = AudioObjectGetPropertyData(*created_device, &theAddressOwned, theQualifierDataSize, theQualifierData, &size, subDevices);
318 fprintf(stderr, "AggregateDevice: kAudioObjectPropertyOwnedObjects error\n");
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);
326 fprintf(stderr, "AggregateDevice: kAudioSubDevicePropertyDriftCompensation error\n");
331 // pause again to give the changes time to take effect
332 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
338 // release the private AD key
339 CFRelease(AggregateDeviceNumberRef);
341 // release the CF objects we have created - we don't need them any more
342 CFRelease(aggDeviceDict);
343 CFRelease(subDevicesArray);
345 // release the device UID
346 for (size_t i = 0; i < captureDeviceUID.size(); ++i) {
347 CFRelease(captureDeviceUID[i]);
350 for (size_t i = 0; i < playbackDeviceUID.size(); ++i) {
351 CFRelease(playbackDeviceUID[i]);
355 printf("AggregateDevice: new aggregate device %u\n", (unsigned int)*created_device);
360 destroy_aggregate_device();