Merge branch 'master' into saveas
[ardour.git] / libs / backends / coreaudio / coreaudio_pcmio.cc
1 /*
2  * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include <glibmm.h>
20 #include "coreaudio_pcmio.h"
21
22 using namespace ARDOUR;
23
24 /* abstraction for deprecated CoreAudio */
25
26 static OSStatus GetPropertyWrapper (
27                 AudioDeviceID id, UInt32 elem, bool input, AudioDevicePropertyID prop, UInt32* size, void * data)
28 {
29 #ifdef COREAUDIO_108
30         AudioObjectPropertyAddress property_address;
31         property_address.mSelector = prop;
32         switch (prop) {
33                 case kAudioDevicePropertyBufferFrameSize:
34                 case kAudioDevicePropertyBufferFrameSizeRange:
35                         property_address.mScope = kAudioObjectPropertyScopeGlobal;
36                         break;
37                 default:
38                         property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
39                         break;
40         }
41         property_address.mElement = kAudioObjectPropertyElementMaster;
42         return AudioObjectGetPropertyData(id, &property_address, elem, NULL, size, data);
43 #else
44         return AudioDeviceGetProperty(id, elem, input, prop, size, data);
45 #endif
46 }
47
48 static OSStatus SetPropertyWrapper (
49                 AudioDeviceID id, const AudioTimeStamp* when, UInt32 chn, bool input, AudioDevicePropertyID prop, UInt32 size, void * data)
50 {
51 #ifdef COREAUDIO_108
52         AudioObjectPropertyAddress property_address;
53         property_address.mSelector = prop;
54         property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
55         property_address.mElement = kAudioObjectPropertyElementMaster;
56         return AudioObjectSetPropertyData (id, &property_address, 0, NULL, size, data);
57 #else
58         return AudioDeviceSetProperty (id, when, chn, input, prop, size, data);
59 #endif
60 }
61
62 static OSStatus GetHardwarePropertyInfoWrapper (AudioDevicePropertyID prop, UInt32* size)
63 {
64 #ifdef COREAUDIO_108
65         AudioObjectPropertyAddress property_address;
66         property_address.mSelector = prop;
67         property_address.mScope = kAudioObjectPropertyScopeGlobal;
68         property_address.mElement = kAudioObjectPropertyElementMaster;
69         return AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, size);
70 #else
71         Boolean outWritable;
72         return AudioHardwareGetPropertyInfo(prop, size, &outWritable);
73 #endif
74 }
75
76 static OSStatus GetHardwarePropertyWrapper (AudioDevicePropertyID prop, UInt32* size, void *d)
77 {
78 #ifdef COREAUDIO_108
79         AudioObjectPropertyAddress property_address;
80         property_address.mSelector = prop;
81         property_address.mScope = kAudioObjectPropertyScopeGlobal;
82         property_address.mElement = kAudioObjectPropertyElementMaster;
83         return AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, size, d);
84 #else
85         return AudioHardwareGetProperty (kAudioHardwarePropertyDevices, size, d);
86 #endif
87 }
88
89 static OSStatus GetPropertyInfoWrapper (AudioDeviceID id, UInt32 elem, bool input, AudioDevicePropertyID prop, UInt32* size)
90 {
91 #ifdef COREAUDIO_108
92         AudioObjectPropertyAddress property_address;
93         property_address.mSelector = prop;
94         property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
95         property_address.mElement = elem;
96         return AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, size);
97 #else
98         Boolean outWritable;
99         return AudioDeviceGetPropertyInfo(id, elem, input, prop, size, &outWritable);
100 #endif
101 }
102
103 static OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name)
104 {
105     UInt32 size = 256;
106     return GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyDeviceName, &size, name);
107 }
108
109 static CFStringRef GetDeviceName(AudioDeviceID id)
110 {
111     UInt32 size = sizeof(CFStringRef);
112     CFStringRef UIname;
113     OSStatus err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyDeviceUID, &size, &UIname);
114     return (err == noErr) ? UIname : NULL;
115 }
116
117 ///////////////////////////////////////////////////////////////////////////////
118
119 #include "coreaudio_pcmio_aggregate.cc"
120
121 /* callbacks */
122
123 #ifdef COREAUDIO_108
124
125 static OSStatus property_callback_ptr (AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) {
126         CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
127         for (UInt32 i = 0; i < inNumberAddresses; ++i) {
128                 switch (inAddresses[i].mSelector) {
129                         case kAudioHardwarePropertyDevices:
130                                 self->hw_changed_callback();
131                                 break;
132                         case kAudioDeviceProcessorOverload:
133                                 self->xrun_callback();
134                                 break;
135                         case kAudioDevicePropertyBufferFrameSize:
136                                 self->buffer_size_callback();
137                                 break;
138                         case kAudioDevicePropertyNominalSampleRate:
139                                 self->sample_rate_callback();
140                                 break;
141                         default:
142                                 break;
143                 }
144         }
145         return noErr;
146 }
147
148 #else
149
150 static OSStatus hw_changed_callback_ptr (AudioHardwarePropertyID inPropertyID, void* arg) {
151         if (inPropertyID == kAudioHardwarePropertyDevices) {
152                 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
153                 self->hw_changed_callback();
154         }
155         return noErr;
156 }
157
158 static OSStatus property_callback_ptr (
159                 AudioDeviceID inDevice,
160                 UInt32 inChannel,
161                 Boolean isInput,
162                 AudioDevicePropertyID inPropertyID,
163                 void* inClientData)
164 {
165         CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inClientData);
166         switch (inPropertyID) {
167                 case kAudioDeviceProcessorOverload:
168                         d->xrun_callback();
169                         break;
170                 case kAudioDevicePropertyBufferFrameSize:
171                         d->buffer_size_callback();
172                         break;
173                 case kAudioDevicePropertyNominalSampleRate:
174                         d->sample_rate_callback();
175                         break;
176         }
177         return noErr;
178 }
179
180 #endif
181
182 static OSStatus render_callback_ptr (
183                 void* inRefCon,
184                 AudioUnitRenderActionFlags* ioActionFlags,
185                 const AudioTimeStamp* inTimeStamp,
186                 UInt32 inBusNumber,
187                 UInt32 inNumberFrames,
188                 AudioBufferList* ioData)
189 {
190         CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
191         return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
192 }
193
194
195 static OSStatus add_listener (AudioDeviceID id, AudioDevicePropertyID selector, void *arg) {
196 #ifdef COREAUDIO_108
197         AudioObjectPropertyAddress property_address;
198         property_address.mSelector = selector;
199         property_address.mScope = kAudioObjectPropertyScopeGlobal;
200         property_address.mElement = 0;
201         return AudioObjectAddPropertyListener(id, &property_address, property_callback_ptr, arg);
202 #else
203         return AudioDeviceAddPropertyListener(id, 0, true, selector, property_callback_ptr, arg);
204 #endif
205 }
206
207
208 ///////////////////////////////////////////////////////////////////////////////
209
210 CoreAudioPCM::CoreAudioPCM ()
211         : _auhal (0)
212         , _device_ids (0)
213         , _input_audio_buffer_list (0)
214         , _active_device_id (0)
215         , _aggregate_device_id (0)
216         , _aggregate_plugin_id (0)
217         , _state (-1)
218         , _capture_channels (0)
219         , _playback_channels (0)
220         , _in_process (false)
221         , _n_devices (0)
222         , _process_callback (0)
223         , _error_callback (0)
224         , _hw_changed_callback (0)
225         , _xrun_callback (0)
226         , _buffer_size_callback (0)
227         , _sample_rate_callback (0)
228         , _device_ins (0)
229         , _device_outs (0)
230 {
231         pthread_mutex_init (&_discovery_lock, 0);
232
233 #ifdef COREAUDIO_108
234         CFRunLoopRef theRunLoop = NULL;
235         AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices };
236         AudioObjectSetPropertyData (kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
237
238         property.mSelector = kAudioHardwarePropertyDevices;
239         property.mScope = kAudioObjectPropertyScopeGlobal;
240         property.mElement = 0;
241         AudioObjectAddPropertyListener(kAudioObjectSystemObject, &property, property_callback_ptr, this);
242 #else
243         AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hw_changed_callback_ptr, this);
244 #endif
245 }
246
247 CoreAudioPCM::~CoreAudioPCM ()
248 {
249         if (_state == 0) {
250                 pcm_stop();
251         }
252         delete _device_ids;
253         free(_device_ins);
254         free(_device_outs);
255 #ifdef COREAUDIO_108
256         AudioObjectPropertyAddress prop;
257         prop.mSelector = kAudioHardwarePropertyDevices;
258         prop.mScope = kAudioObjectPropertyScopeGlobal;
259         prop.mElement = 0;
260         AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &property_callback_ptr, this);
261 #else
262         AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hw_changed_callback_ptr);
263 #endif
264         free(_input_audio_buffer_list);
265         pthread_mutex_destroy (&_discovery_lock);
266 }
267
268
269 void
270 CoreAudioPCM::hw_changed_callback() {
271 #ifndef NDEBUG
272         printf("CoreAudio HW change..\n");
273 #endif
274         discover();
275         if (_hw_changed_callback) {
276                 _hw_changed_callback(_hw_changed_arg);
277         }
278 }
279
280
281 int
282 CoreAudioPCM::available_sample_rates(uint32_t device_id, std::vector<float>& sampleRates)
283 {
284         OSStatus err;
285         UInt32 size = 0;
286         sampleRates.clear();
287
288         if (device_id >= _n_devices) {
289                 return -1;
290         }
291
292         err = GetPropertyInfoWrapper (_device_ids[device_id], 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size);
293
294         if (err != kAudioHardwareNoError) {
295                 return -1;
296         }
297
298         uint32_t numRates = size / sizeof(AudioValueRange);
299         AudioValueRange* supportedRates = new AudioValueRange[numRates];
300
301         err = GetPropertyWrapper (_device_ids[device_id], 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, supportedRates);
302
303         if (err != kAudioHardwareNoError) {
304                 delete [] supportedRates;
305                 return -1;
306         }
307
308         static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
309
310         for(uint32_t i = 0; i < sizeof(ardourRates)/sizeof(float); ++i) {
311                 for(uint32_t j = 0; j < numRates; ++j) {
312                         if ((supportedRates[j].mMinimum <= ardourRates[i]) &&
313                                         (supportedRates[j].mMaximum >= ardourRates[i])) {
314                                 sampleRates.push_back (ardourRates[i]);
315                                 break;
316                         }
317                 }
318         }
319
320         delete [] supportedRates;
321         return 0;
322 }
323
324 int
325 CoreAudioPCM::available_buffer_sizes(uint32_t device_id, std::vector<uint32_t>& bufferSizes)
326 {
327         OSStatus err;
328         UInt32 size = 0;
329         bufferSizes.clear();
330
331         if (device_id >= _n_devices) {
332                 return -1;
333         }
334
335         AudioValueRange supportedRange;
336         size = sizeof (AudioValueRange);
337
338         err = GetPropertyWrapper (_device_ids[device_id], 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &size, &supportedRange);
339         if (err != noErr) {
340                 return -1;
341         }
342
343         static const uint32_t ardourSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
344
345         for(uint32_t i = 0; i < sizeof(ardourSizes)/sizeof(uint32_t); ++i) {
346                 if ((supportedRange.mMinimum <= ardourSizes[i]) &&
347                                 (supportedRange.mMaximum >= ardourSizes[i])) {
348                         bufferSizes.push_back (ardourSizes[i]);
349                 }
350         }
351
352         if (bufferSizes.empty()) {
353                 bufferSizes.push_back ((uint32_t)supportedRange.mMinimum);
354                 bufferSizes.push_back ((uint32_t)supportedRange.mMaximum);
355         }
356         return 0;
357 }
358
359 uint32_t
360 CoreAudioPCM::available_channels(uint32_t device_id, bool input)
361 {
362         OSStatus err;
363         UInt32 size = 0;
364         AudioBufferList *bufferList = NULL;
365         uint32_t channel_count = 0;
366
367         if (device_id >= _n_devices) {
368                 return 0;
369         }
370
371         /* query number of inputs */
372         err = GetPropertyInfoWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreamConfiguration, &size);
373         if (kAudioHardwareNoError != err) {
374                 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed\n");
375                 return 0;
376         }
377
378         bufferList = (AudioBufferList *)(malloc(size));
379         assert(bufferList);
380         if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; }
381         bufferList->mNumberBuffers = 0;
382         err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
383
384         if(kAudioHardwareNoError != err) {
385                 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed\n");
386                 free(bufferList);
387                 return 0;
388         }
389
390         for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
391                 channel_count += bufferList->mBuffers[j].mNumberChannels;
392         }
393         free(bufferList);
394         return channel_count;
395 }
396
397 void
398 CoreAudioPCM::get_stream_latencies(uint32_t device_id, bool input, std::vector<uint32_t>& latencies)
399 {
400         OSStatus err;
401         UInt32 size = 0;
402
403         if (device_id >= _n_devices) {
404                 return;
405         }
406
407         err = GetPropertyInfoWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreams, &size);
408         if (err != noErr) { return; }
409
410         uint32_t stream_count = size / sizeof(UInt32);
411         AudioStreamID streamIDs[stream_count];
412
413         err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreams, &size, &streamIDs);
414         if (err != noErr) {
415                 fprintf(stderr, "GetStreamLatencies kAudioDevicePropertyStreams\n");
416                 return;
417         }
418
419         for (uint32_t i = 0; i < stream_count; i++) {
420                 UInt32 stream_latency;
421                 size = sizeof(UInt32);
422 #ifdef COREAUDIO_108
423                 AudioObjectPropertyAddress property_address;
424                 property_address.mSelector = kAudioDevicePropertyStreams;
425                 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
426                 property_address.mElement = i; // ??
427                 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &stream_latency);
428 #else
429                 err = AudioStreamGetProperty(streamIDs[i], input, kAudioStreamPropertyLatency, &size, &stream_latency);
430 #endif
431                 if (err != noErr) {
432                         fprintf(stderr, "GetStreamLatencies kAudioStreamPropertyLatency\n");
433                         return;
434                 }
435 #ifndef NDEBUG
436                 printf("  ^ Stream %u latency: %u\n", (unsigned int)i, (unsigned int)stream_latency);
437 #endif
438                 latencies.push_back(stream_latency);
439         }
440 }
441
442 uint32_t
443 CoreAudioPCM::get_latency(uint32_t device_id, bool input)
444 {
445         OSStatus err;
446         uint32_t latency = 0;
447         UInt32 size = sizeof(UInt32);
448         UInt32 lat0 = 0;
449         UInt32 latS = 0;
450
451         if (device_id >= _n_devices) {
452                 return 0;
453         }
454
455         err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyLatency, &size, &lat0);
456         if (err != kAudioHardwareNoError) {
457                 fprintf(stderr, "GetLatency kAudioDevicePropertyLatency\n");
458         }
459
460         err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertySafetyOffset, &size, &latS);
461         if (err != kAudioHardwareNoError) {
462                 fprintf(stderr, "GetLatency kAudioDevicePropertySafetyOffset\n");
463         }
464
465 #ifndef NDEBUG
466         printf("%s Latency systemic+safetyoffset = %u + %u\n",
467                         input ? "Input" : "Output", (unsigned int)lat0, (unsigned int)latS);
468 #endif
469         latency = lat0 + latS;
470
471         uint32_t max_stream_latency = 0;
472         std::vector<uint32_t> stream_latencies;
473         get_stream_latencies(device_id, input, stream_latencies);
474         for (size_t i = 0; i < stream_latencies.size(); ++i) {
475                 max_stream_latency = std::max(max_stream_latency, stream_latencies[i]);
476         }
477         latency += max_stream_latency;
478
479         return latency;
480 }
481
482 uint32_t
483 CoreAudioPCM::current_buffer_size_id(AudioDeviceID id) {
484         UInt32 buffer_size;
485         UInt32 size = sizeof(UInt32);
486         OSStatus err;
487         err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyBufferFrameSize, &size, &buffer_size);
488         if (err != noErr) {
489                 return _samples_per_period;
490         }
491         return buffer_size;
492 }
493
494
495 float
496 CoreAudioPCM::current_sample_rate_id(AudioDeviceID id, bool input) {
497         OSStatus err;
498         UInt32 size = 0;
499         Float64 rate;
500         size = sizeof (rate);
501
502         err = GetPropertyWrapper(id, 0, input, kAudioDevicePropertyNominalSampleRate, &size, &rate);
503         if (err == noErr) {
504                 return rate;
505         }
506         return 0;
507 }
508
509 float
510 CoreAudioPCM::current_sample_rate(uint32_t device_id, bool input) {
511         if (device_id >= _n_devices) {
512                 return -1;
513         }
514         return current_sample_rate_id(_device_ids[device_id], input);
515 }
516
517 float
518 CoreAudioPCM::sample_rate() {
519         if (_active_device_id == 0) {
520                 return 0;
521         }
522         return current_sample_rate_id(_active_device_id, _playback_channels > 0 ? false : true);
523 }
524
525 int
526 CoreAudioPCM::set_device_sample_rate_id (AudioDeviceID id, float rate, bool input)
527 {
528         std::vector<int>::iterator intIter;
529         OSStatus err;
530         UInt32 size = 0;
531
532         if (current_sample_rate_id(id, input) == rate) {
533                 return 0;
534         }
535
536         Float64 newNominalRate = rate;
537         size = sizeof (Float64);
538
539         err = SetPropertyWrapper(id, NULL, 0, input, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate);
540         if (err != noErr) {
541                 fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n");
542                 return 0;
543         }
544
545         int timeout = 3000; // 3 sec
546         while (--timeout > 0) {
547                 if (current_sample_rate_id(id, input) == rate) {
548                         break;
549                 }
550                 Glib::usleep (1000);
551         }
552         fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate took %d ms.\n", (3000 - timeout));
553
554         if (timeout == 0) {
555                 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate timed out.\n");
556                 return -1;
557         }
558
559         return 0;
560 }
561
562 int
563 CoreAudioPCM::set_device_sample_rate (uint32_t device_id, float rate, bool input)
564 {
565         return set_device_sample_rate_id(_device_ids[device_id], rate, input);
566 }
567
568 void
569 CoreAudioPCM::discover()
570 {
571         OSStatus err;
572         UInt32 size = 0;
573
574         if (pthread_mutex_trylock (&_discovery_lock)) {
575                 return;
576         }
577
578         if (_device_ids) {
579                 delete _device_ids; _device_ids = 0;
580                 free(_device_ins); _device_ins = 0;
581                 free(_device_outs); _device_outs = 0;
582         }
583         _devices.clear();
584
585         err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyDevices, &size);
586
587         _n_devices = size / sizeof (AudioDeviceID);
588         size = _n_devices * sizeof (AudioDeviceID);
589
590         _device_ids = new AudioDeviceID[_n_devices];
591         _device_ins = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
592         _device_outs = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
593
594         assert(_device_ins && _device_outs && _device_ids);
595         if (!_device_ins || !_device_ins || !_device_ids) {
596                 fprintf(stderr, "OUT OF MEMORY\n");
597                 _device_ids = 0;
598                 _device_ins = 0;
599                 _device_outs = 0;
600                 pthread_mutex_unlock (&_discovery_lock);
601                 return;
602         }
603
604         err = GetHardwarePropertyWrapper (kAudioHardwarePropertyDevices, &size, _device_ids);
605
606         for (size_t idx = 0; idx < _n_devices; ++idx) {
607                 size = 64;
608                 char deviceName[64];
609                 err = GetPropertyWrapper (_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName);
610
611                 if (kAudioHardwareNoError != err) {
612                         fprintf(stderr, "CoreAudioPCM: device name query failed\n");
613                         continue;
614                 }
615
616                 UInt32 inputChannelCount = available_channels(idx, true);
617                 UInt32 outputChannelCount = available_channels(idx, false);
618
619                 {
620                         std::string dn = deviceName;
621                         _device_ins[idx] = inputChannelCount;
622                         _device_outs[idx] = outputChannelCount;
623 #ifndef NDEBUG
624                         printf("CoreAudio Device: #%ld (id:%lu) '%s' in:%u out:%u\n", idx,
625                                         (long unsigned int)_device_ids[idx],
626                                         deviceName,
627                                         (unsigned int)inputChannelCount, (unsigned int)outputChannelCount);
628 #endif
629                         if (outputChannelCount > 0 || inputChannelCount > 0) {
630                                 _devices.insert (std::pair<size_t, std::string> (idx, dn));
631                         }
632                 }
633         }
634         pthread_mutex_unlock (&_discovery_lock);
635 }
636
637 void
638 CoreAudioPCM::xrun_callback ()
639 {
640 #ifndef NDEBUG
641         printf("Coreaudio XRUN\n");
642 #endif
643         if (_xrun_callback) {
644                 _xrun_callback(_xrun_arg);
645         }
646 }
647
648 void
649 CoreAudioPCM::buffer_size_callback ()
650 {
651         _samples_per_period = current_buffer_size_id(_active_device_id);
652
653         if (_buffer_size_callback) {
654                 _buffer_size_callback(_buffer_size_arg);
655         }
656 }
657
658 void
659 CoreAudioPCM::sample_rate_callback ()
660 {
661 #ifndef NDEBUG
662         printf("Sample Rate Changed!\n");
663 #endif
664         if (_sample_rate_callback) {
665                 _sample_rate_callback(_sample_rate_arg);
666         }
667 }
668
669 void
670 CoreAudioPCM::pcm_stop ()
671 {
672         if (!_auhal) return;
673
674         AudioOutputUnitStop(_auhal);
675         if (_state == 0) {
676 #ifdef COREAUDIO_108
677                 AudioObjectPropertyAddress prop;
678                 prop.mScope = kAudioObjectPropertyScopeGlobal;
679                 prop.mElement = 0;
680                 if (_active_device_id > 0) {
681                         prop.mSelector = kAudioDeviceProcessorOverload;
682                         AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
683                         prop.mSelector = kAudioDevicePropertyBufferFrameSize;
684                         AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
685                         prop.mSelector = kAudioDevicePropertyNominalSampleRate;
686                         AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
687                 }
688 #else
689                 if (_active_device_id > 0) {
690                         AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDeviceProcessorOverload, property_callback_ptr);
691                         AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyBufferFrameSize, property_callback_ptr);
692                         AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyNominalSampleRate, property_callback_ptr);
693                 }
694 #endif
695         }
696         if (_aggregate_plugin_id) {
697                 destroy_aggregate_device();
698         }
699
700         AudioUnitUninitialize(_auhal);
701 #ifdef COREAUDIO_108
702         AudioComponentInstanceDispose(_auhal);
703 #else
704         CloseComponent(_auhal);
705 #endif
706         _auhal = 0;
707         _state = -1;
708         _capture_channels = 0;
709         _playback_channels = 0;
710         _aggregate_plugin_id = 0;
711         _aggregate_device_id = 0;
712         _active_device_id = 0;
713
714         free(_input_audio_buffer_list);
715         _input_audio_buffer_list = 0;
716
717         _input_names.clear();
718         _output_names.clear();
719
720         _error_callback = 0;
721         _process_callback = 0;
722         _xrun_callback = 0;
723 }
724
725 #ifndef NDEBUG
726 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
727 {
728         printf ("- - - - - - - - - - - - - - - - - - - -\n");
729         printf ("  Sample Rate:%.2f",        inDesc->mSampleRate);
730         printf ("  Format ID:%.*s\n",        (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
731         printf ("  Format Flags:%X\n",       (unsigned int)inDesc->mFormatFlags);
732         printf ("  Bytes per Packet:%d\n",   (int)inDesc->mBytesPerPacket);
733         printf ("  Frames per Packet:%d\n",  (int)inDesc->mFramesPerPacket);
734         printf ("  Bytes per Frame:%d\n",    (int)inDesc->mBytesPerFrame);
735         printf ("  Channels per Frame:%d\n", (int)inDesc->mChannelsPerFrame);
736         printf ("  Bits per Channel:%d\n",   (int)inDesc->mBitsPerChannel);
737         printf ("- - - - - - - - - - - - - - - - - - - -\n");
738 }
739 #endif
740
741 int
742 CoreAudioPCM::set_device_buffer_size_id (AudioDeviceID id, uint32_t samples_per_period)
743 {
744         OSStatus err;
745         UInt32 uint32val;
746
747         uint32val = samples_per_period;
748         err = SetPropertyWrapper(id, NULL, 0, true, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
749         if (err != noErr) { return -1; }
750         err = SetPropertyWrapper(id, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
751         if (err != noErr) { return -1; }
752         return 0;
753 }
754
755 int
756 CoreAudioPCM::set_samples_per_period (uint32_t n_samples)
757 {
758
759         if (_state != 0 || _active_device_id == 0) {
760                 return -1;
761         }
762         set_device_buffer_size_id (_active_device_id, n_samples);
763         return 0;
764 }
765
766 int
767 CoreAudioPCM::pcm_start (
768                 uint32_t device_id_in, uint32_t device_id_out,
769                 uint32_t sample_rate, uint32_t samples_per_period,
770                 int (process_callback (void*, const uint32_t, const uint64_t)), void *process_arg)
771 {
772
773         assert(_device_ids);
774         std::string errorMsg;
775         _state = -2;
776
777         // TODO add "none' device to force half-duplex
778
779         if (device_id_out >= _n_devices || device_id_in >= _n_devices) {
780                 return -1;
781         }
782
783         pthread_mutex_lock (&_discovery_lock);
784
785         _process_callback = process_callback;
786         _process_arg = process_arg;
787         _samples_per_period = samples_per_period;
788         _cur_samples_per_period = 0;
789         _active_device_id = 0;
790         _capture_channels = 0;
791         _playback_channels = 0;
792
793         const uint32_t chn_in = _device_ins[device_id_in] + ((device_id_out != device_id_in) ? _device_ins[device_id_out] : 0);
794         const uint32_t chn_out = _device_outs[device_id_out] + ((device_id_out != device_id_in) ? _device_outs[device_id_in] : 0);
795
796         assert (chn_in > 0 || chn_out > 0);
797
798         ComponentResult err;
799         UInt32 uint32val;
800         UInt32 size;
801         AudioDeviceID device_id;
802         AudioStreamBasicDescription srcFormat, dstFormat;
803
804         AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
805         AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
806         if (!HALOutput) { errorMsg="AudioComponentFindNext"; goto error; }
807
808         err = AudioComponentInstanceNew(HALOutput, &_auhal);
809         if (err != noErr) { errorMsg="AudioComponentInstanceNew"; goto error; }
810
811         err = AudioUnitInitialize(_auhal);
812         if (err != noErr) { errorMsg="AudioUnitInitialize"; goto error; }
813
814         // explicitly change samplerate of the device
815         if (set_device_sample_rate(device_id_in, sample_rate, true)) {
816                 errorMsg="Failed to set SampleRate, Capture Device"; goto error;
817         }
818         if (set_device_sample_rate(device_id_out, sample_rate, false)) {
819                 errorMsg="Failed to set SampleRate, Playback Device"; goto error;
820         }
821
822         // explicitly request device buffer size
823         if (set_device_buffer_size_id(_device_ids[device_id_in], samples_per_period)) {
824                 errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; goto error;
825         }
826         if (set_device_buffer_size_id(_device_ids[device_id_out], samples_per_period)) {
827                 errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; goto error;
828         }
829
830         // create aggregate device..
831         if (_device_ids[device_id_in] != _device_ids[device_id_out]) {
832                 if (0 == create_aggregate_device(_device_ids[device_id_in], _device_ids[device_id_out], sample_rate, &_aggregate_device_id)) {
833                         device_id = _aggregate_device_id;
834                 } else {
835                         _aggregate_device_id = 0;
836                         _aggregate_plugin_id = 0;
837                         errorMsg="Cannot create Aggregate Device"; goto error;
838                 }
839         } else {
840                 device_id = _device_ids[device_id_out];
841         }
842
843         if (device_id_out != device_id_in) {
844                 assert(_aggregate_device_id > 0);
845         }
846
847         // enableIO to progress further
848         uint32val = (chn_in > 0) ? 1 : 0;
849         err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32));
850         if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; goto error; }
851
852         uint32val = (chn_out > 0) ? 1 : 0;
853         err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &uint32val, sizeof(UInt32));
854         if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Output"; goto error; }
855
856         err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
857         if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Input"; goto error; }
858
859         if (chn_in > 0) {
860                 // set sample format
861                 srcFormat.mSampleRate = sample_rate;
862                 srcFormat.mFormatID = kAudioFormatLinearPCM;
863                 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
864                 srcFormat.mBytesPerPacket = sizeof(float);
865                 srcFormat.mFramesPerPacket = 1;
866                 srcFormat.mBytesPerFrame = sizeof(float);
867                 srcFormat.mChannelsPerFrame = chn_in;
868                 srcFormat.mBitsPerChannel = 32;
869
870                 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription));
871                 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; goto error; }
872
873                 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
874                 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Input"; goto error; }
875         }
876
877         if (chn_out > 0) {
878                 dstFormat.mSampleRate = sample_rate;
879                 dstFormat.mFormatID = kAudioFormatLinearPCM;
880                 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
881                 dstFormat.mBytesPerPacket = sizeof(float);
882                 dstFormat.mFramesPerPacket = 1;
883                 dstFormat.mBytesPerFrame = sizeof(float);
884                 dstFormat.mChannelsPerFrame = chn_out;
885                 dstFormat.mBitsPerChannel = 32;
886
887                 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription));
888                 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; }
889
890                 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
891                 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Output"; goto error; }
892         }
893
894         /* read back stream descriptions */
895         if (chn_in > 0) {
896                 size = sizeof(AudioStreamBasicDescription);
897                 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
898                 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; goto error; }
899                 _capture_channels = srcFormat.mChannelsPerFrame;
900 #ifndef NDEBUG
901                 PrintStreamDesc(&srcFormat);
902 #endif
903         }
904
905         if (chn_out > 0) {
906                 size = sizeof(AudioStreamBasicDescription);
907                 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size);
908                 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; goto error; }
909                 _playback_channels = dstFormat.mChannelsPerFrame;
910
911 #ifndef NDEBUG
912                 PrintStreamDesc(&dstFormat);
913 #endif
914         }
915
916         /* prepare buffers for input */
917         if (_capture_channels > 0) {
918                 _input_audio_buffer_list = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_capture_channels - 1) * sizeof(AudioBuffer));
919                 assert(_input_audio_buffer_list);
920                 if (!_input_audio_buffer_list) { errorMsg="Out of Memory."; goto error; }
921         }
922
923         _active_device_id = device_id;
924
925         // add Listeners
926         err = add_listener (_active_device_id, kAudioDeviceProcessorOverload, this);
927         if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Listen"; goto error; }
928
929         err = add_listener (_active_device_id, kAudioDevicePropertyBufferFrameSize, this);
930         if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; goto error; }
931
932         err = add_listener (_active_device_id, kAudioDevicePropertyNominalSampleRate, this);
933         if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; goto error; }
934
935         _samples_per_period = current_buffer_size_id(_active_device_id);
936
937         // Setup callback
938         AURenderCallbackStruct renderCallback;
939         memset (&renderCallback, 0, sizeof (renderCallback));
940         renderCallback.inputProc = render_callback_ptr;
941         renderCallback.inputProcRefCon = this;
942         if (_playback_channels == 0) {
943                 err = AudioUnitSetProperty(_auhal,
944                                 kAudioOutputUnitProperty_SetInputCallback,
945                                 kAudioUnitScope_Output, 1,
946                                 &renderCallback, sizeof (renderCallback));
947         } else {
948                 err = AudioUnitSetProperty(_auhal,
949                                 kAudioUnitProperty_SetRenderCallback,
950                                 kAudioUnitScope_Output, 0,
951                                 &renderCallback, sizeof (renderCallback));
952         }
953
954         if (err != noErr) { errorMsg="kAudioUnitProperty_SetRenderCallback"; goto error; }
955
956         /* setup complete, now get going.. */
957         if (AudioOutputUnitStart(_auhal) == noErr) {
958                 _input_names.clear();
959                 _output_names.clear();
960                 cache_port_names (device_id, true);
961                 cache_port_names (device_id, false);
962                 _state = 0;
963                 pthread_mutex_unlock (&_discovery_lock);
964
965                 // kick device
966                 if (set_device_buffer_size_id(_active_device_id, samples_per_period)) {
967                         errorMsg="kAudioDevicePropertyBufferFrameSize"; goto error;
968                 }
969
970                 return 0;
971         }
972
973 error:
974         char *rv = (char*)&err;
975         fprintf(stderr, "CoreaudioPCM Error: %c%c%c%c %s\n", rv[0], rv[1], rv[2], rv[3], errorMsg.c_str());
976         pcm_stop();
977         _state = -3;
978         _active_device_id = 0;
979         pthread_mutex_unlock (&_discovery_lock);
980         return -1;
981 }
982
983 void
984 CoreAudioPCM::cache_port_names(AudioDeviceID id, bool input)
985 {
986         uint32_t n_chn;
987
988         if (input) {
989                 n_chn = _capture_channels;
990         } else {
991                 n_chn = _playback_channels;;
992         }
993 #ifdef COREAUDIO_108
994         AudioObjectPropertyAddress property_address;
995         property_address.mSelector = kAudioObjectPropertyElementName;
996         property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
997 #endif
998
999         for (uint32_t c = 0; c < n_chn; ++c) {
1000                 CFStringRef name = NULL;
1001                 std::stringstream ss;
1002                 UInt32 size = 0;
1003                 OSStatus err;
1004
1005 #ifdef COREAUDIO_108
1006                 property_address.mElement = c + 1;
1007                 err = AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, &size);
1008 #else
1009                 err = AudioDeviceGetPropertyInfo (id, c + 1, input,
1010                                 kAudioDevicePropertyChannelNameCFString,
1011                                 &size,
1012                                 NULL);
1013 #endif
1014
1015                 if (err == kAudioHardwareNoError) {
1016 #ifdef COREAUDIO_108
1017                         err = AudioObjectGetPropertyData(id, &property_address, c + 1, NULL, &size, &name);
1018 #else
1019                         err = AudioDeviceGetProperty (id, c + 1, input,
1020                                         kAudioDevicePropertyChannelNameCFString,
1021                                         &size,
1022                                         &name);
1023 #endif
1024                 }
1025
1026                 bool decoded = false;
1027                 char* cstr_name = 0;
1028                 if (err == kAudioHardwareNoError) {
1029                         CFIndex length = CFStringGetLength(name);
1030                         CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
1031                         cstr_name = new char[maxSize];
1032                         decoded = CFStringGetCString(name, cstr_name, maxSize, kCFStringEncodingUTF8);
1033                 }
1034
1035                 ss << (c + 1);
1036
1037                 if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
1038                         ss << " - " <<  cstr_name;
1039                 }
1040 #if 0
1041                 printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
1042 #endif
1043
1044                 if (input) {
1045                         _input_names.push_back (ss.str());
1046                 } else {
1047                         _output_names.push_back (ss.str());
1048                 }
1049
1050                 if (name) {
1051                         CFRelease (name);
1052                 }
1053                 delete [] cstr_name;
1054         }
1055 }
1056
1057 std::string
1058 CoreAudioPCM::cached_port_name(uint32_t port, bool input) const
1059 {
1060         if (_state != 0) { return ""; }
1061
1062         if (input) {
1063                 if (port >= _input_names.size()) {
1064                         return "";
1065                 }
1066                 return _input_names[port];
1067         } else {
1068                 if (port >= _output_names.size()) {
1069                         return "";
1070                 }
1071                 return _output_names[port];
1072         }
1073 }
1074
1075
1076 OSStatus
1077 CoreAudioPCM::render_callback (
1078                 AudioUnitRenderActionFlags* ioActionFlags,
1079                 const AudioTimeStamp* inTimeStamp,
1080                 UInt32 inBusNumber,
1081                 UInt32 inNumberFrames,
1082                 AudioBufferList* ioData)
1083 {
1084         OSStatus retVal = kAudioHardwareNoError;
1085
1086         if (_samples_per_period < inNumberFrames) {
1087 #ifndef NDEBUG
1088                 printf("samples per period exceeds configured value, cycle skipped (%u < %u)\n",
1089                                 (unsigned int)_samples_per_period, (unsigned int)inNumberFrames);
1090 #endif
1091                 for (uint32_t i = 0; _playback_channels > 0 && i < ioData->mNumberBuffers; ++i) {
1092                         float* ob = (float*) ioData->mBuffers[i].mData;
1093                         memset(ob, 0, sizeof(float) * inNumberFrames);
1094                 }
1095                 return noErr;
1096         }
1097
1098         assert(_playback_channels == 0 || ioData->mNumberBuffers == _playback_channels);
1099
1100         UInt64 cur_cycle_start = AudioGetCurrentHostTime ();
1101         _cur_samples_per_period = inNumberFrames;
1102
1103         if (_capture_channels > 0) {
1104                 _input_audio_buffer_list->mNumberBuffers = _capture_channels;
1105                 for (uint32_t i = 0; i < _capture_channels; ++i) {
1106                         _input_audio_buffer_list->mBuffers[i].mNumberChannels = 1;
1107                         _input_audio_buffer_list->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float);
1108                         _input_audio_buffer_list->mBuffers[i].mData = NULL;
1109                 }
1110
1111                 retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list);
1112         }
1113
1114         if (retVal != kAudioHardwareNoError) {
1115 #if 0
1116                 char *rv = (char*)&retVal;
1117                 printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
1118 #endif
1119                 if (_error_callback) {
1120                         _error_callback(_error_arg);
1121                 }
1122                 return retVal;
1123         }
1124
1125         _output_audio_buffer_list = ioData;
1126
1127         _in_process = true;
1128
1129         int rv = -1;
1130
1131         if (_process_callback) {
1132                 rv = _process_callback(_process_arg, inNumberFrames, cur_cycle_start);
1133         }
1134
1135         _in_process = false;
1136
1137         if (rv != 0 && _playback_channels > 0) {
1138                 // clear output
1139                 for (uint32_t i = 0; i < ioData->mNumberBuffers; ++i) {
1140                         float* ob = (float*) ioData->mBuffers[i].mData;
1141                         memset(ob, 0, sizeof(float) * inNumberFrames);
1142                 }
1143         }
1144         return noErr;
1145 }
1146
1147 int
1148 CoreAudioPCM::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
1149 {
1150         if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
1151                 return -1;
1152         }
1153         assert(_input_audio_buffer_list->mNumberBuffers > chn);
1154         memcpy((void*)input, (void*)_input_audio_buffer_list->mBuffers[chn].mData, sizeof(float) * n_samples);
1155         return 0;
1156
1157 }
1158 int
1159 CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
1160 {
1161         if (!_in_process || chn > _playback_channels || n_samples > _cur_samples_per_period) {
1162                 return -1;
1163         }
1164
1165         assert(_output_audio_buffer_list && _output_audio_buffer_list->mNumberBuffers > chn);
1166         memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
1167         return 0;
1168 }
1169
1170
1171 void
1172 CoreAudioPCM::launch_control_app (uint32_t device_id)
1173 {
1174         if (device_id >= _n_devices) {
1175                 return;
1176         }
1177
1178         CFStringRef config_app = NULL;
1179         UInt32 size = sizeof (config_app);
1180         OSStatus err;
1181
1182         err = GetPropertyWrapper(_device_ids[device_id], 0, false, kAudioDevicePropertyConfigurationApplication, &size, &config_app);
1183         if (kAudioHardwareNoError != err) {
1184                 return;
1185         }
1186
1187         FSRef appFSRef;
1188         if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, config_app, NULL, &appFSRef, NULL)) {
1189                 LSOpenFSRef(&appFSRef, NULL);
1190         } else {
1191                 // open default AudioMIDISetup if device app is not found
1192                 CFStringRef audioMidiSetup = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman);
1193                 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, audioMidiSetup, NULL, &appFSRef, NULL)) {
1194                         LSOpenFSRef(&appFSRef, NULL);
1195                 }
1196         }
1197         if (config_app) {
1198                 CFRelease (config_app);
1199         }
1200 }