]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/auhal.c
opensles: remove obsolete code
[vlc] / modules / audio_output / auhal.c
index 36134b48a0cc008b47b40135b5ffe5138d3885bb..e36611e6e70a87c1a2892a519371fdd1c53de5b7 100644 (file)
@@ -1,23 +1,23 @@
 /*****************************************************************************
  * auhal.c: AUHAL and Coreaudio output plugin
  *****************************************************************************
- * Copyright (C) 2005, 2012 the VideoLAN team
+ * Copyright (C) 2005 - 2012 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
  *          Felix Paul Kühne <fkuehne at videolan dot org>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
+ * You should have received a copy of the GNU Lesser General Public License
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
@@ -34,7 +34,6 @@
 #import <vlc_plugin.h>
 #import <vlc_dialog.h>                   // dialog_Fatal
 #import <vlc_aout.h>                     // aout_*
-#import <vlc_aout_intf.h>
 
 #import <AudioUnit/AudioUnit.h>          // AudioUnit
 #import <CoreAudio/CoreAudio.h>      // AudioDeviceID
 #define BUFSIZE (FRAMESIZE * 8) * 8
 #define AOUT_VAR_SPDIF_FLAG 0xf00000
 
-/*
- * TODO:
- * - clean up the debug info
- * - be better at changing stream setup or devices setup changes while playing.
- * - fix 6.1 and 7.1
- */
+#define AOUT_VOLUME_DEFAULT             256
+#define AOUT_VOLUME_MAX                 512
+
 
 /*****************************************************************************
  * aout_sys_t: private audio output method descriptor
@@ -79,6 +75,9 @@ struct aout_sys_t
     bool                        b_digital;           /* Are we running in digital mode? */
     mtime_t                     clock_diff;          /* Difference between VLC clock and Device clock */
 
+    uint8_t                     chans_to_reorder;    /* do we need channel reordering */
+    uint8_t                     chan_table[AOUT_CHAN_MAX];
+
     /* AUHAL specific */
     Component                   au_component;        /* The Audiocomponent we use */
     AudioUnit                   au_unit;             /* The AudioUnit we use */
@@ -100,8 +99,8 @@ struct aout_sys_t
  * Local prototypes.
  *****************************************************************************/
 static int      Open                    (vlc_object_t *);
-static int      OpenAnalog              (audio_output_t *);
-static int      OpenSPDIF               (audio_output_t *);
+static int      OpenAnalog              (audio_output_t *, audio_sample_format_t *);
+static int      OpenSPDIF               (audio_output_t *, audio_sample_format_t *);
 static void     Close                   (vlc_object_t *);
 
 static void     Probe                   (audio_output_t *);
@@ -132,6 +131,9 @@ static int      MuteSet                 (audio_output_t *, bool);
     "audio device, as listed in your 'Audio Device' menu. This device will " \
     "then be used by default for audio playback.")
 
+#define VOLUME_TEXT N_("Audio volume")
+#define VOLUME_LONGTEXT VOLUME_TEXT
+
 vlc_module_begin ()
     set_shortname("auhal")
     set_description(N_("HAL AudioUnit output"))
@@ -140,28 +142,25 @@ vlc_module_begin ()
     set_subcategory(SUBCAT_AUDIO_AOUT)
     set_callbacks(Open, Close)
     add_integer("macosx-audio-device", 0, ADEV_TEXT, ADEV_LONGTEXT, false)
+    add_integer( "auhal-volume", AOUT_VOLUME_DEFAULT,
+                VOLUME_TEXT, VOLUME_LONGTEXT, true )
+    change_integer_range( 0, AOUT_VOLUME_MAX )
 vlc_module_end ()
 
 /*****************************************************************************
  * Open: open macosx audio output
  *****************************************************************************/
