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