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