static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged( IMMNotificationClient *This, LPCWSTR pwstrDeviceId, DWORD dwNewState) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDeviceStateChanged triggered: sending hotplug event\n"); ao_hotplug_event(ao); } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { switch (dwNewState) { case DEVICE_STATE_DISABLED: case DEVICE_STATE_NOTPRESENT: case DEVICE_STATE_UNPLUGGED: MP_VERBOSE(ao, "OnDeviceStateChanged triggered on device %S: " "requesting ao reload\n", pwstrDeviceId); ao_request_reload(ao); break; case DEVICE_STATE_ACTIVE: break; } } return S_OK; }
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged( IMMNotificationClient *This, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; struct wasapi_state *state = (struct wasapi_state *)ao->priv; MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered for role:%s flow:%s\n", ERole_to_str(role), EDataFlow_to_str(flow)); if(pwstrDeviceId) MP_VERBOSE(ao, "New default device %S\n", pwstrDeviceId); /* don't care about "eCapture" or non-"eMultimedia" roles */ if (flow == eCapture || role != eMultimedia) return S_OK; /* stay on the device the user specified */ if (state->opt_device) { MP_VERBOSE(ao, "Staying on specified device \"%s\"", state->opt_device); return S_OK; } /* don't reload if already on the new default */ if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) { MP_VERBOSE(ao, "Already using default device, no reload required\n"); return S_OK; } /* if we got here, we need to reload */ ao_request_reload(ao); MP_VERBOSE(ao, "Requesting ao reload\n"); return S_OK; }
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged( IMMNotificationClient *This, LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; if (!change->is_hotplug && pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { MP_VERBOSE(ao, "OnPropertyValueChanged triggered on device %S\n", pwstrDeviceId); if (IsEqualPropertyKey(PKEY_AudioEngine_DeviceFormat, key)) { MP_VERBOSE(change->ao, "Changed property: PKEY_AudioEngine_DeviceFormat " "- requesting ao reload\n"); ao_request_reload(change->ao); } else { MP_VERBOSE(ao, "Changed property: %s\n", mp_PKEY_to_str(&key)); } } return S_OK; }
// Common code for handling ENODEV, which happens if a device gets "lost", and // can't be used anymore. Returns true if alsa_err is not ENODEV. static bool check_device_present(struct ao *ao, int alsa_err) { struct priv *p = ao->priv; if (alsa_err != -ENODEV) return true; if (!p->device_lost) { MP_WARN(ao, "Device lost, trying to recover...\n"); ao_request_reload(ao); p->device_lost = true; } return false; }
/* maybe MPV can go over to the prefered device once it is plugged in? */ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved( IMMNotificationClient *This, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) { MP_VERBOSE(ao, "OnDeviceRemoved triggered - requesting ao reload\n"); ao_request_reload(ao); } return S_OK; }
static void stream_state_cb(pa_stream *s, void *userdata) { struct ao *ao = userdata; struct priv *priv = ao->priv; switch (pa_stream_get_state(s)) { case PA_STREAM_FAILED: MP_VERBOSE(ao, "Stream failed.\n"); ao_request_reload(ao); pa_threaded_mainloop_signal(priv->mainloop, 0); break; case PA_STREAM_READY: case PA_STREAM_TERMINATED: pa_threaded_mainloop_signal(priv->mainloop, 0); break; } }
static void thread_feed(struct ao *ao) { struct wasapi_state *state = ao->priv; HRESULT hr; UINT32 frame_count = state->bufferFrameCount; if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) { UINT32 padding = 0; hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding); EXIT_ON_ERROR(hr); frame_count -= padding; MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n", frame_count, padding); } double delay_us; hr = get_device_delay(state, &delay_us); EXIT_ON_ERROR(hr); // add the buffer delay delay_us += frame_count * 1e6 / state->format.Format.nSamplesPerSec; BYTE *pData; hr = IAudioRenderClient_GetBuffer(state->pRenderClient, frame_count, &pData); EXIT_ON_ERROR(hr); BYTE *data[1] = {pData}; ao_read_data(ao, (void **)data, frame_count, mp_time_us() + (int64_t)llrint(delay_us)); // note, we can't use ao_read_data return value here since we already // commited to frame_count above in the GetBuffer call hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient, frame_count, 0); EXIT_ON_ERROR(hr); atomic_fetch_add(&state->sample_count, frame_count); return; exit_label: MP_ERR(state, "Error feeding audio: %s\n", mp_HRESULT_to_str(hr)); MP_VERBOSE(ao, "Requesting ao reload\n"); ao_request_reload(ao); return; }
// maybe MPV can go over to the prefered device once it is plugged in? static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved( IMMNotificationClient *This, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDeviceRemoved triggered: sending hotplug event\n"); ao_hotplug_event(ao); } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { MP_VERBOSE(ao, "OnDeviceRemoved triggered for device %S: " "requesting ao reload\n", pwstrDeviceId); ao_request_reload(ao); } return S_OK; }
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged( IMMNotificationClient *This, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; // don't care about "eCapture" or non-"eMultimedia" roles if (flow == eCapture || role != eMultimedia) return S_OK; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: sending hotplug event\n"); ao_hotplug_event(ao); } else { // stay on the device the user specified bstr device = wasapi_get_specified_device_string(ao); if (device.len) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " "staying on specified device %.*s\n", BSTR_P(device)); return S_OK; } // don't reload if already on the new default if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " "already using default device, no reload required\n"); return S_OK; } // if we got here, we need to reload MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: requesting ao reload\n"); ao_request_reload(ao); } return S_OK; }
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged( IMMNotificationClient *This, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; struct wasapi_state *state = ao->priv; /* don't care about "eCapture" or non-"eMultimedia" roles */ if (flow == eCapture || role != eMultimedia) return S_OK; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: sending hotplug event\n"); ao_hotplug_event(ao); } else { /* stay on the device the user specified */ if (state->opt_device) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " "staying on specified device %s\n", state->opt_device); return S_OK; } /* don't reload if already on the new default */ if (pwstrDeviceId && !lstrcmpW(pwstrDeviceId, change->monitored)) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " "already using default device, no reload required\n"); return S_OK; } /* if we got here, we need to reload */ MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: requesting ao reload\n"); ao_request_reload(ao); } return S_OK; }
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged( IMMNotificationClient *This, LPCWSTR pwstrDeviceId, DWORD dwNewState) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) { switch (dwNewState) { case DEVICE_STATE_DISABLED: case DEVICE_STATE_NOTPRESENT: case DEVICE_STATE_UNPLUGGED: MP_VERBOSE(ao, "OnDeviceStateChange triggered - requesting ao reload\n"); ao_request_reload(ao); case DEVICE_STATE_ACTIVE: default: return S_OK; } } return S_OK; }