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>
29 #import <CoreServices/CoreServices.h>
30 #import <vlc_dialog.h>
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
;
40 FramesToUs(struct aout_sys_common
*p_sys
, uint64_t i_nb_frames
)
42 return i_nb_frames
* CLOCK_FREQ
/ p_sys
->i_rate
;
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
;
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 */
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
;
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
);
91 /* Pull audio from buffer */
93 void *p_data
= TPCircularBufferTail(&p_sys
->circular_buffer
,
98 size_t i_tocopy
= __MIN(i_requested
, (size_t) i_available
);
102 memcpy(p_output
, p_data
, i_tocopy
);
103 TPCircularBufferConsume(&p_sys
->circular_buffer
, i_tocopy
);
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
;
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
;
129 ca_Flush(audio_output_t
*p_aout
, bool wait
)
131 struct aout_sys_common
*p_sys
= (struct aout_sys_common
*) p_aout
->sys
;
137 while (TPCircularBufferTail(&p_sys
->circular_buffer
, &i_bytes
) != NULL
)
139 if (atomic_load(&p_sys
->b_paused
))
141 TPCircularBufferClear(&p_sys
->circular_buffer
);
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);
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
);
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
);
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
;
182 atomic_store_explicit(&p_sys
->b_paused
, pause
, memory_order_relaxed
);
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
,
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 "
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
))
215 TPCircularBufferProduceBytes(&p_sys
->circular_buffer
,
216 p_block
->p_buffer
, i_avalaible_bytes
);
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",
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;
271 /* 2 seconds of buffering */
272 i_audiobuffer_size
= i_audiobuffer_size
* AOUT_MAX_ADVANCE_TIME
275 if (!TPCircularBufferInit(&p_sys
->circular_buffer
, i_audiobuffer_size
))
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
);
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
);
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
,
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");
327 OSStatus err
= AudioComponentInstanceNew(au_component
, &au
);
330 ca_LogErr("cannot open AudioComponent, PCM output failed");
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 *****************************************************************************/
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
);
358 static AudioChannelLayout
*
359 GetLayoutDescription(audio_output_t
*p_aout
,
360 const AudioChannelLayout
*outlayout
)
362 AudioFormatPropertyID id
;
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
;
375 id
= kAudioFormatProperty_ChannelLayoutForTag
;
376 size
= sizeof(AudioChannelLayoutTag
);
377 data
= &outlayout
->mChannelLayoutTag
;
381 OSStatus err
= AudioFormatGetPropertyInfo(id
, size
, data
, ¶m_size
);
385 AudioChannelLayout
*reslayout
= malloc(param_size
);
386 if (reslayout
== NULL
)
389 err
= AudioFormatGetProperty(id
, size
, data
, ¶m_size
, reslayout
);
390 if (err
!= noErr
|| reslayout
->mNumberChannelDescriptions
== 0)
392 msg_Err(p_aout
, "insufficient number of output channels");
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
;
416 if (outlayout
->mChannelLayoutTag
!=
417 kAudioChannelLayoutTag_UseChannelDescriptions
)
419 reslayout
= GetLayoutDescription(p_aout
, outlayout
);
420 if (reslayout
== NULL
)
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");
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
;
469 msg_Dbg(p_aout
, "this is channel: %d", (int) chan
);
471 if (chan
< i_auhal_size
&& i_auhal_channel_mapping
[chan
] > 0)
472 fmt
->i_physical_channels
|= i_auhal_channel_mapping
[chan
];
474 msg_Dbg(p_aout
, "found nonrecognized channel %d at index "
477 if (fmt
->i_physical_channels
== 0)
479 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
480 if (warn_configuration
)
481 *warn_configuration
= true;
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
));
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:
509 * Rs - right surround
510 * Cs - center surround
511 * Rls - rear left surround
512 * Rrs - rear right surround
515 * Lsd - left surround direct
516 * Rsd - right surround direct
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
))
529 *inlayout_tag
= kAudioChannelLayoutTag_Mono
;
532 *inlayout_tag
= kAudioChannelLayoutTag_Stereo
;
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
;
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
;
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
;
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
,
571 if (p_sys
->chans_to_reorder
)
572 msg_Dbg(p_aout
, "channel reordering needed for 5.1 output");
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
,
590 if (p_sys
->chans_to_reorder
)
591 msg_Dbg(p_aout
, "channel reordering needed for 6.0 output");
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
,
610 if (p_sys
->chans_to_reorder
)
611 msg_Dbg(p_aout
, "channel reordering needed for 6.1 output");
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
;
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
,
647 if (p_sys
->chans_to_reorder
)
648 msg_Dbg(p_aout
, "channel reordering needed for 7.1 / 8.0 output");
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
,
667 if (p_sys
->chans_to_reorder
)
668 msg_Dbg(p_aout
, "channel reordering needed for 8.1 output");
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
)
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)
691 fmt
->i_format
= VLC_CODEC_FL32
;
692 ret
= MapOutputLayout(p_aout
, fmt
, outlayout
, warn_configuration
);
693 if (ret
!= VLC_SUCCESS
)
696 ret
= SetupInputLayout(p_aout
, fmt
, &inlayout_tag
);
697 if (ret
!= VLC_SUCCESS
)
700 desc
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
;
701 desc
.mChannelsPerFrame
= aout_FormatNbChannels(fmt
);
702 desc
.mBitsPerChannel
= 32;
704 else if (AOUT_FMT_SPDIF(fmt
))
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;
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
,
732 ca_LogErr("failed to set stream format");
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
) });
743 ca_LogErr("failed to set stream format");
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
,
758 ca_LogErr("failed to setup render callback");
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
,
772 ca_LogErr("failed to setup input layout");
777 err
= AudioUnitInitialize(au
);
781 ca_LogErr("AudioUnitInitialize failed");
785 ret
= ca_Initialize(p_aout
, fmt
, i_dev_latency_us
);
786 if (ret
!= VLC_SUCCESS
)
788 AudioUnitUninitialize(au
);
796 au_Uninitialize(audio_output_t
*p_aout
, AudioUnit au
)
798 OSStatus err
= AudioUnitUninitialize(au
);
800 ca_LogWarn("AudioUnitUninitialize failed");
802 ca_Uninitialize(p_aout
);