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