osx-linearize-operations.patch 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. From: Paul Adenot <paul@paul.cx>
  2. Subject: Linearize operations on AudioUnits to sidestep a deadlock.
  3. ---
  4. diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp
  5. --- a/src/cubeb_audiounit.cpp
  6. +++ b/src/cubeb_audiounit.cpp
  7. @@ -53,40 +53,45 @@ typedef UInt32 AudioFormatFlags;
  8. #define AU_OUT_BUS 0
  9. #define AU_IN_BUS 1
  10. #define PRINT_ERROR_CODE(str, r) do { \
  11. LOG("System call failed: %s (rv: %d)", str, r); \
  12. } while(0)
  13. +const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
  14. +
  15. /* Testing empirically, some headsets report a minimal latency that is very
  16. * low, but this does not work in practice. Lie and say the minimum is 256
  17. * frames. */
  18. const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
  19. const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
  20. void audiounit_stream_stop_internal(cubeb_stream * stm);
  21. void audiounit_stream_start_internal(cubeb_stream * stm);
  22. -static void close_audiounit_stream(cubeb_stream * stm);
  23. -static int setup_audiounit_stream(cubeb_stream * stm);
  24. +static void audiounit_close_stream(cubeb_stream *stm);
  25. +static int audiounit_setup_stream(cubeb_stream *stm);
  26. extern cubeb_ops const audiounit_ops;
  27. struct cubeb {
  28. cubeb_ops const * ops;
  29. owned_critical_section mutex;
  30. std::atomic<int> active_streams;
  31. + uint32_t global_latency_frames = 0;
  32. int limit_streams;
  33. cubeb_device_collection_changed_callback collection_changed_callback;
  34. void * collection_changed_user_ptr;
  35. /* Differentiate input from output devices. */
  36. cubeb_device_type collection_changed_devtype;
  37. uint32_t devtype_device_count;
  38. AudioObjectID * devtype_device_array;
  39. + // The queue is asynchronously deallocated once all references to it are released
  40. + dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL);
  41. };
  42. class auto_array_wrapper
  43. {
  44. public:
  45. explicit auto_array_wrapper(auto_array<float> * ar)
  46. : float_ar(ar)
  47. , short_ar(nullptr)
  48. @@ -205,16 +210,17 @@ struct cubeb_stream {
  49. cubeb_resampler * resampler;
  50. /* This is the number of output callback we got in a row. This is usually one,
  51. * but can be two when the input and output rate are different, and more when
  52. * a device has been plugged or unplugged, as there can be some time before
  53. * the device is ready. */
  54. std::atomic<int> output_callback_in_a_row;
  55. /* This is true if a device change callback is currently running. */
  56. std::atomic<bool> switching_device;
  57. + std::atomic<bool> buffer_size_change_state{ false };
  58. };
  59. bool has_input(cubeb_stream * stm)
  60. {
  61. return stm->input_stream_params.rate != 0;
  62. }
  63. bool has_output(cubeb_stream * stm)
  64. @@ -256,16 +262,24 @@ audiotimestamp_to_latency(AudioTimeStamp
  65. uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime);
  66. uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
  67. return ((pres - now) * stream->output_desc.mSampleRate) / 1000000000LL;
  68. }
  69. static void
  70. +audiounit_set_global_latency(cubeb_stream * stm, uint32_t latency_frames)
  71. +{
  72. + stm->mutex.assert_current_thread_owns();
  73. + assert(stm->context->active_streams == 1);
  74. + stm->context->global_latency_frames = latency_frames;
  75. +}
  76. +
  77. +static void
  78. audiounit_make_silent(AudioBuffer * ioData)
  79. {
  80. assert(ioData);
  81. assert(ioData->mData);
  82. memset(ioData->mData, 0, ioData->mDataByteSize);
  83. }
  84. static OSStatus
  85. @@ -576,29 +590,54 @@ audiounit_get_input_device_id(AudioDevic
  86. device_id);
  87. if (r != noErr) {
  88. return CUBEB_ERROR;
  89. }
  90. return CUBEB_OK;
  91. }
  92. +static int
  93. +audiounit_reinit_stream(cubeb_stream * stm, bool is_started)
  94. +{
  95. + if (is_started) {
  96. + audiounit_stream_stop_internal(stm);
  97. + }
  98. +
  99. + {
  100. + auto_lock lock(stm->mutex);
  101. +
  102. + audiounit_close_stream(stm);
  103. +
  104. + if (audiounit_setup_stream(stm) != CUBEB_OK) {
  105. + LOG("(%p) Stream reinit failed.", stm);
  106. + return CUBEB_ERROR;
  107. + }
  108. +
  109. + // Reset input frames to force new stream pre-buffer
  110. + // silence if needed, check `is_extra_input_needed()`
  111. + stm->frames_read = 0;
  112. +
  113. + // If the stream was running, start it again.
  114. + if (is_started) {
  115. + audiounit_stream_start_internal(stm);
  116. + }
  117. + }
  118. + return CUBEB_OK;
  119. +}
  120. +
  121. static OSStatus
  122. audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_count,
  123. const AudioObjectPropertyAddress * addresses,
  124. void * user)
  125. {
  126. cubeb_stream * stm = (cubeb_stream*) user;
  127. - int rv;
  128. - bool was_running = false;
  129. -
  130. stm->switching_device = true;
  131. -
  132. // Note if the stream was running or not
  133. - was_running = !stm->shutdown;
  134. + bool was_running = !stm->shutdown;
  135. LOG("(%p) Audio device changed, %d events.", stm, address_count);
  136. for (UInt32 i = 0; i < address_count; i++) {
  137. switch(addresses[i].mSelector) {
  138. case kAudioHardwarePropertyDefaultOutputDevice: {
  139. LOG("Event[%d] - mSelector == kAudioHardwarePropertyDefaultOutputDevice", i);
  140. // Allow restart to choose the new default
  141. stm->output_device = nullptr;
  142. @@ -639,38 +678,25 @@ audiounit_property_listener_callback(Aud
  143. if (stm->device_changed_callback) {
  144. stm->device_changed_callback(stm->user_ptr);
  145. }
  146. break;
  147. }
  148. }
  149. }
  150. - // This means the callback won't be called again.
  151. - audiounit_stream_stop_internal(stm);
  152. -
  153. - {
  154. - auto_lock lock(stm->mutex);
  155. - close_audiounit_stream(stm);
  156. - rv = setup_audiounit_stream(stm);
  157. - if (rv != CUBEB_OK) {
  158. - LOG("(%p) Could not reopen a stream after switching.", stm);
  159. + // Use a new thread, through the queue, to avoid deadlock when calling
  160. + // Get/SetProperties method from inside notify callback
  161. + dispatch_async(stm->context->serial_queue, ^() {
  162. + if (audiounit_reinit_stream(stm, was_running) != CUBEB_OK) {
  163. stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
  164. - return noErr;
  165. + LOG("(%p) Could not reopen the stream after switching.", stm);
  166. }
  167. -
  168. - stm->frames_read = 0;
  169. -
  170. - // If the stream was running, start it again.
  171. - if (was_running) {
  172. - audiounit_stream_start_internal(stm);
  173. - }
  174. - }
  175. -
  176. - stm->switching_device = false;
  177. + stm->switching_device = false;
  178. + });
  179. return noErr;
  180. }
  181. OSStatus
  182. audiounit_add_listener(cubeb_stream * stm, AudioDeviceID id, AudioObjectPropertySelector selector,
  183. AudioObjectPropertyScope scope, AudioObjectPropertyListenerProc listener)
  184. {
  185. @@ -1155,18 +1181,17 @@ audiounit_init_input_linear_buffer(cubeb
  186. static void
  187. audiounit_destroy_input_linear_buffer(cubeb_stream * stream)
  188. {
  189. delete stream->input_linear_buffer;
  190. }
  191. static uint32_t
  192. -audiounit_clamp_latency(cubeb_stream * stm,
  193. - uint32_t latency_frames)
  194. +audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
  195. {
  196. // For the 1st stream set anything within safe min-max
  197. assert(stm->context->active_streams > 0);
  198. if (stm->context->active_streams == 1) {
  199. return std::max(std::min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES),
  200. SAFE_MIN_LATENCY_FRAMES);
  201. }
  202. @@ -1219,26 +1244,374 @@ audiounit_clamp_latency(cubeb_stream * s
  203. } else {
  204. upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
  205. }
  206. return std::max(std::min<uint32_t>(latency_frames, upper_latency_limit),
  207. SAFE_MIN_LATENCY_FRAMES);
  208. }
  209. +/*
  210. + * Change buffer size is prone to deadlock thus we change it
  211. + * following the steps:
  212. + * - register a listener for the buffer size property
  213. + * - change the property
  214. + * - wait until the listener is executed
  215. + * - property has changed, remove the listener
  216. + * */
  217. +static void
  218. +buffer_size_changed_callback(void * inClientData,
  219. + AudioUnit inUnit,
  220. + AudioUnitPropertyID inPropertyID,
  221. + AudioUnitScope inScope,
  222. + AudioUnitElement inElement)
  223. +{
  224. + cubeb_stream * stm = (cubeb_stream *)inClientData;
  225. +
  226. + AudioUnit au = inUnit;
  227. + AudioUnitScope au_scope = kAudioUnitScope_Input;
  228. + AudioUnitElement au_element = inElement;
  229. + const char * au_type = "output";
  230. +
  231. + if (au == stm->input_unit) {
  232. + au_scope = kAudioUnitScope_Output;
  233. + au_type = "input";
  234. + }
  235. +
  236. + switch (inPropertyID) {
  237. +
  238. + case kAudioDevicePropertyBufferFrameSize: {
  239. + if (inScope != au_scope) {
  240. + break;
  241. + }
  242. + UInt32 new_buffer_size;
  243. + UInt32 outSize = sizeof(UInt32);
  244. + OSStatus r = AudioUnitGetProperty(au,
  245. + kAudioDevicePropertyBufferFrameSize,
  246. + au_scope,
  247. + au_element,
  248. + &new_buffer_size,
  249. + &outSize);
  250. + if (r != noErr) {
  251. + LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current buffer size", stm);
  252. + } else {
  253. + LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: New %s buffer size = %d for scope %d", stm,
  254. + au_type, new_buffer_size, inScope);
  255. + }
  256. + stm->buffer_size_change_state = true;
  257. + break;
  258. + }
  259. + }
  260. +}
  261. +
  262. +enum set_buffer_size_side {
  263. + INPUT,
  264. + OUTPUT,
  265. +};
  266. +
  267. static int
  268. -setup_audiounit_stream(cubeb_stream * stm)
  269. +audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, set_buffer_size_side set_side)
  270. +{
  271. + AudioUnit au = stm->output_unit;
  272. + AudioUnitScope au_scope = kAudioUnitScope_Input;
  273. + AudioUnitElement au_element = AU_OUT_BUS;
  274. + const char * au_type = "output";
  275. +
  276. + if (set_side == INPUT) {
  277. + au = stm->input_unit;
  278. + au_scope = kAudioUnitScope_Output;
  279. + au_element = AU_IN_BUS;
  280. + au_type = "input";
  281. + }
  282. +
  283. + uint32_t buffer_frames = 0;
  284. + UInt32 size = sizeof(buffer_frames);
  285. + int r = AudioUnitGetProperty(au,
  286. + kAudioDevicePropertyBufferFrameSize,
  287. + au_scope,
  288. + au_element,
  289. + &buffer_frames,
  290. + &size);
  291. + if (r != noErr) {
  292. + if (set_side == INPUT) {
  293. + PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
  294. + } else {
  295. + PRINT_ERROR_CODE("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
  296. + }
  297. + return CUBEB_ERROR;
  298. + }
  299. +
  300. + if (new_size_frames == buffer_frames) {
  301. + LOG("(%p) No need to update %s buffer size already %u frames", stm, au_type, buffer_frames);
  302. + return CUBEB_OK;
  303. + }
  304. +
  305. + r = AudioUnitAddPropertyListener(au,
  306. + kAudioDevicePropertyBufferFrameSize,
  307. + buffer_size_changed_callback,
  308. + stm);
  309. + if (r != noErr) {
  310. + if (set_side == INPUT) {
  311. + PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
  312. + } else {
  313. + PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
  314. + }
  315. + return CUBEB_ERROR;
  316. + }
  317. +
  318. + stm->buffer_size_change_state = false;
  319. +
  320. + r = AudioUnitSetProperty(au,
  321. + kAudioDevicePropertyBufferFrameSize,
  322. + au_scope,
  323. + au_element,
  324. + &new_size_frames,
  325. + sizeof(new_size_frames));
  326. + if (r != noErr) {
  327. + if (set_side == INPUT) {
  328. + PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
  329. + } else {
  330. + PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
  331. + }
  332. +
  333. + r = AudioUnitRemovePropertyListenerWithUserData(au,
  334. + kAudioDevicePropertyBufferFrameSize,
  335. + buffer_size_changed_callback,
  336. + stm);
  337. + if (r != noErr) {
  338. + if (set_side == INPUT) {
  339. + PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
  340. + } else {
  341. + PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
  342. + }
  343. + }
  344. +
  345. + return CUBEB_ERROR;
  346. + }
  347. +
  348. + int count = 0;
  349. + while (!stm->buffer_size_change_state && count++ < 30) {
  350. + struct timespec req, rem;
  351. + req.tv_sec = 0;
  352. + req.tv_nsec = 100000000L; // 0.1 sec
  353. + if (nanosleep(&req , &rem) < 0 ) {
  354. + LOG("(%p) Warning: nanosleep call failed or interrupted. Remaining time %ld nano secs \n", stm, rem.tv_nsec);
  355. + }
  356. + LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count);
  357. + }
  358. +
  359. + r = AudioUnitRemovePropertyListenerWithUserData(au,
  360. + kAudioDevicePropertyBufferFrameSize,
  361. + buffer_size_changed_callback,
  362. + stm);
  363. + if (r != noErr) {
  364. + return CUBEB_ERROR;
  365. + if (set_side == INPUT) {
  366. + PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
  367. + } else {
  368. + PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
  369. + }
  370. + }
  371. +
  372. + if (!stm->buffer_size_change_state && count >= 30) {
  373. + LOG("(%p) Error, did not get buffer size change callback ...", stm);
  374. + return CUBEB_ERROR;
  375. + }
  376. +
  377. + LOG("(%p) %s buffer size changed to %u frames.", stm, au_type, new_size_frames);
  378. + return CUBEB_OK;
  379. +}
  380. +
  381. +static int
  382. +audiounit_configure_input(cubeb_stream * stm)
  383. +{
  384. + int r = 0;
  385. + UInt32 size;
  386. + AURenderCallbackStruct aurcbs_in;
  387. +
  388. + LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in frames %u.",
  389. + stm, stm->input_stream_params.rate, stm->input_stream_params.channels,
  390. + stm->input_stream_params.format, stm->latency_frames);
  391. +
  392. + /* Get input device sample rate. */
  393. + AudioStreamBasicDescription input_hw_desc;
  394. + size = sizeof(AudioStreamBasicDescription);
  395. + r = AudioUnitGetProperty(stm->input_unit,
  396. + kAudioUnitProperty_StreamFormat,
  397. + kAudioUnitScope_Input,
  398. + AU_IN_BUS,
  399. + &input_hw_desc,
  400. + &size);
  401. + if (r != noErr) {
  402. + PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
  403. + return CUBEB_ERROR;
  404. + }
  405. + stm->input_hw_rate = input_hw_desc.mSampleRate;
  406. + LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate);
  407. +
  408. + /* Set format description according to the input params. */
  409. + r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
  410. + if (r != CUBEB_OK) {
  411. + LOG("(%p) Setting format description for input failed.", stm);
  412. + return r;
  413. + }
  414. +
  415. + // Use latency to set buffer size
  416. + stm->input_buffer_frames = stm->latency_frames;
  417. + r = audiounit_set_buffer_size(stm, stm->input_buffer_frames, INPUT);
  418. + if (r != CUBEB_OK) {
  419. + LOG("(%p) Error in change input buffer size.", stm);
  420. + return CUBEB_ERROR;
  421. + }
  422. +
  423. + AudioStreamBasicDescription src_desc = stm->input_desc;
  424. + /* Input AudioUnit must be configured with device's sample rate.
  425. + we will resample inside input callback. */
  426. + src_desc.mSampleRate = stm->input_hw_rate;
  427. +
  428. + r = AudioUnitSetProperty(stm->input_unit,
  429. + kAudioUnitProperty_StreamFormat,
  430. + kAudioUnitScope_Output,
  431. + AU_IN_BUS,
  432. + &src_desc,
  433. + sizeof(AudioStreamBasicDescription));
  434. + if (r != noErr) {
  435. + PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat", r);
  436. + return CUBEB_ERROR;
  437. + }
  438. +
  439. + /* Frames per buffer in the input callback. */
  440. + r = AudioUnitSetProperty(stm->input_unit,
  441. + kAudioUnitProperty_MaximumFramesPerSlice,
  442. + kAudioUnitScope_Global,
  443. + AU_IN_BUS,
  444. + &stm->input_buffer_frames,
  445. + sizeof(UInt32));
  446. + if (r != noErr) {
  447. + PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
  448. + return CUBEB_ERROR;
  449. + }
  450. +
  451. + // Input only capacity
  452. + unsigned int array_capacity = 1;
  453. + if (has_output(stm)) {
  454. + // Full-duplex increase capacity
  455. + array_capacity = 8;
  456. + }
  457. + if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
  458. + return CUBEB_ERROR;
  459. + }
  460. +
  461. + assert(stm->input_unit != NULL);
  462. + aurcbs_in.inputProc = audiounit_input_callback;
  463. + aurcbs_in.inputProcRefCon = stm;
  464. +
  465. + r = AudioUnitSetProperty(stm->input_unit,
  466. + kAudioOutputUnitProperty_SetInputCallback,
  467. + kAudioUnitScope_Global,
  468. + AU_OUT_BUS,
  469. + &aurcbs_in,
  470. + sizeof(aurcbs_in));
  471. + if (r != noErr) {
  472. + PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
  473. + return CUBEB_ERROR;
  474. + }
  475. + LOG("(%p) Input audiounit init successfully.", stm);
  476. +
  477. + return CUBEB_OK;
  478. +}
  479. +
  480. +static int
  481. +audiounit_configure_output(cubeb_stream * stm)
  482. +{
  483. + int r;
  484. + AURenderCallbackStruct aurcbs_out;
  485. + UInt32 size;
  486. +
  487. +
  488. + LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in frames %u.",
  489. + stm, stm->output_stream_params.rate, stm->output_stream_params.channels,
  490. + stm->output_stream_params.format, stm->latency_frames);
  491. +
  492. + r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
  493. + if (r != CUBEB_OK) {
  494. + LOG("(%p) Could not initialize the audio stream description.", stm);
  495. + return r;
  496. + }
  497. +
  498. + /* Get output device sample rate. */
  499. + AudioStreamBasicDescription output_hw_desc;
  500. + size = sizeof(AudioStreamBasicDescription);
  501. + memset(&output_hw_desc, 0, size);
  502. + r = AudioUnitGetProperty(stm->output_unit,
  503. + kAudioUnitProperty_StreamFormat,
  504. + kAudioUnitScope_Output,
  505. + AU_OUT_BUS,
  506. + &output_hw_desc,
  507. + &size);
  508. + if (r != noErr) {
  509. + PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
  510. + return CUBEB_ERROR;
  511. + }
  512. + stm->output_hw_rate = output_hw_desc.mSampleRate;
  513. + LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate);
  514. +
  515. + r = AudioUnitSetProperty(stm->output_unit,
  516. + kAudioUnitProperty_StreamFormat,
  517. + kAudioUnitScope_Input,
  518. + AU_OUT_BUS,
  519. + &stm->output_desc,
  520. + sizeof(AudioStreamBasicDescription));
  521. + if (r != noErr) {
  522. + PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
  523. + return CUBEB_ERROR;
  524. + }
  525. +
  526. + r = audiounit_set_buffer_size(stm, stm->latency_frames, OUTPUT);
  527. + if (r != CUBEB_OK) {
  528. + LOG("(%p) Error in change output buffer size.", stm);
  529. + return CUBEB_ERROR;
  530. + }
  531. +
  532. + /* Frames per buffer in the input callback. */
  533. + r = AudioUnitSetProperty(stm->output_unit,
  534. + kAudioUnitProperty_MaximumFramesPerSlice,
  535. + kAudioUnitScope_Global,
  536. + AU_OUT_BUS,
  537. + &stm->latency_frames,
  538. + sizeof(UInt32));
  539. + if (r != noErr) {
  540. + PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice", r);
  541. + return CUBEB_ERROR;
  542. + }
  543. +
  544. + assert(stm->output_unit != NULL);
  545. + aurcbs_out.inputProc = audiounit_output_callback;
  546. + aurcbs_out.inputProcRefCon = stm;
  547. + r = AudioUnitSetProperty(stm->output_unit,
  548. + kAudioUnitProperty_SetRenderCallback,
  549. + kAudioUnitScope_Global,
  550. + AU_OUT_BUS,
  551. + &aurcbs_out,
  552. + sizeof(aurcbs_out));
  553. + if (r != noErr) {
  554. + PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
  555. + return CUBEB_ERROR;
  556. + }
  557. +
  558. + LOG("(%p) Output audiounit init successfully.", stm);
  559. + return CUBEB_OK;
  560. +}
  561. +
  562. +static int
  563. +audiounit_setup_stream(cubeb_stream * stm)
  564. {
  565. stm->mutex.assert_current_thread_owns();
  566. - int r;
  567. - AURenderCallbackStruct aurcbs_in;
  568. - AURenderCallbackStruct aurcbs_out;
  569. - UInt32 size;
  570. -
  571. + int r = 0;
  572. if (has_input(stm)) {
  573. r = audiounit_create_unit(&stm->input_unit, true,
  574. &stm->input_stream_params,
  575. stm->input_device);
  576. if (r != CUBEB_OK) {
  577. LOG("(%p) AudioUnit creation for input failed.", stm);
  578. return r;
  579. }
  580. @@ -1249,180 +1622,46 @@ setup_audiounit_stream(cubeb_stream * st
  581. &stm->output_stream_params,
  582. stm->output_device);
  583. if (r != CUBEB_OK) {
  584. LOG("(%p) AudioUnit creation for output failed.", stm);
  585. return r;
  586. }
  587. }
  588. + /* Latency cannot change if another stream is operating in parallel. In this case
  589. + * latecy is set to the other stream value. */
  590. + if (stm->context->active_streams > 1) {
  591. + LOG("(%p) More than one active stream, use global latency.", stm);
  592. + stm->latency_frames = stm->context->global_latency_frames;
  593. + } else {
  594. + /* Silently clamp the latency down to the platform default, because we
  595. + * synthetize the clock from the callbacks, and we want the clock to update
  596. + * often. */
  597. + stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames);
  598. + assert(stm->latency_frames); // Ungly error check
  599. + audiounit_set_global_latency(stm, stm->latency_frames);
  600. + }
  601. +
  602. /* Setup Input Stream! */
  603. if (has_input(stm)) {
  604. - LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in frames %u.",
  605. - stm, stm->input_stream_params.rate, stm->input_stream_params.channels,
  606. - stm->input_stream_params.format, stm->latency_frames);
  607. - /* Get input device sample rate. */
  608. - AudioStreamBasicDescription input_hw_desc;
  609. - size = sizeof(AudioStreamBasicDescription);
  610. - r = AudioUnitGetProperty(stm->input_unit,
  611. - kAudioUnitProperty_StreamFormat,
  612. - kAudioUnitScope_Input,
  613. - AU_IN_BUS,
  614. - &input_hw_desc,
  615. - &size);
  616. - if (r != noErr) {
  617. - PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
  618. - return CUBEB_ERROR;
  619. - }
  620. - stm->input_hw_rate = input_hw_desc.mSampleRate;
  621. - LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate);
  622. -
  623. - /* Set format description according to the input params. */
  624. - r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
  625. + r = audiounit_configure_input(stm);
  626. if (r != CUBEB_OK) {
  627. - LOG("(%p) Setting format description for input failed.", stm);
  628. + LOG("(%p) Configure audiounit input failed.", stm);
  629. return r;
  630. }
  631. -
  632. - // Use latency to set buffer size
  633. - stm->input_buffer_frames = stm->latency_frames;
  634. - LOG("(%p) Input buffer frame count %u.", stm, unsigned(stm->input_buffer_frames));
  635. - r = AudioUnitSetProperty(stm->input_unit,
  636. - kAudioDevicePropertyBufferFrameSize,
  637. - kAudioUnitScope_Output,
  638. - AU_IN_BUS,
  639. - &stm->input_buffer_frames,
  640. - sizeof(UInt32));
  641. - if (r != noErr) {
  642. - PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
  643. - return CUBEB_ERROR;
  644. - }
  645. -
  646. - AudioStreamBasicDescription src_desc = stm->input_desc;
  647. - /* Input AudioUnit must be configured with device's sample rate.
  648. - we will resample inside input callback. */
  649. - src_desc.mSampleRate = stm->input_hw_rate;
  650. -
  651. - r = AudioUnitSetProperty(stm->input_unit,
  652. - kAudioUnitProperty_StreamFormat,
  653. - kAudioUnitScope_Output,
  654. - AU_IN_BUS,
  655. - &src_desc,
  656. - sizeof(AudioStreamBasicDescription));
  657. - if (r != noErr) {
  658. - PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat", r);
  659. - return CUBEB_ERROR;
  660. - }
  661. -
  662. - /* Frames per buffer in the input callback. */
  663. - r = AudioUnitSetProperty(stm->input_unit,
  664. - kAudioUnitProperty_MaximumFramesPerSlice,
  665. - kAudioUnitScope_Output,
  666. - AU_IN_BUS,
  667. - &stm->input_buffer_frames,
  668. - sizeof(UInt32));
  669. - if (r != noErr) {
  670. - PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
  671. - return CUBEB_ERROR;
  672. - }
  673. -
  674. - // Input only capacity
  675. - unsigned int array_capacity = 1;
  676. - if (has_output(stm)) {
  677. - // Full-duplex increase capacity
  678. - array_capacity = 8;
  679. - }
  680. - if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
  681. - return CUBEB_ERROR;
  682. - }
  683. -
  684. - assert(stm->input_unit != NULL);
  685. - aurcbs_in.inputProc = audiounit_input_callback;
  686. - aurcbs_in.inputProcRefCon = stm;
  687. -
  688. - r = AudioUnitSetProperty(stm->input_unit,
  689. - kAudioOutputUnitProperty_SetInputCallback,
  690. - kAudioUnitScope_Global,
  691. - AU_OUT_BUS,
  692. - &aurcbs_in,
  693. - sizeof(aurcbs_in));
  694. - if (r != noErr) {
  695. - PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
  696. - return CUBEB_ERROR;
  697. - }
  698. - LOG("(%p) Input audiounit init successfully.", stm);
  699. }
  700. /* Setup Output Stream! */
  701. if (has_output(stm)) {
  702. - LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in frames %u.",
  703. - stm, stm->output_stream_params.rate, stm->output_stream_params.channels,
  704. - stm->output_stream_params.format, stm->latency_frames);
  705. - r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
  706. + r = audiounit_configure_output(stm);
  707. if (r != CUBEB_OK) {
  708. - LOG("(%p) Could not initialize the audio stream description.", stm);
  709. + LOG("(%p) Configure audiounit output failed.", stm);
  710. return r;
  711. }
  712. -
  713. - /* Get output device sample rate. */
  714. - AudioStreamBasicDescription output_hw_desc;
  715. - size = sizeof(AudioStreamBasicDescription);
  716. - memset(&output_hw_desc, 0, size);
  717. - r = AudioUnitGetProperty(stm->output_unit,
  718. - kAudioUnitProperty_StreamFormat,
  719. - kAudioUnitScope_Output,
  720. - AU_OUT_BUS,
  721. - &output_hw_desc,
  722. - &size);
  723. - if (r != noErr) {
  724. - PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
  725. - return CUBEB_ERROR;
  726. - }
  727. - stm->output_hw_rate = output_hw_desc.mSampleRate;
  728. - LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate);
  729. -
  730. - r = AudioUnitSetProperty(stm->output_unit,
  731. - kAudioUnitProperty_StreamFormat,
  732. - kAudioUnitScope_Input,
  733. - AU_OUT_BUS,
  734. - &stm->output_desc,
  735. - sizeof(AudioStreamBasicDescription));
  736. - if (r != noErr) {
  737. - PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
  738. - return CUBEB_ERROR;
  739. - }
  740. -
  741. - // Use latency to calculate buffer size
  742. - uint32_t output_buffer_frames = stm->latency_frames;
  743. - LOG("(%p) Output buffer frame count %u.", stm, output_buffer_frames);
  744. - r = AudioUnitSetProperty(stm->output_unit,
  745. - kAudioDevicePropertyBufferFrameSize,
  746. - kAudioUnitScope_Input,
  747. - AU_OUT_BUS,
  748. - &output_buffer_frames,
  749. - sizeof(output_buffer_frames));
  750. - if (r != noErr) {
  751. - PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
  752. - return CUBEB_ERROR;
  753. - }
  754. -
  755. - assert(stm->output_unit != NULL);
  756. - aurcbs_out.inputProc = audiounit_output_callback;
  757. - aurcbs_out.inputProcRefCon = stm;
  758. - r = AudioUnitSetProperty(stm->output_unit,
  759. - kAudioUnitProperty_SetRenderCallback,
  760. - kAudioUnitScope_Global,
  761. - AU_OUT_BUS,
  762. - &aurcbs_out,
  763. - sizeof(aurcbs_out));
  764. - if (r != noErr) {
  765. - PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
  766. - return CUBEB_ERROR;
  767. - }
  768. - LOG("(%p) Output audiounit init successfully.", stm);
  769. }
  770. // Setting the latency doesn't work well for USB headsets (eg. plantronics).
  771. // Keep the default latency for now.
  772. #if 0
  773. buffer_size = latency;
  774. /* Get the range of latency this particular device can work with, and clamp
  775. @@ -1535,63 +1774,60 @@ audiounit_stream_init(cubeb * context,
  776. void * user_ptr)
  777. {
  778. cubeb_stream * stm;
  779. int r;
  780. assert(context);
  781. *stream = NULL;
  782. + assert(latency_frames > 0);
  783. if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
  784. LOG("Reached the stream limit of %d", CUBEB_STREAM_MAX);
  785. return CUBEB_ERROR;
  786. }
  787. - context->active_streams += 1;
  788. stm = (cubeb_stream *) calloc(1, sizeof(cubeb_stream));
  789. assert(stm);
  790. // Placement new to call the ctors of cubeb_stream members.
  791. new (stm) cubeb_stream();
  792. /* These could be different in the future if we have both
  793. * full-duplex stream and different devices for input vs output. */
  794. stm->context = context;
  795. stm->data_callback = data_callback;
  796. stm->state_callback = state_callback;
  797. stm->user_ptr = user_ptr;
  798. + stm->latency_frames = latency_frames;
  799. stm->device_changed_callback = NULL;
  800. if (input_stream_params) {
  801. stm->input_stream_params = *input_stream_params;
  802. stm->input_device = input_device;
  803. stm->is_default_input = input_device == nullptr ||
  804. (audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT) ==
  805. reinterpret_cast<intptr_t>(input_device));
  806. }
  807. if (output_stream_params) {
  808. stm->output_stream_params = *output_stream_params;
  809. stm->output_device = output_device;
  810. }
  811. /* Init data members where necessary */
  812. stm->hw_latency_frames = UINT64_MAX;
  813. - /* Silently clamp the latency down to the platform default, because we
  814. - * synthetize the clock from the callbacks, and we want the clock to update
  815. - * often. */
  816. - stm->latency_frames = audiounit_clamp_latency(stm, latency_frames);
  817. - assert(latency_frames > 0);
  818. -
  819. stm->switching_device = false;
  820. + auto_lock context_lock(context->mutex);
  821. {
  822. // It's not critical to lock here, because no other thread has been started
  823. // yet, but it allows to assert that the lock has been taken in
  824. - // `setup_audiounit_stream`.
  825. + // `audiounit_setup_stream`.
  826. + context->active_streams += 1;
  827. auto_lock lock(stm->mutex);
  828. - r = setup_audiounit_stream(stm);
  829. + r = audiounit_setup_stream(stm);
  830. }
  831. if (r != CUBEB_OK) {
  832. LOG("(%p) Could not setup the audiounit stream.", stm);
  833. audiounit_stream_destroy(stm);
  834. return r;
  835. }
  836. @@ -1602,17 +1838,17 @@ audiounit_stream_init(cubeb * context,
  837. }
  838. *stream = stm;
  839. LOG("Cubeb stream (%p) init successful.", stm);
  840. return CUBEB_OK;
  841. }
  842. static void
  843. -close_audiounit_stream(cubeb_stream * stm)
  844. +audiounit_close_stream(cubeb_stream *stm)
  845. {
  846. stm->mutex.assert_current_thread_owns();
  847. if (stm->input_unit) {
  848. AudioUnitUninitialize(stm->input_unit);
  849. AudioComponentInstanceDispose(stm->input_unit);
  850. }
  851. audiounit_destroy_input_linear_buffer(stm);
  852. @@ -1625,33 +1861,36 @@ close_audiounit_stream(cubeb_stream * st
  853. cubeb_resampler_destroy(stm->resampler);
  854. }
  855. static void
  856. audiounit_stream_destroy(cubeb_stream * stm)
  857. {
  858. stm->shutdown = true;
  859. + auto_lock context_locl(stm->context->mutex);
  860. audiounit_stream_stop_internal(stm);
  861. {
  862. auto_lock lock(stm->mutex);
  863. - close_audiounit_stream(stm);
  864. + audiounit_close_stream(stm);
  865. }
  866. #if !TARGET_OS_IPHONE
  867. int r = audiounit_uninstall_device_changed_callback(stm);
  868. if (r != CUBEB_OK) {
  869. LOG("(%p) Could not uninstall the device changed callback", stm);
  870. }
  871. #endif
  872. assert(stm->context->active_streams >= 1);
  873. stm->context->active_streams -= 1;
  874. + LOG("Cubeb stream (%p) destroyed successful.", stm);
  875. +
  876. stm->~cubeb_stream();
  877. free(stm);
  878. }
  879. void
  880. audiounit_stream_start_internal(cubeb_stream * stm)
  881. {
  882. OSStatus r;
  883. @@ -1666,16 +1905,17 @@ audiounit_stream_start_internal(cubeb_st
  884. }
  885. static int
  886. audiounit_stream_start(cubeb_stream * stm)
  887. {
  888. stm->shutdown = false;
  889. stm->draining = false;
  890. + auto_lock context_locl(stm->context->mutex);
  891. audiounit_stream_start_internal(stm);
  892. stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
  893. LOG("Cubeb stream (%p) started successfully.", stm);
  894. return CUBEB_OK;
  895. }
  896. @@ -1693,16 +1933,17 @@ audiounit_stream_stop_internal(cubeb_str
  897. }
  898. }
  899. static int
  900. audiounit_stream_stop(cubeb_stream * stm)
  901. {
  902. stm->shutdown = true;
  903. + auto_lock context_locl(stm->context->mutex);
  904. audiounit_stream_stop_internal(stm);
  905. stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
  906. LOG("Cubeb stream (%p) stopped successfully.", stm);
  907. return CUBEB_OK;
  908. }