]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/auhal.c
auhal: do not change volume when muted
[vlc] / modules / audio_output / auhal.c
index 34b60dd1828b588d0eca80572aae0be888e886d2..e2b9068f4ae92597a2a3e607126c52a2bb6e697b 100644 (file)
  *****************************************************************************/
 struct aout_sys_t
 {
-    AudioObjectID               i_default_dev;      /* DeviceID of defaultOutputDevice */
     AudioObjectID               i_selected_dev;     /* DeviceID of the selected device */
     AudioObjectID               i_new_selected_dev; /* DeviceID of device which will be selected on start */
     bool                        b_selected_dev_is_digital;
+    bool                        b_selected_dev_is_default; /* true if the user selected the default audio device (id 0) */
+
     AudioDeviceIOProcID         i_procID;           /* DeviceID of current device */
     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];
 
-    UInt32                      i_numberOfChannels;
     TPCircularBuffer            circular_buffer;    /* circular buffer to swap the audio data */
 
     /* AUHAL specific */
@@ -119,6 +118,8 @@ struct aout_sys_t
 
     vlc_mutex_t                 lock;
     vlc_cond_t                  cond;
+
+    bool                        b_ignore_streams_changed_callback;
 };
 
 #pragma mark -
@@ -148,6 +149,7 @@ static OSStatus RenderCallbackSPDIF     (AudioDeviceID, const AudioTimeStamp *,
 
 static OSStatus DevicesListener         (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
 static OSStatus DeviceAliveListener     (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
+static OSStatus DefaultDeviceChangedListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
 static OSStatus StreamsChangedListener  (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
 
 static OSStatus StreamListener          (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
@@ -189,6 +191,8 @@ static int Open(vlc_object_t *obj)
     vlc_mutex_init(&p_sys->lock);
     vlc_cond_init(&p_sys->cond);
     p_sys->b_digital = false;
+    p_sys->b_ignore_streams_changed_callback = false;
+    p_sys->b_selected_dev_is_default = false;
 
     p_aout->sys = p_sys;
     p_aout->start = Start;
@@ -198,12 +202,18 @@ static int Open(vlc_object_t *obj)
     p_aout->device_select = SwitchAudioDevice;
     p_sys->device_list = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
 
-    /* Attach a Listener so that we are notified of a change in the Device setup */
+    /* Attach a listener so that we are notified of a change in the device setup */
     AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
     err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, DevicesListener, (void *)p_aout);
     if (err != noErr)
         msg_Err(p_aout, "failed to add listener for audio device configuration [%4.4s]", (char *)&err);
 
+    /* Attach a listener to be notified about changes in default audio device */
+    AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+    err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, DefaultDeviceChangedListener, (void *)p_aout);
+    if (err != noErr)
+        msg_Err(p_aout, "failed to add listener for default audio device [%4.4s]", (char *)&err);
+
     RebuildDeviceList(p_aout);
 
     /* remember the volume */
@@ -232,6 +242,12 @@ static void Close(vlc_object_t *obj)
     if (err != noErr)
         msg_Err(p_aout, "AudioHardwareRemovePropertyListener failed [%4.4s]", (char *)&err);
 
+    /* remove listener to be notified about changes in default audio device */
+    AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+    err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, DefaultDeviceChangedListener, (void *)p_aout);
+    if (err != noErr)
+        msg_Err(p_aout, "failed to remove listener for default audio device [%4.4s]", (char *)&err);
+
     vlc_mutex_lock(&p_sys->var_lock);
     /* remove streams callbacks */
     CFIndex count = CFArrayGetCount(p_sys->device_list);
@@ -279,7 +295,6 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
     p_sys->b_digital = false;
     p_sys->au_component = NULL;
     p_sys->au_unit = NULL;
-    p_sys->clock_diff = (mtime_t) 0;
     p_sys->i_hog_pid = -1;
     p_sys->i_stream_id = 0;
     p_sys->i_stream_index = -1;
@@ -310,14 +325,18 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
             msg_Warn(p_aout, "selected audio device is not alive, switching to default device");
     }
 
+    p_sys->b_selected_dev_is_default = false;
     if (!b_alive || p_sys->i_selected_dev == 0) {
+        p_sys->b_selected_dev_is_default = true;
+
         AudioObjectID defaultDeviceID = 0;
-        UInt32 propertySize = 0;
+        UInt32 propertySize = sizeof(AudioObjectID);
         AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
         propertySize = sizeof(AudioObjectID);
         err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &propertySize, &defaultDeviceID);
         if (err != noErr) {
             msg_Err(p_aout, "could not get default audio device [%4.4s]", (char *)&err);
+            vlc_mutex_unlock(&p_sys->var_lock);
             goto error;
         }
         else
@@ -326,6 +345,7 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
         p_sys->i_selected_dev = defaultDeviceID;
         p_sys->b_selected_dev_is_digital = var_InheritBool(p_aout, "spdif");
     }
