1 /*
2 * Copyright © 2011 Mozilla Foundation
3 *
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
6 */
7 #undef NDEBUG
8
9 #include <TargetConditionals.h>
10 #include <assert.h>
11 #include <mach/mach_time.h>
12 #include <pthread.h>
13 #include <stdlib.h>
14 #include <AudioUnit/AudioUnit.h>
15 #if !TARGET_OS_IPHONE
16 #include <AvailabilityMacros.h>
17 #include <CoreAudio/AudioHardware.h>
18 #include <CoreAudio/HostTime.h>
19 #include <CoreFoundation/CoreFoundation.h>
20 #endif
21 #include <CoreAudio/CoreAudioTypes.h>
22 #include <AudioToolbox/AudioToolbox.h>
23 #include "cubeb/cubeb.h"
24 #include "cubeb-internal.h"
25 #include "cubeb_mixer.h"
26 #if !TARGET_OS_IPHONE
27 #include "cubeb_osx_run_loop.h"
28 #endif
29 #include "cubeb_resampler.h"
30 #include "cubeb_ring_array.h"
31 #include <algorithm>
32 #include <atomic>
33 #include <vector>
34 #include <set>
35 #include <sys/time.h>
36 #include <string>
37
38 using namespace std;
39
40 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
41 typedef UInt32 AudioFormatFlags;
42 #endif
43
44 #define AU_OUT_BUS 0
45 #define AU_IN_BUS 1
46
47 const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
48 const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice";
49
50 #ifdef ALOGV
51 #undef ALOGV
52 #endif
53 #define ALOGV(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOGV(msg, ##__VA_ARGS__);})
54
55 #ifdef ALOG
56 #undef ALOG
57 #endif
58 #define ALOG(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOG(msg, ##__VA_ARGS__);})
59
60 /* Testing empirically, some headsets report a minimal latency that is very
61 * low, but this does not work in practice. Lie and say the minimum is 256
62 * frames. */
63 const uint32_t SAFE_MIN_LATENCY_FRAMES = 128;
64 const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
65
66 const AudioObjectPropertyAddress DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS = {
67 kAudioHardwarePropertyDefaultInputDevice,
68 kAudioObjectPropertyScopeGlobal,
69 kAudioObjectPropertyElementMaster
70 };
71
72 const AudioObjectPropertyAddress DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS = {
73 kAudioHardwarePropertyDefaultOutputDevice,
74 kAudioObjectPropertyScopeGlobal,
75 kAudioObjectPropertyElementMaster
76 };
77
78 const AudioObjectPropertyAddress DEVICE_IS_ALIVE_PROPERTY_ADDRESS = {
79 kAudioDevicePropertyDeviceIsAlive,
80 kAudioObjectPropertyScopeGlobal,
81 kAudioObjectPropertyElementMaster
82 };
83
84 const AudioObjectPropertyAddress DEVICES_PROPERTY_ADDRESS = {
85 kAudioHardwarePropertyDevices,
86 kAudioObjectPropertyScopeGlobal,
87 kAudioObjectPropertyElementMaster
88 };
89
90 const AudioObjectPropertyAddress INPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
91 kAudioDevicePropertyDataSource,
92 kAudioDevicePropertyScopeInput,
93 kAudioObjectPropertyElementMaster
94 };
95
96 const AudioObjectPropertyAddress OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
97 kAudioDevicePropertyDataSource,
98 kAudioDevicePropertyScopeOutput,
99 kAudioObjectPropertyElementMaster
100 };
101
102 typedef uint32_t device_flags_value;
103
104 enum device_flags {
105 DEV_UNKNOWN = 0x00, /* Unknown */
106 DEV_INPUT = 0x01, /* Record device like mic */
107 DEV_OUTPUT = 0x02, /* Playback device like speakers */
108 DEV_SYSTEM_DEFAULT = 0x04, /* System default device */
109 DEV_SELECTED_DEFAULT = 0x08, /* User selected to use the system default device */
110 };
111
112 void audiounit_stream_stop_internal(cubeb_stream * stm);
113 static int audiounit_stream_start_internal(cubeb_stream * stm);
114 static void audiounit_close_stream(cubeb_stream *stm);
115 static int audiounit_setup_stream(cubeb_stream *stm);
116 static vector<AudioObjectID>
117 audiounit_get_devices_of_type(cubeb_device_type devtype);
118 static UInt32 audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope);
119
120 #if !TARGET_OS_IPHONE
121 static AudioObjectID audiounit_get_default_device_id(cubeb_device_type type);
122 static int audiounit_uninstall_device_changed_callback(cubeb_stream * stm);
123 static int audiounit_uninstall_system_changed_callback(cubeb_stream * stm);
124 static void audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags);
125 #endif
126
127 extern cubeb_ops const audiounit_ops;
128
129 struct cubeb {
130 cubeb_ops const * ops = &audiounit_ops;
131 owned_critical_section mutex;
132 int active_streams = 0;
133 uint32_t global_latency_frames = 0;
134 cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr;
135 void * input_collection_changed_user_ptr = nullptr;
136 cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr;
137 void * output_collection_changed_user_ptr = nullptr;
138 // Store list of devices to detect changes
139 vector<AudioObjectID> input_device_array;
140 vector<AudioObjectID> output_device_array;
141 // The queue should be released when it’s no longer needed.
142 dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL);
143 // Current used channel layout
144 atomic<cubeb_channel_layout> layout{ CUBEB_LAYOUT_UNDEFINED };
145 uint32_t channels = 0;
146 };
147
148 static unique_ptr<AudioChannelLayout, decltype(&free)>
make_sized_audio_channel_layout(size_t sz)149 make_sized_audio_channel_layout(size_t sz)
150 {
151 assert(sz >= sizeof(AudioChannelLayout));
152 AudioChannelLayout * acl = reinterpret_cast<AudioChannelLayout *>(calloc(1, sz));
153 assert(acl); // Assert the allocation works.
154 return unique_ptr<AudioChannelLayout, decltype(&free)>(acl, free);
155 }
156
157 enum class io_side {
158 INPUT,
159 OUTPUT,
160 };
161
162 static char const *
to_string(io_side side)163 to_string(io_side side)
164 {
165 switch (side) {
166 case io_side::INPUT:
167 return "input";
168 case io_side::OUTPUT:
169 return "output";
170 }
171 }
172
173 struct device_info {
174 AudioDeviceID id = kAudioObjectUnknown;
175 device_flags_value flags = DEV_UNKNOWN;
176 };
177
178 struct property_listener {
179 AudioDeviceID device_id;
180 const AudioObjectPropertyAddress * property_address;
181 AudioObjectPropertyListenerProc callback;
182 cubeb_stream * stream;
183
property_listenerproperty_listener184 property_listener(AudioDeviceID id,
185 const AudioObjectPropertyAddress * address,
186 AudioObjectPropertyListenerProc proc,
187 cubeb_stream * stm)
188 : device_id(id)
189 , property_address(address)
190 , callback(proc)
191 , stream(stm)
192 {}
193 };
194
195 struct cubeb_stream {
196 explicit cubeb_stream(cubeb * context);
197
198 /* Note: Must match cubeb_stream layout in cubeb.c. */
199 cubeb * context;
200 void * user_ptr = nullptr;
201 /**/
202
203 cubeb_data_callback data_callback = nullptr;
204 cubeb_state_callback state_callback = nullptr;
205 cubeb_device_changed_callback device_changed_callback = nullptr;
206 owned_critical_section device_changed_callback_lock;
207 /* Stream creation parameters */
208 cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
209 cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
210 device_info input_device;
211 device_info output_device;
212 /* Format descriptions */
213 AudioStreamBasicDescription input_desc;
214 AudioStreamBasicDescription output_desc;
215 /* I/O AudioUnits */
216 AudioUnit input_unit = nullptr;
217 AudioUnit output_unit = nullptr;
218 /* I/O device sample rate */
219 Float64 input_hw_rate = 0;
220 Float64 output_hw_rate = 0;
221 /* Expected I/O thread interleave,
222 * calculated from I/O hw rate. */
223 int expected_output_callbacks_in_a_row = 0;
224 owned_critical_section mutex;
225 // Hold the input samples in every input callback iteration.
226 // Only accessed on input/output callback thread and during initial configure.
227 unique_ptr<auto_array_wrapper> input_linear_buffer;
228 /* Frame counters */
229 atomic<uint64_t> frames_played{ 0 };
230 uint64_t frames_queued = 0;
231 // How many frames got read from the input since the stream started (includes
232 // padded silence)
233 atomic<int64_t> frames_read{ 0 };
234 // How many frames got written to the output device since the stream started
235 atomic<int64_t> frames_written{ 0 };
236 atomic<bool> shutdown{ true };
237 atomic<bool> draining{ false };
238 atomic<bool> reinit_pending { false };
239 atomic<bool> destroy_pending{ false };
240 /* Latency requested by the user. */
241 uint32_t latency_frames = 0;
242 atomic<uint32_t> current_latency_frames{ 0 };
243 atomic<uint32_t> total_output_latency_frames { 0 };
244 unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
245 /* This is true if a device change callback is currently running. */
246 atomic<bool> switching_device{ false };
247 atomic<bool> buffer_size_change_state{ false };
248 AudioDeviceID aggregate_device_id = kAudioObjectUnknown; // the aggregate device id
249 AudioObjectID plugin_id = kAudioObjectUnknown; // used to create aggregate device
250 /* Mixer interface */
251 unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer;
252 /* Buffer where remixing/resampling will occur when upmixing is required */
253 /* Only accessed from callback thread */
254 unique_ptr<uint8_t[]> temp_buffer;
255 size_t temp_buffer_size = 0; // size in bytes.
256 /* Listeners indicating what system events are monitored. */
257 unique_ptr<property_listener> default_input_listener;
258 unique_ptr<property_listener> default_output_listener;
259 unique_ptr<property_listener> input_alive_listener;
260 unique_ptr<property_listener> input_source_listener;
261 unique_ptr<property_listener> output_source_listener;
262 };
263
has_input(cubeb_stream * stm)264 bool has_input(cubeb_stream * stm)
265 {
266 return stm->input_stream_params.rate != 0;
267 }
268
has_output(cubeb_stream * stm)269 bool has_output(cubeb_stream * stm)
270 {
271 return stm->output_stream_params.rate != 0;
272 }
273
274 cubeb_channel
channel_label_to_cubeb_channel(UInt32 label)275 channel_label_to_cubeb_channel(UInt32 label)
276 {
277 switch (label) {
278 case kAudioChannelLabel_Left:
279 return CHANNEL_FRONT_LEFT;
280 case kAudioChannelLabel_Right:
281 return CHANNEL_FRONT_RIGHT;
282 case kAudioChannelLabel_Center:
283 return CHANNEL_FRONT_CENTER;
284 case kAudioChannelLabel_LFEScreen:
285 return CHANNEL_LOW_FREQUENCY;
286 case kAudioChannelLabel_LeftSurround:
287 return CHANNEL_BACK_LEFT;
288 case kAudioChannelLabel_RightSurround:
289 return CHANNEL_BACK_RIGHT;
290 case kAudioChannelLabel_LeftCenter:
291 return CHANNEL_FRONT_LEFT_OF_CENTER;
292 case kAudioChannelLabel_RightCenter:
293 return CHANNEL_FRONT_RIGHT_OF_CENTER;
294 case kAudioChannelLabel_CenterSurround:
295 return CHANNEL_BACK_CENTER;
296 case kAudioChannelLabel_LeftSurroundDirect:
297 return CHANNEL_SIDE_LEFT;
298 case kAudioChannelLabel_RightSurroundDirect:
299 return CHANNEL_SIDE_RIGHT;
300 case kAudioChannelLabel_TopCenterSurround:
301 return CHANNEL_TOP_CENTER;
302 case kAudioChannelLabel_VerticalHeightLeft:
303 return CHANNEL_TOP_FRONT_LEFT;
304 case kAudioChannelLabel_VerticalHeightCenter:
305 return CHANNEL_TOP_FRONT_CENTER;
306 case kAudioChannelLabel_VerticalHeightRight:
307 return CHANNEL_TOP_FRONT_RIGHT;
308 case kAudioChannelLabel_TopBackLeft:
309 return CHANNEL_TOP_BACK_LEFT;
310 case kAudioChannelLabel_TopBackCenter:
311 return CHANNEL_TOP_BACK_CENTER;
312 case kAudioChannelLabel_TopBackRight:
313 return CHANNEL_TOP_BACK_RIGHT;
314 default:
315 return CHANNEL_UNKNOWN;
316 }
317 }
318
319 AudioChannelLabel
cubeb_channel_to_channel_label(cubeb_channel channel)320 cubeb_channel_to_channel_label(cubeb_channel channel)
321 {
322 switch (channel) {
323 case CHANNEL_FRONT_LEFT:
324 return kAudioChannelLabel_Left;
325 case CHANNEL_FRONT_RIGHT:
326 return kAudioChannelLabel_Right;
327 case CHANNEL_FRONT_CENTER:
328 return kAudioChannelLabel_Center;
329 case CHANNEL_LOW_FREQUENCY:
330 return kAudioChannelLabel_LFEScreen;
331 case CHANNEL_BACK_LEFT:
332 return kAudioChannelLabel_LeftSurround;
333 case CHANNEL_BACK_RIGHT:
334 return kAudioChannelLabel_RightSurround;
335 case CHANNEL_FRONT_LEFT_OF_CENTER:
336 return kAudioChannelLabel_LeftCenter;
337 case CHANNEL_FRONT_RIGHT_OF_CENTER:
338 return kAudioChannelLabel_RightCenter;
339 case CHANNEL_BACK_CENTER:
340 return kAudioChannelLabel_CenterSurround;
341 case CHANNEL_SIDE_LEFT:
342 return kAudioChannelLabel_LeftSurroundDirect;
343 case CHANNEL_SIDE_RIGHT:
344 return kAudioChannelLabel_RightSurroundDirect;
345 case CHANNEL_TOP_CENTER:
346 return kAudioChannelLabel_TopCenterSurround;
347 case CHANNEL_TOP_FRONT_LEFT:
348 return kAudioChannelLabel_VerticalHeightLeft;
349 case CHANNEL_TOP_FRONT_CENTER:
350 return kAudioChannelLabel_VerticalHeightCenter;
351 case CHANNEL_TOP_FRONT_RIGHT:
352 return kAudioChannelLabel_VerticalHeightRight;
353 case CHANNEL_TOP_BACK_LEFT:
354 return kAudioChannelLabel_TopBackLeft;
355 case CHANNEL_TOP_BACK_CENTER:
356 return kAudioChannelLabel_TopBackCenter;
357 case CHANNEL_TOP_BACK_RIGHT:
358 return kAudioChannelLabel_TopBackRight;
359 default:
360 return kAudioChannelLabel_Unknown;
361 }
362 }
363
364 #if TARGET_OS_IPHONE
365 typedef UInt32 AudioDeviceID;
366 typedef UInt32 AudioObjectID;
367
368 #define AudioGetCurrentHostTime mach_absolute_time
369
370 #endif
371
372 uint64_t
ConvertHostTimeToNanos(uint64_t host_time)373 ConvertHostTimeToNanos(uint64_t host_time)
374 {
375 static struct mach_timebase_info timebase_info;
376 static bool initialized = false;
377 if (!initialized) {
378 mach_timebase_info(&timebase_info);
379 initialized = true;
380 }
381
382 long double answer = host_time;
383 if (timebase_info.numer != timebase_info.denom) {
384 answer *= timebase_info.numer;
385 answer /= timebase_info.denom;
386 }
387 return (uint64_t)answer;
388 }
389
390 static void
audiounit_increment_active_streams(cubeb * ctx)391 audiounit_increment_active_streams(cubeb * ctx)
392 {
393 ctx->mutex.assert_current_thread_owns();
394 ctx->active_streams += 1;
395 }
396
397 static void
audiounit_decrement_active_streams(cubeb * ctx)398 audiounit_decrement_active_streams(cubeb * ctx)
399 {
400 ctx->mutex.assert_current_thread_owns();
401 ctx->active_streams -= 1;
402 }
403
404 static int
audiounit_active_streams(cubeb * ctx)405 audiounit_active_streams(cubeb * ctx)
406 {
407 ctx->mutex.assert_current_thread_owns();
408 return ctx->active_streams;
409 }
410
411 static void
audiounit_set_global_latency(cubeb * ctx,uint32_t latency_frames)412 audiounit_set_global_latency(cubeb * ctx, uint32_t latency_frames)
413 {
414 ctx->mutex.assert_current_thread_owns();
415 assert(audiounit_active_streams(ctx) == 1);
416 ctx->global_latency_frames = latency_frames;
417 }
418
419 static void
audiounit_make_silent(AudioBuffer * ioData)420 audiounit_make_silent(AudioBuffer * ioData)
421 {
422 assert(ioData);
423 assert(ioData->mData);
424 memset(ioData->mData, 0, ioData->mDataByteSize);
425 }
426
427 static OSStatus
audiounit_render_input(cubeb_stream * stm,AudioUnitRenderActionFlags * flags,AudioTimeStamp const * tstamp,UInt32 bus,UInt32 input_frames)428 audiounit_render_input(cubeb_stream * stm,
429 AudioUnitRenderActionFlags * flags,
430 AudioTimeStamp const * tstamp,
431 UInt32 bus,
432 UInt32 input_frames)
433 {
434 /* Create the AudioBufferList to store input. */
435 AudioBufferList input_buffer_list;
436 input_buffer_list.mBuffers[0].mDataByteSize =
437 stm->input_desc.mBytesPerFrame * input_frames;
438 input_buffer_list.mBuffers[0].mData = nullptr;
439 input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame;
440 input_buffer_list.mNumberBuffers = 1;
441
442 /* Render input samples */
443 OSStatus r = AudioUnitRender(stm->input_unit,
444 flags,
445 tstamp,
446 bus,
447 input_frames,
448 &input_buffer_list);
449
450 if (r != noErr) {
451 LOG("AudioUnitRender rv=%d", r);
452 if (r != kAudioUnitErr_CannotDoInCurrentContext) {
453 return r;
454 }
455 if (stm->output_unit) {
456 // kAudioUnitErr_CannotDoInCurrentContext is returned when using a BT
457 // headset and the profile is changed from A2DP to HFP/HSP. The previous
458 // output device is no longer valid and must be reset.
459 audiounit_reinit_stream_async(stm, DEV_INPUT | DEV_OUTPUT);
460 }
461 // For now state that no error occurred and feed silence, stream will be
462 // resumed once reinit has completed.
463 ALOGV("(%p) input: reinit pending feeding silence instead", stm);
464 stm->input_linear_buffer->push_silence(input_frames * stm->input_desc.mChannelsPerFrame);
465 } else {
466 /* Copy input data in linear buffer. */
467 stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
468 input_frames * stm->input_desc.mChannelsPerFrame);
469 }
470
471 /* Advance input frame counter. */
472 assert(input_frames > 0);
473 stm->frames_read += input_frames;
474
475 ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, total frames %lu.",
476 stm,
477 (unsigned int) input_buffer_list.mNumberBuffers,
478 (unsigned int) input_buffer_list.mBuffers[0].mDataByteSize,
479 (unsigned int) input_buffer_list.mBuffers[0].mNumberChannels,
480 (unsigned int) input_frames,
481 stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
482
483 return noErr;
484 }
485
486 static OSStatus
audiounit_input_callback(void * user_ptr,AudioUnitRenderActionFlags * flags,AudioTimeStamp const * tstamp,UInt32 bus,UInt32 input_frames,AudioBufferList *)487 audiounit_input_callback(void * user_ptr,
488 AudioUnitRenderActionFlags * flags,
489 AudioTimeStamp const * tstamp,
490 UInt32 bus,
491 UInt32 input_frames,
492 AudioBufferList * /* bufs */)
493 {
494 cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
495
496 assert(stm->input_unit != NULL);
497 assert(AU_IN_BUS == bus);
498
499 if (stm->shutdown) {
500 ALOG("(%p) input shutdown", stm);
501 return noErr;
502 }
503
504 if (stm->draining) {
505 OSStatus r = AudioOutputUnitStop(stm->input_unit);
506 assert(r == 0);
507 // Only fire state callback in input-only stream. For duplex stream,
508 // the state callback will be fired in output callback.
509 if (stm->output_unit == NULL) {
510 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
511 }
512 return noErr;
513 }
514
515 OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
516 if (r != noErr) {
517 return r;
518 }
519
520 // Full Duplex. We'll call data_callback in the AudioUnit output callback.
521 if (stm->output_unit != NULL) {
522 return noErr;
523 }
524
525 /* Input only. Call the user callback through resampler.
526 Resampler will deliver input buffer in the correct rate. */
527 assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
528 long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
529 long outframes = cubeb_resampler_fill(stm->resampler.get(),
530 stm->input_linear_buffer->data(),
531 &total_input_frames,
532 NULL,
533 0);
534 stm->draining = outframes < total_input_frames;
535
536 // Reset input buffer
537 stm->input_linear_buffer->clear();
538
539 return noErr;
540 }
541
542 static void
audiounit_mix_output_buffer(cubeb_stream * stm,size_t output_frames,void * input_buffer,size_t input_buffer_size,void * output_buffer,size_t output_buffer_size)543 audiounit_mix_output_buffer(cubeb_stream * stm,
544 size_t output_frames,
545 void * input_buffer,
546 size_t input_buffer_size,
547 void * output_buffer,
548 size_t output_buffer_size)
549 {
550 assert(input_buffer_size >=
551 cubeb_sample_size(stm->output_stream_params.format) *
552 stm->output_stream_params.channels * output_frames);
553 assert(output_buffer_size >= stm->output_desc.mBytesPerFrame * output_frames);
554
555 int r = cubeb_mixer_mix(stm->mixer.get(),
556 output_frames,
557 input_buffer,
558 input_buffer_size,
559 output_buffer,
560 output_buffer_size);
561 if (r != 0) {
562 LOG("Remix error = %d", r);
563 }
564 }
565
566 // Return how many input frames (sampled at input_hw_rate) are needed to provide
567 // output_frames (sampled at output_stream_params.rate)
568 static int64_t
minimum_resampling_input_frames(cubeb_stream * stm,uint32_t output_frames)569 minimum_resampling_input_frames(cubeb_stream * stm, uint32_t output_frames)
570 {
571 if (stm->input_hw_rate == stm->output_stream_params.rate) {
572 // Fast path.
573 return output_frames;
574 }
575 return ceil(stm->input_hw_rate * output_frames /
576 stm->output_stream_params.rate);
577 }
578
579 static OSStatus
audiounit_output_callback(void * user_ptr,AudioUnitRenderActionFlags *,AudioTimeStamp const * tstamp,UInt32 bus,UInt32 output_frames,AudioBufferList * outBufferList)580 audiounit_output_callback(void * user_ptr,
581 AudioUnitRenderActionFlags * /* flags */,
582 AudioTimeStamp const * tstamp,
583 UInt32 bus,
584 UInt32 output_frames,
585 AudioBufferList * outBufferList)
586 {
587 assert(AU_OUT_BUS == bus);
588 assert(outBufferList->mNumberBuffers == 1);
589
590 cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
591
592 uint64_t now = ConvertHostTimeToNanos(mach_absolute_time());
593 uint64_t audio_output_time = ConvertHostTimeToNanos(tstamp->mHostTime);
594 uint64_t output_latency_ns = audio_output_time - now;
595
596 const int ns2s = 1e9;
597 // The total output latency is the timestamp difference + the stream latency +
598 // the hardware latency.
599 stm->total_output_latency_frames = output_latency_ns * stm->output_hw_rate / ns2s + stm->current_latency_frames;
600
601 ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %lu.",
602 stm,
603 (unsigned int) outBufferList->mNumberBuffers,
604 (unsigned int) outBufferList->mBuffers[0].mDataByteSize,
605 (unsigned int) outBufferList->mBuffers[0].mNumberChannels,
606 (unsigned int) output_frames,
607 has_input(stm) ? stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame : 0);
608
609 long input_frames = 0;
610 void * output_buffer = NULL, * input_buffer = NULL;
611
612 if (stm->shutdown) {
613 ALOG("(%p) output shutdown.", stm);
614 audiounit_make_silent(&outBufferList->mBuffers[0]);
615 return noErr;
616 }
617
618 if (stm->draining) {
619 OSStatus r = AudioOutputUnitStop(stm->output_unit);
620 assert(r == 0);
621 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
622 audiounit_make_silent(&outBufferList->mBuffers[0]);
623 return noErr;
624 }
625
626 /* Get output buffer. */
627 if (stm->mixer) {
628 // If remixing needs to occur, we can't directly work in our final
629 // destination buffer as data may be overwritten or too small to start with.
630 size_t size_needed = output_frames * stm->output_stream_params.channels *
631 cubeb_sample_size(stm->output_stream_params.format);
632 if (stm->temp_buffer_size < size_needed) {
633 stm->temp_buffer.reset(new uint8_t[size_needed]);
634 stm->temp_buffer_size = size_needed;
635 }
636 output_buffer = stm->temp_buffer.get();
637 } else {
638 output_buffer = outBufferList->mBuffers[0].mData;
639 }
640
641 stm->frames_written += output_frames;
642
643 /* If Full duplex get also input buffer */
644 if (stm->input_unit != NULL) {
645 /* If the output callback came first and this is a duplex stream, we need to
646 * fill in some additional silence in the resampler.
647 * Otherwise, if we had more than expected callbacks in a row, or we're
648 * currently switching, we add some silence as well to compensate for the
649 * fact that we're lacking some input data. */
650 uint32_t input_frames_needed =
651 minimum_resampling_input_frames(stm, stm->frames_written);
652 long missing_frames = input_frames_needed - stm->frames_read;
653 if (missing_frames > 0) {
654 stm->input_linear_buffer->push_silence(missing_frames * stm->input_desc.mChannelsPerFrame);
655 stm->frames_read = input_frames_needed;
656
657 ALOG("(%p) %s pushed %ld frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
658 stm->switching_device ? "Device switching," : "Drop out,", missing_frames);
659 }
660 input_buffer = stm->input_linear_buffer->data();
661 // Number of input frames in the buffer. It will change to actually used frames
662 // inside fill
663 input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
664 }
665
666 /* Call user callback through resampler. */
667 long outframes = cubeb_resampler_fill(stm->resampler.get(),
668 input_buffer,
669 input_buffer ? &input_frames : NULL,
670 output_buffer,
671 output_frames);
672
673 if (input_buffer) {
674 // Pop from the buffer the frames used by the the resampler.
675 stm->input_linear_buffer->pop(input_frames * stm->input_desc.mChannelsPerFrame);
676 }
677
678 if (outframes < 0 || outframes > output_frames) {
679 stm->shutdown = true;
680 OSStatus r = AudioOutputUnitStop(stm->output_unit);
681 assert(r == 0);
682 if (stm->input_unit) {
683 r = AudioOutputUnitStop(stm->input_unit);
684 assert(r == 0);
685 }
686 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
687 audiounit_make_silent(&outBufferList->mBuffers[0]);
688 return noErr;
689 }
690
691 stm->draining = (UInt32) outframes < output_frames;
692 stm->frames_played = stm->frames_queued;
693 stm->frames_queued += outframes;
694
695 /* Post process output samples. */
696 if (stm->draining) {
697 /* Clear missing frames (silence) */
698 size_t channels = stm->output_stream_params.channels;
699 size_t missing_samples = (output_frames - outframes) * channels;
700 size_t size_sample = cubeb_sample_size(stm->output_stream_params.format);
701 /* number of bytes that have been filled with valid audio by the callback. */
702 size_t audio_byte_count = outframes * channels * size_sample;
703 PodZero((uint8_t*)output_buffer + audio_byte_count,
704 missing_samples * size_sample);
705 }
706
707 /* Mixing */
708 if (stm->mixer) {
709 audiounit_mix_output_buffer(stm,
710 output_frames,
711 output_buffer,
712 stm->temp_buffer_size,
713 outBufferList->mBuffers[0].mData,
714 outBufferList->mBuffers[0].mDataByteSize);
715 }
716
717 return noErr;
718 }
719
720 extern "C" {
721 int
audiounit_init(cubeb ** context,char const *)722 audiounit_init(cubeb ** context, char const * /* context_name */)
723 {
724 #if !TARGET_OS_IPHONE
725 cubeb_set_coreaudio_notification_runloop();
726 #endif
727
728 *context = new cubeb;
729
730 return CUBEB_OK;
731 }
732 }
733
734 static char const *
audiounit_get_backend_id(cubeb *)735 audiounit_get_backend_id(cubeb * /* ctx */)
736 {
737 return "audiounit";
738 }
739
740 #if !TARGET_OS_IPHONE
741
742 static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
743 static int audiounit_stream_set_volume(cubeb_stream * stm, float volume);
744
745 static int
audiounit_set_device_info(cubeb_stream * stm,AudioDeviceID id,io_side side)746 audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side)
747 {
748 assert(stm);
749
750 device_info * info = nullptr;
751 cubeb_device_type type = CUBEB_DEVICE_TYPE_UNKNOWN;
752
753 if (side == io_side::INPUT) {
754 info = &stm->input_device;
755 type = CUBEB_DEVICE_TYPE_INPUT;
756 } else if (side == io_side::OUTPUT) {
757 info = &stm->output_device;
758 type = CUBEB_DEVICE_TYPE_OUTPUT;
759 }
760 memset(info, 0, sizeof(device_info));
761 info->id = id;
762
763 if (side == io_side::INPUT) {
764 info->flags |= DEV_INPUT;
765 } else if (side == io_side::OUTPUT) {
766 info->flags |= DEV_OUTPUT;
767 }
768
769 AudioDeviceID default_device_id = audiounit_get_default_device_id(type);
770 if (default_device_id == kAudioObjectUnknown) {
771 return CUBEB_ERROR;
772 }
773 if (id == kAudioObjectUnknown) {
774 info->id = default_device_id;
775 info->flags |= DEV_SELECTED_DEFAULT;
776 }
777
778 if (info->id == default_device_id) {
779 info->flags |= DEV_SYSTEM_DEFAULT;
780 }
781
782 assert(info->id);
783 assert(info->flags & DEV_INPUT && !(info->flags & DEV_OUTPUT) ||
784 !(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT);
785
786 return CUBEB_OK;
787 }
788
789
790 static int
audiounit_reinit_stream(cubeb_stream * stm,device_flags_value flags)791 audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags)
792 {
793 auto_lock context_lock(stm->context->mutex);
794 assert((flags & DEV_INPUT && stm->input_unit) ||
795 (flags & DEV_OUTPUT && stm->output_unit));
796 if (!stm->shutdown) {
797 audiounit_stream_stop_internal(stm);
798 }
799
800 int r = audiounit_uninstall_device_changed_callback(stm);
801 if (r != CUBEB_OK) {
802 LOG("(%p) Could not uninstall all device change listeners.", stm);
803 }
804
805 {
806 auto_lock lock(stm->mutex);
807 float volume = 0.0;
808 int vol_rv = CUBEB_ERROR;
809 if (stm->output_unit) {
810 vol_rv = audiounit_stream_get_volume(stm, &volume);
811 }
812
813 audiounit_close_stream(stm);
814
815 /* Reinit occurs in one of the following case:
816 * - When the device is not alive any more
817 * - When the default system device change.
818 * - The bluetooth device changed from A2DP to/from HFP/HSP profile
819 * We first attempt to re-use the same device id, should that fail we will
820 * default to the (potentially new) default device. */
821 AudioDeviceID input_device = flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown;
822 if (flags & DEV_INPUT) {
823 r = audiounit_set_device_info(stm, input_device, io_side::INPUT);
824 if (r != CUBEB_OK) {
825 LOG("(%p) Set input device info failed. This can happen when last media device is unplugged", stm);
826 return CUBEB_ERROR;
827 }
828 }
829
830 /* Always use the default output on reinit. This is not correct in every
831 * case but it is sufficient for Firefox and prevent reinit from reporting
832 * failures. It will change soon when reinit mechanism will be updated. */
833 r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT);
834 if (r != CUBEB_OK) {
835 LOG("(%p) Set output device info failed. This can happen when last media device is unplugged", stm);
836 return CUBEB_ERROR;
837 }
838
839 if (audiounit_setup_stream(stm) != CUBEB_OK) {
840 LOG("(%p) Stream reinit failed.", stm);
841 if (flags & DEV_INPUT && input_device != kAudioObjectUnknown) {
842 // Attempt to re-use the same device-id failed, so attempt again with
843 // default input device.
844 audiounit_close_stream(stm);
845 if (audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::INPUT) != CUBEB_OK ||
846 audiounit_setup_stream(stm) != CUBEB_OK) {
847 LOG("(%p) Second stream reinit failed.", stm);
848 return CUBEB_ERROR;
849 }
850 }
851 }
852
853 if (vol_rv == CUBEB_OK) {
854 audiounit_stream_set_volume(stm, volume);
855 }
856
857 // If the stream was running, start it again.
858 if (!stm->shutdown) {
859 r = audiounit_stream_start_internal(stm);
860 if (r != CUBEB_OK) {
861 return CUBEB_ERROR;
862 }
863 }
864 }
865 return CUBEB_OK;
866 }
867
868 static void
audiounit_reinit_stream_async(cubeb_stream * stm,device_flags_value flags)869 audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags)
870 {
871 if (std::atomic_exchange(&stm->reinit_pending, true)) {
872 // A reinit task is already pending, nothing more to do.
873 ALOG("(%p) re-init stream task already pending, cancelling request", stm);
874 return;
875 }
876
877 // Use a new thread, through the queue, to avoid deadlock when calling
878 // Get/SetProperties method from inside notify callback
879 dispatch_async(stm->context->serial_queue, ^() {
880 if (stm->destroy_pending) {
881 ALOG("(%p) stream pending destroy, cancelling reinit task", stm);
882 return;
883 }
884
885 if (audiounit_reinit_stream(stm, flags) != CUBEB_OK) {
886 if (audiounit_uninstall_system_changed_callback(stm) != CUBEB_OK) {
887 LOG("(%p) Could not uninstall system changed callback", stm);
888 }
889 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
890 LOG("(%p) Could not reopen the stream after switching.", stm);
891 }
892 stm->switching_device = false;
893 stm->reinit_pending = false;
894 });
895 }
896
897 static char const *
event_addr_to_string(AudioObjectPropertySelector selector)898 event_addr_to_string(AudioObjectPropertySelector selector)
899 {
900 switch(selector) {
901 case kAudioHardwarePropertyDefaultOutputDevice:
902 return "kAudioHardwarePropertyDefaultOutputDevice";
903 case kAudioHardwarePropertyDefaultInputDevice:
904 return "kAudioHardwarePropertyDefaultInputDevice";
905 case kAudioDevicePropertyDeviceIsAlive:
906 return "kAudioDevicePropertyDeviceIsAlive";
907 case kAudioDevicePropertyDataSource:
908 return "kAudioDevicePropertyDataSource";
909 default:
910 return "Unknown";
911 }
912 }
913
914 static OSStatus
audiounit_property_listener_callback(AudioObjectID id,UInt32 address_count,const AudioObjectPropertyAddress * addresses,void * user)915 audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
916 const AudioObjectPropertyAddress * addresses,
917 void * user)
918 {
919 cubeb_stream * stm = (cubeb_stream*) user;
920 if (stm->switching_device) {
921 LOG("Switching is already taking place. Skip Event %s for id=%d", event_addr_to_string(addresses[0].mSelector), id);
922 return noErr;
923 }
924 stm->switching_device = true;
925
926 LOG("(%p) Audio device changed, %u events.", stm, (unsigned int) address_count);
927 for (UInt32 i = 0; i < address_count; i++) {
928 switch(addresses[i].mSelector) {
929 case kAudioHardwarePropertyDefaultOutputDevice: {
930 LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultOutputDevice for id=%d", (unsigned int) i, id);
931 }
932 break;
933 case kAudioHardwarePropertyDefaultInputDevice: {
934 LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice for id=%d", (unsigned int) i, id);
935 }
936 break;
937 case kAudioDevicePropertyDeviceIsAlive: {
938 LOG("Event[%u] - mSelector == kAudioDevicePropertyDeviceIsAlive for id=%d", (unsigned int) i, id);
939 // If this is the default input device ignore the event,
940 // kAudioHardwarePropertyDefaultInputDevice will take care of the switch
941 if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) {
942 LOG("It's the default input device, ignore the event");
943 stm->switching_device = false;
944 return noErr;
945 }
946 }
947 break;
948 case kAudioDevicePropertyDataSource: {
949 LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d", (unsigned int) i, id);
950 }
951 break;
952 default:
953 LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
954 stm->switching_device = false;
955 return noErr;
956 }
957 }
958
959 // Allow restart to choose the new default
960 device_flags_value switch_side = DEV_UNKNOWN;
961 if (has_input(stm)) {
962 switch_side |= DEV_INPUT;
963 }
964 if (has_output(stm)) {
965 switch_side |= DEV_OUTPUT;
966 }
967
968 for (UInt32 i = 0; i < address_count; i++) {
969 switch(addresses[i].mSelector) {
970 case kAudioHardwarePropertyDefaultOutputDevice:
971 case kAudioHardwarePropertyDefaultInputDevice:
972 case kAudioDevicePropertyDeviceIsAlive:
973 /* fall through */
974 case kAudioDevicePropertyDataSource: {
975 auto_lock dev_cb_lock(stm->device_changed_callback_lock);
976 if (stm->device_changed_callback) {
977 stm->device_changed_callback(stm->user_ptr);
978 }
979 break;
980 }
981 }
982 }
983
984 audiounit_reinit_stream_async(stm, switch_side);
985
986 return noErr;
987 }
988
989 OSStatus
audiounit_add_listener(const property_listener * listener)990 audiounit_add_listener(const property_listener * listener)
991 {
992 assert(listener);
993 return AudioObjectAddPropertyListener(listener->device_id,
994 listener->property_address,
995 listener->callback,
996 listener->stream);
997 }
998
999 OSStatus
audiounit_remove_listener(const property_listener * listener)1000 audiounit_remove_listener(const property_listener * listener)
1001 {
1002 assert(listener);
1003 return AudioObjectRemovePropertyListener(listener->device_id,
1004 listener->property_address,
1005 listener->callback,
1006 listener->stream);
1007 }
1008
1009 static int
audiounit_install_device_changed_callback(cubeb_stream * stm)1010 audiounit_install_device_changed_callback(cubeb_stream * stm)
1011 {
1012 OSStatus rv;
1013 int r = CUBEB_OK;
1014
1015 if (stm->output_unit) {
1016 /* This event will notify us when the data source on the same device changes,
1017 * for example when the user plugs in a normal (non-usb) headset in the
1018 * headphone jack. */
1019 stm->output_source_listener.reset(new property_listener(
1020 stm->output_device.id, &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
1021 &audiounit_property_listener_callback, stm));
1022 rv = audiounit_add_listener(stm->output_source_listener.get());
1023 if (rv != noErr) {
1024 stm->output_source_listener.reset();
1025 LOG("AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id);
1026 r = CUBEB_ERROR;
1027 }
1028 }
1029
1030 if (stm->input_unit) {
1031 /* This event will notify us when the data source on the input device changes. */
1032 stm->input_source_listener.reset(new property_listener(
1033 stm->input_device.id, &INPUT_DATA_SOURCE_PROPERTY_ADDRESS,
1034 &audiounit_property_listener_callback, stm));
1035 rv = audiounit_add_listener(stm->input_source_listener.get());
1036 if (rv != noErr) {
1037 stm->input_source_listener.reset();
1038 LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id);
1039 r = CUBEB_ERROR;
1040 }
1041
1042 /* Event to notify when the input is going away. */
1043 stm->input_alive_listener.reset(new property_listener(
1044 stm->input_device.id, &DEVICE_IS_ALIVE_PROPERTY_ADDRESS,
1045 &audiounit_property_listener_callback, stm));
1046 rv = audiounit_add_listener(stm->input_alive_listener.get());
1047 if (rv != noErr) {
1048 stm->input_alive_listener.reset();
1049 LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d", rv, stm->input_device.id);
1050 r = CUBEB_ERROR;
1051 }
1052 }
1053
1054 return r;
1055 }
1056
1057 static int
audiounit_install_system_changed_callback(cubeb_stream * stm)1058 audiounit_install_system_changed_callback(cubeb_stream * stm)
1059 {
1060 OSStatus r;
1061
1062 if (stm->output_unit) {
1063 /* This event will notify us when the default audio device changes,
1064 * for example when the user plugs in a USB headset and the system chooses it
1065 * automatically as the default, or when another device is chosen in the
1066 * dropdown list. */
1067 stm->default_output_listener.reset(new property_listener(
1068 kAudioObjectSystemObject, &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS,
1069 &audiounit_property_listener_callback, stm));
1070 r = audiounit_add_listener(stm->default_output_listener.get());
1071 if (r != noErr) {
1072 stm->default_output_listener.reset();
1073 LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r);
1074 return CUBEB_ERROR;
1075 }
1076 }
1077
1078 if (stm->input_unit) {
1079 /* This event will notify us when the default input device changes. */
1080 stm->default_input_listener.reset(new property_listener(
1081 kAudioObjectSystemObject, &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS,
1082 &audiounit_property_listener_callback, stm));
1083 r = audiounit_add_listener(stm->default_input_listener.get());
1084 if (r != noErr) {
1085 stm->default_input_listener.reset();
1086 LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r);
1087 return CUBEB_ERROR;
1088 }
1089 }
1090
1091 return CUBEB_OK;
1092 }
1093
1094 static int
audiounit_uninstall_device_changed_callback(cubeb_stream * stm)1095 audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
1096 {
1097 OSStatus rv;
1098 // Failing to uninstall listeners is not a fatal error.
1099 int r = CUBEB_OK;
1100
1101 if (stm->output_source_listener) {
1102 rv = audiounit_remove_listener(stm->output_source_listener.get());
1103 if (rv != noErr) {
1104 LOG("AudioObjectRemovePropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id);
1105 r = CUBEB_ERROR;
1106 }
1107 stm->output_source_listener.reset();
1108 }
1109
1110 if (stm->input_source_listener) {
1111 rv = audiounit_remove_listener(stm->input_source_listener.get());
1112 if (rv != noErr) {
1113 LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id);
1114 r = CUBEB_ERROR;
1115 }
1116 stm->input_source_listener.reset();
1117 }
1118
1119 if (stm->input_alive_listener) {
1120 rv = audiounit_remove_listener(stm->input_alive_listener.get());
1121 if (rv != noErr) {
1122 LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d", rv, stm->input_device.id);
1123 r = CUBEB_ERROR;
1124 }
1125 stm->input_alive_listener.reset();
1126 }
1127
1128 return r;
1129 }
1130
1131 static int
audiounit_uninstall_system_changed_callback(cubeb_stream * stm)1132 audiounit_uninstall_system_changed_callback(cubeb_stream * stm)
1133 {
1134 OSStatus r;
1135
1136 if (stm->default_output_listener) {
1137 r = audiounit_remove_listener(stm->default_output_listener.get());
1138 if (r != noErr) {
1139 return CUBEB_ERROR;
1140 }
1141 stm->default_output_listener.reset();
1142 }
1143
1144 if (stm->default_input_listener) {
1145 r = audiounit_remove_listener(stm->default_input_listener.get());
1146 if (r != noErr) {
1147 return CUBEB_ERROR;
1148 }
1149 stm->default_input_listener.reset();
1150 }
1151 return CUBEB_OK;
1152 }
1153
1154 /* Get the acceptable buffer size (in frames) that this device can work with. */
1155 static int
audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)1156 audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
1157 {
1158 UInt32 size;
1159 OSStatus r;
1160 AudioDeviceID output_device_id;
1161 AudioObjectPropertyAddress output_device_buffer_size_range = {
1162 kAudioDevicePropertyBufferFrameSizeRange,
1163 kAudioDevicePropertyScopeOutput,
1164 kAudioObjectPropertyElementMaster
1165 };
1166
1167 output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
1168 if (output_device_id == kAudioObjectUnknown) {
1169 LOG("Could not get default output device id.");
1170 return CUBEB_ERROR;
1171 }
1172
1173 /* Get the buffer size range this device supports */
1174 size = sizeof(*latency_range);
1175
1176 r = AudioObjectGetPropertyData(output_device_id,
1177 &output_device_buffer_size_range,
1178 0,
1179 NULL,
1180 &size,
1181 latency_range);
1182 if (r != noErr) {
1183 LOG("AudioObjectGetPropertyData/buffer size range rv=%d", r);
1184 return CUBEB_ERROR;
1185 }
1186
1187 return CUBEB_OK;
1188 }
1189 #endif /* !TARGET_OS_IPHONE */
1190
1191 static AudioObjectID
audiounit_get_default_device_id(cubeb_device_type type)1192 audiounit_get_default_device_id(cubeb_device_type type)
1193 {
1194 const AudioObjectPropertyAddress * adr;
1195 if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
1196 adr = &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS;
1197 } else if (type == CUBEB_DEVICE_TYPE_INPUT) {
1198 adr = &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS;
1199 } else {
1200 return kAudioObjectUnknown;
1201 }
1202
1203 AudioDeviceID devid;
1204 UInt32 size = sizeof(AudioDeviceID);
1205 if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
1206 adr, 0, NULL, &size, &devid) != noErr) {
1207 return kAudioObjectUnknown;
1208 }
1209
1210 return devid;
1211 }
1212
1213 int
audiounit_get_max_channel_count(cubeb * ctx,uint32_t * max_channels)1214 audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
1215 {
1216 #if TARGET_OS_IPHONE
1217 //TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels]
1218 *max_channels = 2;
1219 #else
1220 UInt32 size;
1221 OSStatus r;
1222 AudioDeviceID output_device_id;
1223 AudioStreamBasicDescription stream_format;
1224 AudioObjectPropertyAddress stream_format_address = {
1225 kAudioDevicePropertyStreamFormat,
1226 kAudioDevicePropertyScopeOutput,
1227 kAudioObjectPropertyElementMaster
1228 };
1229
1230 assert(ctx && max_channels);
1231
1232 output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
1233 if (output_device_id == kAudioObjectUnknown) {
1234 return CUBEB_ERROR;
1235 }
1236
1237 size = sizeof(stream_format);
1238
1239 r = AudioObjectGetPropertyData(output_device_id,
1240 &stream_format_address,
1241 0,
1242 NULL,
1243 &size,
1244 &stream_format);
1245 if (r != noErr) {
1246 LOG("AudioObjectPropertyAddress/StreamFormat rv=%d", r);
1247 return CUBEB_ERROR;
1248 }
1249
1250 *max_channels = stream_format.mChannelsPerFrame;
1251 #endif
1252 return CUBEB_OK;
1253 }
1254
1255 static int
audiounit_get_min_latency(cubeb *,cubeb_stream_params,uint32_t * latency_frames)1256 audiounit_get_min_latency(cubeb * /* ctx */,
1257 cubeb_stream_params /* params */,
1258 uint32_t * latency_frames)
1259 {
1260 #if TARGET_OS_IPHONE
1261 //TODO: [[AVAudioSession sharedInstance] inputLatency]
1262 return CUBEB_ERROR_NOT_SUPPORTED;
1263 #else
1264 AudioValueRange latency_range;
1265 if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
1266 LOG("Could not get acceptable latency range.");
1267 return CUBEB_ERROR;
1268 }
1269
1270 *latency_frames = max<uint32_t>(latency_range.mMinimum,
1271 SAFE_MIN_LATENCY_FRAMES);
1272 #endif
1273
1274 return CUBEB_OK;
1275 }
1276
1277 static int
audiounit_get_preferred_sample_rate(cubeb *,uint32_t * rate)1278 audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate)
1279 {
1280 #if TARGET_OS_IPHONE
1281 //TODO
1282 return CUBEB_ERROR_NOT_SUPPORTED;
1283 #else
1284 UInt32 size;
1285 OSStatus r;
1286 Float64 fsamplerate;
1287 AudioDeviceID output_device_id;
1288 AudioObjectPropertyAddress samplerate_address = {
1289 kAudioDevicePropertyNominalSampleRate,
1290 kAudioObjectPropertyScopeGlobal,
1291 kAudioObjectPropertyElementMaster
1292 };
1293
1294 output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
1295 if (output_device_id == kAudioObjectUnknown) {
1296 return CUBEB_ERROR;
1297 }
1298
1299 size = sizeof(fsamplerate);
1300 r = AudioObjectGetPropertyData(output_device_id,
1301 &samplerate_address,
1302 0,
1303 NULL,
1304 &size,
1305 &fsamplerate);
1306
1307 if (r != noErr) {
1308 return CUBEB_ERROR;
1309 }
1310
1311 *rate = static_cast<uint32_t>(fsamplerate);
1312 #endif
1313 return CUBEB_OK;
1314 }
1315
1316 static cubeb_channel_layout
audiounit_convert_channel_layout(AudioChannelLayout * layout)1317 audiounit_convert_channel_layout(AudioChannelLayout * layout)
1318 {
1319 // When having one or two channel, force mono or stereo. Some devices (namely,
1320 // Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
1321 // some reason.
1322 if (layout->mNumberChannelDescriptions == 1) {
1323 return CUBEB_LAYOUT_MONO;
1324 } else if (layout->mNumberChannelDescriptions == 2) {
1325 return CUBEB_LAYOUT_STEREO;
1326 }
1327
1328 if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) {
1329 // kAudioChannelLayoutTag_UseChannelBitmap
1330 // kAudioChannelLayoutTag_Mono
1331 // kAudioChannelLayoutTag_Stereo
1332 // ....
1333 LOG("Only handle UseChannelDescriptions for now.\n");
1334 return CUBEB_LAYOUT_UNDEFINED;
1335 }
1336
1337 cubeb_channel_layout cl = 0;
1338 for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
1339 cubeb_channel cc = channel_label_to_cubeb_channel(
1340 layout->mChannelDescriptions[i].mChannelLabel);
1341 if (cc == CHANNEL_UNKNOWN) {
1342 return CUBEB_LAYOUT_UNDEFINED;
1343 }
1344 cl |= cc;
1345 }
1346
1347 return cl;
1348 }
1349
1350 static cubeb_channel_layout
audiounit_get_preferred_channel_layout(AudioUnit output_unit)1351 audiounit_get_preferred_channel_layout(AudioUnit output_unit)
1352 {
1353 OSStatus rv = noErr;
1354 UInt32 size = 0;
1355 rv = AudioUnitGetPropertyInfo(output_unit,
1356 kAudioDevicePropertyPreferredChannelLayout,
1357 kAudioUnitScope_Output,
1358 AU_OUT_BUS,
1359 &size,
1360 nullptr);
1361 if (rv != noErr) {
1362 LOG("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout rv=%d", rv);
1363 return CUBEB_LAYOUT_UNDEFINED;
1364 }
1365 assert(size > 0);
1366
1367 auto layout = make_sized_audio_channel_layout(size);
1368 rv = AudioUnitGetProperty(output_unit,
1369 kAudioDevicePropertyPreferredChannelLayout,
1370 kAudioUnitScope_Output,
1371 AU_OUT_BUS,
1372 layout.get(),
1373 &size);
1374 if (rv != noErr) {
1375 LOG("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv=%d", rv);
1376 return CUBEB_LAYOUT_UNDEFINED;
1377 }
1378
1379 return audiounit_convert_channel_layout(layout.get());
1380 }
1381
1382 static cubeb_channel_layout
audiounit_get_current_channel_layout(AudioUnit output_unit)1383 audiounit_get_current_channel_layout(AudioUnit output_unit)
1384 {
1385 OSStatus rv = noErr;
1386 UInt32 size = 0;
1387 rv = AudioUnitGetPropertyInfo(output_unit,
1388 kAudioUnitProperty_AudioChannelLayout,
1389 kAudioUnitScope_Output,
1390 AU_OUT_BUS,
1391 &size,
1392 nullptr);
1393 if (rv != noErr) {
1394 LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
1395 // This property isn't known before macOS 10.12, attempt another method.
1396 return audiounit_get_preferred_channel_layout(output_unit);
1397 }
1398 assert(size > 0);
1399
1400 auto layout = make_sized_audio_channel_layout(size);
1401 rv = AudioUnitGetProperty(output_unit,
1402 kAudioUnitProperty_AudioChannelLayout,
1403 kAudioUnitScope_Output,
1404 AU_OUT_BUS,
1405 layout.get(),
1406 &size);
1407 if (rv != noErr) {
1408 LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
1409 return CUBEB_LAYOUT_UNDEFINED;
1410 }
1411
1412 return audiounit_convert_channel_layout(layout.get());
1413 }
1414
1415 static int audiounit_create_unit(AudioUnit * unit, device_info * device);
1416
1417 static OSStatus audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype);
1418
1419 static void
audiounit_destroy(cubeb * ctx)1420 audiounit_destroy(cubeb * ctx)
1421 {
1422 {
1423 auto_lock lock(ctx->mutex);
1424
1425 // Disabling this assert for bug 1083664 -- we seem to leak a stream
1426 // assert(ctx->active_streams == 0);
1427 if (audiounit_active_streams(ctx) > 0) {
1428 LOG("(%p) API misuse, %d streams active when context destroyed!", ctx, audiounit_active_streams(ctx));
1429 }
1430
1431 /* Unregister the callback if necessary. */
1432 if (ctx->input_collection_changed_callback) {
1433 audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_INPUT);
1434 }
1435 if (ctx->output_collection_changed_callback) {
1436 audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_OUTPUT);
1437 }
1438 }
1439
1440 dispatch_release(ctx->serial_queue);
1441
1442 delete ctx;
1443 }
1444
1445 static void audiounit_stream_destroy(cubeb_stream * stm);
1446
1447 static int
audio_stream_desc_init(AudioStreamBasicDescription * ss,const cubeb_stream_params * stream_params)1448 audio_stream_desc_init(AudioStreamBasicDescription * ss,
1449 const cubeb_stream_params * stream_params)
1450 {
1451 switch (stream_params->format) {
1452 case CUBEB_SAMPLE_S16LE:
1453 ss->mBitsPerChannel = 16;
1454 ss->mFormatFlags = kAudioFormatFlagIsSignedInteger;
1455 break;
1456 case CUBEB_SAMPLE_S16BE:
1457 ss->mBitsPerChannel = 16;
1458 ss->mFormatFlags = kAudioFormatFlagIsSignedInteger |
1459 kAudioFormatFlagIsBigEndian;
1460 break;
1461 case CUBEB_SAMPLE_FLOAT32LE:
1462 ss->mBitsPerChannel = 32;
1463 ss->mFormatFlags = kAudioFormatFlagIsFloat;
1464 break;
1465 case CUBEB_SAMPLE_FLOAT32BE:
1466 ss->mBitsPerChannel = 32;
1467 ss->mFormatFlags = kAudioFormatFlagIsFloat |
1468 kAudioFormatFlagIsBigEndian;
1469 break;
1470 default:
1471 return CUBEB_ERROR_INVALID_FORMAT;
1472 }
1473
1474 ss->mFormatID = kAudioFormatLinearPCM;
1475 ss->mFormatFlags |= kLinearPCMFormatFlagIsPacked;
1476 ss->mSampleRate = stream_params->rate;
1477 ss->mChannelsPerFrame = stream_params->channels;
1478
1479 ss->mBytesPerFrame = (ss->mBitsPerChannel / 8) * ss->mChannelsPerFrame;
1480 ss->mFramesPerPacket = 1;
1481 ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket;
1482
1483 ss->mReserved = 0;
1484
1485 return CUBEB_OK;
1486 }
1487
1488 void
audiounit_init_mixer(cubeb_stream * stm)1489 audiounit_init_mixer(cubeb_stream * stm)
1490 {
1491 // We can't rely on macOS' AudioUnit to properly downmix (or upmix) the audio
1492 // data, it silently drop the channels so we need to remix the
1493 // audio data by ourselves to keep all the information.
1494 stm->mixer.reset(cubeb_mixer_create(stm->output_stream_params.format,
1495 stm->output_stream_params.channels,
1496 stm->output_stream_params.layout,
1497 stm->context->channels,
1498 stm->context->layout));
1499 assert(stm->mixer);
1500 }
1501
1502 static int
audiounit_set_channel_layout(AudioUnit unit,io_side side,cubeb_channel_layout layout)1503 audiounit_set_channel_layout(AudioUnit unit,
1504 io_side side,
1505 cubeb_channel_layout layout)
1506 {
1507 if (side != io_side::OUTPUT) {
1508 return CUBEB_ERROR;
1509 }
1510
1511 if (layout == CUBEB_LAYOUT_UNDEFINED) {
1512 // We leave everything as-is...
1513 return CUBEB_OK;
1514 }
1515
1516
1517 OSStatus r;
1518 uint32_t nb_channels = cubeb_channel_layout_nb_channels(layout);
1519
1520 // We do not use CoreAudio standard layout for lack of documentation on what
1521 // the actual channel orders are. So we set a custom layout.
1522 size_t size = offsetof(AudioChannelLayout, mChannelDescriptions[nb_channels]);
1523 auto au_layout = make_sized_audio_channel_layout(size);
1524 au_layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
1525 au_layout->mNumberChannelDescriptions = nb_channels;
1526
1527 uint32_t channels = 0;
1528 cubeb_channel_layout channelMap = layout;
1529 for (uint32_t i = 0; channelMap != 0; ++i) {
1530 XASSERT(channels < nb_channels);
1531 uint32_t channel = (channelMap & 1) << i;
1532 if (channel != 0) {
1533 au_layout->mChannelDescriptions[channels].mChannelLabel =
1534 cubeb_channel_to_channel_label(static_cast<cubeb_channel>(channel));
1535 au_layout->mChannelDescriptions[channels].mChannelFlags = kAudioChannelFlags_AllOff;
1536 channels++;
1537 }
1538 channelMap = channelMap >> 1;
1539 }
1540
1541 r = AudioUnitSetProperty(unit,
1542 kAudioUnitProperty_AudioChannelLayout,
1543 kAudioUnitScope_Input,
1544 AU_OUT_BUS,
1545 au_layout.get(),
1546 size);
1547 if (r != noErr) {
1548 LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d", to_string(side), r);
1549 return CUBEB_ERROR;
1550 }
1551
1552 return CUBEB_OK;
1553 }
1554
1555 void
audiounit_layout_init(cubeb_stream * stm,io_side side)1556 audiounit_layout_init(cubeb_stream * stm, io_side side)
1557 {
1558 // We currently don't support the input layout setting.
1559 if (side == io_side::INPUT) {
1560 return;
1561 }
1562
1563 stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit);
1564
1565 audiounit_set_channel_layout(stm->output_unit, io_side::OUTPUT, stm->context->layout);
1566 }
1567
1568 static vector<AudioObjectID>
audiounit_get_sub_devices(AudioDeviceID device_id)1569 audiounit_get_sub_devices(AudioDeviceID device_id)
1570 {
1571 vector<AudioDeviceID> sub_devices;
1572 AudioObjectPropertyAddress property_address = { kAudioAggregateDevicePropertyActiveSubDeviceList,
1573 kAudioObjectPropertyScopeGlobal,
1574 kAudioObjectPropertyElementMaster };
1575 UInt32 size = 0;
1576 OSStatus rv = AudioObjectGetPropertyDataSize(device_id,
1577 &property_address,
1578 0,
1579 nullptr,
1580 &size);
1581
1582 if (rv != noErr) {
1583 sub_devices.push_back(device_id);
1584 return sub_devices;
1585 }
1586
1587 uint32_t count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
1588 sub_devices.resize(count);
1589 rv = AudioObjectGetPropertyData(device_id,
1590 &property_address,
1591 0,
1592 nullptr,
1593 &size,
1594 sub_devices.data());
1595 if (rv != noErr) {
1596 sub_devices.clear();
1597 sub_devices.push_back(device_id);
1598 } else {
1599 LOG("Found %u sub-devices", count);
1600 }
1601 return sub_devices;
1602 }
1603
1604 static int
audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id,AudioDeviceID * aggregate_device_id)1605 audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, AudioDeviceID * aggregate_device_id)
1606 {
1607 AudioObjectPropertyAddress address_plugin_bundle_id = { kAudioHardwarePropertyPlugInForBundleID,
1608 kAudioObjectPropertyScopeGlobal,
1609 kAudioObjectPropertyElementMaster };
1610 UInt32 size = 0;
1611 OSStatus r = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
1612 &address_plugin_bundle_id,
1613 0, NULL,
1614 &size);
1615 if (r != noErr) {
1616 LOG("AudioObjectGetPropertyDataSize/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
1617 return CUBEB_ERROR;
1618 }
1619
1620 AudioValueTranslation translation_value;
1621 CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio");
1622 translation_value.mInputData = &in_bundle_ref;
1623 translation_value.mInputDataSize = sizeof(in_bundle_ref);
1624 translation_value.mOutputData = plugin_id;
1625 translation_value.mOutputDataSize = sizeof(*plugin_id);
1626
1627 r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
1628 &address_plugin_bundle_id,
1629 0,
1630 nullptr,
1631 &size,
1632 &translation_value);
1633 if (r != noErr) {
1634 LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
1635 return CUBEB_ERROR;
1636 }
1637
1638 AudioObjectPropertyAddress create_aggregate_device_address = { kAudioPlugInCreateAggregateDevice,
1639 kAudioObjectPropertyScopeGlobal,
1640 kAudioObjectPropertyElementMaster };
1641 r = AudioObjectGetPropertyDataSize(*plugin_id,
1642 &create_aggregate_device_address,
1643 0,
1644 nullptr,
1645 &size);
1646 if (r != noErr) {
1647 LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, rv=%d", r);
1648 return CUBEB_ERROR;
1649 }
1650
1651 CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1652 &kCFTypeDictionaryKeyCallBacks,
1653 &kCFTypeDictionaryValueCallBacks);
1654 struct timeval timestamp;
1655 gettimeofday(×tamp, NULL);
1656 long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec;
1657 CFStringRef aggregate_device_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
1658 CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceNameKey), aggregate_device_name);
1659 CFRelease(aggregate_device_name);
1660
1661 CFStringRef aggregate_device_UID = CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
1662 CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceUIDKey), aggregate_device_UID);
1663 CFRelease(aggregate_device_UID);
1664
1665 int private_value = 1;
1666 CFNumberRef aggregate_device_private_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_value);
1667 CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceIsPrivateKey), aggregate_device_private_key);
1668 CFRelease(aggregate_device_private_key);
1669
1670 int stacked_value = 0;
1671 CFNumberRef aggregate_device_stacked_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stacked_value);
1672 CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceIsStackedKey), aggregate_device_stacked_key);
1673 CFRelease(aggregate_device_stacked_key);
1674
1675 r = AudioObjectGetPropertyData(*plugin_id,
1676 &create_aggregate_device_address,
1677 sizeof(aggregate_device_dict),
1678 &aggregate_device_dict,
1679 &size,
1680 aggregate_device_id);
1681 CFRelease(aggregate_device_dict);
1682 if (r != noErr) {
1683 LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d", r);
1684 return CUBEB_ERROR;
1685 }
1686 LOG("New aggregate device %u", *aggregate_device_id);
1687
1688 return CUBEB_OK;
1689 }
1690
1691 // The returned CFStringRef object needs to be released (via CFRelease)
1692 // if it's not NULL, since the reference count of the returned CFStringRef
1693 // object is increased.
1694 static CFStringRef
get_device_name(AudioDeviceID id)1695 get_device_name(AudioDeviceID id)
1696 {
1697 UInt32 size = sizeof(CFStringRef);
1698 CFStringRef UIname = nullptr;
1699 AudioObjectPropertyAddress address_uuid = { kAudioDevicePropertyDeviceUID,
1700 kAudioObjectPropertyScopeGlobal,
1701 kAudioObjectPropertyElementMaster };
1702 OSStatus err = AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname);
1703 return (err == noErr) ? UIname : NULL;
1704 }
1705
1706 static int
audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id,AudioDeviceID input_device_id,AudioDeviceID output_device_id)1707 audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id,
1708 AudioDeviceID input_device_id,
1709 AudioDeviceID output_device_id)
1710 {
1711 LOG("Add devices input %u and output %u into aggregate device %u",
1712 input_device_id, output_device_id, aggregate_device_id);
1713 const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
1714 const vector<AudioDeviceID> input_sub_devices = audiounit_get_sub_devices(input_device_id);
1715
1716 CFMutableArrayRef aggregate_sub_devices_array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1717 /* The order of the items in the array is significant and is used to determine the order of the streams
1718 of the AudioAggregateDevice. */
1719 for (UInt32 i = 0; i < output_sub_devices.size(); i++) {
1720 CFStringRef ref = get_device_name(output_sub_devices[i]);
1721 if (ref == NULL) {
1722 CFRelease(aggregate_sub_devices_array);
1723 return CUBEB_ERROR;
1724 }
1725 CFArrayAppendValue(aggregate_sub_devices_array, ref);
1726 CFRelease(ref);
1727 }
1728 for (UInt32 i = 0; i < input_sub_devices.size(); i++) {
1729 CFStringRef ref = get_device_name(input_sub_devices[i]);
1730 if (ref == NULL) {
1731 CFRelease(aggregate_sub_devices_array);
1732 return CUBEB_ERROR;
1733 }
1734 CFArrayAppendValue(aggregate_sub_devices_array, ref);
1735 CFRelease(ref);
1736 }
1737
1738 AudioObjectPropertyAddress aggregate_sub_device_list = { kAudioAggregateDevicePropertyFullSubDeviceList,
1739 kAudioObjectPropertyScopeGlobal,
1740 kAudioObjectPropertyElementMaster };
1741 UInt32 size = sizeof(CFMutableArrayRef);
1742 OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id,
1743 &aggregate_sub_device_list,
1744 0,
1745 nullptr,
1746 size,
1747 &aggregate_sub_devices_array);
1748 CFRelease(aggregate_sub_devices_array);
1749 if (rv != noErr) {
1750 LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d", rv);
1751 return CUBEB_ERROR;
1752 }
1753
1754 return CUBEB_OK;
1755 }
1756
1757 static int
audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)1758 audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)
1759 {
1760 assert(aggregate_device_id != kAudioObjectUnknown);
1761 AudioObjectPropertyAddress master_aggregate_sub_device = { kAudioAggregateDevicePropertyMasterSubDevice,
1762 kAudioObjectPropertyScopeGlobal,
1763 kAudioObjectPropertyElementMaster };
1764
1765 // Master become the 1st output sub device
1766 AudioDeviceID output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
1767 const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
1768 CFStringRef master_sub_device = get_device_name(output_sub_devices[0]);
1769
1770 UInt32 size = sizeof(CFStringRef);
1771 OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id,
1772 &master_aggregate_sub_device,
1773 0,
1774 NULL,
1775 size,
1776 &master_sub_device);
1777 if (master_sub_device) {
1778 CFRelease(master_sub_device);
1779 }
1780 if (rv != noErr) {
1781 LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyMasterSubDevice, rv=%d", rv);
1782 return CUBEB_ERROR;
1783 }
1784
1785 return CUBEB_OK;
1786 }
1787
1788 static int
audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device_id)1789 audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device_id)
1790 {
1791 assert(aggregate_device_id != kAudioObjectUnknown);
1792 AudioObjectPropertyAddress address_owned = { kAudioObjectPropertyOwnedObjects,
1793 kAudioObjectPropertyScopeGlobal,
1794 kAudioObjectPropertyElementMaster };
1795
1796 UInt32 qualifier_data_size = sizeof(AudioObjectID);
1797 AudioClassID class_id = kAudioSubDeviceClassID;
1798 void * qualifier_data = &class_id;
1799 UInt32 size = 0;
1800 OSStatus rv = AudioObjectGetPropertyDataSize(aggregate_device_id,
1801 &address_owned,
1802 qualifier_data_size,
1803 qualifier_data,
1804 &size);
1805 if (rv != noErr) {
1806 LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, rv=%d", rv);
1807 return CUBEB_ERROR;
1808 }
1809
1810 UInt32 subdevices_num = 0;
1811 subdevices_num = size / sizeof(AudioObjectID);
1812 AudioObjectID sub_devices[subdevices_num];
1813 size = sizeof(sub_devices);
1814
1815 rv = AudioObjectGetPropertyData(aggregate_device_id,
1816 &address_owned,
1817 qualifier_data_size,
1818 qualifier_data,
1819 &size,
1820 sub_devices);
1821 if (rv != noErr) {
1822 LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d", rv);
1823 return CUBEB_ERROR;
1824 }
1825
1826 AudioObjectPropertyAddress address_drift = { kAudioSubDevicePropertyDriftCompensation,
1827 kAudioObjectPropertyScopeGlobal,
1828 kAudioObjectPropertyElementMaster };
1829
1830 // Start from the second device since the first is the master clock
1831 for (UInt32 i = 1; i < subdevices_num; ++i) {
1832 UInt32 drift_compensation_value = 1;
1833 rv = AudioObjectSetPropertyData(sub_devices[i],
1834 &address_drift,
1835 0,
1836 nullptr,
1837 sizeof(UInt32),
1838 &drift_compensation_value);
1839 if (rv != noErr) {
1840 LOG("AudioObjectSetPropertyData/kAudioSubDevicePropertyDriftCompensation, rv=%d", rv);
1841 return CUBEB_OK;
1842 }
1843 }
1844 return CUBEB_OK;
1845 }
1846
1847 static int audiounit_destroy_aggregate_device(AudioObjectID plugin_id, AudioDeviceID * aggregate_device_id);
1848 static void audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope,
1849 uint32_t * min, uint32_t * max, uint32_t * def);
1850 static int
1851 audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID devid, cubeb_device_type type);
1852 static void audiounit_device_destroy(cubeb_device_info * device);
1853
1854 static void
audiounit_workaround_for_airpod(cubeb_stream * stm)1855 audiounit_workaround_for_airpod(cubeb_stream * stm)
1856 {
1857 cubeb_device_info input_device_info;
1858 audiounit_create_device_from_hwdev(&input_device_info, stm->input_device.id, CUBEB_DEVICE_TYPE_INPUT);
1859
1860 cubeb_device_info output_device_info;
1861 audiounit_create_device_from_hwdev(&output_device_info, stm->output_device.id, CUBEB_DEVICE_TYPE_OUTPUT);
1862
1863 std::string input_name_str(input_device_info.friendly_name);
1864 std::string output_name_str(output_device_info.friendly_name);
1865
1866 if(input_name_str.find("AirPods") != std::string::npos &&
1867 output_name_str.find("AirPods") != std::string::npos) {
1868 uint32_t input_min_rate = 0;
1869 uint32_t input_max_rate = 0;
1870 uint32_t input_nominal_rate = 0;
1871 audiounit_get_available_samplerate(stm->input_device.id, kAudioObjectPropertyScopeGlobal,
1872 &input_min_rate, &input_max_rate, &input_nominal_rate);
1873 LOG("(%p) Input device %u, name: %s, min: %u, max: %u, nominal rate: %u", stm, stm->input_device.id
1874 , input_device_info.friendly_name, input_min_rate, input_max_rate, input_nominal_rate);
1875 uint32_t output_min_rate = 0;
1876 uint32_t output_max_rate = 0;
1877 uint32_t output_nominal_rate = 0;
1878 audiounit_get_available_samplerate(stm->output_device.id, kAudioObjectPropertyScopeGlobal,
1879 &output_min_rate, &output_max_rate, &output_nominal_rate);
1880 LOG("(%p) Output device %u, name: %s, min: %u, max: %u, nominal rate: %u", stm, stm->output_device.id
1881 , output_device_info.friendly_name, output_min_rate, output_max_rate, output_nominal_rate);
1882
1883 Float64 rate = input_nominal_rate;
1884 AudioObjectPropertyAddress addr = {kAudioDevicePropertyNominalSampleRate,
1885 kAudioObjectPropertyScopeGlobal,
1886 kAudioObjectPropertyElementMaster};
1887
1888 OSStatus rv = AudioObjectSetPropertyData(stm->aggregate_device_id,
1889 &addr,
1890 0,
1891 nullptr,
1892 sizeof(Float64),
1893 &rate);
1894 if (rv != noErr) {
1895 LOG("Non fatal error, AudioObjectSetPropertyData/kAudioDevicePropertyNominalSampleRate, rv=%d", rv);
1896 }
1897 }
1898 audiounit_device_destroy(&input_device_info);
1899 audiounit_device_destroy(&output_device_info);
1900 }
1901
1902 /*
1903 * Aggregate Device is a virtual audio interface which utilizes inputs and outputs
1904 * of one or more physical audio interfaces. It is possible to use the clock of
1905 * one of the devices as a master clock for all the combined devices and enable
1906 * drift compensation for the devices that are not designated clock master.
1907 *
1908 * Creating a new aggregate device programmatically requires [0][1]:
1909 * 1. Locate the base plug-in ("com.apple.audio.CoreAudio")
1910 * 2. Create a dictionary that describes the aggregate device
1911 * (don't add sub-devices in that step, prone to fail [0])
1912 * 3. Ask the base plug-in to create the aggregate device (blank)
1913 * 4. Add the array of sub-devices.
1914 * 5. Set the master device (1st output device in our case)
1915 * 6. Enable drift compensation for the non-master devices
1916 *
1917 * [0] https://lists.apple.com/archives/coreaudio-api/2006/Apr/msg00092.html
1918 * [1] https://lists.apple.com/archives/coreaudio-api/2005/Jul/msg00150.html
1919 * [2] CoreAudio.framework/Headers/AudioHardware.h
1920 * */
1921 static int
audiounit_create_aggregate_device(cubeb_stream * stm)1922 audiounit_create_aggregate_device(cubeb_stream * stm)
1923 {
1924 int r = audiounit_create_blank_aggregate_device(&stm->plugin_id, &stm->aggregate_device_id);
1925 if (r != CUBEB_OK) {
1926 LOG("(%p) Failed to create blank aggregate device", stm);
1927 return CUBEB_ERROR;
1928 }
1929
1930 r = audiounit_set_aggregate_sub_device_list(stm->aggregate_device_id, stm->input_device.id, stm->output_device.id);
1931 if (r != CUBEB_OK) {
1932 LOG("(%p) Failed to set aggregate sub-device list", stm);
1933 audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
1934 return CUBEB_ERROR;
1935 }
1936
1937 r = audiounit_set_master_aggregate_device(stm->aggregate_device_id);
1938 if (r != CUBEB_OK) {
1939 LOG("(%p) Failed to set master sub-device for aggregate device", stm);
1940 audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
1941 return CUBEB_ERROR;
1942 }
1943
1944 r = audiounit_activate_clock_drift_compensation(stm->aggregate_device_id);
1945 if (r != CUBEB_OK) {
1946 LOG("(%p) Failed to activate clock drift compensation for aggregate device", stm);
1947 audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
1948 return CUBEB_ERROR;
1949 }
1950
1951 audiounit_workaround_for_airpod(stm);
1952
1953 return CUBEB_OK;
1954 }
1955
1956 static int
audiounit_destroy_aggregate_device(AudioObjectID plugin_id,AudioDeviceID * aggregate_device_id)1957 audiounit_destroy_aggregate_device(AudioObjectID plugin_id, AudioDeviceID * aggregate_device_id)
1958 {
1959 assert(aggregate_device_id &&
1960 *aggregate_device_id != kAudioDeviceUnknown &&
1961 plugin_id != kAudioObjectUnknown);
1962 AudioObjectPropertyAddress destroy_aggregate_device_addr = { kAudioPlugInDestroyAggregateDevice,
1963 kAudioObjectPropertyScopeGlobal,
1964 kAudioObjectPropertyElementMaster};
1965 UInt32 size;
1966 OSStatus rv = AudioObjectGetPropertyDataSize(plugin_id,
1967 &destroy_aggregate_device_addr,
1968 0,
1969 NULL,
1970 &size);
1971 if (rv != noErr) {
1972 LOG("AudioObjectGetPropertyDataSize/kAudioPlugInDestroyAggregateDevice, rv=%d", rv);
1973 return CUBEB_ERROR;
1974 }
1975
1976 rv = AudioObjectGetPropertyData(plugin_id,
1977 &destroy_aggregate_device_addr,
1978 0,
1979 NULL,
1980 &size,
1981 aggregate_device_id);
1982 if (rv != noErr) {
1983 LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d", rv);
1984 return CUBEB_ERROR;
1985 }
1986
1987 LOG("Destroyed aggregate device %d", *aggregate_device_id);
1988 *aggregate_device_id = kAudioObjectUnknown;
1989 return CUBEB_OK;
1990 }
1991
1992 static int
audiounit_new_unit_instance(AudioUnit * unit,device_info * device)1993 audiounit_new_unit_instance(AudioUnit * unit, device_info * device)
1994 {
1995 AudioComponentDescription desc;
1996 AudioComponent comp;
1997 OSStatus rv;
1998
1999 desc.componentType = kAudioUnitType_Output;
2000 #if TARGET_OS_IPHONE
2001 desc.componentSubType = kAudioUnitSubType_RemoteIO;
2002 #else
2003 // Use the DefaultOutputUnit for output when no device is specified
2004 // so we retain automatic output device switching when the default
2005 // changes. Once we have complete support for device notifications
2006 // and switching, we can use the AUHAL for everything.
2007 if ((device->flags & DEV_SYSTEM_DEFAULT) &&
2008 (device->flags & DEV_OUTPUT)) {
2009 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
2010 } else {
2011 desc.componentSubType = kAudioUnitSubType_HALOutput;
2012 }
2013 #endif
2014 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
2015 desc.componentFlags = 0;
2016 desc.componentFlagsMask = 0;
2017 comp = AudioComponentFindNext(NULL, &desc);
2018 if (comp == NULL) {
2019 LOG("Could not find matching audio hardware.");
2020 return CUBEB_ERROR;
2021 }
2022
2023 rv = AudioComponentInstanceNew(comp, unit);
2024 if (rv != noErr) {
2025 LOG("AudioComponentInstanceNew rv=%d", rv);
2026 return CUBEB_ERROR;
2027 }
2028 return CUBEB_OK;
2029 }
2030
2031 enum enable_state {
2032 DISABLE,
2033 ENABLE,
2034 };
2035
2036 static int
audiounit_enable_unit_scope(AudioUnit * unit,io_side side,enable_state state)2037 audiounit_enable_unit_scope(AudioUnit * unit, io_side side, enable_state state)
2038 {
2039 OSStatus rv;
2040 UInt32 enable = state;
2041 rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
2042 (side == io_side::INPUT) ? kAudioUnitScope_Input : kAudioUnitScope_Output,
2043 (side == io_side::INPUT) ? AU_IN_BUS : AU_OUT_BUS,
2044 &enable,
2045 sizeof(UInt32));
2046 if (rv != noErr) {
2047 LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO rv=%d", rv);
2048 return CUBEB_ERROR;
2049 }
2050 return CUBEB_OK;
2051 }
2052
2053 static int
audiounit_create_unit(AudioUnit * unit,device_info * device)2054 audiounit_create_unit(AudioUnit * unit, device_info * device)
2055 {
2056 assert(*unit == nullptr);
2057 assert(device);
2058
2059 OSStatus rv;
2060 int r;
2061
2062 r = audiounit_new_unit_instance(unit, device);
2063 if (r != CUBEB_OK) {
2064 return r;
2065 }
2066 assert(*unit);
2067
2068 if ((device->flags & DEV_SYSTEM_DEFAULT) &&
2069 (device->flags & DEV_OUTPUT)) {
2070 return CUBEB_OK;
2071 }
2072
2073
2074 if (device->flags & DEV_INPUT) {
2075 r = audiounit_enable_unit_scope(unit, io_side::INPUT, ENABLE);
2076 if (r != CUBEB_OK) {
2077 LOG("Failed to enable audiounit input scope");
2078 return r;
2079 }
2080 r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, DISABLE);
2081 if (r != CUBEB_OK) {
2082 LOG("Failed to disable audiounit output scope");
2083 return r;
2084 }
2085 } else if (device->flags & DEV_OUTPUT) {
2086 r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, ENABLE);
2087 if (r != CUBEB_OK) {
2088 LOG("Failed to enable audiounit output scope");
2089 return r;
2090 }
2091 r = audiounit_enable_unit_scope(unit, io_side::INPUT, DISABLE);
2092 if (r != CUBEB_OK) {
2093 LOG("Failed to disable audiounit input scope");
2094 return r;
2095 }
2096 } else {
2097 assert(false);
2098 }
2099
2100 rv = AudioUnitSetProperty(*unit,
2101 kAudioOutputUnitProperty_CurrentDevice,
2102 kAudioUnitScope_Global,
2103 0,
2104 &device->id, sizeof(AudioDeviceID));
2105 if (rv != noErr) {
2106 LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice rv=%d", rv);
2107 return CUBEB_ERROR;
2108 }
2109
2110 return CUBEB_OK;
2111 }
2112
2113 static int
audiounit_init_input_linear_buffer(cubeb_stream * stream,uint32_t capacity)2114 audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
2115 {
2116 uint32_t size = capacity * stream->latency_frames * stream->input_desc.mChannelsPerFrame;
2117 if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) {
2118 stream->input_linear_buffer.reset(new auto_array_wrapper_impl<short>(size));
2119 } else {
2120 stream->input_linear_buffer.reset(new auto_array_wrapper_impl<float>(size));
2121 }
2122 assert(stream->input_linear_buffer->length() == 0);
2123
2124 return CUBEB_OK;
2125 }
2126
2127 static uint32_t
audiounit_clamp_latency(cubeb_stream * stm,uint32_t latency_frames)2128 audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
2129 {
2130 // For the 1st stream set anything within safe min-max
2131 assert(audiounit_active_streams(stm->context) > 0);
2132 if (audiounit_active_streams(stm->context) == 1) {
2133 return max(min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES),
2134 SAFE_MIN_LATENCY_FRAMES);
2135 }
2136 assert(stm->output_unit);
2137
2138 // If more than one stream operates in parallel
2139 // allow only lower values of latency
2140 int r;
2141 UInt32 output_buffer_size = 0;
2142 UInt32 size = sizeof(output_buffer_size);
2143 if (stm->output_unit) {
2144 r = AudioUnitGetProperty(stm->output_unit,
2145 kAudioDevicePropertyBufferFrameSize,
2146 kAudioUnitScope_Output,
2147 AU_OUT_BUS,
2148 &output_buffer_size,
2149 &size);
2150 if (r != noErr) {
2151 LOG("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize rv=%d", r);
2152 return 0;
2153 }
2154
2155 output_buffer_size = max(min<uint32_t>(output_buffer_size, SAFE_MAX_LATENCY_FRAMES),
2156 SAFE_MIN_LATENCY_FRAMES);
2157 }
2158
2159 UInt32 input_buffer_size = 0;
2160 if (stm->input_unit) {
2161 r = AudioUnitGetProperty(stm->input_unit,
2162 kAudioDevicePropertyBufferFrameSize,
2163 kAudioUnitScope_Input,
2164 AU_IN_BUS,
2165 &input_buffer_size,
2166 &size);
2167 if (r != noErr) {
2168 LOG("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize rv=%d", r);
2169 return 0;
2170 }
2171
2172 input_buffer_size = max(min<uint32_t>(input_buffer_size, SAFE_MAX_LATENCY_FRAMES),
2173 SAFE_MIN_LATENCY_FRAMES);
2174 }
2175
2176 // Every following active streams can only set smaller latency
2177 UInt32 upper_latency_limit = 0;
2178 if (input_buffer_size != 0 && output_buffer_size != 0) {
2179 upper_latency_limit = min<uint32_t>(input_buffer_size, output_buffer_size);
2180 } else if (input_buffer_size != 0) {
2181 upper_latency_limit = input_buffer_size;
2182 } else if (output_buffer_size != 0) {
2183 upper_latency_limit = output_buffer_size;
2184 } else {
2185 upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
2186 }
2187
2188 return max(min<uint32_t>(latency_frames, upper_latency_limit),
2189 SAFE_MIN_LATENCY_FRAMES);
2190 }
2191
2192 /*
2193 * Change buffer size is prone to deadlock thus we change it
2194 * following the steps:
2195 * - register a listener for the buffer size property
2196 * - change the property
2197 * - wait until the listener is executed
2198 * - property has changed, remove the listener
2199 * */
2200 static void
buffer_size_changed_callback(void * inClientData,AudioUnit inUnit,AudioUnitPropertyID inPropertyID,AudioUnitScope inScope,AudioUnitElement inElement)2201 buffer_size_changed_callback(void * inClientData,
2202 AudioUnit inUnit,
2203 AudioUnitPropertyID inPropertyID,
2204 AudioUnitScope inScope,
2205 AudioUnitElement inElement)
2206 {
2207 cubeb_stream * stm = (cubeb_stream *)inClientData;
2208
2209 AudioUnit au = inUnit;
2210 AudioUnitScope au_scope = kAudioUnitScope_Input;
2211 AudioUnitElement au_element = inElement;
2212 char const * au_type = "output";
2213
2214 if (AU_IN_BUS == inElement) {
2215 au_scope = kAudioUnitScope_Output;
2216 au_type = "input";
2217 }
2218
2219 switch (inPropertyID) {
2220
2221 case kAudioDevicePropertyBufferFrameSize: {
2222 if (inScope != au_scope) {
2223 break;
2224 }
2225 UInt32 new_buffer_size;
2226 UInt32 outSize = sizeof(UInt32);
2227 OSStatus r = AudioUnitGetProperty(au,
2228 kAudioDevicePropertyBufferFrameSize,
2229 au_scope,
2230 au_element,
2231 &new_buffer_size,
2232 &outSize);
2233 if (r != noErr) {
2234 LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current buffer size", stm);
2235 } else {
2236 LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: New %s buffer size = %d for scope %d", stm,
2237 au_type, new_buffer_size, inScope);
2238 }
2239 stm->buffer_size_change_state = true;
2240 break;
2241 }
2242 }
2243 }
2244
2245 static int
audiounit_set_buffer_size(cubeb_stream * stm,uint32_t new_size_frames,io_side side)2246 audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, io_side side)
2247 {
2248 AudioUnit au = stm->output_unit;
2249 AudioUnitScope au_scope = kAudioUnitScope_Input;
2250 AudioUnitElement au_element = AU_OUT_BUS;
2251
2252 if (side == io_side::INPUT) {
2253 au = stm->input_unit;
2254 au_scope = kAudioUnitScope_Output;
2255 au_element = AU_IN_BUS;
2256 }
2257
2258 uint32_t buffer_frames = 0;
2259 UInt32 size = sizeof(buffer_frames);
2260 int r = AudioUnitGetProperty(au,
2261 kAudioDevicePropertyBufferFrameSize,
2262 au_scope,
2263 au_element,
2264 &buffer_frames,
2265 &size);
2266 if (r != noErr) {
2267 LOG("AudioUnitGetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
2268 return CUBEB_ERROR;
2269 }
2270
2271 if (new_size_frames == buffer_frames) {
2272 LOG("(%p) No need to update %s buffer size already %u frames", stm, to_string(side), buffer_frames);
2273 return CUBEB_OK;
2274 }
2275
2276 r = AudioUnitAddPropertyListener(au,
2277 kAudioDevicePropertyBufferFrameSize,
2278 buffer_size_changed_callback,
2279 stm);
2280 if (r != noErr) {
2281 LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
2282 return CUBEB_ERROR;
2283 }
2284
2285 stm->buffer_size_change_state = false;
2286
2287 r = AudioUnitSetProperty(au,
2288 kAudioDevicePropertyBufferFrameSize,
2289 au_scope,
2290 au_element,
2291 &new_size_frames,
2292 sizeof(new_size_frames));
2293 if (r != noErr) {
2294 LOG("AudioUnitSetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
2295
2296 r = AudioUnitRemovePropertyListenerWithUserData(au,
2297 kAudioDevicePropertyBufferFrameSize,
2298 buffer_size_changed_callback,
2299 stm);
2300 if (r != noErr) {
2301 LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
2302 }
2303
2304 return CUBEB_ERROR;
2305 }
2306
2307 int count = 0;
2308 while (!stm->buffer_size_change_state && count++ < 30) {
2309 struct timespec req, rem;
2310 req.tv_sec = 0;
2311 req.tv_nsec = 100000000L; // 0.1 sec
2312 if (nanosleep(&req , &rem) < 0 ) {
2313 LOG("(%p) Warning: nanosleep call failed or interrupted. Remaining time %ld nano secs \n", stm, rem.tv_nsec);
2314 }
2315 LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count);
2316 }
2317
2318 r = AudioUnitRemovePropertyListenerWithUserData(au,
2319 kAudioDevicePropertyBufferFrameSize,
2320 buffer_size_changed_callback,
2321 stm);
2322 if (r != noErr) {
2323 LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r);
2324 return CUBEB_ERROR;
2325 }
2326
2327 if (!stm->buffer_size_change_state && count >= 30) {
2328 LOG("(%p) Error, did not get buffer size change callback ...", stm);
2329 return CUBEB_ERROR;
2330 }
2331
2332 LOG("(%p) %s buffer size changed to %u frames.", stm, to_string(side), new_size_frames);
2333 return CUBEB_OK;
2334 }
2335
2336 static int
audiounit_configure_input(cubeb_stream * stm)2337 audiounit_configure_input(cubeb_stream * stm)
2338 {
2339 assert(stm && stm->input_unit);
2340
2341 int r = 0;
2342 UInt32 size;
2343 AURenderCallbackStruct aurcbs_in;
2344
2345 LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in frames %u.",
2346 stm, stm->input_stream_params.rate, stm->input_stream_params.channels,
2347 stm->input_stream_params.format, stm->latency_frames);
2348
2349 /* Get input device sample rate. */
2350 AudioStreamBasicDescription input_hw_desc;
2351 size = sizeof(AudioStreamBasicDescription);
2352 r = AudioUnitGetProperty(stm->input_unit,
2353 kAudioUnitProperty_StreamFormat,
2354 kAudioUnitScope_Input,
2355 AU_IN_BUS,
2356 &input_hw_desc,
2357 &size);
2358 if (r != noErr) {
2359 LOG("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r);
2360 return CUBEB_ERROR;
2361 }
2362 stm->input_hw_rate = input_hw_desc.mSampleRate;
2363 LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate);
2364
2365 /* Set format description according to the input params. */
2366 r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
2367 if (r != CUBEB_OK) {
2368 LOG("(%p) Setting format description for input failed.", stm);
2369 return r;
2370 }
2371
2372 // Use latency to set buffer size
2373 r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::INPUT);
2374 if (r != CUBEB_OK) {
2375 LOG("(%p) Error in change input buffer size.", stm);
2376 return CUBEB_ERROR;
2377 }
2378
2379 AudioStreamBasicDescription src_desc = stm->input_desc;
2380 /* Input AudioUnit must be configured with device's sample rate.
2381 we will resample inside input callback. */
2382 src_desc.mSampleRate = stm->input_hw_rate;
2383
2384 r = AudioUnitSetProperty(stm->input_unit,
2385 kAudioUnitProperty_StreamFormat,
2386 kAudioUnitScope_Output,
2387 AU_IN_BUS,
2388 &src_desc,
2389 sizeof(AudioStreamBasicDescription));
2390 if (r != noErr) {
2391 LOG("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r);
2392 return CUBEB_ERROR;
2393 }
2394
2395 /* Frames per buffer in the input callback. */
2396 r = AudioUnitSetProperty(stm->input_unit,
2397 kAudioUnitProperty_MaximumFramesPerSlice,
2398 kAudioUnitScope_Global,
2399 AU_IN_BUS,
2400 &stm->latency_frames,
2401 sizeof(UInt32));
2402 if (r != noErr) {
2403 LOG("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice rv=%d", r);
2404 return CUBEB_ERROR;
2405 }
2406
2407 // Input only capacity
2408 unsigned int array_capacity = 1;
2409 if (has_output(stm)) {
2410 // Full-duplex increase capacity
2411 array_capacity = 8;
2412 }
2413 if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
2414 return CUBEB_ERROR;
2415 }
2416
2417 aurcbs_in.inputProc = audiounit_input_callback;
2418 aurcbs_in.inputProcRefCon = stm;
2419
2420 r = AudioUnitSetProperty(stm->input_unit,
2421 kAudioOutputUnitProperty_SetInputCallback,
2422 kAudioUnitScope_Global,
2423 AU_OUT_BUS,
2424 &aurcbs_in,
2425 sizeof(aurcbs_in));
2426 if (r != noErr) {
2427 LOG("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback rv=%d", r);
2428 return CUBEB_ERROR;
2429 }
2430
2431 stm->frames_read = 0;
2432
2433 LOG("(%p) Input audiounit init successfully.", stm);
2434
2435 return CUBEB_OK;
2436 }
2437
2438 static int
audiounit_configure_output(cubeb_stream * stm)2439 audiounit_configure_output(cubeb_stream * stm)
2440 {
2441 assert(stm && stm->output_unit);
2442
2443 int r;
2444 AURenderCallbackStruct aurcbs_out;
2445 UInt32 size;
2446
2447
2448 LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in frames %u.",
2449 stm, stm->output_stream_params.rate, stm->output_stream_params.channels,
2450 stm->output_stream_params.format, stm->latency_frames);
2451
2452 r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
2453 if (r != CUBEB_OK) {
2454 LOG("(%p) Could not initialize the audio stream description.", stm);
2455 return r;
2456 }
2457
2458 /* Get output device sample rate. */
2459 AudioStreamBasicDescription output_hw_desc;
2460 size = sizeof(AudioStreamBasicDescription);
2461 memset(&output_hw_desc, 0, size);
2462 r = AudioUnitGetProperty(stm->output_unit,
2463 kAudioUnitProperty_StreamFormat,
2464 kAudioUnitScope_Output,
2465 AU_OUT_BUS,
2466 &output_hw_desc,
2467 &size);
2468 if (r != noErr) {
2469 LOG("AudioUnitGetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
2470 return CUBEB_ERROR;
2471 }
2472 stm->output_hw_rate = output_hw_desc.mSampleRate;
2473 LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate);
2474 stm->context->channels = output_hw_desc.mChannelsPerFrame;
2475
2476 // Set the input layout to match the output device layout.
2477 audiounit_layout_init(stm, io_side::OUTPUT);
2478 if (stm->context->channels != stm->output_stream_params.channels ||
2479 stm->context->layout != stm->output_stream_params.layout) {
2480 LOG("Incompatible channel layouts detected, setting up remixer");
2481 audiounit_init_mixer(stm);
2482 // We will be remixing the data before it reaches the output device.
2483 // We need to adjust the number of channels and other
2484 // AudioStreamDescription details.
2485 stm->output_desc.mChannelsPerFrame = stm->context->channels;
2486 stm->output_desc.mBytesPerFrame = (stm->output_desc.mBitsPerChannel / 8) *
2487 stm->output_desc.mChannelsPerFrame;
2488 stm->output_desc.mBytesPerPacket =
2489 stm->output_desc.mBytesPerFrame * stm->output_desc.mFramesPerPacket;
2490 } else {
2491 stm->mixer = nullptr;
2492 }
2493
2494 r = AudioUnitSetProperty(stm->output_unit,
2495 kAudioUnitProperty_StreamFormat,
2496 kAudioUnitScope_Input,
2497 AU_OUT_BUS,
2498 &stm->output_desc,
2499 sizeof(AudioStreamBasicDescription));
2500 if (r != noErr) {
2501 LOG("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
2502 return CUBEB_ERROR;
2503 }
2504
2505 r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::OUTPUT);
2506 if (r != CUBEB_OK) {
2507 LOG("(%p) Error in change output buffer size.", stm);
2508 return CUBEB_ERROR;
2509 }
2510
2511 /* Frames per buffer in the input callback. */
2512 r = AudioUnitSetProperty(stm->output_unit,
2513 kAudioUnitProperty_MaximumFramesPerSlice,
2514 kAudioUnitScope_Global,
2515 AU_OUT_BUS,
2516 &stm->latency_frames,
2517 sizeof(UInt32));
2518 if (r != noErr) {
2519 LOG("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice rv=%d", r);
2520 return CUBEB_ERROR;
2521 }
2522
2523 aurcbs_out.inputProc = audiounit_output_callback;
2524 aurcbs_out.inputProcRefCon = stm;
2525 r = AudioUnitSetProperty(stm->output_unit,
2526 kAudioUnitProperty_SetRenderCallback,
2527 kAudioUnitScope_Global,
2528 AU_OUT_BUS,
2529 &aurcbs_out,
2530 sizeof(aurcbs_out));
2531 if (r != noErr) {
2532 LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback rv=%d", r);
2533 return CUBEB_ERROR;
2534 }
2535
2536 stm->frames_written = 0;
2537
2538 LOG("(%p) Output audiounit init successfully.", stm);
2539 return CUBEB_OK;
2540 }
2541
2542 static int
audiounit_setup_stream(cubeb_stream * stm)2543 audiounit_setup_stream(cubeb_stream * stm)
2544 {
2545 stm->mutex.assert_current_thread_owns();
2546
2547 if ((stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) ||
2548 (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK)) {
2549 LOG("(%p) Loopback not supported for audiounit.", stm);
2550 return CUBEB_ERROR_NOT_SUPPORTED;
2551 }
2552
2553 int r = 0;
2554
2555 device_info in_dev_info = stm->input_device;
2556 device_info out_dev_info = stm->output_device;
2557
2558 if (has_input(stm) && has_output(stm) &&
2559 stm->input_device.id != stm->output_device.id) {
2560 r = audiounit_create_aggregate_device(stm);
2561 if (r != CUBEB_OK) {
2562 stm->aggregate_device_id = kAudioObjectUnknown;
2563 LOG("(%p) Create aggregate devices failed.", stm);
2564 // !!!NOTE: It is not necessary to return here. If it does not
2565 // return it will fallback to the old implementation. The intention
2566 // is to investigate how often it fails. I plan to remove
2567 // it after a couple of weeks.
2568 return r;
2569 } else {
2570 in_dev_info.id = out_dev_info.id = stm->aggregate_device_id;
2571 in_dev_info.flags = DEV_INPUT;
2572 out_dev_info.flags = DEV_OUTPUT;
2573 }
2574 }
2575
2576 if (has_input(stm)) {
2577 r = audiounit_create_unit(&stm->input_unit, &in_dev_info);
2578 if (r != CUBEB_OK) {
2579 LOG("(%p) AudioUnit creation for input failed.", stm);
2580 return r;
2581 }
2582 }
2583
2584 if (has_output(stm)) {
2585 r = audiounit_create_unit(&stm->output_unit, &out_dev_info);
2586 if (r != CUBEB_OK) {
2587 LOG("(%p) AudioUnit creation for output failed.", stm);
2588 return r;
2589 }
2590 }
2591
2592 /* Latency cannot change if another stream is operating in parallel. In this case
2593 * latency is set to the other stream value. */
2594 if (audiounit_active_streams(stm->context) > 1) {
2595 LOG("(%p) More than one active stream, use global latency.", stm);
2596 stm->latency_frames = stm->context->global_latency_frames;
2597 } else {
2598 /* Silently clamp the latency down to the platform default, because we
2599 * synthetize the clock from the callbacks, and we want the clock to update
2600 * often. */
2601 stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames);
2602 assert(stm->latency_frames); // Ugly error check
2603 audiounit_set_global_latency(stm->context, stm->latency_frames);
2604 }
2605
2606 /* Configure I/O stream */
2607 if (has_input(stm)) {
2608 r = audiounit_configure_input(stm);
2609 if (r != CUBEB_OK) {
2610 LOG("(%p) Configure audiounit input failed.", stm);
2611 return r;
2612 }
2613 }
2614
2615 if (has_output(stm)) {
2616 r = audiounit_configure_output(stm);
2617 if (r != CUBEB_OK) {
2618 LOG("(%p) Configure audiounit output failed.", stm);
2619 return r;
2620 }
2621 }
2622
2623 // Setting the latency doesn't work well for USB headsets (eg. plantronics).
2624 // Keep the default latency for now.
2625 #if 0
2626 buffer_size = latency;
2627
2628 /* Get the range of latency this particular device can work with, and clamp
2629 * the requested latency to this acceptable range. */
2630 #if !TARGET_OS_IPHONE
2631 if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
2632 return CUBEB_ERROR;
2633 }
2634
2635 if (buffer_size < (unsigned int) latency_range.mMinimum) {
2636 buffer_size = (unsigned int) latency_range.mMinimum;
2637 } else if (buffer_size > (unsigned int) latency_range.mMaximum) {
2638 buffer_size = (unsigned int) latency_range.mMaximum;
2639 }
2640
2641 /**
2642 * Get the default buffer size. If our latency request is below the default,
2643 * set it. Otherwise, use the default latency.
2644 **/
2645 size = sizeof(default_buffer_size);
2646 if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
2647 kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) {
2648 return CUBEB_ERROR;
2649 }
2650
2651 if (buffer_size < default_buffer_size) {
2652 /* Set the maximum number of frame that the render callback will ask for,
2653 * effectively setting the latency of the stream. This is process-wide. */
2654 if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
2655 kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) {
2656 return CUBEB_ERROR;
2657 }
2658 }
2659 #else // TARGET_OS_IPHONE
2660 //TODO: [[AVAudioSession sharedInstance] inputLatency]
2661 // http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios
2662 #endif
2663 #endif
2664
2665 /* We use a resampler because input AudioUnit operates
2666 * reliable only in the capture device sample rate.
2667 * Resampler will convert it to the user sample rate
2668 * and deliver it to the callback. */
2669 uint32_t target_sample_rate;
2670 if (has_input(stm)) {
2671 target_sample_rate = stm->input_stream_params.rate;
2672 } else {
2673 assert(has_output(stm));
2674 target_sample_rate = stm->output_stream_params.rate;
2675 }
2676
2677 cubeb_stream_params input_unconverted_params;
2678 if (has_input(stm)) {
2679 input_unconverted_params = stm->input_stream_params;
2680 /* Use the rate of the input device. */
2681 input_unconverted_params.rate = stm->input_hw_rate;
2682 }
2683
2684 /* Create resampler. Output params are unchanged
2685 * because we do not need conversion on the output. */
2686 stm->resampler.reset(cubeb_resampler_create(stm,
2687 has_input(stm) ? &input_unconverted_params : NULL,
2688 has_output(stm) ? &stm->output_stream_params : NULL,
2689 target_sample_rate,
2690 stm->data_callback,
2691 stm->user_ptr,
2692 CUBEB_RESAMPLER_QUALITY_DESKTOP));
2693 if (!stm->resampler) {
2694 LOG("(%p) Could not create resampler.", stm);
2695 return CUBEB_ERROR;
2696 }
2697
2698 if (stm->input_unit != NULL) {
2699 r = AudioUnitInitialize(stm->input_unit);
2700 if (r != noErr) {
2701 LOG("AudioUnitInitialize/input rv=%d", r);
2702 return CUBEB_ERROR;
2703 }
2704 }
2705
2706 if (stm->output_unit != NULL) {
2707 r = AudioUnitInitialize(stm->output_unit);
2708 if (r != noErr) {
2709 LOG("AudioUnitInitialize/output rv=%d", r);
2710 return CUBEB_ERROR;
2711 }
2712
2713 stm->current_latency_frames = audiounit_get_device_presentation_latency(stm->output_device.id, kAudioDevicePropertyScopeOutput);
2714
2715 Float64 unit_s;
2716 UInt32 size = sizeof(unit_s);
2717 if (AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &unit_s, &size) == noErr) {
2718 stm->current_latency_frames += static_cast<uint32_t>(unit_s * stm->output_desc.mSampleRate);
2719 }
2720 }
2721
2722 if (stm->input_unit && stm->output_unit) {
2723 // According to the I/O hardware rate it is expected a specific pattern of callbacks
2724 // for example is input is 44100 and output is 48000 we expected no more than 2
2725 // out callback in a row.
2726 stm->expected_output_callbacks_in_a_row = ceilf(stm->output_hw_rate / stm->input_hw_rate);
2727 }
2728
2729 r = audiounit_install_device_changed_callback(stm);
2730 if (r != CUBEB_OK) {
2731 LOG("(%p) Could not install all device change callback.", stm);
2732 }
2733
2734
2735 return CUBEB_OK;
2736 }
2737
cubeb_stream(cubeb * context)2738 cubeb_stream::cubeb_stream(cubeb * context)
2739 : context(context)
2740 , resampler(nullptr, cubeb_resampler_destroy)
2741 , mixer(nullptr, cubeb_mixer_destroy)
2742 {
2743 PodZero(&input_desc, 1);
2744 PodZero(&output_desc, 1);
2745 }
2746
2747 static void audiounit_stream_destroy_internal(cubeb_stream * stm);
2748
2749 static int
audiounit_stream_init(cubeb * context,cubeb_stream ** stream,char const *,cubeb_devid input_device,cubeb_stream_params * input_stream_params,cubeb_devid output_device,cubeb_stream_params * output_stream_params,unsigned int latency_frames,cubeb_data_callback data_callback,cubeb_state_callback state_callback,void * user_ptr)2750 audiounit_stream_init(cubeb * context,
2751 cubeb_stream ** stream,
2752 char const * /* stream_name */,
2753 cubeb_devid input_device,
2754 cubeb_stream_params * input_stream_params,
2755 cubeb_devid output_device,
2756 cubeb_stream_params * output_stream_params,
2757 unsigned int latency_frames,
2758 cubeb_data_callback data_callback,
2759 cubeb_state_callback state_callback,
2760 void * user_ptr)
2761 {
2762 assert(context);
2763 auto_lock context_lock(context->mutex);
2764 audiounit_increment_active_streams(context);
2765 unique_ptr<cubeb_stream, decltype(&audiounit_stream_destroy)> stm(new cubeb_stream(context),
2766 audiounit_stream_destroy_internal);
2767 int r;
2768 *stream = NULL;
2769 assert(latency_frames > 0);
2770
2771 /* These could be different in the future if we have both
2772 * full-duplex stream and different devices for input vs output. */
2773 stm->data_callback = data_callback;
2774 stm->state_callback = state_callback;
2775 stm->user_ptr = user_ptr;
2776 stm->latency_frames = latency_frames;
2777
2778 if ((input_device && !input_stream_params) ||
2779 (output_device && !output_stream_params)) {
2780 return CUBEB_ERROR_INVALID_PARAMETER;
2781 }
2782 if (input_stream_params) {
2783 stm->input_stream_params = *input_stream_params;
2784 r = audiounit_set_device_info(stm.get(), reinterpret_cast<uintptr_t>(input_device), io_side::INPUT);
2785 if (r != CUBEB_OK) {
2786 LOG("(%p) Fail to set device info for input.", stm.get());
2787 return r;
2788 }
2789 }
2790 if (output_stream_params) {
2791 stm->output_stream_params = *output_stream_params;
2792 r = audiounit_set_device_info(stm.get(), reinterpret_cast<uintptr_t>(output_device), io_side::OUTPUT);
2793 if (r != CUBEB_OK) {
2794 LOG("(%p) Fail to set device info for output.", stm.get());
2795 return r;
2796 }
2797 }
2798
2799 {
2800 // It's not critical to lock here, because no other thread has been started
2801 // yet, but it allows to assert that the lock has been taken in
2802 // `audiounit_setup_stream`.
2803 auto_lock lock(stm->mutex);
2804 r = audiounit_setup_stream(stm.get());
2805 }
2806
2807 if (r != CUBEB_OK) {
2808 LOG("(%p) Could not setup the audiounit stream.", stm.get());
2809 return r;
2810 }
2811
2812 r = audiounit_install_system_changed_callback(stm.get());
2813 if (r != CUBEB_OK) {
2814 LOG("(%p) Could not install the device change callback.", stm.get());
2815 return r;
2816 }
2817
2818 *stream = stm.release();
2819 LOG("(%p) Cubeb stream init successful.", *stream);
2820 return CUBEB_OK;
2821 }
2822
2823 static void
audiounit_close_stream(cubeb_stream * stm)2824 audiounit_close_stream(cubeb_stream *stm)
2825 {
2826 stm->mutex.assert_current_thread_owns();
2827
2828 if (stm->input_unit) {
2829 AudioUnitUninitialize(stm->input_unit);
2830 AudioComponentInstanceDispose(stm->input_unit);
2831 stm->input_unit = nullptr;
2832 }
2833
2834 stm->input_linear_buffer.reset();
2835
2836 if (stm->output_unit) {
2837 AudioUnitUninitialize(stm->output_unit);
2838 AudioComponentInstanceDispose(stm->output_unit);
2839 stm->output_unit = nullptr;
2840 }
2841
2842 stm->resampler.reset();
2843 stm->mixer.reset();
2844
2845 if (stm->aggregate_device_id != kAudioObjectUnknown) {
2846 audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
2847 stm->aggregate_device_id = kAudioObjectUnknown;
2848 }
2849 }
2850
2851 static void
audiounit_stream_destroy_internal(cubeb_stream * stm)2852 audiounit_stream_destroy_internal(cubeb_stream *stm)
2853 {
2854 stm->context->mutex.assert_current_thread_owns();
2855
2856 int r = audiounit_uninstall_system_changed_callback(stm);
2857 if (r != CUBEB_OK) {
2858 LOG("(%p) Could not uninstall the device changed callback", stm);
2859 }
2860 r = audiounit_uninstall_device_changed_callback(stm);
2861 if (r != CUBEB_OK) {
2862 LOG("(%p) Could not uninstall all device change listeners", stm);
2863 }
2864
2865 auto_lock lock(stm->mutex);
2866 audiounit_close_stream(stm);
2867 assert(audiounit_active_streams(stm->context) >= 1);
2868 audiounit_decrement_active_streams(stm->context);
2869 }
2870
2871 static void
audiounit_stream_destroy(cubeb_stream * stm)2872 audiounit_stream_destroy(cubeb_stream * stm)
2873 {
2874 int r = audiounit_uninstall_system_changed_callback(stm);
2875 if (r != CUBEB_OK) {
2876 LOG("(%p) Could not uninstall the device changed callback", stm);
2877 }
2878 r = audiounit_uninstall_device_changed_callback(stm);
2879 if (r != CUBEB_OK) {
2880 LOG("(%p) Could not uninstall all device change listeners", stm);
2881 }
2882
2883 if (!stm->shutdown.load()){
2884 auto_lock context_lock(stm->context->mutex);
2885 audiounit_stream_stop_internal(stm);
2886 stm->shutdown = true;
2887 }
2888
2889 stm->destroy_pending = true;
2890 // Execute close in serial queue to avoid collision
2891 // with reinit when un/plug devices
2892 dispatch_sync(stm->context->serial_queue, ^() {
2893 auto_lock context_lock(stm->context->mutex);
2894 audiounit_stream_destroy_internal(stm);
2895 });
2896
2897 LOG("Cubeb stream (%p) destroyed successful.", stm);
2898 delete stm;
2899 }
2900
2901 static int
audiounit_stream_start_internal(cubeb_stream * stm)2902 audiounit_stream_start_internal(cubeb_stream * stm)
2903 {
2904 OSStatus r;
2905 if (stm->input_unit != NULL) {
2906 r = AudioOutputUnitStart(stm->input_unit);
2907 if (r != noErr) {
2908 LOG("AudioOutputUnitStart (input) rv=%d", r);
2909 return CUBEB_ERROR;
2910 }
2911 }
2912 if (stm->output_unit != NULL) {
2913 r = AudioOutputUnitStart(stm->output_unit);
2914 if (r != noErr) {
2915 LOG("AudioOutputUnitStart (output) rv=%d", r);
2916 return CUBEB_ERROR;
2917 }
2918 }
2919 return CUBEB_OK;
2920 }
2921
2922 static int
audiounit_stream_start(cubeb_stream * stm)2923 audiounit_stream_start(cubeb_stream * stm)
2924 {
2925 auto_lock context_lock(stm->context->mutex);
2926 stm->shutdown = false;
2927 stm->draining = false;
2928
2929 int r = audiounit_stream_start_internal(stm);
2930 if (r != CUBEB_OK) {
2931 return r;
2932 }
2933
2934 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
2935
2936 LOG("Cubeb stream (%p) started successfully.", stm);
2937 return CUBEB_OK;
2938 }
2939
2940 void
audiounit_stream_stop_internal(cubeb_stream * stm)2941 audiounit_stream_stop_internal(cubeb_stream * stm)
2942 {
2943 OSStatus r;
2944 if (stm->input_unit != NULL) {
2945 r = AudioOutputUnitStop(stm->input_unit);
2946 assert(r == 0);
2947 }
2948 if (stm->output_unit != NULL) {
2949 r = AudioOutputUnitStop(stm->output_unit);
2950 assert(r == 0);
2951 }
2952 }
2953
2954 static int
audiounit_stream_stop(cubeb_stream * stm)2955 audiounit_stream_stop(cubeb_stream * stm)
2956 {
2957 auto_lock context_lock(stm->context->mutex);
2958 stm->shutdown = true;
2959
2960 audiounit_stream_stop_internal(stm);
2961
2962 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
2963
2964 LOG("Cubeb stream (%p) stopped successfully.", stm);
2965 return CUBEB_OK;
2966 }
2967
2968 static int
audiounit_stream_get_position(cubeb_stream * stm,uint64_t * position)2969 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
2970 {
2971 assert(stm);
2972 if (stm->current_latency_frames > stm->frames_played) {
2973 *position = 0;
2974 } else {
2975 *position = stm->frames_played - stm->current_latency_frames;
2976 }
2977 return CUBEB_OK;
2978 }
2979
2980 int
audiounit_stream_get_latency(cubeb_stream * stm,uint32_t * latency)2981 audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
2982 {
2983 #if TARGET_OS_IPHONE
2984 //TODO
2985 return CUBEB_ERROR_NOT_SUPPORTED;
2986 #else
2987 *latency = stm->total_output_latency_frames;
2988 return CUBEB_OK;
2989 #endif
2990 }
2991
2992 static int
audiounit_stream_get_volume(cubeb_stream * stm,float * volume)2993 audiounit_stream_get_volume(cubeb_stream * stm, float * volume)
2994 {
2995 assert(stm->output_unit);
2996 OSStatus r = AudioUnitGetParameter(stm->output_unit,
2997 kHALOutputParam_Volume,
2998 kAudioUnitScope_Global,
2999 0, volume);
3000 if (r != noErr) {
3001 LOG("AudioUnitGetParameter/kHALOutputParam_Volume rv=%d", r);
3002 return CUBEB_ERROR;
3003 }
3004 return CUBEB_OK;
3005 }
3006
3007 static int
audiounit_stream_set_volume(cubeb_stream * stm,float volume)3008 audiounit_stream_set_volume(cubeb_stream * stm, float volume)
3009 {
3010 assert(stm->output_unit);
3011 OSStatus r;
3012 r = AudioUnitSetParameter(stm->output_unit,
3013 kHALOutputParam_Volume,
3014 kAudioUnitScope_Global,
3015 0, volume, 0);
3016
3017 if (r != noErr) {
3018 LOG("AudioUnitSetParameter/kHALOutputParam_Volume rv=%d", r);
3019 return CUBEB_ERROR;
3020 }
3021 return CUBEB_OK;
3022 }
3023
convert_uint32_into_string(UInt32 data)3024 unique_ptr<char[]> convert_uint32_into_string(UInt32 data)
3025 {
3026 // Simply create an empty string if no data.
3027 size_t size = data == 0 ? 0 : 4; // 4 bytes for uint32.
3028 auto str = unique_ptr<char[]> { new char[size + 1] }; // + 1 for '\0'.
3029 str[size] = '\0';
3030 if (size < 4) {
3031 return str;
3032 }
3033
3034 // Reverse 0xWXYZ into 0xZYXW.
3035 str[0] = (char)(data >> 24);
3036 str[1] = (char)(data >> 16);
3037 str[2] = (char)(data >> 8);
3038 str[3] = (char)(data);
3039 return str;
3040 }
3041
audiounit_get_default_device_datasource(cubeb_device_type type,UInt32 * data)3042 int audiounit_get_default_device_datasource(cubeb_device_type type,
3043 UInt32 * data)
3044 {
3045 AudioDeviceID id = audiounit_get_default_device_id(type);
3046 if (id == kAudioObjectUnknown) {
3047 return CUBEB_ERROR;
3048 }
3049
3050 UInt32 size = sizeof(*data);
3051 /* This fails with some USB headsets (e.g., Plantronic .Audio 628). */
3052 OSStatus r = AudioObjectGetPropertyData(id,
3053 type == CUBEB_DEVICE_TYPE_INPUT ?
3054 &INPUT_DATA_SOURCE_PROPERTY_ADDRESS :
3055 &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
3056 0, NULL, &size, data);
3057 if (r != noErr) {
3058 *data = 0;
3059 }
3060
3061 return CUBEB_OK;
3062 }
3063
audiounit_get_default_device_name(cubeb_stream * stm,cubeb_device * const device,cubeb_device_type type)3064 int audiounit_get_default_device_name(cubeb_stream * stm,
3065 cubeb_device * const device,
3066 cubeb_device_type type)
3067 {
3068 assert(stm);
3069 assert(device);
3070
3071 UInt32 data;
3072 int r = audiounit_get_default_device_datasource(type, &data);
3073 if (r != CUBEB_OK) {
3074 return r;
3075 }
3076 char ** name = type == CUBEB_DEVICE_TYPE_INPUT ?
3077 &device->input_name : &device->output_name;
3078 *name = convert_uint32_into_string(data).release();
3079 if (!strlen(*name)) { // empty string.
3080 LOG("(%p) name of %s device is empty!", stm,
3081 type == CUBEB_DEVICE_TYPE_INPUT ? "input" : "output");
3082 }
3083 return CUBEB_OK;
3084 }
3085
3086
audiounit_stream_get_current_device(cubeb_stream * stm,cubeb_device ** const device)3087 int audiounit_stream_get_current_device(cubeb_stream * stm,
3088 cubeb_device ** const device)
3089 {
3090 #if TARGET_OS_IPHONE
3091 //TODO
3092 return CUBEB_ERROR_NOT_SUPPORTED;
3093 #else
3094 *device = new cubeb_device;
3095 if (!*device) {
3096 return CUBEB_ERROR;
3097 }
3098 PodZero(*device, 1);
3099
3100 int r = audiounit_get_default_device_name(stm, *device,
3101 CUBEB_DEVICE_TYPE_OUTPUT);
3102 if (r != CUBEB_OK) {
3103 return r;
3104 }
3105
3106 r = audiounit_get_default_device_name(stm, *device,
3107 CUBEB_DEVICE_TYPE_INPUT);
3108 if (r != CUBEB_OK) {
3109 return r;
3110 }
3111
3112 return CUBEB_OK;
3113 #endif
3114 }
3115
audiounit_stream_device_destroy(cubeb_stream *,cubeb_device * device)3116 int audiounit_stream_device_destroy(cubeb_stream * /* stream */,
3117 cubeb_device * device)
3118 {
3119 delete [] device->output_name;
3120 delete [] device->input_name;
3121 delete device;
3122 return CUBEB_OK;
3123 }
3124
audiounit_stream_register_device_changed_callback(cubeb_stream * stream,cubeb_device_changed_callback device_changed_callback)3125 int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
3126 cubeb_device_changed_callback device_changed_callback)
3127 {
3128 auto_lock dev_cb_lock(stream->device_changed_callback_lock);
3129 /* Note: second register without unregister first causes 'nope' error.
3130 * Current implementation requires unregister before register a new cb. */
3131 assert(!device_changed_callback || !stream->device_changed_callback);
3132 stream->device_changed_callback = device_changed_callback;
3133 return CUBEB_OK;
3134 }
3135
3136 static char *
audiounit_strref_to_cstr_utf8(CFStringRef strref)3137 audiounit_strref_to_cstr_utf8(CFStringRef strref)
3138 {
3139 CFIndex len, size;
3140 char * ret;
3141 if (strref == NULL) {
3142 return NULL;
3143 }
3144
3145 len = CFStringGetLength(strref);
3146 // Add 1 to size to allow for '\0' termination character.
3147 size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
3148 ret = new char[size];
3149
3150 if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
3151 delete [] ret;
3152 ret = NULL;
3153 }
3154
3155 return ret;
3156 }
3157
3158 static uint32_t
audiounit_get_channel_count(AudioObjectID devid,AudioObjectPropertyScope scope)3159 audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
3160 {
3161 AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
3162 UInt32 size = 0;
3163 uint32_t i, ret = 0;
3164
3165 adr.mSelector = kAudioDevicePropertyStreamConfiguration;
3166
3167 if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) {
3168 AudioBufferList * list = static_cast<AudioBufferList *>(alloca(size));
3169 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) {
3170 for (i = 0; i < list->mNumberBuffers; i++)
3171 ret += list->mBuffers[i].mNumberChannels;
3172 }
3173 }
3174
3175 return ret;
3176 }
3177
3178 static void
audiounit_get_available_samplerate(AudioObjectID devid,AudioObjectPropertyScope scope,uint32_t * min,uint32_t * max,uint32_t * def)3179 audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope,
3180 uint32_t * min, uint32_t * max, uint32_t * def)
3181 {
3182 AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
3183
3184 adr.mSelector = kAudioDevicePropertyNominalSampleRate;
3185 if (AudioObjectHasProperty(devid, &adr)) {
3186 UInt32 size = sizeof(Float64);
3187 Float64 fvalue = 0.0;
3188 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr) {
3189 *def = fvalue;
3190 }
3191 }
3192
3193 adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
3194 UInt32 size = 0;
3195 AudioValueRange range;
3196 if (AudioObjectHasProperty(devid, &adr) &&
3197 AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) {
3198 uint32_t count = size / sizeof(AudioValueRange);
3199 vector<AudioValueRange> ranges(count);
3200 range.mMinimum = 9999999999.0;
3201 range.mMaximum = 0.0;
3202 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges.data()) == noErr) {
3203 for (uint32_t i = 0; i < count; i++) {
3204 if (ranges[i].mMaximum > range.mMaximum)
3205 range.mMaximum = ranges[i].mMaximum;
3206 if (ranges[i].mMinimum < range.mMinimum)
3207 range.mMinimum = ranges[i].mMinimum;
3208 }
3209 }
3210 *max = static_cast<uint32_t>(range.mMaximum);
3211 *min = static_cast<uint32_t>(range.mMinimum);
3212 } else {
3213 *min = *max = 0;
3214 }
3215
3216 }
3217
3218 static UInt32
audiounit_get_device_presentation_latency(AudioObjectID devid,AudioObjectPropertyScope scope)3219 audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope)
3220 {
3221 AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
3222 UInt32 size, dev, stream = 0;
3223 AudioStreamID sid[1];
3224
3225 adr.mSelector = kAudioDevicePropertyLatency;
3226 size = sizeof(UInt32);
3227 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) {
3228 dev = 0;
3229 }
3230
3231 adr.mSelector = kAudioDevicePropertyStreams;
3232 size = sizeof(sid);
3233 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) {
3234 adr.mSelector = kAudioStreamPropertyLatency;
3235 size = sizeof(UInt32);
3236 AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream);
3237 }
3238
3239 return dev + stream;
3240 }
3241
3242 static int
audiounit_create_device_from_hwdev(cubeb_device_info * dev_info,AudioObjectID devid,cubeb_device_type type)3243 audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID devid, cubeb_device_type type)
3244 {
3245 AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster };
3246 UInt32 size;
3247
3248 if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
3249 adr.mScope = kAudioDevicePropertyScopeOutput;
3250 } else if (type == CUBEB_DEVICE_TYPE_INPUT) {
3251 adr.mScope = kAudioDevicePropertyScopeInput;
3252 } else {
3253 return CUBEB_ERROR;
3254 }
3255
3256 UInt32 ch = audiounit_get_channel_count(devid, adr.mScope);
3257 if (ch == 0) {
3258 return CUBEB_ERROR;
3259 }
3260
3261 PodZero(dev_info, 1);
3262
3263 CFStringRef device_id_str = nullptr;
3264 size = sizeof(CFStringRef);
3265 adr.mSelector = kAudioDevicePropertyDeviceUID;
3266 OSStatus ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &device_id_str);
3267 if ( ret == noErr && device_id_str != NULL) {
3268 dev_info->device_id = audiounit_strref_to_cstr_utf8(device_id_str);
3269 static_assert(sizeof(cubeb_devid) >= sizeof(decltype(devid)), "cubeb_devid can't represent devid");
3270 dev_info->devid = reinterpret_cast<cubeb_devid>(devid);
3271 dev_info->group_id = dev_info->device_id;
3272 CFRelease(device_id_str);
3273 }
3274
3275 CFStringRef friendly_name_str = nullptr;
3276 UInt32 ds;
3277 size = sizeof(UInt32);
3278 adr.mSelector = kAudioDevicePropertyDataSource;
3279 ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds);
3280 if (ret == noErr) {
3281 AudioValueTranslation trl = { &ds, sizeof(ds), &friendly_name_str, sizeof(CFStringRef) };
3282 adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
3283 size = sizeof(AudioValueTranslation);
3284 AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl);
3285 }
3286
3287 // If there is no datasource for this device, fall back to the
3288 // device name.
3289 if (!friendly_name_str) {
3290 size = sizeof(CFStringRef);
3291 adr.mSelector = kAudioObjectPropertyName;
3292 AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &friendly_name_str);
3293 }
3294
3295 if (friendly_name_str) {
3296 dev_info->friendly_name = audiounit_strref_to_cstr_utf8(friendly_name_str);
3297 CFRelease(friendly_name_str);
3298 } else {
3299 // Couldn't get a datasource name nor a device name, return a
3300 // valid string of length 0.
3301 char * fallback_name = new char[1];
3302 fallback_name[0] = '\0';
3303 dev_info->friendly_name = fallback_name;
3304 }
3305
3306 CFStringRef vendor_name_str = nullptr;
3307 size = sizeof(CFStringRef);
3308 adr.mSelector = kAudioObjectPropertyManufacturer;
3309 ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &vendor_name_str);
3310 if (ret == noErr && vendor_name_str != NULL) {
3311 dev_info->vendor_name = audiounit_strref_to_cstr_utf8(vendor_name_str);
3312 CFRelease(vendor_name_str);
3313 }
3314
3315 dev_info->type = type;
3316 dev_info->state = CUBEB_DEVICE_STATE_ENABLED;
3317 dev_info->preferred = (devid == audiounit_get_default_device_id(type)) ?
3318 CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
3319
3320 dev_info->max_channels = ch;
3321 dev_info->format = (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
3322 /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */
3323 dev_info->default_format = CUBEB_DEVICE_FMT_F32NE;
3324 audiounit_get_available_samplerate(devid, adr.mScope,
3325 &dev_info->min_rate, &dev_info->max_rate, &dev_info->default_rate);
3326
3327 UInt32 latency = audiounit_get_device_presentation_latency(devid, adr.mScope);
3328
3329 AudioValueRange range;
3330 adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
3331 size = sizeof(AudioValueRange);
3332 ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range);
3333 if (ret == noErr) {
3334 dev_info->latency_lo = latency + range.mMinimum;
3335 dev_info->latency_hi = latency + range.mMaximum;
3336 } else {
3337 dev_info->latency_lo = 10 * dev_info->default_rate / 1000; /* Default to 10ms */
3338 dev_info->latency_hi = 100 * dev_info->default_rate / 1000; /* Default to 100ms */
3339 }
3340
3341 return CUBEB_OK;
3342 }
3343
3344 bool
is_aggregate_device(cubeb_device_info * device_info)3345 is_aggregate_device(cubeb_device_info * device_info)
3346 {
3347 assert(device_info->friendly_name);
3348 return !strncmp(device_info->friendly_name, PRIVATE_AGGREGATE_DEVICE_NAME,
3349 strlen(PRIVATE_AGGREGATE_DEVICE_NAME));
3350 }
3351
3352 static int
audiounit_enumerate_devices(cubeb *,cubeb_device_type type,cubeb_device_collection * collection)3353 audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
3354 cubeb_device_collection * collection)
3355 {
3356 vector<AudioObjectID> input_devs;
3357 vector<AudioObjectID> output_devs;
3358
3359 // Count number of input and output devices. This is not
3360 // necessarily the same as the count of raw devices supported by the
3361 // system since, for example, with Soundflower installed, some
3362 // devices may report as being both input *and* output and cubeb
3363 // separates those into two different devices.
3364
3365 if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
3366 output_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
3367 }
3368
3369 if (type & CUBEB_DEVICE_TYPE_INPUT) {
3370 input_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
3371 }
3372
3373 auto devices = new cubeb_device_info[output_devs.size() + input_devs.size()];
3374 collection->count = 0;
3375
3376 if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
3377 for (auto dev: output_devs) {
3378 auto device = &devices[collection->count];
3379 auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_OUTPUT);
3380 if (err != CUBEB_OK || is_aggregate_device(device)) {
3381 continue;
3382 }
3383 collection->count += 1;
3384 }
3385 }
3386
3387 if (type & CUBEB_DEVICE_TYPE_INPUT) {
3388 for (auto dev: input_devs) {
3389 auto device = &devices[collection->count];
3390 auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_INPUT);
3391 if (err != CUBEB_OK || is_aggregate_device(device)) {
3392 continue;
3393 }
3394 collection->count += 1;
3395 }
3396 }
3397
3398 if (collection->count > 0) {
3399 collection->device = devices;
3400 } else {
3401 delete [] devices;
3402 collection->device = NULL;
3403 }
3404
3405 return CUBEB_OK;
3406 }
3407
3408 static void
audiounit_device_destroy(cubeb_device_info * device)3409 audiounit_device_destroy(cubeb_device_info * device)
3410 {
3411 delete [] device->device_id;
3412 delete [] device->friendly_name;
3413 delete [] device->vendor_name;
3414 }
3415
3416 static int
audiounit_device_collection_destroy(cubeb *,cubeb_device_collection * collection)3417 audiounit_device_collection_destroy(cubeb * /* context */,
3418 cubeb_device_collection * collection)
3419 {
3420 for (size_t i = 0; i < collection->count; i++) {
3421 audiounit_device_destroy(&collection->device[i]);
3422 }
3423 delete [] collection->device;
3424
3425 return CUBEB_OK;
3426 }
3427
3428 static vector<AudioObjectID>
audiounit_get_devices_of_type(cubeb_device_type devtype)3429 audiounit_get_devices_of_type(cubeb_device_type devtype)
3430 {
3431 UInt32 size = 0;
3432 OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
3433 &DEVICES_PROPERTY_ADDRESS, 0,
3434 NULL, &size);
3435 if (ret != noErr) {
3436 return vector<AudioObjectID>();
3437 }
3438 vector<AudioObjectID> devices(size / sizeof(AudioObjectID));
3439 ret = AudioObjectGetPropertyData(kAudioObjectSystemObject,
3440 &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size,
3441 devices.data());
3442 if (ret != noErr) {
3443 return vector<AudioObjectID>();
3444 }
3445
3446 // Remove the aggregate device from the list of devices (if any).
3447 for (auto it = devices.begin(); it != devices.end();) {
3448 CFStringRef name = get_device_name(*it);
3449 if (name && CFStringFind(name, CFSTR("CubebAggregateDevice"), 0).location !=
3450 kCFNotFound) {
3451 it = devices.erase(it);
3452 } else {
3453 it++;
3454 }
3455 if (name) {
3456 CFRelease(name);
3457 }
3458 }
3459
3460 /* Expected sorted but did not find anything in the docs. */
3461 sort(devices.begin(), devices.end(), [](AudioObjectID a, AudioObjectID b) {
3462 return a < b;
3463 });
3464
3465 if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) {
3466 return devices;
3467 }
3468
3469 AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT) ?
3470 kAudioDevicePropertyScopeInput :
3471 kAudioDevicePropertyScopeOutput;
3472
3473 vector<AudioObjectID> devices_in_scope;
3474 for (uint32_t i = 0; i < devices.size(); ++i) {
3475 /* For device in the given scope channel must be > 0. */
3476 if (audiounit_get_channel_count(devices[i], scope) > 0) {
3477 devices_in_scope.push_back(devices[i]);
3478 }
3479 }
3480
3481 return devices_in_scope;
3482 }
3483
3484 static OSStatus
audiounit_collection_changed_callback(AudioObjectID,UInt32,const AudioObjectPropertyAddress *,void * inClientData)3485 audiounit_collection_changed_callback(AudioObjectID /* inObjectID */,
3486 UInt32 /* inNumberAddresses */,
3487 const AudioObjectPropertyAddress * /* inAddresses */,
3488 void * inClientData)
3489 {
3490 cubeb * context = static_cast<cubeb *>(inClientData);
3491
3492 // This can be called from inside an AudioUnit function, dispatch to another queue.
3493 dispatch_async(context->serial_queue, ^() {
3494 auto_lock lock(context->mutex);
3495 if (!context->input_collection_changed_callback &&
3496 !context->output_collection_changed_callback) {
3497 /* Listener removed while waiting in mutex, abort. */
3498 return;
3499 }
3500 if (context->input_collection_changed_callback) {
3501 vector<AudioObjectID> devices = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
3502 /* Elements in the vector expected sorted. */
3503 if (context->input_device_array != devices) {
3504 context->input_device_array = devices;
3505 context->input_collection_changed_callback(context, context->input_collection_changed_user_ptr);
3506 }
3507 }
3508 if (context->output_collection_changed_callback) {
3509 vector<AudioObjectID> devices = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
3510 /* Elements in the vector expected sorted. */
3511 if (context->output_device_array != devices) {
3512 context->output_device_array = devices;
3513 context->output_collection_changed_callback(context, context->output_collection_changed_user_ptr);
3514 }
3515 }
3516 });
3517 return noErr;
3518 }
3519
3520 static OSStatus
audiounit_add_device_listener(cubeb * context,cubeb_device_type devtype,cubeb_device_collection_changed_callback collection_changed_callback,void * user_ptr)3521 audiounit_add_device_listener(cubeb * context,
3522 cubeb_device_type devtype,
3523 cubeb_device_collection_changed_callback collection_changed_callback,
3524 void * user_ptr)
3525 {
3526 context->mutex.assert_current_thread_owns();
3527 assert(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT));
3528 /* Note: second register without unregister first causes 'nope' error.
3529 * Current implementation requires unregister before register a new cb. */
3530 assert((devtype & CUBEB_DEVICE_TYPE_INPUT) && !context->input_collection_changed_callback ||
3531 (devtype & CUBEB_DEVICE_TYPE_OUTPUT) && !context->output_collection_changed_callback);
3532
3533 if (!context->input_collection_changed_callback &&
3534 !context->output_collection_changed_callback) {
3535 OSStatus ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
3536 &DEVICES_PROPERTY_ADDRESS,
3537 audiounit_collection_changed_callback,
3538 context);
3539 if (ret != noErr) {
3540 return ret;
3541 }
3542 }
3543 if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
3544 /* Expected empty after unregister. */
3545 assert(context->input_device_array.empty());
3546 context->input_device_array = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
3547 context->input_collection_changed_callback = collection_changed_callback;
3548 context->input_collection_changed_user_ptr = user_ptr;
3549 }
3550 if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
3551 /* Expected empty after unregister. */
3552 assert(context->output_device_array.empty());
3553 context->output_device_array = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
3554 context->output_collection_changed_callback = collection_changed_callback;
3555 context->output_collection_changed_user_ptr = user_ptr;
3556 }
3557 return noErr;
3558 }
3559
3560 static OSStatus
audiounit_remove_device_listener(cubeb * context,cubeb_device_type devtype)3561 audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype)
3562 {
3563 context->mutex.assert_current_thread_owns();
3564
3565 if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
3566 context->input_collection_changed_callback = nullptr;
3567 context->input_collection_changed_user_ptr = nullptr;
3568 context->input_device_array.clear();
3569 }
3570 if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
3571 context->output_collection_changed_callback = nullptr;
3572 context->output_collection_changed_user_ptr = nullptr;
3573 context->output_device_array.clear();
3574 }
3575
3576 if (context->input_collection_changed_callback ||
3577 context->output_collection_changed_callback) {
3578 return noErr;
3579 }
3580 /* Note: unregister a non registered cb is not a problem, not checking. */
3581 return AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
3582 &DEVICES_PROPERTY_ADDRESS,
3583 audiounit_collection_changed_callback,
3584 context);
3585 }
3586
audiounit_register_device_collection_changed(cubeb * context,cubeb_device_type devtype,cubeb_device_collection_changed_callback collection_changed_callback,void * user_ptr)3587 int audiounit_register_device_collection_changed(cubeb * context,
3588 cubeb_device_type devtype,
3589 cubeb_device_collection_changed_callback collection_changed_callback,
3590 void * user_ptr)
3591 {
3592 if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) {
3593 return CUBEB_ERROR_INVALID_PARAMETER;
3594 }
3595 OSStatus ret;
3596 auto_lock lock(context->mutex);
3597 if (collection_changed_callback) {
3598 ret = audiounit_add_device_listener(context,
3599 devtype,
3600 collection_changed_callback,
3601 user_ptr);
3602 } else {
3603 ret = audiounit_remove_device_listener(context, devtype);
3604 }
3605 return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
3606 }
3607
3608 cubeb_ops const audiounit_ops = {
3609 /*.init =*/ audiounit_init,
3610 /*.get_backend_id =*/ audiounit_get_backend_id,
3611 /*.get_max_channel_count =*/ audiounit_get_max_channel_count,
3612 /*.get_min_latency =*/ audiounit_get_min_latency,
3613 /*.get_preferred_sample_rate =*/ audiounit_get_preferred_sample_rate,
3614 /*.enumerate_devices =*/ audiounit_enumerate_devices,
3615 /*.device_collection_destroy =*/ audiounit_device_collection_destroy,
3616 /*.destroy =*/ audiounit_destroy,
3617 /*.stream_init =*/ audiounit_stream_init,
3618 /*.stream_destroy =*/ audiounit_stream_destroy,
3619 /*.stream_start =*/ audiounit_stream_start,
3620 /*.stream_stop =*/ audiounit_stream_stop,
3621 /*.stream_get_position =*/ audiounit_stream_get_position,
3622 /*.stream_get_latency =*/ audiounit_stream_get_latency,
3623 /*.stream_get_input_latency =*/ NULL,
3624 /*.stream_set_volume =*/ audiounit_stream_set_volume,
3625 /*.stream_set_name =*/ NULL,
3626 /*.stream_get_current_device =*/ audiounit_stream_get_current_device,
3627 /*.stream_device_destroy =*/ audiounit_stream_device_destroy,
3628 /*.stream_register_device_changed_callback =*/ audiounit_stream_register_device_changed_callback,
3629 /*.register_device_collection_changed =*/ audiounit_register_device_collection_changed
3630 };
3631