-static int Open(vlc_object_t * p_this)
+static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
 {
     OSStatus                err = noErr;
     UInt32                  i_param_size = 0;
     struct aout_sys_t       *p_sys = NULL;
     vlc_value_t             val;
-    audio_output_t          *p_aout = (audio_output_t *)p_this;
 
     /* Use int here, to match kAudioDevicePropertyDeviceIsAlive
      * property size */
     int                     b_alive = false;
 
-    /* Allocate structure */
-    p_aout->sys = malloc(sizeof(aout_sys_t));
-    if (p_aout->sys == NULL)
-        return VLC_ENOMEM;
-
     p_sys = p_aout->sys;
     p_sys->i_default_dev = 0;
     p_sys->i_selected_dev = 0;
@@ -179,11 +178,12 @@ static int Open(vlc_object_t * p_this)
     p_sys->b_changed_mixing = false;
     memset(p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE);
 
-    p_aout->pf_play = aout_PacketPlay;
-    p_aout->pf_pause = aout_PacketPause;
-    p_aout->pf_flush = aout_PacketFlush;
+    p_aout->time_get = aout_PacketTimeGet;
+    p_aout->play = aout_PacketPlay;
+    p_aout->pause = NULL;
+    p_aout->flush = aout_PacketFlush;
 
-    aout_FormatPrint(p_aout, "VLC is looking for:", &p_aout->format);
+    aout_FormatPrint(p_aout, "VLC is looking for:", fmt);
 
     /* Persistent device variable */
     if (var_Type(p_aout->p_libvlc, "macosx-audio-device") == 0)
@@ -255,13 +255,13 @@ static int Open(vlc_object_t * p_this)
     var_AddCallback(p_aout, "audio-device", AudioDeviceCallback, NULL);
 
     /* Check for Digital mode or Analog output mode */
-    if (AOUT_FMT_SPDIF (&p_aout->format) && b_supports_digital) {
-        if (OpenSPDIF (p_aout)) {
+    if (AOUT_FMT_SPDIF (fmt) && b_supports_digital) {
+        if (OpenSPDIF (p_aout, fmt)) {
             msg_Dbg(p_aout, "digital output successfully opened");
             return VLC_SUCCESS;
         }
     } else {
-        if (OpenAnalog(p_aout)) {
+        if (OpenAnalog(p_aout, fmt)) {
             msg_Dbg(p_aout, "analog output successfully opened");
             return VLC_SUCCESS;
         }
@@ -271,14 +271,13 @@ error:
     /* If we reach this, this aout has failed */
     msg_Err(p_aout, "opening the auhal output failed");
     var_Destroy(p_aout, "audio-device");
-    free(p_sys);
     return VLC_EGENERIC;
 }
 
 /*****************************************************************************
  * Open: open and setup a HAL AudioUnit to do analog (multichannel) audio output
  *****************************************************************************/
-static int OpenAnalog(audio_output_t *p_aout)
+static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
 {
     struct aout_sys_t           *p_sys = p_aout->sys;
     OSStatus                    err = noErr;
@@ -374,54 +373,56 @@ static int OpenAnalog(audio_output_t *p_aout)
         msg_Dbg(p_aout, "layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions);
 
         /* Initialize the VLC core channel count */
-        p_aout->format.i_physical_channels = 0;
-        i_original = p_aout->format.i_original_channels & AOUT_CHAN_PHYSMASK;
+        fmt->i_physical_channels = 0;
+        i_original = fmt->i_original_channels & AOUT_CHAN_PHYSMASK;
 
         if (i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2) {
             /* We only need Mono or cannot output more than 1 channel */
-            p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
+            fmt->i_physical_channels = AOUT_CHAN_CENTER;
         } else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3) {
             /* We only need Stereo or cannot output more than 2 channels */
-            p_aout->format.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
+            fmt->i_physical_channels = AOUT_CHANS_STEREO;
         } else {
             /* We want more than stereo and we can do that */
             for (unsigned int i = 0; i < layout->mNumberChannelDescriptions; i++) {
+#ifndef NDEBUG
                 msg_Dbg(p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel);
+#endif
 
                 switch(layout->mChannelDescriptions[i].mChannelLabel) {
                     case kAudioChannelLabel_Left:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_LEFT;
+                        fmt->i_physical_channels |= AOUT_CHAN_LEFT;
                         continue;
                     case kAudioChannelLabel_Right:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_RIGHT;
+                        fmt->i_physical_channels |= AOUT_CHAN_RIGHT;
                         continue;
                     case kAudioChannelLabel_Center:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_CENTER;
+                        fmt->i_physical_channels |= AOUT_CHAN_CENTER;
                         continue;
                     case kAudioChannelLabel_LFEScreen:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_LFE;
+                        fmt->i_physical_channels |= AOUT_CHAN_LFE;
                         continue;
                     case kAudioChannelLabel_LeftSurround:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_REARLEFT;
+                        fmt->i_physical_channels |= AOUT_CHAN_REARLEFT;
                         continue;
                     case kAudioChannelLabel_RightSurround:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_REARRIGHT;
+                        fmt->i_physical_channels |= AOUT_CHAN_REARRIGHT;
                         continue;
                     case kAudioChannelLabel_RearSurroundLeft:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
+                        fmt->i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
                         continue;
                     case kAudioChannelLabel_RearSurroundRight:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
+                        fmt->i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
                         continue;
                     case kAudioChannelLabel_CenterSurround:
-                        p_aout->format.i_physical_channels |= AOUT_CHAN_REARCENTER;
+                        fmt->i_physical_channels |= AOUT_CHAN_REARCENTER;
                         continue;
                     default:
                         msg_Warn(p_aout, "unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel);
                 }
             }
-            if (p_aout->format.i_physical_channels == 0) {
-                p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
+            if (fmt->i_physical_channels == 0) {
+                fmt->i_physical_channels = AOUT_CHANS_STEREO;
                 msg_Err(p_aout, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode.");
                 dialog_Fatal(p_aout, _("Audio device is not configured"), "%s",
                                 _("You should configure your speaker layout with "
@@ -432,14 +433,16 @@ static int OpenAnalog(audio_output_t *p_aout)
         free(layout);
     } else {
         msg_Warn(p_aout, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!");
-        p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
+        fmt->i_physical_channels = AOUT_CHANS_STEREO;
     }
 
-    msg_Dbg(p_aout, "selected %d physical channels for device output", aout_FormatNbChannels(&p_aout->format));
-    msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(&p_aout->format));
+    msg_Dbg(p_aout, "selected %d physical channels for device output", aout_FormatNbChannels(fmt));
+    msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
 
     memset (&new_layout, 0, sizeof(new_layout));
-    switch(aout_FormatNbChannels(&p_aout->format)) {
+    uint32_t chans_out[AOUT_CHAN_MAX];
+
+    switch(aout_FormatNbChannels(fmt)) {
         case 1:
             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
             break;
@@ -447,50 +450,75 @@ static int OpenAnalog(audio_output_t *p_aout)
             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
             break;
         case 3:
-            if (p_aout->format.i_physical_channels & AOUT_CHAN_CENTER)
+            if (fmt->i_physical_channels & AOUT_CHAN_CENTER)
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
-            else if (p_aout->format.i_physical_channels & AOUT_CHAN_LFE)
+            else if (fmt->i_physical_channels & AOUT_CHAN_LFE)
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
             break;
         case 4:
-            if (p_aout->format.i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE))
+            if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE))
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
-            else if (p_aout->format.i_physical_channels & (AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT))
+            else if (fmt->i_physical_channels & (AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT))
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
-            else if (p_aout->format.i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER))
+            else if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER))
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
             break;
         case 5:
-            if (p_aout->format.i_physical_channels & (AOUT_CHAN_CENTER))
+            if (fmt->i_physical_channels & (AOUT_CHAN_CENTER))
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
-            else if (p_aout->format.i_physical_channels & (AOUT_CHAN_LFE))
+            else if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
             break;
         case 6:
-            if (p_aout->format.i_physical_channels & (AOUT_CHAN_LFE))
+            if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
             else
                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
             break;
         case 7:
-            /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
-            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
+            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A;
+
+            chans_out[0] = AOUT_CHAN_LEFT;
+            chans_out[1] = AOUT_CHAN_RIGHT;
+            chans_out[2] = AOUT_CHAN_CENTER;
+            chans_out[3] = AOUT_CHAN_LFE;
+            chans_out[4] = AOUT_CHAN_REARLEFT;
+            chans_out[5] = AOUT_CHAN_REARRIGHT;
+            chans_out[6] = AOUT_CHAN_REARCENTER;
+
+            p_aout->sys->chans_to_reorder = aout_CheckChannelReorder( NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table );
+            if (p_aout->sys->chans_to_reorder)
+                msg_Dbg( p_aout, "channel reordering needed" );
+
             break;
         case 8:
-            /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
-            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
+            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A;
+
+            chans_out[0] = AOUT_CHAN_LEFT;
+            chans_out[1] = AOUT_CHAN_RIGHT;
+            chans_out[2] = AOUT_CHAN_CENTER;
+            chans_out[3] = AOUT_CHAN_LFE;
+            chans_out[4] = AOUT_CHAN_MIDDLELEFT;
+            chans_out[5] = AOUT_CHAN_MIDDLERIGHT;
+            chans_out[6] = AOUT_CHAN_REARLEFT;
+            chans_out[7] = AOUT_CHAN_REARRIGHT;
+
+            p_aout->sys->chans_to_reorder = aout_CheckChannelReorder( NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table );
+            if (p_aout->sys->chans_to_reorder)
+                msg_Dbg( p_aout, "channel reordering needed" );
+
             break;
     }
 
     /* Set up the format to be used */
-    DeviceFormat.mSampleRate = p_aout->format.i_rate;
+    DeviceFormat.mSampleRate = fmt->i_rate;
     DeviceFormat.mFormatID = kAudioFormatLinearPCM;
 
     /* We use float 32. It's the best supported format by both VLC and Coreaudio */
-    p_aout->format.i_format = VLC_CODEC_FL32;
+    fmt->i_format = VLC_CODEC_FL32;
     DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
     DeviceFormat.mBitsPerChannel = 32;
-    DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels(&p_aout->format);
+    DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels(fmt);
 
     /* Calculate framesizes and stuff */
     DeviceFormat.mFramesPerPacket = 1;
@@ -519,10 +547,8 @@ static int OpenAnalog(audio_output_t *p_aout)
     msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is " , DeviceFormat));
 
     /* Do the last VLC aout setups */
-    aout_FormatPrepare(&p_aout->format);
-    aout_PacketInit(p_aout, &p_sys->packet, FRAMESIZE);
-    p_aout->volume_set = VolumeSet;
-    p_aout->mute_set = MuteSet;
+    aout_FormatPrepare(fmt);
+    aout_PacketInit(p_aout, &p_sys->packet, FRAMESIZE, fmt);
 
     /* set the IOproc callback */
     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
@@ -533,13 +559,10 @@ static int OpenAnalog(audio_output_t *p_aout)
                             kAudioUnitScope_Input,
                             0, &input, sizeof(input)));
 
-    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
-    input.inputProcRefCon = p_aout;
-
     /* Set the new_layout as the layout VLC will use to feed the AU unit */
     verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
                             kAudioUnitProperty_AudioChannelLayout,
-                            kAudioUnitScope_Input,
+                            kAudioUnitScope_Output,
                             0, &new_layout, sizeof(new_layout)));
 
     if (new_layout.mNumberChannelDescriptions > 0)
@@ -556,13 +579,22 @@ static int OpenAnalog(audio_output_t *p_aout)
     /* Start the AU */
     verify_noerr(AudioOutputUnitStart(p_sys->au_unit));
 
+    /* Set volume for output unit */
+    float volume = var_InheritInteger(p_aout, "auhal-volume") / (float)AOUT_VOLUME_DEFAULT;
+    verify_noerr(AudioUnitSetParameter(p_sys->au_unit,
+                                    kHALOutputParam_Volume,
+                                    kAudioUnitScope_Global,
+                                    0,
+                                    volume * volume * volume,
+                                    0));
+
     return true;
 }
 
 /*****************************************************************************
  * Setup a encoded digital stream (SPDIF)
  *****************************************************************************/
-static int OpenSPDIF (audio_output_t * p_aout)
+static int OpenSPDIF (audio_output_t * p_aout, audio_sample_format_t *fmt)
 {
     struct aout_sys_t       *p_sys = p_aout->sys;
     OSStatus                err = noErr;
@@ -690,7 +722,7 @@ static int OpenSPDIF (audio_output_t * p_aout)
                    p_format_list[j].mFormat.mFormatID == 'iac3' ||
                    p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
                    p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
-                    if (p_format_list[j].mFormat.mSampleRate == p_aout->format.i_rate) {
+                    if (p_format_list[j].mFormat.mSampleRate == fmt->i_rate) {
                         i_requested_rate_format = j;
                         break;
                     } else if (p_format_list[j].mFormat.mSampleRate == p_sys->sfmt_revert.mSampleRate)
@@ -728,16 +760,14 @@ static int OpenSPDIF (audio_output_t * p_aout)
 
     /* Set the format flags */
     if (p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)
-        p_aout->format.i_format = VLC_CODEC_SPDIFB;
+        fmt->i_format = VLC_CODEC_SPDIFB;
     else
-        p_aout->format.i_format = VLC_CODEC_SPDIFL;
-    p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
-    p_aout->format.i_frame_length = A52_FRAME_NB;
-    p_aout->format.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
-    aout_FormatPrepare(&p_aout->format);
-    aout_PacketInit(p_aout, &p_sys->packet, A52_FRAME_NB);
-    p_aout->volume_set = NULL;
-    p_aout->mute_set = NULL;
+        fmt->i_format = VLC_CODEC_SPDIFL;
+    fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
+    fmt->i_frame_length = A52_FRAME_NB;
+    fmt->i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
+    aout_FormatPrepare(fmt);
+    aout_PacketInit(p_aout, &p_sys->packet, A52_FRAME_NB, fmt);
 
     /* Add IOProc callback */
     err = AudioDeviceCreateIOProcID(p_sys->i_selected_dev,
@@ -775,9 +805,8 @@ static int OpenSPDIF (audio_output_t * p_aout)
 /*****************************************************************************
  * Close: Close HAL AudioUnit
  *****************************************************************************/
-static void Close(vlc_object_t * p_this)
+static void Stop(audio_output_t *p_aout)
 {
-    audio_output_t     *p_aout = (audio_output_t *)p_this;
     struct aout_sys_t   *p_sys = p_aout->sys;
     OSStatus            err = noErr;
     UInt32              i_param_size = 0;
@@ -855,7 +884,6 @@ static void Close(vlc_object_t * p_this)
     var_DelCallback(p_aout, "audio-device", AudioDeviceCallback, NULL);
 
     aout_PacketDestroy(p_aout);
-    free(p_sys);
 }
 
 /*****************************************************************************
@@ -1079,7 +1107,9 @@ static int AudioStreamSupportsDigital(audio_output_t *p_aout, AudioStreamID i_st
     }
 
     for (int i = 0; i < i_formats; i++) {
+#ifndef NDEBUG
         msg_Dbg(p_aout, STREAM_FORMAT_MSG("supported format: ", p_format_list[i].mFormat));
+#endif
 
         if (p_format_list[i].mFormat.mFormatID == 'IAC3' ||
            p_format_list[i].mFormat.mFormatID == 'iac3' ||
@@ -1208,15 +1238,14 @@ static OSStatus RenderCallbackAnalog(vlc_object_t *_p_aout,
     if (ioData->mNumberBuffers > 1)
         msg_Err(p_aout, "well this is weird. seems like there is more than one buffer...");
 
-
     if (p_sys->i_total_bytes > 0) {
         i_mData_bytes = __MIN(p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize);
         memcpy(ioData->mBuffers[0].mData,
                     &p_sys->p_remainder_buffer[p_sys->i_read_bytes],
                     i_mData_bytes);
         p_sys->i_read_bytes += i_mData_bytes;
-        current_date += (mtime_t) ((mtime_t) 1000000 / p_aout->format.i_rate) *
-                        (i_mData_bytes / 4 / aout_FormatNbChannels(&p_aout->format)); // 4 is fl32 specific
+        current_date += (mtime_t) ((mtime_t) 1000000 / p_sys->packet.format.i_rate) *
+                        (i_mData_bytes / 4 / aout_FormatNbChannels(&p_sys->packet.format)); // 4 is fl32 specific
 
         if (p_sys->i_read_bytes >= p_sys->i_total_bytes)
             p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
@@ -1229,6 +1258,16 @@ static OSStatus RenderCallbackAnalog(vlc_object_t *_p_aout,
 
         if (p_buffer != NULL)
         {
+            /* Do the channel reordering */
+            if (p_buffer && p_sys->chans_to_reorder)
+            {
+                aout_ChannelReorder(p_buffer->p_buffer,
+                                    p_buffer->i_buffer,
+                                    p_sys->chans_to_reorder,
+                                    p_sys->chan_table,
+                                    32);
+            }
+
             uint32_t i_second_mData_bytes = __MIN(p_buffer->i_buffer, ioData->mBuffers[0].mDataByteSize - i_mData_bytes);
 
             memcpy((uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes,
@@ -1245,8 +1284,8 @@ static OSStatus RenderCallbackAnalog(vlc_object_t *_p_aout,
                 break;
             } else
                 /* update current_date */
-                current_date += (mtime_t) ((mtime_t) 1000000 / p_aout->format.i_rate) *
-                                (i_second_mData_bytes / 4 / aout_FormatNbChannels(&p_aout->format)); // 4 is fl32 specific
+                current_date += (mtime_t) ((mtime_t) 1000000 / p_sys->packet.format.i_rate) *
+                                (i_second_mData_bytes / 4 / aout_FormatNbChannels(&p_sys->packet.format)); // 4 is fl32 specific
             block_Release(p_buffer);
         } else {
             memset((uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes,
@@ -1370,31 +1409,75 @@ static int AudioDeviceCallback(vlc_object_t *p_this, const char *psz_variable,
 
 
 /*****************************************************************************
- * VolumeSet: Implements pf_volume_set(). Update the CoreAudio AU volume immediately.
+ * VolumeSet: Implements volume_set(). Update the CoreAudio AU volume immediately.
  *****************************************************************************/
 static int VolumeSet(audio_output_t * p_aout, float volume)
 {
-    struct   aout_sys_t *p_sys = p_aout->sys;
+    struct aout_sys_t *p_sys = p_aout->sys;
     OSStatus ostatus;
 
     aout_VolumeReport(p_aout, volume);
 
-    volume = volume * volume * volume; // cubic mapping from output.c
-
     /* Set volume for output unit */
     ostatus = AudioUnitSetParameter(p_sys->au_unit,
-                                     kHALOutputParam_Volume,
-                                     kAudioUnitScope_Global,
-                                     0,
-                                     volume,
-                                     0);
+                                    kHALOutputParam_Volume,
+                                    kAudioUnitScope_Global,
+                                    0,
+                                    volume * volume * volume,
+                                    0);
+
+    if (var_InheritBool(p_aout, "volume-save"))
+        config_PutInt(p_aout, "auhal-volume", lroundf(volume * AOUT_VOLUME_DEFAULT));
 
     return ostatus;
 }
 
 static int MuteSet(audio_output_t * p_aout, bool mute)
 {
+    struct   aout_sys_t *p_sys = p_aout->sys;
+    OSStatus ostatus;
+
     aout_MuteReport(p_aout, mute);
 
-    return 0;
+    float volume = .0;
+
+    if (!mute)
+        volume = var_InheritInteger(p_aout, "auhal-volume") / (float)AOUT_VOLUME_DEFAULT;
+
+    ostatus = AudioUnitSetParameter(p_sys->au_unit,
+                                    kHALOutputParam_Volume,
+                                    kAudioUnitScope_Global,
+                                    0,
+                                    volume * volume * volume,
+                                    0);
+
+    return ostatus;
+}
+
+static int Open(vlc_object_t *obj)
+{
+    audio_output_t *aout = (audio_output_t *)obj;
+    aout_sys_t *sys = malloc(sizeof (*sys));
+
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+    aout->sys = sys;
+    aout->start = Start;
+    aout->stop = Stop;
+    aout->volume_set = VolumeSet;
+    aout->mute_set = MuteSet;
+
+    /* remember the volume */
+    aout_VolumeReport(aout, var_InheritInteger(aout, "auhal-volume") / (float)AOUT_VOLUME_DEFAULT);
+    MuteSet(aout, var_InheritBool(aout, "mute"));
+
+    return VLC_SUCCESS;
+}
+
+static void Close(vlc_object_t *obj)
+{
+    audio_output_t *aout = (audio_output_t *)obj;
+    aout_sys_t *sys = aout->sys;
+
+    free(sys);
 }