+    vlc_mutex_unlock(&p_sys->var_lock);
 
     // recheck if device still supports digital
     b_start_digital = p_sys->b_selected_dev_is_digital;
@@ -379,7 +399,6 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
             b_success = true;
         }
     }
-    vlc_mutex_unlock(&p_sys->var_lock);
 
     if (b_success) {
         p_aout->play = Play;
@@ -391,7 +410,6 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
 
 error:
     /* If we reach this, this aout has failed */
-    vlc_mutex_unlock(&p_sys->var_lock);
     msg_Err(p_aout, "opening auhal output failed");
     return VLC_EGENERIC;
 }
@@ -571,10 +589,10 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
 
     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));
-    p_sys->i_numberOfChannels = aout_FormatNbChannels(fmt);
 
     memset (&new_layout, 0, sizeof(new_layout));
     uint32_t chans_out[AOUT_CHAN_MAX];
+    memset(&chans_out, 0, sizeof(chans_out));
 
     /* Some channel abbreviations used below:
      * L - left
@@ -627,14 +645,16 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
             break;
         case 6:
             if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) {
-                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
+                /* note: for some reason, kAudioChannelLayoutTag_DVD_20, which would not require channel reordering, 
+                 does not work anymore */
+                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1; // L R C LFE Ls Rs
 
                 chans_out[0] = AOUT_CHAN_LEFT;
                 chans_out[1] = AOUT_CHAN_RIGHT;
-                chans_out[2] = AOUT_CHAN_REARLEFT;
-                chans_out[3] = AOUT_CHAN_REARRIGHT;
-                chans_out[4] = AOUT_CHAN_CENTER;
-                chans_out[5] = AOUT_CHAN_LFE;
+                chans_out[2] = AOUT_CHAN_CENTER;
+                chans_out[3] = AOUT_CHAN_LFE;
+                chans_out[4] = AOUT_CHAN_REARLEFT;
+                chans_out[5] = 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)
@@ -791,11 +811,6 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
     /* AU initiliaze */
     verify_noerr(AudioUnitInitialize(p_sys->au_unit));
 
-    /* Find the difference between device clock and mdate clock */
-    p_sys->clock_diff = - (mtime_t)
-        AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
-    p_sys->clock_diff += mdate();
-
     /* setup circular buffer */
     TPCircularBufferInit(&p_sys->circular_buffer, AUDIO_BUFFER_SIZE_IN_SECONDS *
                          fmt->i_rate * fmt->i_bytes_per_frame);
@@ -829,7 +844,13 @@ static int StartSPDIF(audio_output_t * p_aout, audio_sample_format_t *fmt)
     i_param_size = sizeof(p_sys->i_hog_pid);
     p_sys->i_hog_pid = getpid() ;
 
+    /*
+     * HACK: On 10.6, auhal will trigger the streams changed callback when calling below line,
+     * directly in the same thread. This call needs to be ignored to avoid endless restarting.
+     */
+    p_sys->b_ignore_streams_changed_callback = true;
     err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
+    p_sys->b_ignore_streams_changed_callback = false;
 
     if (err != noErr) {
         msg_Err(p_aout, "failed to set hogmode [%4.4s]", (char *)&err);
@@ -992,11 +1013,6 @@ static int StartSPDIF(audio_output_t * p_aout, audio_sample_format_t *fmt)
         return false;
     }
 
-    /* Check for the difference between the Device clock and mdate */
-    p_sys->clock_diff = - (mtime_t)
-        AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
-    p_sys->clock_diff += mdate();
-
     /* Start device */
     err = AudioDeviceStart(p_sys->i_selected_dev, p_sys->i_procID);
     if (err != noErr) {
@@ -1027,7 +1043,6 @@ static void Stop(audio_output_t *p_aout)
         verify_noerr(AudioComponentInstanceDispose(p_sys->au_unit));
     }
 
-    vlc_mutex_lock(&p_sys->var_lock);
     if (p_sys->b_digital) {
         /* Stop device */
         err = AudioDeviceStop(p_sys->i_selected_dev,
@@ -1047,6 +1062,7 @@ static void Stop(audio_output_t *p_aout)
         if (p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3) {
             int b_mix;
             Boolean b_writeable = false;
+            i_param_size = sizeof(int);
             /* Revert mixable to true if we are allowed to */
             AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
             err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
@@ -1069,7 +1085,14 @@ static void Stop(audio_output_t *p_aout)
         AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
             kAudioDevicePropertyScopeOutput,
             kAudioObjectPropertyElementMaster };
+
+        /*
+         * HACK: On 10.6, auhal will trigger the streams changed callback when calling below line,
+         * directly in the same thread. This call needs to be ignored to avoid endless restarting.
+         */
+        p_sys->b_ignore_streams_changed_callback = true;
         err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
+        p_sys->b_ignore_streams_changed_callback = false;
         if (err != noErr)
             msg_Err(p_aout, "Failed to release hogmode [%4.4s]", (char *)&err);
     }
@@ -1082,8 +1105,6 @@ static void Stop(audio_output_t *p_aout)
         msg_Warn(p_aout, "failed to remove audio device life checker [%4.4s]", (char *)&err);
     }
 
