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