gl: rename opengl_shaders_api_t to opengl_vtable_t
[vlc.git] / modules / audio_output / coreaudio_common.c
blob3039d452cdabac2da8bb555e01c9a8d800712604
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 /* Called from render callbacks. No lock, wait, and IO here */
46 void
47 ca_Render(audio_output_t *p_aout, uint8_t *p_output, size_t i_requested)
49 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
51 if (atomic_load_explicit(&p_sys->b_paused, memory_order_relaxed))
53 memset(p_output, 0, i_requested);
54 return;
57 /* Pull audio from buffer */
58 int32_t i_available;
59 void *p_data = TPCircularBufferTail(&p_sys->circular_buffer,
60 &i_available);
61 if (i_available < 0)
62 i_available = 0;
64 size_t i_tocopy = __MIN(i_requested, (size_t) i_available);
66 if (i_tocopy > 0)
68 memcpy(p_output, p_data, i_tocopy);
69 TPCircularBufferConsume(&p_sys->circular_buffer, i_tocopy);
72 /* Pad with 0 */
73 if (i_requested > i_tocopy)
75 atomic_fetch_add(&p_sys->i_underrun_size, i_requested - i_tocopy);
76 memset(&p_output[i_tocopy], 0, i_requested - i_tocopy);
80 int
81 ca_TimeGet(audio_output_t *p_aout, mtime_t *delay)
83 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
85 int32_t i_bytes;
86 TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes);
88 int64_t i_frames = BytesToFrames(p_sys, i_bytes);
89 *delay = FramesToUs(p_sys, i_frames) + p_sys->i_dev_latency_us;
91 return 0;
94 void
95 ca_Flush(audio_output_t *p_aout, bool wait)
97 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
99 if (wait)
101 int32_t i_bytes;
103 while (TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes) != NULL)
105 /* Calculate the duration of the circular buffer, in order to wait
106 * for the render thread to play it all */
107 const mtime_t i_frame_us =
108 FramesToUs(p_sys, BytesToFrames(p_sys, i_bytes)) + 10000;
110 msleep(i_frame_us / 2);
113 else
115 /* flush circular buffer if data is left */
116 TPCircularBufferClear(&p_sys->circular_buffer);
120 void
121 ca_Pause(audio_output_t * p_aout, bool pause, mtime_t date)
123 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
124 VLC_UNUSED(date);
126 atomic_store_explicit(&p_sys->b_paused, pause, memory_order_relaxed);
129 void
130 ca_Play(audio_output_t * p_aout, block_t * p_block)
132 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
134 /* Do the channel reordering */
135 if (p_sys->chans_to_reorder)
136 aout_ChannelReorder(p_block->p_buffer, p_block->i_buffer,
137 p_sys->chans_to_reorder, p_sys->chan_table,
138 VLC_CODEC_FL32);
140 /* move data to buffer */
141 while (!TPCircularBufferProduceBytes(&p_sys->circular_buffer,
142 p_block->p_buffer, p_block->i_buffer))
144 if (atomic_load_explicit(&p_sys->b_paused, memory_order_relaxed))
146 msg_Warn(p_aout, "dropping block because the circular buffer is "
147 "full and paused");
148 break;
151 /* Try to play what we can */
152 int32_t i_avalaible_bytes;
153 TPCircularBufferHead(&p_sys->circular_buffer, &i_avalaible_bytes);
154 assert(i_avalaible_bytes >= 0);
155 if (unlikely((size_t) i_avalaible_bytes >= p_block->i_buffer))
156 continue;
158 bool ret =
159 TPCircularBufferProduceBytes(&p_sys->circular_buffer,
160 p_block->p_buffer, i_avalaible_bytes);
161 assert(ret == true);
162 p_block->p_buffer += i_avalaible_bytes;
163 p_block->i_buffer -= i_avalaible_bytes;
165 /* Wait for the render buffer to play the remaining data */
166 const mtime_t i_frame_us =
167 FramesToUs(p_sys, BytesToFrames(p_sys, p_block->i_buffer));
168 msleep(i_frame_us / 2);
171 unsigned i_underrun_size = atomic_exchange(&p_sys->i_underrun_size, 0);
172 if (i_underrun_size > 0)
173 msg_Warn(p_aout, "underrun of %u bytes", i_underrun_size);
175 block_Release(p_block);
179 ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
180 mtime_t i_dev_latency_us)
182 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
184 atomic_init(&p_sys->i_underrun_size, 0);
185 atomic_init(&p_sys->b_paused, false);
187 p_sys->i_rate = fmt->i_rate;
188 p_sys->i_bytes_per_frame = fmt->i_bytes_per_frame;
189 p_sys->i_frame_length = fmt->i_frame_length;
190 p_sys->chans_to_reorder = 0;
192 msg_Dbg(p_aout, "Current device has a latency of %lld us",
193 i_dev_latency_us);
195 /* TODO VLC can't handle latency higher than 1 seconds */
196 if (i_dev_latency_us > 1000000)
198 i_dev_latency_us = 1000000;
199 msg_Warn(p_aout, "VLC can't handle this device latency, lowering it to "
200 "%lld", i_dev_latency_us);
202 p_sys->i_dev_latency_us = i_dev_latency_us;
204 /* setup circular buffer */
205 size_t i_audiobuffer_size = fmt->i_rate * fmt->i_bytes_per_frame
206 / p_sys->i_frame_length;
207 if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
209 /* low latency: 40 ms of buffering */
210 i_audiobuffer_size = i_audiobuffer_size / 25;
212 else
214 /* 2 seconds of buffering */
215 i_audiobuffer_size = i_audiobuffer_size * AOUT_MAX_ADVANCE_TIME
216 / CLOCK_FREQ;
218 if (!TPCircularBufferInit(&p_sys->circular_buffer, i_audiobuffer_size))
219 return VLC_EGENERIC;
221 p_aout->play = ca_Play;
222 p_aout->pause = ca_Pause;
223 p_aout->flush = ca_Flush;
224 p_aout->time_get = ca_TimeGet;
225 return VLC_SUCCESS;
228 void
229 ca_Uninitialize(audio_output_t *p_aout)
231 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
233 /* clean-up circular buffer */
234 TPCircularBufferCleanup(&p_sys->circular_buffer);
237 AudioUnit
238 au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type)
240 AudioComponentDescription desc = {
241 .componentType = kAudioUnitType_Output,
242 .componentSubType = comp_sub_type,
243 .componentManufacturer = kAudioUnitManufacturer_Apple,
244 .componentFlags = 0,
245 .componentFlagsMask = 0,
248 AudioComponent au_component;
249 au_component = AudioComponentFindNext(NULL, &desc);
250 if (au_component == NULL)
252 msg_Err(p_aout, "cannot find any AudioComponent, PCM output failed");
253 return NULL;
256 AudioUnit au;
257 OSStatus err = AudioComponentInstanceNew(au_component, &au);
258 if (err != noErr)
260 ca_LogErr("cannot open AudioComponent, PCM output failed");
261 return NULL;
263 return au;
266 /*****************************************************************************
267 * RenderCallback: This function is called everytime the AudioUnit wants
268 * us to provide some more audio data.
269 * Don't print anything during normal playback, calling blocking function from
270 * this callback is not allowed.
271 *****************************************************************************/
272 static OSStatus
273 RenderCallback(void *p_data, AudioUnitRenderActionFlags *ioActionFlags,
274 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
275 UInt32 inNumberFrames, AudioBufferList *ioData)
277 VLC_UNUSED(ioActionFlags);
278 VLC_UNUSED(inTimeStamp);
279 VLC_UNUSED(inBusNumber);
280 VLC_UNUSED(inNumberFrames);
282 ca_Render(p_data, ioData->mBuffers[0].mData,
283 ioData->mBuffers[0].mDataByteSize);
285 return noErr;
288 static AudioChannelLayout *
289 GetLayoutDescription(audio_output_t *p_aout,
290 const AudioChannelLayout *outlayout)
292 AudioFormatPropertyID id;
293 UInt32 size;
294 const void *data;
295 /* We need to "fill out" the ChannelLayout, because there are multiple
296 * ways that it can be set */
297 if (outlayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
299 id = kAudioFormatProperty_ChannelLayoutForBitmap;
300 size = sizeof(UInt32);
301 data = &outlayout->mChannelBitmap;
303 else
305 id = kAudioFormatProperty_ChannelLayoutForTag;
306 size = sizeof(AudioChannelLayoutTag);
307 data = &outlayout->mChannelLayoutTag;
310 UInt32 param_size;
311 OSStatus err = AudioFormatGetPropertyInfo(id, size, data, &param_size);
312 if (err != noErr)
313 return NULL;
315 AudioChannelLayout *reslayout = malloc(param_size);
316 if (reslayout == NULL)
317 return NULL;
319 err = AudioFormatGetProperty(id, size, data, &param_size, reslayout);
320 if (err != noErr || reslayout->mNumberChannelDescriptions == 0)
322 msg_Err(p_aout, "insufficient number of output channels");
323 free(reslayout);
324 return NULL;
327 return reslayout;
330 static int
331 MapOutputLayout(audio_output_t *p_aout, audio_sample_format_t *fmt,
332 const AudioChannelLayout *outlayout)
334 /* Fill VLC physical_channels from output layout */
335 fmt->i_physical_channels = 0;
336 uint32_t i_original = fmt->i_physical_channels;
337 AudioChannelLayout *reslayout = NULL;
339 if (outlayout == NULL)
341 msg_Dbg(p_aout, "not output layout, default to Stereo");
342 fmt->i_physical_channels = AOUT_CHANS_STEREO;
343 goto end;
346 if (outlayout->mChannelLayoutTag !=
347 kAudioChannelLayoutTag_UseChannelDescriptions)
349 reslayout = GetLayoutDescription(p_aout, outlayout);
350 if (reslayout == NULL)
351 return VLC_EGENERIC;
352 outlayout = reslayout;
355 if (i_original == AOUT_CHAN_CENTER
356 || outlayout->mNumberChannelDescriptions < 2)
358 /* We only need Mono or cannot output more than 1 channel */
359 fmt->i_physical_channels = AOUT_CHAN_CENTER;
360 msg_Dbg(p_aout, "output layout of AUHAL has 1 channel");
362 else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
363 || outlayout->mNumberChannelDescriptions < 3)
365 /* We only need Stereo or cannot output more than 2 channels */
366 fmt->i_physical_channels = AOUT_CHANS_STEREO;
367 msg_Dbg(p_aout, "output layout of AUHAL is Stereo");
369 else
371 assert(outlayout->mNumberChannelDescriptions > 0);
373 msg_Dbg(p_aout, "output layout of AUHAL has %i channels",
374 outlayout->mNumberChannelDescriptions);
376 /* maps auhal channels to vlc ones */
377 static const unsigned i_auhal_channel_mapping[] = {
378 [kAudioChannelLabel_Left] = AOUT_CHAN_LEFT,
379 [kAudioChannelLabel_Right] = AOUT_CHAN_RIGHT,
380 [kAudioChannelLabel_Center] = AOUT_CHAN_CENTER,
381 [kAudioChannelLabel_LFEScreen] = AOUT_CHAN_LFE,
382 [kAudioChannelLabel_LeftSurround] = AOUT_CHAN_REARLEFT,
383 [kAudioChannelLabel_RightSurround] = AOUT_CHAN_REARRIGHT,
384 /* needs to be swapped with rear */
385 [kAudioChannelLabel_RearSurroundLeft] = AOUT_CHAN_MIDDLELEFT,
386 /* needs to be swapped with rear */
387 [kAudioChannelLabel_RearSurroundRight] = AOUT_CHAN_MIDDLERIGHT,
388 [kAudioChannelLabel_CenterSurround] = AOUT_CHAN_REARCENTER
390 static const size_t i_auhal_size = sizeof(i_auhal_channel_mapping)
391 / sizeof(i_auhal_channel_mapping[0]);
393 /* We want more than stereo and we can do that */
394 for (unsigned i = 0; i < outlayout->mNumberChannelDescriptions; i++)
396 AudioChannelLabel chan =
397 outlayout->mChannelDescriptions[i].mChannelLabel;
398 #ifndef NDEBUG
399 msg_Dbg(p_aout, "this is channel: %d", (int) chan);
400 #endif
401 if (chan < i_auhal_size && i_auhal_channel_mapping[chan] > 0)
402 fmt->i_physical_channels |= i_auhal_channel_mapping[chan];
403 else
404 msg_Dbg(p_aout, "found nonrecognized channel %d at index "
405 "%d", chan, i);
407 if (fmt->i_physical_channels == 0)
409 fmt->i_physical_channels = AOUT_CHANS_STEREO;
410 msg_Err(p_aout, "You should configure your speaker layout with "
411 "Audio Midi Setup in /Applications/Utilities. VLC will "
412 "output Stereo only.");
413 #if !TARGET_OS_IPHONE
414 vlc_dialog_display_error(p_aout,
415 _("Audio device is not configured"), "%s",
416 _("You should configure your speaker layout with "
417 "\"Audio Midi Setup\" in /Applications/"
418 "Utilities. VLC will output Stereo only."));
419 #endif
422 if (aout_FormatNbChannels(fmt) >= 8
423 && fmt->i_physical_channels != AOUT_CHANS_7_1)
425 #if TARGET_OS_IPHONE
426 const bool b_8x_support = true;
427 #else
428 SInt32 osx_min_version;
429 if (Gestalt(gestaltSystemVersionMinor, &osx_min_version) != noErr)
430 msg_Err(p_aout, "failed to check OSX version");
431 const bool b_8x_support = osx_min_version >= 7;
432 #endif
434 if (!b_8x_support)
436 msg_Warn(p_aout, "8.0 audio output not supported on this "
437 "device, layout will be incorrect");
438 fmt->i_physical_channels = AOUT_CHANS_7_1;
444 end:
445 free(reslayout);
446 aout_FormatPrepare(fmt);
448 msg_Dbg(p_aout, "selected %d physical channels for device output",
449 aout_FormatNbChannels(fmt));
450 msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
452 return VLC_SUCCESS;
455 static int
456 SetupInputLayout(audio_output_t *p_aout, const audio_sample_format_t *fmt,
457 AudioChannelLayoutTag *inlayout_tag)
459 struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
460 uint32_t chans_out[AOUT_CHAN_MAX];
462 /* Some channel abbreviations used below:
463 * L - left
464 * R - right
465 * C - center
466 * Ls - left surround
467 * Rs - right surround
468 * Cs - center surround
469 * Rls - rear left surround
470 * Rrs - rear right surround
471 * Lw - left wide
472 * Rw - right wide
473 * Lsd - left surround direct
474 * Rsd - right surround direct
475 * Lc - left center
476 * Rc - right center
477 * Ts - top surround
478 * Vhl - vertical height left
479 * Vhc - vertical height center
480 * Vhr - vertical height right
481 * Lt - left matrix total. for matrix encoded stereo.
482 * Rt - right matrix total. for matrix encoded stereo. */
484 switch (aout_FormatNbChannels(fmt))
486 case 1:
487 *inlayout_tag = kAudioChannelLayoutTag_Mono;
488 break;
489 case 2:
490 *inlayout_tag = kAudioChannelLayoutTag_Stereo;
491 break;
492 case 3:
493 if (fmt->i_physical_channels & AOUT_CHAN_CENTER) /* L R C */
494 *inlayout_tag = kAudioChannelLayoutTag_DVD_7;
495 else if (fmt->i_physical_channels & AOUT_CHAN_LFE) /* L R LFE */
496 *inlayout_tag = kAudioChannelLayoutTag_DVD_4;
497 break;
498 case 4:
499 if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE)) /* L R C LFE */
500 *inlayout_tag = kAudioChannelLayoutTag_DVD_10;
501 else if (fmt->i_physical_channels & AOUT_CHANS_REAR) /* L R Ls Rs */
502 *inlayout_tag = kAudioChannelLayoutTag_DVD_3;
503 else if (fmt->i_physical_channels & AOUT_CHANS_CENTER) /* L R C Cs */
504 *inlayout_tag = kAudioChannelLayoutTag_DVD_3;
505 break;
506 case 5:
507 if (fmt->i_physical_channels & (AOUT_CHAN_CENTER)) /* L R Ls Rs C */
508 *inlayout_tag = kAudioChannelLayoutTag_DVD_19;
509 else if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) /* L R Ls Rs LFE */
510 *inlayout_tag = kAudioChannelLayoutTag_DVD_18;
511 break;
512 case 6:
513 if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
515 /* L R Ls Rs C LFE */
516 *inlayout_tag = kAudioChannelLayoutTag_DVD_20;
518 chans_out[0] = AOUT_CHAN_LEFT;
519 chans_out[1] = AOUT_CHAN_RIGHT;
520 chans_out[2] = AOUT_CHAN_REARLEFT;
521 chans_out[3] = AOUT_CHAN_REARRIGHT;
522 chans_out[4] = AOUT_CHAN_CENTER;
523 chans_out[5] = AOUT_CHAN_LFE;
525 p_sys->chans_to_reorder =
526 aout_CheckChannelReorder(NULL, chans_out,
527 fmt->i_physical_channels,
528 p_sys->chan_table);
529 if (p_sys->chans_to_reorder)
530 msg_Dbg(p_aout, "channel reordering needed for 5.1 output");
532 else
534 /* L R Ls Rs C Cs */
535 *inlayout_tag = kAudioChannelLayoutTag_AudioUnit_6_0;
537 chans_out[0] = AOUT_CHAN_LEFT;
538 chans_out[1] = AOUT_CHAN_RIGHT;
539 chans_out[2] = AOUT_CHAN_REARLEFT;
540 chans_out[3] = AOUT_CHAN_REARRIGHT;
541 chans_out[4] = AOUT_CHAN_CENTER;
542 chans_out[5] = AOUT_CHAN_REARCENTER;
544 p_sys->chans_to_reorder =
545 aout_CheckChannelReorder(NULL, chans_out,
546 fmt->i_physical_channels,
547 p_sys->chan_table);
548 if (p_sys->chans_to_reorder)
549 msg_Dbg(p_aout, "channel reordering needed for 6.0 output");
551 break;
552 case 7:
553 /* L R C LFE Ls Rs Cs */
554 *inlayout_tag = kAudioChannelLayoutTag_MPEG_6_1_A;
556 chans_out[0] = AOUT_CHAN_LEFT;
557 chans_out[1] = AOUT_CHAN_RIGHT;
558 chans_out[2] = AOUT_CHAN_CENTER;
559 chans_out[3] = AOUT_CHAN_LFE;
560 chans_out[4] = AOUT_CHAN_REARLEFT;
561 chans_out[5] = AOUT_CHAN_REARRIGHT;
562 chans_out[6] = AOUT_CHAN_REARCENTER;
564 p_sys->chans_to_reorder =
565 aout_CheckChannelReorder(NULL, chans_out,
566 fmt->i_physical_channels,
567 p_sys->chan_table);
568 if (p_sys->chans_to_reorder)
569 msg_Dbg(p_aout, "channel reordering needed for 6.1 output");
571 break;
572 case 8:
573 if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
575 /* L R C LFE Ls Rs Rls Rrs */
576 *inlayout_tag = kAudioChannelLayoutTag_MPEG_7_1_C;
578 chans_out[0] = AOUT_CHAN_LEFT;
579 chans_out[1] = AOUT_CHAN_RIGHT;
580 chans_out[2] = AOUT_CHAN_CENTER;
581 chans_out[3] = AOUT_CHAN_LFE;
582 chans_out[4] = AOUT_CHAN_MIDDLELEFT;
583 chans_out[5] = AOUT_CHAN_MIDDLERIGHT;
584 chans_out[6] = AOUT_CHAN_REARLEFT;
585 chans_out[7] = AOUT_CHAN_REARRIGHT;
587 else
589 /* Lc C Rc L R Ls Cs Rs */
590 *inlayout_tag = kAudioChannelLayoutTag_DTS_8_0_B;
592 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
593 chans_out[1] = AOUT_CHAN_CENTER;
594 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
595 chans_out[3] = AOUT_CHAN_LEFT;
596 chans_out[4] = AOUT_CHAN_RIGHT;
597 chans_out[5] = AOUT_CHAN_REARLEFT;
598 chans_out[6] = AOUT_CHAN_REARCENTER;
599 chans_out[7] = AOUT_CHAN_REARRIGHT;
601 p_sys->chans_to_reorder =
602 aout_CheckChannelReorder(NULL, chans_out,
603 fmt->i_physical_channels,
604 p_sys->chan_table);
605 if (p_sys->chans_to_reorder)
606 msg_Dbg(p_aout, "channel reordering needed for 7.1 / 8.0 output");
607 break;
608 case 9:
609 /* Lc C Rc L R Ls Cs Rs LFE */
610 *inlayout_tag = kAudioChannelLayoutTag_DTS_8_1_B;
611 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
612 chans_out[1] = AOUT_CHAN_CENTER;
613 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
614 chans_out[3] = AOUT_CHAN_LEFT;
615 chans_out[4] = AOUT_CHAN_RIGHT;
616 chans_out[5] = AOUT_CHAN_REARLEFT;
617 chans_out[6] = AOUT_CHAN_REARCENTER;
618 chans_out[7] = AOUT_CHAN_REARRIGHT;
619 chans_out[8] = AOUT_CHAN_LFE;
621 p_sys->chans_to_reorder =
622 aout_CheckChannelReorder(NULL, chans_out,
623 fmt->i_physical_channels,
624 p_sys->chan_table);
625 if (p_sys->chans_to_reorder)
626 msg_Dbg(p_aout, "channel reordering needed for 8.1 output");
627 break;
630 return VLC_SUCCESS;
634 au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt,
635 const AudioChannelLayout *outlayout, mtime_t i_dev_latency_us)
637 int ret;
638 AudioChannelLayoutTag inlayout_tag;
640 /* Set the desired format */
641 AudioStreamBasicDescription desc;
642 if (aout_BitsPerSample(fmt->i_format) != 0)
644 /* PCM */
645 fmt->i_format = VLC_CODEC_FL32;
646 ret = MapOutputLayout(p_aout, fmt, outlayout);
647 if (ret != VLC_SUCCESS)
648 return ret;
650 ret = SetupInputLayout(p_aout, fmt, &inlayout_tag);
651 if (ret != VLC_SUCCESS)
652 return ret;
654 desc.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
655 desc.mChannelsPerFrame = aout_FormatNbChannels(fmt);
656 desc.mBitsPerChannel = 32;
658 else if (AOUT_FMT_SPDIF(fmt))
660 /* Passthrough */
661 fmt->i_format = VLC_CODEC_SPDIFL;
662 fmt->i_bytes_per_frame = 4;
663 fmt->i_frame_length = 1;
665 inlayout_tag = kAudioChannelLayoutTag_Stereo;
667 desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
668 kLinearPCMFormatFlagIsPacked; /* S16LE */
669 desc.mChannelsPerFrame = 2;
670 desc.mBitsPerChannel = 16;
672 else
673 return VLC_EGENERIC;
675 desc.mSampleRate = fmt->i_rate;
676 desc.mFormatID = kAudioFormatLinearPCM;
677 desc.mFramesPerPacket = 1;
678 desc.mBytesPerFrame = desc.mBitsPerChannel * desc.mChannelsPerFrame / 8;
679 desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket;
681 OSStatus err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat,
682 kAudioUnitScope_Input, 0, &desc,
683 sizeof(desc));
684 if (err != noErr)
686 ca_LogErr("failed to set stream format");
687 return VLC_EGENERIC;
689 msg_Dbg(p_aout, STREAM_FORMAT_MSG("Current AU format: " , desc));
691 /* Retrieve actual format */
692 err = AudioUnitGetProperty(au, kAudioUnitProperty_StreamFormat,
693 kAudioUnitScope_Input, 0, &desc,
694 &(UInt32) { sizeof(desc) });
695 if (err != noErr)
697 ca_LogErr("failed to set stream format");
698 return VLC_EGENERIC;
701 /* Set the IOproc callback */
702 const AURenderCallbackStruct callback = {
703 .inputProc = RenderCallback,
704 .inputProcRefCon = p_aout,
707 err = AudioUnitSetProperty(au, kAudioUnitProperty_SetRenderCallback,
708 kAudioUnitScope_Input, 0, &callback,
709 sizeof(callback));
710 if (err != noErr)
712 ca_LogErr("failed to setup render callback");
713 return VLC_EGENERIC;
716 /* Set the input_layout as the layout VLC will use to feed the AU unit.
717 * Yes, it must be the INPUT scope */
718 AudioChannelLayout inlayout = {
719 .mChannelLayoutTag = inlayout_tag,
721 err = AudioUnitSetProperty(au, kAudioUnitProperty_AudioChannelLayout,
722 kAudioUnitScope_Input, 0, &inlayout,
723 sizeof(inlayout));
724 if (err != noErr)
726 ca_LogErr("failed to setup input layout");
727 return VLC_EGENERIC;
730 /* AU init */
731 err = AudioUnitInitialize(au);
733 if (err != noErr)
735 ca_LogErr("AudioUnitInitialize failed");
736 return VLC_EGENERIC;
739 ret = ca_Initialize(p_aout, fmt, i_dev_latency_us);
740 if (ret != VLC_SUCCESS)
742 AudioUnitUninitialize(au);
743 return VLC_EGENERIC;
746 return VLC_SUCCESS;
749 void
750 au_Uninitialize(audio_output_t *p_aout, AudioUnit au)
752 OSStatus err = AudioUnitUninitialize(au);
753 if (err != noErr)
754 ca_LogWarn("AudioUnitUninitialize failed");
756 ca_Uninitialize(p_aout);