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? */
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 *);
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;
p_aout->device_select = SwitchAudioDevice;
p_sys->device_list = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
+ /*
+ * Force an own run loop for callbacks.
+ *
+ * According to rtaudio, this is absolutely necessary since 10.6 to get correct notifications.
+ * It might fix issues when using the module as a library where a proper loop is not setup already.
+ */
+ CFRunLoopRef theRunLoop = NULL;
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster };
+ err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
+ if (err != noErr) {
+ msg_Err(p_aout, "failed to set the run loop property [%4.4s]", (char *)&err);
+ }
+
/* 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 */
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);
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 (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);
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;
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));
}
/*
- * Callback when streams of any audio device changed (e.g. SPDIF gets (un)available)
+ * Callback when default audio device changed
+ */
+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);
+
+ /* Default device is changed by the os to allow other apps to play sound while in digital
+ mode. But this should not affect ourself. */
+ if (p_aout->sys->b_digital) {
+ msg_Dbg(p_aout, "ignore, as digital mode is active");
+ 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 physical formats for device change
*/
static OSStatus StreamsChangedListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
{
}
/*
- * StreamListener: check whether the device's physical format changes on-the-fly (unlikely)
+ * StreamListener: check whether the device's physical format change is complete
*/
static OSStatus StreamListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
{