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