-    vlc_mutex_unlock(&p_sys->var_lock);
-
     p_sys->i_bytes_per_sample = 0;
     p_sys->b_digital = false;
 
@@ -1254,7 +1275,7 @@ static int SwitchAudioDevice(audio_output_t *p_aout, const char *name)
 static int VolumeSet(audio_output_t * p_aout, float volume)
 {
     struct aout_sys_t *p_sys = p_aout->sys;
-    OSStatus ostatus;
+    OSStatus ostatus = 0;
 
     if(p_sys->b_digital)
         return VLC_EGENERIC;
@@ -1263,12 +1284,14 @@ static int VolumeSet(audio_output_t * p_aout, float volume)
     aout_VolumeReport(p_aout, volume);
 
     /* Set volume for output unit */
-    ostatus = AudioUnitSetParameter(p_sys->au_unit,
-                                    kHALOutputParam_Volume,
-                                    kAudioUnitScope_Global,
-                                    0,
-                                    volume * volume * volume,
-                                    0);
+    if(!p_sys->b_mute) {
+        ostatus = AudioUnitSetParameter(p_sys->au_unit,
+                                        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));
@@ -1489,7 +1512,7 @@ static OSStatus RenderCallbackSPDIF(AudioDeviceID inDevice,
 /*
  * Callback when device list changed
  */
-static OSStatus DevicesListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+static OSStatus DevicesListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
 {
     VLC_UNUSED(inObjectID);
     VLC_UNUSED(inNumberAddresses);
@@ -1514,7 +1537,7 @@ static OSStatus DevicesListener(AudioObjectID inObjectID,  UInt32 inNumberAddres
 /*
  * Callback when current device is not alive anymore
  */
-static OSStatus DeviceAliveListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+static OSStatus DeviceAliveListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
 {
     VLC_UNUSED(inObjectID);
     VLC_UNUSED(inNumberAddresses);
@@ -1531,9 +1554,55 @@ static OSStatus DeviceAliveListener(AudioObjectID inObjectID,  UInt32 inNumberAd
 }
 
 /*
- * Callback when streams of any audio device changed (e.g. SPDIF gets (un)available)
+ * Callback when current device is not alive anymore
+ */
+static OSStatus DefaultDeviceChangedListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
+{
+    VLC_UNUSED(inObjectID);
+    VLC_UNUSED(inNumberAddresses);
+    VLC_UNUSED(inAddresses);
+
+    audio_output_t *p_aout = (audio_output_t *)inClientData;
+    if (!p_aout)
+        return -1;
+
+    if (!p_aout->sys->b_selected_dev_is_default)
+        return noErr;
+
+    AudioObjectID defaultDeviceID = 0;
+    UInt32 propertySize = sizeof(AudioObjectID);
+    AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
+    propertySize = sizeof(AudioObjectID);
+    OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &propertySize, &defaultDeviceID);
+    if (err != noErr) {
+        msg_Err(p_aout, "could not get default audio device [%4.4s]", (char *)&err);
+        return -1;
+    }
+
+    msg_Dbg(p_aout, "default device changed to %i", defaultDeviceID);
+
+    /* 
+     * The default device id changes to 0 when switching to SPDIF for whatever reason.
+     * We need to ignore that.
+     */
+    if(defaultDeviceID == 0)
+        return noErr;
+
+    /* Also ignore events which announce the same device id */
+    if(defaultDeviceID == p_aout->sys->i_selected_dev)
+        return noErr;
+
+    msg_Dbg(p_aout, "default device actually changed, resetting aout");
+    aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
+
+    return noErr;
+}
+
+
+/*
+ * Callback when default audio device changed
  */
-static OSStatus StreamsChangedListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+static OSStatus StreamsChangedListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
 {
     OSStatus                    err = noErr;
     UInt32                      i_param_size = 0;
@@ -1548,6 +1617,8 @@ static OSStatus StreamsChangedListener(AudioObjectID inObjectID,  UInt32 inNumbe
         return -1;
 
     aout_sys_t *p_sys = p_aout->sys;
+    if(unlikely(p_sys->b_ignore_streams_changed_callback == true))
+        return 0;
 
     msg_Dbg(p_aout, "available physical formats for audio device changed");
     RebuildDeviceList(p_aout);
@@ -1590,7 +1661,7 @@ static OSStatus StreamsChangedListener(AudioObjectID inObjectID,  UInt32 inNumbe
 /*
  * StreamListener: check whether the device's physical format changes on-the-fly (unlikely)
  */
-static OSStatus StreamListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+static OSStatus StreamListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
 {
     OSStatus err = noErr;
     struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;