input/clock: (docs) fix old function name
[vlc.git] / modules / audio_output / coreaudio_common.c
blob0413dea57033a6540ad11f88e25b14012de9175a
1 /*****************************************************************************
2 * coreaudio_common.c: Common AudioUnit code for iOS and macOS
3 *****************************************************************************
4 * Copyright (C) 2005 - 2017 VLC authors and VideoLAN
6 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
7 * Felix Paul Kühne <fkuehne at videolan dot org>
8 * David Fuhrmann <david dot fuhrmann at googlemail dot com>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 #import "coreaudio_common.h"
26 #import <CoreAudio/CoreAudioTypes.h>
28 #if !TARGET_OS_IPHONE
29 #import <CoreServices/CoreServices.h>
30 #import <vlc_dialog.h>
31 #endif
33 static inline uint64_t
34 BytesToFrames(struct aout_sys_common *p_sys, size_t i_bytes)
36 return i_bytes * p_sys->i_frame_length / p_sys->i_bytes_per_frame;
39 static inline mtime_t
40 FramesToUs(struct aout_sys_common *p_sys, uint64_t i_nb_frames)
42 return i_nb_frames * CLOCK_FREQ / p_sys->i_rate;
45 void
46 ca_Open(audio_output_t *p_aout)
48 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
50 atomic_init(&p_sys->i_underrun_size, 0);
51 atomic_init(&p_sys->b_paused, false);
52 atomic_init(&p_sys->b_do_flush, false);
53 vlc_sem_init(&p_sys->flush_sem, 0);
54 vlc_mutex_init(&p_sys->lock);
56 p_aout->play = ca_Play;
57 p_aout->pause = ca_Pause;
58 p_aout->flush = ca_Flush;
59 p_aout->time_get = ca_TimeGet;
62 void
63 ca_Close(audio_output_t *p_aout)
65 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
67 vlc_sem_destroy(&p_sys->flush_sem);
68 vlc_mutex_destroy(&p_sys->lock);
71 /* Called from render callbacks. No lock, wait, and IO here */
72 void
73 ca_Render(audio_output_t *p_aout, uint8_t *p_output, size_t i_requested)
75 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
77 bool expected = true;
78 if (atomic_compare_exchange_weak(&p_sys->b_do_flush, &expected, false))
80 TPCircularBufferClear(&p_sys->circular_buffer);
81 /* Signal that the renderer is flushed */
82 vlc_sem_post(&p_sys->flush_sem);
85 if (atomic_load_explicit(&p_sys->b_paused, memory_order_relaxed))
87 memset(p_output, 0, i_requested);
88 return;
91 /* Pull audio from buffer */
92 int32_t i_available;
93 void *p_data = TPCircularBufferTail(&p_sys->circular_buffer,
94 &i_available);
95 if (i_available < 0)
96 i_available = 0;
98 size_t i_tocopy = __MIN(i_requested, (size_t) i_available);
100 if (i_tocopy > 0)
102 memcpy(p_output, p_data, i_tocopy);
103 TPCircularBufferConsume(&p_sys->circular_buffer, i_tocopy);
106 /* Pad with 0 */
107 if (i_requested > i_tocopy)
109 atomic_fetch_add(&p_sys->i_underrun_size, i_requested - i_tocopy);
110 memset(&p_output[i_tocopy], 0, i_requested - i_tocopy);
115 ca_TimeGet(audio_output_t *p_aout, mtime_t *delay)
117 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
119 int32_t i_bytes;
120 TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes);
122 int64_t i_frames = BytesToFrames(p_sys, i_bytes);
123 *delay = FramesToUs(p_sys, i_frames) + p_sys->i_dev_latency_us;
125 return 0;
128 void
129 ca_Flush(audio_output_t *p_aout, bool wait)
131 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
133 if (wait)
135 int32_t i_bytes;
137 while (TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes) != NULL)
139 if (atomic_load(&p_sys->b_paused))
141 TPCircularBufferClear(&p_sys->circular_buffer);
142 return;
145 /* Calculate the duration of the circular buffer, in order to wait
146 * for the render thread to play it all */
147 const mtime_t i_frame_us =
148 FramesToUs(p_sys, BytesToFrames(p_sys, i_bytes)) + 10000;
150 msleep(i_frame_us / 2);
153 else
155 /* Request the renderer to flush, and wait for an ACK.
156 * b_do_flush and b_paused need to be locked together in order to not
157 * get stuck here when b_paused is being set after reading. This can
158 * happen when setAliveState() is called from any thread through an
159 * interrupt notification */
161 vlc_mutex_lock(&p_sys->lock);
162 assert(!atomic_load(&p_sys->b_do_flush));
163 if (atomic_load(&p_sys->b_paused))
165 vlc_mutex_unlock(&p_sys->lock);
166 TPCircularBufferClear(&p_sys->circular_buffer);
167 return;
170 atomic_store_explicit(&p_sys->b_do_flush, true, memory_order_release);
171 vlc_mutex_unlock(&p_sys->lock);
172 vlc_sem_wait(&p_sys->flush_sem);
176 void
177 ca_Pause(audio_output_t * p_aout, bool pause, mtime_t date)
179 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
180 VLC_UNUSED(date);
182 atomic_store_explicit(&p_sys->b_paused, pause, memory_order_relaxed);
185 void
186 ca_Play(audio_output_t * p_aout, block_t * p_block)
188 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
190 /* Do the channel reordering */
191 if (p_sys->chans_to_reorder)
192 aout_ChannelReorder(p_block->p_buffer, p_block->i_buffer,
193 p_sys->chans_to_reorder, p_sys->chan_table,
194 VLC_CODEC_FL32);
196 /* move data to buffer */
197 while (!TPCircularBufferProduceBytes(&p_sys->circular_buffer,
198 p_block->p_buffer, p_block->i_buffer))
200 if (atomic_load_explicit(&p_sys->b_paused, memory_order_relaxed))
202 msg_Warn(p_aout, "dropping block because the circular buffer is "
203 "full and paused");
204 break;
207 /* Try to play what we can */
208 int32_t i_avalaible_bytes;
209 TPCircularBufferHead(&p_sys->circular_buffer, &i_avalaible_bytes);
210 assert(i_avalaible_bytes >= 0);
211 if (unlikely((size_t) i_avalaible_bytes >= p_block->i_buffer))
212 continue;
214 bool ret =
215 TPCircularBufferProduceBytes(&p_sys->circular_buffer,
216 p_block->p_buffer, i_avalaible_bytes);
217 VLC_UNUSED(ret);
218 assert(ret == true);
219 p_block->p_buffer += i_avalaible_bytes;
220 p_block->i_buffer -= i_avalaible_bytes;
222 /* Wait for the render buffer to play the remaining data */
223 const mtime_t i_frame_us =
224 FramesToUs(p_sys, BytesToFrames(p_sys, p_block->i_buffer));
225 msleep(i_frame_us / 2);
228 unsigned i_underrun_size = atomic_exchange(&p_sys->i_underrun_size, 0);
229 if (i_underrun_size > 0)
230 msg_Warn(p_aout, "underrun of %u bytes", i_underrun_size);
232 block_Release(p_block);
236 ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
237 mtime_t i_dev_latency_us)
239 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
241 atomic_store(&p_sys->i_underrun_size, 0);
242 atomic_store(&p_sys->b_paused, false);
243 p_sys->i_rate = fmt->i_rate;
244 p_sys->i_bytes_per_frame = fmt->i_bytes_per_frame;
245 p_sys->i_frame_length = fmt->i_frame_length;
246 p_sys->chans_to_reorder = 0;
248 msg_Dbg(p_aout, "Current device has a latency of %lld us",
249 i_dev_latency_us);
251 /* TODO VLC can't handle latency higher than 1 seconds */
252 if (i_dev_latency_us > 1000000)
254 i_dev_latency_us = 1000000;
255 msg_Warn(p_aout, "VLC can't handle this device latency, lowering it to "
256 "%lld", i_dev_latency_us);
258 p_sys->i_dev_latency_us = i_dev_latency_us;
260 /* setup circular buffer */
261 size_t i_audiobuffer_size = fmt->i_rate * fmt->i_bytes_per_frame
262 / p_sys->i_frame_length;
263 if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
265 /* lower latency: 200 ms of buffering. XXX: Decrease when VLC's core
266 * can handle lower audio latency */
267 i_audiobuffer_size = i_audiobuffer_size / 5;
269 else
271 /* 2 seconds of buffering */
272 i_audiobuffer_size = i_audiobuffer_size * AOUT_MAX_ADVANCE_TIME
273 / CLOCK_FREQ;
275 if (!TPCircularBufferInit(&p_sys->circular_buffer, i_audiobuffer_size))
276 return VLC_EGENERIC;
278 return VLC_SUCCESS;
281 void
282 ca_Uninitialize(audio_output_t *p_aout)
284 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
285 /* clean-up circular buffer */
286 TPCircularBufferCleanup(&p_sys->circular_buffer);
289 void
290 ca_SetAliveState(audio_output_t *p_aout, bool alive)
292 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
294 vlc_mutex_lock(&p_sys->lock);
295 atomic_store(&p_sys->b_paused, !alive);
297 bool expected = true;
298 if (!alive && atomic_compare_exchange_strong(&p_sys->b_do_flush, &expected, false))
300 TPCircularBufferClear(&p_sys->circular_buffer);
301 /* Signal that the renderer is flushed */
302 vlc_sem_post(&p_sys->flush_sem);
304 vlc_mutex_unlock(&p_sys->lock);
307 AudioUnit
308 au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type)
310 AudioComponentDescription desc = {
311 .componentType = kAudioUnitType_Output,
312 .componentSubType = comp_sub_type,
313 .componentManufacturer = kAudioUnitManufacturer_Apple,
314 .componentFlags = 0,
315 .componentFlagsMask = 0,
318 AudioComponent au_component;
319 au_component = AudioComponentFindNext(NULL, &desc);
320 if (au_component == NULL)
322 msg_Err(p_aout, "cannot find any AudioComponent, PCM output failed");
323 return NULL;
326 AudioUnit au;
327 OSStatus err = AudioComponentInstanceNew(au_component, &au);
328 if (err != noErr)
330 ca_LogErr("cannot open AudioComponent, PCM output failed");
331 return NULL;
333 return au;
336 /*****************************************************************************
337 * RenderCallback: This function is called everytime the AudioUnit wants
338 * us to provide some more audio data.
339 * Don't print anything during normal playback, calling blocking function from
340 * this callback is not allowed.
341 *****************************************************************************/
342 static OSStatus
343 RenderCallback(void *p_data, AudioUnitRenderActionFlags *ioActionFlags,
344 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
345 UInt32 inNumberFrames, AudioBufferList *ioData)
347 VLC_UNUSED(ioActionFlags);
348 VLC_UNUSED(inTimeStamp);
349 VLC_UNUSED(inBusNumber);
350 VLC_UNUSED(inNumberFrames);
352 ca_Render(p_data, ioData->mBuffers[0].mData,
353 ioData->mBuffers[0].mDataByteSize);
355 return noErr;
358 static AudioChannelLayout *
359 GetLayoutDescription(audio_output_t *p_aout,
360 const AudioChannelLayout *outlayout)
362 AudioFormatPropertyID id;
363 UInt32 size;
364 const void *data;
365 /* We need to "fill out" the ChannelLayout, because there are multiple
366 * ways that it can be set */
367 if (outlayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
369 id = kAudioFormatProperty_ChannelLayoutForBitmap;
370 size = sizeof(UInt32);
371 data = &outlayout->mChannelBitmap;
373 else
375 id = kAudioFormatProperty_ChannelLayoutForTag;
376 size = sizeof(AudioChannelLayoutTag);
377 data = &outlayout->mChannelLayoutTag;
380 UInt32 param_size;
381 OSStatus err = AudioFormatGetPropertyInfo(id, size, data, &param_size);
382 if (err != noErr)
383 return NULL;
385 AudioChannelLayout *reslayout = malloc(param_size);
386 if (reslayout == NULL)
387 return NULL;
389 err = AudioFormatGetProperty(id, size, data, &param_size, reslayout);
390 if (err != noErr || reslayout->mNumberChannelDescriptions == 0)
392 msg_Err(p_aout, "insufficient number of output channels");
393 free(reslayout);
394 return NULL;
397 return reslayout;
400 static int
401 MapOutputLayout(audio_output_t *p_aout, audio_sample_format_t *fmt,
402 const AudioChannelLayout *outlayout, bool *warn_configuration)
404 /* Fill VLC physical_channels from output layout */
405 fmt->i_physical_channels = 0;
406 uint32_t i_original = fmt->i_physical_channels;
407 AudioChannelLayout *reslayout = NULL;
409 if (outlayout == NULL)
411 msg_Dbg(p_aout, "not output layout, default to Stereo");
412 fmt->i_physical_channels = AOUT_CHANS_STEREO;
413 goto end;
416 if (outlayout->mChannelLayoutTag !=
417 kAudioChannelLayoutTag_UseChannelDescriptions)
419 reslayout = GetLayoutDescription(p_aout, outlayout);
420 if (reslayout == NULL)
421 return VLC_EGENERIC;
422 outlayout = reslayout;
425 if (i_original == AOUT_CHAN_CENTER
426 || outlayout->mNumberChannelDescriptions < 2)
428 /* We only need Mono or cannot output more than 1 channel */
429 fmt->i_physical_channels = AOUT_CHAN_CENTER;
430 msg_Dbg(p_aout, "output layout of AUHAL has 1 channel");
432 else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
433 || outlayout->mNumberChannelDescriptions < 3)
435 /* We only need Stereo or cannot output more than 2 channels */
436 fmt->i_physical_channels = AOUT_CHANS_STEREO;
437 msg_Dbg(p_aout, "output layout of AUHAL is Stereo");
439 else
441 assert(outlayout->mNumberChannelDescriptions > 0);
443 msg_Dbg(p_aout, "output layout of AUHAL has %i channels",
444 outlayout->mNumberChannelDescriptions);
446 /* maps auhal channels to vlc ones */
447 static const unsigned i_auhal_channel_mapping[] = {
448 [kAudioChannelLabel_Left] = AOUT_CHAN_LEFT,
449 [kAudioChannelLabel_Right] = AOUT_CHAN_RIGHT,
450 [kAudioChannelLabel_Center] = AOUT_CHAN_CENTER,
451 [kAudioChannelLabel_LFEScreen] = AOUT_CHAN_LFE,
452 [kAudioChannelLabel_LeftSurround] = AOUT_CHAN_REARLEFT,
453 [kAudioChannelLabel_RightSurround] = AOUT_CHAN_REARRIGHT,
454 /* needs to be swapped with rear */
455 [kAudioChannelLabel_RearSurroundLeft] = AOUT_CHAN_MIDDLELEFT,
456 /* needs to be swapped with rear */
457 [kAudioChannelLabel_RearSurroundRight] = AOUT_CHAN_MIDDLERIGHT,
458 [kAudioChannelLabel_CenterSurround] = AOUT_CHAN_REARCENTER
460 static const size_t i_auhal_size = sizeof(i_auhal_channel_mapping)
461 / sizeof(i_auhal_channel_mapping[0]);
463 /* We want more than stereo and we can do that */
464 for (unsigned i = 0; i < outlayout->mNumberChannelDescriptions; i++)
466 AudioChannelLabel chan =
467 outlayout->mChannelDescriptions[i].mChannelLabel;
468 #ifndef NDEBUG
469 msg_Dbg(p_aout, "this is channel: %d", (int) chan);
470 #endif
471 if (chan < i_auhal_size && i_auhal_channel_mapping[chan] > 0)
472 fmt->i_physical_channels |= i_auhal_channel_mapping[chan];
473 else
474 msg_Dbg(p_aout, "found nonrecognized channel %d at index "
475 "%d", chan, i);
477 if (fmt->i_physical_channels == 0)
479 fmt->i_physical_channels = AOUT_CHANS_STEREO;
480 if (warn_configuration)
481 *warn_configuration = true;
486 end:
487 free(reslayout);
488 aout_FormatPrepare(fmt);
490 msg_Dbg(p_aout, "selected %d physical channels for device output",
491 aout_FormatNbChannels(fmt));
492 msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
494 return VLC_SUCCESS;
497 static int
498 SetupInputLayout(audio_output_t *p_aout, const audio_sample_format_t *fmt,
499 AudioChannelLayoutTag *inlayout_tag)
501 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
502 uint32_t chans_out[AOUT_CHAN_MAX];
504 /* Some channel abbreviations used below:
505 * L - left
506 * R - right
507 * C - center
508 * Ls - left surround
509 * Rs - right surround
510 * Cs - center surround
511 * Rls - rear left surround
512 * Rrs - rear right surround
513 * Lw - left wide
514 * Rw - right wide
515 * Lsd - left surround direct
516 * Rsd - right surround direct
517 * Lc - left center
518 * Rc - right center
519 * Ts - top surround
520 * Vhl - vertical height left
521 * Vhc - vertical height center
522 * Vhr - vertical height right
523 * Lt - left matrix total. for matrix encoded stereo.
524 * Rt - right matrix total. for matrix encoded stereo. */
526 switch (aout_FormatNbChannels(fmt))
528 case 1:
529 *inlayout_tag = kAudioChannelLayoutTag_Mono;
530 break;
531 case 2:
532 *inlayout_tag = kAudioChannelLayoutTag_Stereo;
533 break;
534 case 3:
535 if (fmt->i_physical_channels & AOUT_CHAN_CENTER) /* L R C */
536 *inlayout_tag = kAudioChannelLayoutTag_DVD_7;
537 else if (fmt->i_physical_channels & AOUT_CHAN_LFE) /* L R LFE */
538 *inlayout_tag = kAudioChannelLayoutTag_DVD_4;
539 break;
540 case 4:
541 if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE)) /* L R C LFE */
542 *inlayout_tag = kAudioChannelLayoutTag_DVD_10;
543 else if (fmt->i_physical_channels & AOUT_CHANS_REAR) /* L R Ls Rs */
544 *inlayout_tag = kAudioChannelLayoutTag_DVD_3;
545 else if (fmt->i_physical_channels & AOUT_CHANS_CENTER) /* L R C Cs */
546 *inlayout_tag = kAudioChannelLayoutTag_DVD_3;
547 break;
548 case 5:
549 if (fmt->i_physical_channels & (AOUT_CHAN_CENTER)) /* L R Ls Rs C */
550 *inlayout_tag = kAudioChannelLayoutTag_DVD_19;
551 else if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) /* L R Ls Rs LFE */
552 *inlayout_tag = kAudioChannelLayoutTag_DVD_18;
553 break;
554 case 6:
555 if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
557 /* L R Ls Rs C LFE */
558 *inlayout_tag = kAudioChannelLayoutTag_DVD_20;
560 chans_out[0] = AOUT_CHAN_LEFT;
561 chans_out[1] = AOUT_CHAN_RIGHT;
562 chans_out[2] = AOUT_CHAN_REARLEFT;
563 chans_out[3] = AOUT_CHAN_REARRIGHT;
564 chans_out[4] = AOUT_CHAN_CENTER;
565 chans_out[5] = AOUT_CHAN_LFE;
567 p_sys->chans_to_reorder =
568 aout_CheckChannelReorder(NULL, chans_out,
569 fmt->i_physical_channels,
570 p_sys->chan_table);
571 if (p_sys->chans_to_reorder)
572 msg_Dbg(p_aout, "channel reordering needed for 5.1 output");
574 else
576 /* L R Ls Rs C Cs */
577 *inlayout_tag = kAudioChannelLayoutTag_AudioUnit_6_0;
579 chans_out[0] = AOUT_CHAN_LEFT;
580 chans_out[1] = AOUT_CHAN_RIGHT;
581 chans_out[2] = AOUT_CHAN_REARLEFT;
582 chans_out[3] = AOUT_CHAN_REARRIGHT;
583 chans_out[4] = AOUT_CHAN_CENTER;
584 chans_out[5] = AOUT_CHAN_REARCENTER;
586 p_sys->chans_to_reorder =
587 aout_CheckChannelReorder(NULL, chans_out,
588 fmt->i_physical_channels,
589 p_sys->chan_table);
590 if (p_sys->chans_to_reorder)
591 msg_Dbg(p_aout, "channel reordering needed for 6.0 output");
593 break;
594 case 7:
595 /* L R C LFE Ls Rs Cs */
596 *inlayout_tag = kAudioChannelLayoutTag_MPEG_6_1_A;
598 chans_out[0] = AOUT_CHAN_LEFT;
599 chans_out[1] = AOUT_CHAN_RIGHT;
600 chans_out[2] = AOUT_CHAN_CENTER;
601 chans_out[3] = AOUT_CHAN_LFE;
602 chans_out[4] = AOUT_CHAN_REARLEFT;
603 chans_out[5] = AOUT_CHAN_REARRIGHT;
604 chans_out[6] = AOUT_CHAN_REARCENTER;
606 p_sys->chans_to_reorder =
607 aout_CheckChannelReorder(NULL, chans_out,
608 fmt->i_physical_channels,
609 p_sys->chan_table);
610 if (p_sys->chans_to_reorder)
611 msg_Dbg(p_aout, "channel reordering needed for 6.1 output");
613 break;
614 case 8:
615 if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
617 /* L R C LFE Ls Rs Rls Rrs */
618 *inlayout_tag = kAudioChannelLayoutTag_MPEG_7_1_C;
620 chans_out[0] = AOUT_CHAN_LEFT;
621 chans_out[1] = AOUT_CHAN_RIGHT;
622 chans_out[2] = AOUT_CHAN_CENTER;
623 chans_out[3] = AOUT_CHAN_LFE;
624 chans_out[4] = AOUT_CHAN_MIDDLELEFT;
625 chans_out[5] = AOUT_CHAN_MIDDLERIGHT;
626 chans_out[6] = AOUT_CHAN_REARLEFT;
627 chans_out[7] = AOUT_CHAN_REARRIGHT;
629 else
631 /* Lc C Rc L R Ls Cs Rs */
632 *inlayout_tag = kAudioChannelLayoutTag_DTS_8_0_B;
634 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
635 chans_out[1] = AOUT_CHAN_CENTER;
636 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
637 chans_out[3] = AOUT_CHAN_LEFT;
638 chans_out[4] = AOUT_CHAN_RIGHT;
639 chans_out[5] = AOUT_CHAN_REARLEFT;
640 chans_out[6] = AOUT_CHAN_REARCENTER;
641 chans_out[7] = AOUT_CHAN_REARRIGHT;
643 p_sys->chans_to_reorder =
644 aout_CheckChannelReorder(NULL, chans_out,
645 fmt->i_physical_channels,
646 p_sys->chan_table);
647 if (p_sys->chans_to_reorder)
648 msg_Dbg(p_aout, "channel reordering needed for 7.1 / 8.0 output");
649 break;
650 case 9:
651 /* Lc C Rc L R Ls Cs Rs LFE */
652 *inlayout_tag = kAudioChannelLayoutTag_DTS_8_1_B;
653 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
654 chans_out[1] = AOUT_CHAN_CENTER;
655 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
656 chans_out[3] = AOUT_CHAN_LEFT;
657 chans_out[4] = AOUT_CHAN_RIGHT;
658 chans_out[5] = AOUT_CHAN_REARLEFT;
659 chans_out[6] = AOUT_CHAN_REARCENTER;
660 chans_out[7] = AOUT_CHAN_REARRIGHT;
661 chans_out[8] = AOUT_CHAN_LFE;
663 p_sys->chans_to_reorder =
664 aout_CheckChannelReorder(NULL, chans_out,
665 fmt->i_physical_channels,
666 p_sys->chan_table);
667 if (p_sys->chans_to_reorder)
668 msg_Dbg(p_aout, "channel reordering needed for 8.1 output");
669 break;
672 return VLC_SUCCESS;
676 au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt,
677 const AudioChannelLayout *outlayout, mtime_t i_dev_latency_us,
678 bool *warn_configuration)
680 int ret;
681 AudioChannelLayoutTag inlayout_tag;
683 if (warn_configuration)
684 *warn_configuration = false;
686 /* Set the desired format */
687 AudioStreamBasicDescription desc;
688 if (aout_BitsPerSample(fmt->i_format) != 0)
690 /* PCM */
691 fmt->i_format = VLC_CODEC_FL32;
692 ret = MapOutputLayout(p_aout, fmt, outlayout, warn_configuration);
693 if (ret != VLC_SUCCESS)
694 return ret;
696 ret = SetupInputLayout(p_aout, fmt, &inlayout_tag);
697 if (ret != VLC_SUCCESS)
698 return ret;
700 desc.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
701 desc.mChannelsPerFrame = aout_FormatNbChannels(fmt);
702 desc.mBitsPerChannel = 32;
704 else if (AOUT_FMT_SPDIF(fmt))
706 /* Passthrough */
707 fmt->i_format = VLC_CODEC_SPDIFL;
708 fmt->i_bytes_per_frame = 4;
709 fmt->i_frame_length = 1;
711 inlayout_tag = kAudioChannelLayoutTag_Stereo;
713 desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
714 kLinearPCMFormatFlagIsPacked; /* S16LE */
715 desc.mChannelsPerFrame = 2;
716 desc.mBitsPerChannel = 16;
718 else
719 return VLC_EGENERIC;
721 desc.mSampleRate = fmt->i_rate;
722 desc.mFormatID = kAudioFormatLinearPCM;
723 desc.mFramesPerPacket = 1;
724 desc.mBytesPerFrame = desc.mBitsPerChannel * desc.mChannelsPerFrame / 8;
725 desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket;
727 OSStatus err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat,
728 kAudioUnitScope_Input, 0, &desc,
729 sizeof(desc));
730 if (err != noErr)
732 ca_LogErr("failed to set stream format");
733 return VLC_EGENERIC;
735 msg_Dbg(p_aout, STREAM_FORMAT_MSG("Current AU format: " , desc));
737 /* Retrieve actual format */
738 err = AudioUnitGetProperty(au, kAudioUnitProperty_StreamFormat,
739 kAudioUnitScope_Input, 0, &desc,
740 &(UInt32) { sizeof(desc) });
741 if (err != noErr)
743 ca_LogErr("failed to set stream format");
744 return VLC_EGENERIC;
747 /* Set the IOproc callback */
748 const AURenderCallbackStruct callback = {
749 .inputProc = RenderCallback,
750 .inputProcRefCon = p_aout,
753 err = AudioUnitSetProperty(au, kAudioUnitProperty_SetRenderCallback,
754 kAudioUnitScope_Input, 0, &callback,
755 sizeof(callback));
756 if (err != noErr)
758 ca_LogErr("failed to setup render callback");
759 return VLC_EGENERIC;
762 /* Set the input_layout as the layout VLC will use to feed the AU unit.
763 * Yes, it must be the INPUT scope */
764 AudioChannelLayout inlayout = {
765 .mChannelLayoutTag = inlayout_tag,
767 err = AudioUnitSetProperty(au, kAudioUnitProperty_AudioChannelLayout,
768 kAudioUnitScope_Input, 0, &inlayout,
769 sizeof(inlayout));
770 if (err != noErr)
772 ca_LogErr("failed to setup input layout");
773 return VLC_EGENERIC;
776 /* AU init */
777 err = AudioUnitInitialize(au);
779 if (err != noErr)
781 ca_LogErr("AudioUnitInitialize failed");
782 return VLC_EGENERIC;
785 ret = ca_Initialize(p_aout, fmt, i_dev_latency_us);
786 if (ret != VLC_SUCCESS)
788 AudioUnitUninitialize(au);
789 return VLC_EGENERIC;
792 return VLC_SUCCESS;
795 void
796 au_Uninitialize(audio_output_t *p_aout, AudioUnit au)
798 OSStatus err = AudioUnitUninitialize(au);
799 if (err != noErr)
800 ca_LogWarn("AudioUnitUninitialize failed");
802 ca_Uninitialize(p_aout);