globally change all use of "frame" to refer to audio into "sample".
[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 (prop, 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 inNumberSamples,
188                 AudioBufferList* ioData)
189 {
190         CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
191         return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberSamples, 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::get_latency(bool input)
484 {
485         if (_active_device_id == 0) {
486                 return 0;
487         }
488         return get_latency (_active_device_id, input);
489 }
490
491 uint32_t
492 CoreAudioPCM::current_buffer_size_id(AudioDeviceID id) {
493         UInt32 buffer_size;
494         UInt32 size = sizeof(UInt32);
495         OSStatus err;
496         err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyBufferFrameSize, &size, &buffer_size);
497         if (err != noErr) {
498                 return _samples_per_period;
499         }
500         return buffer_size;
501 }
502
503
504 float
505 CoreAudioPCM::current_sample_rate_id(AudioDeviceID id, bool input) {
506         OSStatus err;
507         UInt32 size = 0;
508         Float64 rate;
509         size = sizeof (rate);
510
511         err = GetPropertyWrapper(id, 0, input, kAudioDevicePropertyNominalSampleRate, &size, &rate);
512         if (err == noErr) {
513                 return rate;
514         }
515         return 0;
516 }
517
518 float
519 CoreAudioPCM::current_sample_rate(uint32_t device_id, bool input) {
520         if (device_id >= _n_devices) {
521                 return -1;
522         }
523         return current_sample_rate_id(_device_ids[device_id], input);
524 }
525
526 float
527 CoreAudioPCM::sample_rate() {
528         if (_active_device_id == 0) {
529                 return 0;
530         }
531         return current_sample_rate_id(_active_device_id, _playback_channels > 0 ? false : true);
532 }
533
534 int
535 CoreAudioPCM::set_device_sample_rate_id (AudioDeviceID id, float rate, bool input)
536 {
537         std::vector<int>::iterator intIter;
538         OSStatus err;
539         UInt32 size = 0;
540
541         if (current_sample_rate_id(id, input) == rate) {
542                 return 0;
543         }
544
545         Float64 newNominalRate = rate;
546         size = sizeof (Float64);
547
548         err = SetPropertyWrapper(id, NULL, 0, input, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate);
549         if (err != noErr) {
550                 fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n");
551                 return 0;
552         }
553
554         int timeout = 3000; // 3 sec
555         while (--timeout > 0) {
556                 if (current_sample_rate_id(id, input) == rate) {
557                         break;
558                 }
559                 Glib::usleep (1000);
560         }
561         fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate took %d ms.\n", (3000 - timeout));
562
563         if (timeout == 0) {
564                 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate timed out.\n");
565                 return -1;
566         }
567
568         return 0;
569 }
570
571 int
572 CoreAudioPCM::set_device_sample_rate (uint32_t device_id, float rate, bool input)
573 {
574         if (device_id >= _n_devices) {
575                 return 0;
576         }
577         return set_device_sample_rate_id(_device_ids[device_id], rate, input);
578 }
579
580 void
581 CoreAudioPCM::discover()
582 {
583         OSStatus err;
584         UInt32 size = 0;
585
586         if (pthread_mutex_trylock (&_discovery_lock)) {
587                 return;
588         }
589
590         if (_device_ids) {
591                 delete _device_ids; _device_ids = 0;
592                 free(_device_ins); _device_ins = 0;
593                 free(_device_outs); _device_outs = 0;
594         }
595         _devices.clear();
596
597         err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyDevices, &size);
598
599         _n_devices = size / sizeof (AudioDeviceID);
600         size = _n_devices * sizeof (AudioDeviceID);
601
602         _device_ids = new AudioDeviceID[_n_devices];
603         _device_ins = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
604         _device_outs = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
605
606         assert(_device_ins && _device_outs && _device_ids);
607         if (!_device_ins || !_device_ins || !_device_ids) {
608                 fprintf(stderr, "OUT OF MEMORY\n");
609                 _device_ids = 0;
610                 _device_ins = 0;
611                 _device_outs = 0;
612                 pthread_mutex_unlock (&_discovery_lock);
613                 return;
614         }
615
616         err = GetHardwarePropertyWrapper (kAudioHardwarePropertyDevices, &size, _device_ids);
617
618         for (size_t idx = 0; idx < _n_devices; ++idx) {
619                 size = 64;
620                 char deviceName[64];
621                 err = GetPropertyWrapper (_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName);
622
623                 if (kAudioHardwareNoError != err) {
624                         fprintf(stderr, "CoreAudioPCM: device name query failed\n");
625                         continue;
626                 }
627
628                 UInt32 inputChannelCount = available_channels(idx, true);
629                 UInt32 outputChannelCount = available_channels(idx, false);
630
631                 {
632                         std::string dn = deviceName;
633                         _device_ins[idx] = inputChannelCount;
634                         _device_outs[idx] = outputChannelCount;
635 #ifndef NDEBUG
636                         printf("CoreAudio Device: #%ld (id:%lu) '%s' in:%u out:%u\n", idx,
637                                         (long unsigned int)_device_ids[idx],
638                                         deviceName,
639                                         (unsigned int)inputChannelCount, (unsigned int)outputChannelCount);
640 #endif
641                         if (outputChannelCount > 0 || inputChannelCount > 0) {
642                                 _devices.insert (std::pair<size_t, std::string> (idx, dn));
643                         }
644                         if (inputChannelCount > 0) {
645                                 _input_devices.insert (std::pair<size_t, std::string> (idx, dn));
646                         }
647                         if (outputChannelCount > 0) {
648                                 _output_devices.insert (std::pair<size_t, std::string> (idx, dn));
649                         }
650                         if (outputChannelCount > 0 && inputChannelCount > 0) {
651                                 _duplex_devices.insert (std::pair<size_t, std::string> (idx, dn));
652                         }
653                 }
654         }
655         pthread_mutex_unlock (&_discovery_lock);
656 }
657
658 void
659 CoreAudioPCM::xrun_callback ()
660 {
661 #ifndef NDEBUG
662         printf("Coreaudio XRUN\n");
663 #endif
664         if (_xrun_callback) {
665                 _xrun_callback(_xrun_arg);
666         }
667 }
668
669 void
670 CoreAudioPCM::buffer_size_callback ()
671 {
672         _samples_per_period = current_buffer_size_id(_active_device_id);
673
674         if (_buffer_size_callback) {
675                 _buffer_size_callback(_buffer_size_arg);
676         }
677 }
678
679 void
680 CoreAudioPCM::sample_rate_callback ()
681 {
682 #ifndef NDEBUG
683         printf("Sample Rate Changed!\n");
684 #endif
685         if (_sample_rate_callback) {
686                 _sample_rate_callback(_sample_rate_arg);
687         }
688 }
689
690 void
691 CoreAudioPCM::pcm_stop ()
692 {
693         if (!_auhal) return;
694
695         AudioOutputUnitStop(_auhal);
696         if (_state == 0) {
697 #ifdef COREAUDIO_108
698                 AudioObjectPropertyAddress prop;
699                 prop.mScope = kAudioObjectPropertyScopeGlobal;
700                 prop.mElement = 0;
701                 if (_active_device_id > 0) {
702                         prop.mSelector = kAudioDeviceProcessorOverload;
703                         AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
704                         prop.mSelector = kAudioDevicePropertyBufferFrameSize;
705                         AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
706                         prop.mSelector = kAudioDevicePropertyNominalSampleRate;
707                         AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
708                 }
709 #else
710                 if (_active_device_id > 0) {
711                         AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDeviceProcessorOverload, property_callback_ptr);
712                         AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyBufferFrameSize, property_callback_ptr);
713                         AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyNominalSampleRate, property_callback_ptr);
714                 }
715 #endif
716         }
717         if (_aggregate_plugin_id) {
718                 destroy_aggregate_device();
719                 discover();
720         }
721
722         AudioUnitUninitialize(_auhal);
723 #ifdef COREAUDIO_108
724         AudioComponentInstanceDispose(_auhal);
725 #else
726         CloseComponent(_auhal);
727 #endif
728         _auhal = 0;
729         _state = -1;
730         _capture_channels = 0;
731         _playback_channels = 0;
732         _aggregate_plugin_id = 0;
733         _aggregate_device_id = 0;
734         _active_device_id = 0;
735
736         free(_input_audio_buffer_list);
737         _input_audio_buffer_list = 0;
738
739         _input_names.clear();
740         _output_names.clear();
741
742         _error_callback = 0;
743         _process_callback = 0;
744         _xrun_callback = 0;
745 }
746
747 #ifndef NDEBUG
748 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
749 {
750         printf ("- - - - - - - - - - - - - - - - - - - -\n");
751         printf ("  Sample Rate:%.2f",        inDesc->mSampleRate);
752         printf ("  Format ID:%.*s\n",        (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
753         printf ("  Format Flags:%X\n",       (unsigned int)inDesc->mFormatFlags);
754         printf ("  Bytes per Packet:%d\n",   (int)inDesc->mBytesPerPacket);
755         printf ("  Samples per Packet:%d\n",  (int)inDesc->mSamplesPerPacket);
756         printf ("  Bytes per Frame:%d\n",    (int)inDesc->mBytesPerFrame);
757         printf ("  Channels per Frame:%d\n", (int)inDesc->mChannelsPerFrame);
758         printf ("  Bits per Channel:%d\n",   (int)inDesc->mBitsPerChannel);
759         printf ("- - - - - - - - - - - - - - - - - - - -\n");
760 }
761 #endif
762
763 int
764 CoreAudioPCM::set_device_buffer_size_id (AudioDeviceID id, uint32_t samples_per_period)
765 {
766         OSStatus err;
767         UInt32 uint32val;
768
769         uint32val = samples_per_period;
770         err = SetPropertyWrapper(id, NULL, 0, true, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
771         if (err != noErr) { return -1; }
772         err = SetPropertyWrapper(id, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
773         if (err != noErr) { return -1; }
774         return 0;
775 }
776
777 int
778 CoreAudioPCM::set_samples_per_period (uint32_t n_samples)
779 {
780
781         if (_state != 0 || _active_device_id == 0) {
782                 return -1;
783         }
784         set_device_buffer_size_id (_active_device_id, n_samples);
785         return 0;
786 }
787
788 int
789 CoreAudioPCM::pcm_start (
790                 uint32_t device_id_in, uint32_t device_id_out,
791                 uint32_t sample_rate, uint32_t samples_per_period,
792                 int (process_callback (void*, const uint32_t, const uint64_t)), void *process_arg)
793 {
794
795         assert(_device_ids);
796         std::string errorMsg;
797         _state = -99;
798
799         // "None" = UINT32_MAX
800         if (device_id_out >= _n_devices && device_id_in >= _n_devices) {
801                 return -1;
802         }
803
804         pthread_mutex_lock (&_discovery_lock);
805
806         _process_callback = process_callback;
807         _process_arg = process_arg;
808         _samples_per_period = samples_per_period;
809         _cur_samples_per_period = 0;
810         _active_device_id = 0;
811         _capture_channels = 0;
812         _playback_channels = 0;
813
814         const uint32_t chn_in = (device_id_in < _n_devices ? _device_ins[device_id_in] : 0) + ((device_id_out != device_id_in && device_id_out < _n_devices) ? _device_ins[device_id_out] : 0);
815         const uint32_t chn_out =(device_id_out < _n_devices ? _device_outs[device_id_out] : 0) + ((device_id_out != device_id_in && device_id_in < _n_devices) ? _device_outs[device_id_in] : 0);
816
817         assert (chn_in > 0 || chn_out > 0);
818
819         ComponentResult err;
820         UInt32 uint32val;
821         UInt32 size;
822         AudioDeviceID device_id;
823         AudioStreamBasicDescription srcFormat, dstFormat;
824
825 #ifndef COREAUDIO_108
826         ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
827         Component HALOutput = FindNextComponent(NULL, &cd);
828         if (!HALOutput) { errorMsg="FindNextComponent"; _state = -2; goto error; }
829
830         err = OpenAComponent(HALOutput, &_auhal);
831         if (err != noErr) { errorMsg="OpenAComponent"; _state = -2; goto error; }
832 #else
833         AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
834         AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
835         if (!HALOutput) { errorMsg="AudioComponentFindNext"; _state = -2; goto error; }
836
837         err = AudioComponentInstanceNew(HALOutput, &_auhal);
838         if (err != noErr) { errorMsg="AudioComponentInstanceNew"; _state = -2; goto error; }
839 #endif
840
841         err = AudioUnitInitialize(_auhal);
842         if (err != noErr) { errorMsg="AudioUnitInitialize"; _state = -3; goto error; }
843
844         // explicitly change samplerate of the devices, TODO allow separate rates with aggregates
845         if (set_device_sample_rate(device_id_in, sample_rate, true)) {
846                 errorMsg="Failed to set SampleRate, Capture Device"; _state = -4; goto error;
847         }
848         if (set_device_sample_rate(device_id_out, sample_rate, false)) {
849                 errorMsg="Failed to set SampleRate, Playback Device"; _state = -4; goto error;
850         }
851
852         // explicitly request device buffer size
853         if (device_id_in < _n_devices && set_device_buffer_size_id(_device_ids[device_id_in], samples_per_period)) {
854                 errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; _state = -5; goto error;
855         }
856         if (device_id_out < _n_devices && set_device_buffer_size_id(_device_ids[device_id_out], samples_per_period)) {
857                 errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; _state = -5; goto error;
858         }
859
860         // create aggregate device..
861         if (device_id_in < _n_devices && device_id_out < _n_devices && _device_ids[device_id_in] != _device_ids[device_id_out]) {
862                 if (0 == create_aggregate_device(_device_ids[device_id_in], _device_ids[device_id_out], sample_rate, &_aggregate_device_id)) {
863                         device_id = _aggregate_device_id;
864                 } else {
865                         _aggregate_device_id = 0;
866                         _aggregate_plugin_id = 0;
867                         errorMsg="Cannot create Aggregate Device"; _state = -12; goto error;
868                 }
869         } else if (device_id_out < _n_devices) {
870                 device_id = _device_ids[device_id_out];
871         } else {
872                 assert (device_id_in < _n_devices);
873                 device_id = _device_ids[device_id_in];
874         }
875
876         if (device_id_out != device_id_in) {
877                 assert(_aggregate_device_id > 0 || device_id_in >= _n_devices || device_id_out >= _n_devices);
878         }
879
880         // enableIO to progress further
881         uint32val = (chn_in > 0) ? 1 : 0;
882         err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32));
883         if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; _state = -7; goto error; }
884
885         uint32val = (chn_out > 0) ? 1 : 0;
886         err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &uint32val, sizeof(UInt32));
887         if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Output"; _state = -7; goto error; }
888
889         err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
890         if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Input"; _state = -7; goto error; }
891
892         if (chn_in > 0) {
893                 // set sample format
894                 srcFormat.mSampleRate = sample_rate;
895                 srcFormat.mFormatID = kAudioFormatLinearPCM;
896                 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
897                 srcFormat.mBytesPerPacket = sizeof(float);
898                 srcFormat.mSamplesPerPacket = 1;
899                 srcFormat.mBytesPerFrame = sizeof(float);
900                 srcFormat.mChannelsPerFrame = chn_in;
901                 srcFormat.mBitsPerChannel = 32;
902
903                 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription));
904                 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; _state = -6; goto error; }
905
906                 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumSamplesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
907                 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumSamplesPerSlice, Input"; _state = -6; goto error; }
908         }
909
910         if (chn_out > 0) {
911                 dstFormat.mSampleRate = sample_rate;
912                 dstFormat.mFormatID = kAudioFormatLinearPCM;
913                 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
914                 dstFormat.mBytesPerPacket = sizeof(float);
915                 dstFormat.mSamplesPerPacket = 1;
916                 dstFormat.mBytesPerFrame = sizeof(float);
917                 dstFormat.mChannelsPerFrame = chn_out;
918                 dstFormat.mBitsPerChannel = 32;
919
920                 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription));
921                 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; _state = -5; goto error; }
922
923                 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumSamplesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
924                 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumSamplesPerSlice, Output"; _state = -5; goto error; }
925         }
926
927         /* read back stream descriptions */
928         if (chn_in > 0) {
929                 size = sizeof(AudioStreamBasicDescription);
930                 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
931                 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; _state = -5; goto error; }
932                 _capture_channels = srcFormat.mChannelsPerFrame;
933 #ifndef NDEBUG
934                 PrintStreamDesc(&srcFormat);
935 #endif
936         }
937
938         if (chn_out > 0) {
939                 size = sizeof(AudioStreamBasicDescription);
940                 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size);
941                 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; _state = -5; goto error; }
942                 _playback_channels = dstFormat.mChannelsPerFrame;
943
944 #ifndef NDEBUG
945                 PrintStreamDesc(&dstFormat);
946 #endif
947         }
948
949         /* prepare buffers for input */
950         if (_capture_channels > 0) {
951                 _input_audio_buffer_list = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_capture_channels - 1) * sizeof(AudioBuffer));
952                 assert(_input_audio_buffer_list);
953                 if (!_input_audio_buffer_list) { errorMsg="Out of Memory."; _state = -8; goto error; }
954         }
955
956         _active_device_id = device_id;
957
958         // add Listeners
959         err = add_listener (_active_device_id, kAudioDeviceProcessorOverload, this);
960         if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Listen"; _state = -9; goto error; }
961
962         err = add_listener (_active_device_id, kAudioDevicePropertyBufferFrameSize, this);
963         if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; _state = -9; goto error; }
964
965         err = add_listener (_active_device_id, kAudioDevicePropertyNominalSampleRate, this);
966         if (err != noErr) { errorMsg="kAudioDevicePropertyNominalSampleRate, Listen"; _state = -9; goto error; }
967
968         _samples_per_period = current_buffer_size_id(_active_device_id);
969
970         // Setup callback
971         AURenderCallbackStruct renderCallback;
972         memset (&renderCallback, 0, sizeof (renderCallback));
973         renderCallback.inputProc = render_callback_ptr;
974         renderCallback.inputProcRefCon = this;
975         if (_playback_channels == 0) {
976                 err = AudioUnitSetProperty(_auhal,
977                                 kAudioOutputUnitProperty_SetInputCallback,
978                                 kAudioUnitScope_Output, 1,
979                                 &renderCallback, sizeof (renderCallback));
980         } else {
981                 err = AudioUnitSetProperty(_auhal,
982                                 kAudioUnitProperty_SetRenderCallback,
983                                 kAudioUnitScope_Output, 0,
984                                 &renderCallback, sizeof (renderCallback));
985         }
986
987         if (err != noErr) { errorMsg="kAudioUnitProperty_SetRenderCallback"; _state = -10; goto error; }
988
989         /* setup complete, now get going.. */
990         if (AudioOutputUnitStart(_auhal) == noErr) {
991                 _input_names.clear();
992                 _output_names.clear();
993                 cache_port_names (device_id, true);
994                 cache_port_names (device_id, false);
995                 _state = 0;
996                 pthread_mutex_unlock (&_discovery_lock);
997
998                 // kick device
999                 if (set_device_buffer_size_id(_active_device_id, samples_per_period)) {
1000                         errorMsg="kAudioDevicePropertyBufferFrameSize"; _state = -11; goto error;
1001                 }
1002
1003                 return 0;
1004         }
1005
1006 error:
1007         assert (_state != 0);
1008         char *rv = (char*)&err;
1009         fprintf(stderr, "CoreaudioPCM Error: %c%c%c%c %s\n", rv[0], rv[1], rv[2], rv[3], errorMsg.c_str());
1010         pcm_stop();
1011         _active_device_id = 0;
1012         pthread_mutex_unlock (&_discovery_lock);
1013         return -1;
1014 }
1015
1016 void
1017 CoreAudioPCM::cache_port_names(AudioDeviceID id, bool input)
1018 {
1019         uint32_t n_chn;
1020
1021         if (input) {
1022                 n_chn = _capture_channels;
1023         } else {
1024                 n_chn = _playback_channels;;
1025         }
1026 #ifdef COREAUDIO_108
1027         AudioObjectPropertyAddress property_address;
1028         property_address.mSelector = kAudioObjectPropertyElementName;
1029         property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
1030 #endif
1031
1032         for (uint32_t c = 0; c < n_chn; ++c) {
1033                 CFStringRef name = NULL;
1034                 std::stringstream ss;
1035                 UInt32 size = 0;
1036                 OSStatus err;
1037
1038 #ifdef COREAUDIO_108
1039                 property_address.mElement = c + 1;
1040                 err = AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, &size);
1041 #else
1042                 err = AudioDeviceGetPropertyInfo (id, c + 1, input,
1043                                 kAudioDevicePropertyChannelNameCFString,
1044                                 &size,
1045                                 NULL);
1046 #endif
1047
1048                 if (err == kAudioHardwareNoError) {
1049 #ifdef COREAUDIO_108
1050                         err = AudioObjectGetPropertyData(id, &property_address, c + 1, NULL, &size, &name);
1051 #else
1052                         err = AudioDeviceGetProperty (id, c + 1, input,
1053                                         kAudioDevicePropertyChannelNameCFString,
1054                                         &size,
1055                                         &name);
1056 #endif
1057                 }
1058
1059                 bool decoded = false;
1060                 char* cstr_name = 0;
1061                 if (err == kAudioHardwareNoError) {
1062                         CFIndex length = CFStringGetLength(name);
1063                         CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
1064                         cstr_name = new char[maxSize];
1065                         decoded = CFStringGetCString(name, cstr_name, maxSize, kCFStringEncodingUTF8);
1066                 }
1067
1068                 ss << (c + 1);
1069
1070                 if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
1071                         ss << " - " <<  cstr_name;
1072                 }
1073 #if 0
1074                 printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
1075 #endif
1076
1077                 if (input) {
1078                         _input_names.push_back (ss.str());
1079                 } else {
1080                         _output_names.push_back (ss.str());
1081                 }
1082
1083                 if (name) {
1084                         CFRelease (name);
1085                 }
1086                 delete [] cstr_name;
1087         }
1088 }
1089
1090 std::string
1091 CoreAudioPCM::cached_port_name(uint32_t port, bool input) const
1092 {
1093         if (_state != 0) { return ""; }
1094
1095         if (input) {
1096                 if (port >= _input_names.size()) {
1097                         return "";
1098                 }
1099                 return _input_names[port];
1100         } else {
1101                 if (port >= _output_names.size()) {
1102                         return "";
1103                 }
1104                 return _output_names[port];
1105         }
1106 }
1107
1108
1109 OSStatus
1110 CoreAudioPCM::render_callback (
1111                 AudioUnitRenderActionFlags* ioActionFlags,
1112                 const AudioTimeStamp* inTimeStamp,
1113                 UInt32 inBusNumber,
1114                 UInt32 inNumberSamples,
1115                 AudioBufferList* ioData)
1116 {
1117         OSStatus retVal = kAudioHardwareNoError;
1118
1119         if (_samples_per_period < inNumberSamples) {
1120 #ifndef NDEBUG
1121                 printf("samples per period exceeds configured value, cycle skipped (%u < %u)\n",
1122                                 (unsigned int)_samples_per_period, (unsigned int)inNumberSamples);
1123 #endif
1124                 for (uint32_t i = 0; _playback_channels > 0 && i < ioData->mNumberBuffers; ++i) {
1125                         float* ob = (float*) ioData->mBuffers[i].mData;
1126                         memset(ob, 0, sizeof(float) * inNumberSamples);
1127                 }
1128                 return noErr;
1129         }
1130
1131         assert(_playback_channels == 0 || ioData->mNumberBuffers == _playback_channels);
1132
1133         UInt64 cur_cycle_start = AudioGetCurrentHostTime ();
1134         _cur_samples_per_period = inNumberSamples;
1135
1136         if (_capture_channels > 0) {
1137                 _input_audio_buffer_list->mNumberBuffers = _capture_channels;
1138                 for (uint32_t i = 0; i < _capture_channels; ++i) {
1139                         _input_audio_buffer_list->mBuffers[i].mNumberChannels = 1;
1140                         _input_audio_buffer_list->mBuffers[i].mDataByteSize = inNumberSamples * sizeof(float);
1141                         _input_audio_buffer_list->mBuffers[i].mData = NULL;
1142                 }
1143
1144                 retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberSamples, _input_audio_buffer_list);
1145         }
1146
1147         if (retVal != kAudioHardwareNoError) {
1148 #if 0
1149                 char *rv = (char*)&retVal;
1150                 printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
1151 #endif
1152                 if (_error_callback) {
1153                         _error_callback(_error_arg);
1154                 }
1155                 return retVal;
1156         }
1157
1158         _output_audio_buffer_list = ioData;
1159
1160         _in_process = true;
1161
1162         int rv = -1;
1163
1164         if (_process_callback) {
1165                 rv = _process_callback(_process_arg, inNumberSamples, cur_cycle_start);
1166         }
1167
1168         _in_process = false;
1169
1170         if (rv != 0 && _playback_channels > 0) {
1171                 // clear output
1172                 for (uint32_t i = 0; i < ioData->mNumberBuffers; ++i) {
1173                         float* ob = (float*) ioData->mBuffers[i].mData;
1174                         memset(ob, 0, sizeof(float) * inNumberSamples);
1175                 }
1176         }
1177         return noErr;
1178 }
1179
1180 int
1181 CoreAudioPCM::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
1182 {
1183         if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
1184                 return -1;
1185         }
1186         assert(_input_audio_buffer_list->mNumberBuffers > chn);
1187         memcpy((void*)input, (void*)_input_audio_buffer_list->mBuffers[chn].mData, sizeof(float) * n_samples);
1188         return 0;
1189
1190 }
1191 int
1192 CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
1193 {
1194         if (!_in_process || chn > _playback_channels || n_samples > _cur_samples_per_period) {
1195                 return -1;
1196         }
1197
1198         assert(_output_audio_buffer_list && _output_audio_buffer_list->mNumberBuffers > chn);
1199         memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
1200         return 0;
1201 }
1202
1203
1204 void
1205 CoreAudioPCM::launch_control_app (uint32_t device_id)
1206 {
1207         if (device_id >= _n_devices) {
1208                 return;
1209         }
1210
1211         CFStringRef config_app = NULL;
1212         UInt32 size = sizeof (config_app);
1213         OSStatus err;
1214
1215         err = GetPropertyWrapper(_device_ids[device_id], 0, false, kAudioDevicePropertyConfigurationApplication, &size, &config_app);
1216         if (kAudioHardwareNoError != err) {
1217                 return;
1218         }
1219
1220         FSRef appFSRef;
1221         if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, config_app, NULL, &appFSRef, NULL)) {
1222                 LSOpenFSRef(&appFSRef, NULL);
1223         } else {
1224                 // open default AudioMIDISetup if device app is not found
1225                 CFStringRef audioMidiSetup = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman);
1226                 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, audioMidiSetup, NULL, &appFSRef, NULL)) {
1227                         LSOpenFSRef(&appFSRef, NULL);
1228                 }
1229         }
1230         if (config_app) {
1231                 CFRelease (config_app);
1232         }
1233 }