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