static int control(struct ao *ao, enum aocontrol cmd, void *arg) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; ao_control_vol_t *vol = (ao_control_vol_t *)arg; BOOL mute; switch (cmd) { case AOCONTROL_GET_VOLUME: if (state->opt_exclusive) IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolumeProxy, &state->audio_volume); else ISimpleAudioVolume_GetMasterVolume(state->pAudioVolumeProxy, &state->audio_volume); /* check to see if user manually changed volume through mixer; this information is used in exclusive mode for restoring the mixer volume on uninit */ if (state->audio_volume != state->previous_volume) { MP_VERBOSE(state, "mixer difference: %.2g now, expected %.2g\n", state->audio_volume, state->previous_volume); state->initial_volume = state->audio_volume; } vol->left = vol->right = 100.0f * state->audio_volume; return CONTROL_OK; case AOCONTROL_SET_VOLUME: state->audio_volume = vol->left / 100.f; if (state->opt_exclusive) IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolumeProxy, state->audio_volume, NULL); else ISimpleAudioVolume_SetMasterVolume(state->pAudioVolumeProxy, state->audio_volume, NULL); state->previous_volume = state->audio_volume; return CONTROL_OK; case AOCONTROL_GET_MUTE: if (state->opt_exclusive) IAudioEndpointVolume_GetMute(state->pEndpointVolumeProxy, &mute); else ISimpleAudioVolume_GetMute(state->pAudioVolumeProxy, &mute); *(bool*)arg = mute; return CONTROL_OK; case AOCONTROL_SET_MUTE: mute = *(bool*)arg; if (state->opt_exclusive) IAudioEndpointVolume_SetMute(state->pEndpointVolumeProxy, mute, NULL); else ISimpleAudioVolume_SetMute(state->pAudioVolumeProxy, mute, NULL); return CONTROL_OK; case AOCONTROL_HAS_PER_APP_VOLUME: return CONTROL_TRUE; case AOCONTROL_UPDATE_STREAM_TITLE: { MP_VERBOSE(state, "Updating stream title to \"%s\"\n", (char*)arg); wchar_t *title = mp_from_utf8(NULL, (char*)arg); wchar_t *tmp = NULL; /* There is a weird race condition in the IAudioSessionControl itself -- it seems that *sometimes* the SetDisplayName does not take effect and it still shows the old title. Use this loop to insist until it works. */ do { IAudioSessionControl_SetDisplayName(state->pSessionControlProxy, title, NULL); SAFE_RELEASE(tmp, CoTaskMemFree(tmp)); IAudioSessionControl_GetDisplayName(state->pSessionControlProxy, &tmp); } while (lstrcmpW(title, tmp)); SAFE_RELEASE(tmp, CoTaskMemFree(tmp)); talloc_free(title); return CONTROL_OK; } default: return CONTROL_UNKNOWN; } }
static int control_exclusive(struct ao *ao, enum aocontrol cmd, void *arg) { struct wasapi_state *state = ao->priv; switch (cmd) { case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: if (!state->pEndpointVolumeProxy || !(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_VOLUME)) { return CONTROL_FALSE; } float volume; switch (cmd) { case AOCONTROL_GET_VOLUME: IAudioEndpointVolume_GetMasterVolumeLevelScalar( state->pEndpointVolumeProxy, &volume); *(ao_control_vol_t *)arg = (ao_control_vol_t){ .left = 100.0f * volume, .right = 100.0f * volume, }; return CONTROL_OK; case AOCONTROL_SET_VOLUME: volume = ((ao_control_vol_t *)arg)->left / 100.f; IAudioEndpointVolume_SetMasterVolumeLevelScalar( state->pEndpointVolumeProxy, volume, NULL); return CONTROL_OK; } case AOCONTROL_GET_MUTE: case AOCONTROL_SET_MUTE: if (!state->pEndpointVolumeProxy || !(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_MUTE)) { return CONTROL_FALSE; } BOOL mute; switch (cmd) { case AOCONTROL_GET_MUTE: IAudioEndpointVolume_GetMute(state->pEndpointVolumeProxy, &mute); *(bool *)arg = mute; return CONTROL_OK; case AOCONTROL_SET_MUTE: mute = *(bool *)arg; IAudioEndpointVolume_SetMute(state->pEndpointVolumeProxy, mute, NULL); return CONTROL_OK; } case AOCONTROL_HAS_PER_APP_VOLUME: return CONTROL_FALSE; default: return CONTROL_UNKNOWN; } } static int control_shared(struct ao *ao, enum aocontrol cmd, void *arg) { struct wasapi_state *state = ao->priv; if (!state->pAudioVolumeProxy) return CONTROL_UNKNOWN; float volume; BOOL mute; switch(cmd) { case AOCONTROL_GET_VOLUME: ISimpleAudioVolume_GetMasterVolume(state->pAudioVolumeProxy, &volume); *(ao_control_vol_t *)arg = (ao_control_vol_t){ .left = 100.0f * volume, .right = 100.0f * volume, }; return CONTROL_OK; case AOCONTROL_SET_VOLUME: volume = ((ao_control_vol_t *)arg)->left / 100.f; ISimpleAudioVolume_SetMasterVolume(state->pAudioVolumeProxy, volume, NULL); return CONTROL_OK; case AOCONTROL_GET_MUTE: ISimpleAudioVolume_GetMute(state->pAudioVolumeProxy, &mute); *(bool *)arg = mute; return CONTROL_OK; case AOCONTROL_SET_MUTE: mute = *(bool *)arg; ISimpleAudioVolume_SetMute(state->pAudioVolumeProxy, mute, NULL); return CONTROL_OK; case AOCONTROL_HAS_PER_APP_VOLUME: return CONTROL_TRUE; default: return CONTROL_UNKNOWN; } } static int control(struct ao *ao, enum aocontrol cmd, void *arg) { struct wasapi_state *state = ao->priv; // common to exclusive and shared switch (cmd) { case AOCONTROL_UPDATE_STREAM_TITLE: if (!state->pSessionControlProxy) return CONTROL_FALSE; wchar_t *title = mp_from_utf8(NULL, (char*)arg); wchar_t *tmp = NULL; // There is a weird race condition in the IAudioSessionControl itself -- // it seems that *sometimes* the SetDisplayName does not take effect and // it still shows the old title. Use this loop to insist until it works. do { IAudioSessionControl_SetDisplayName(state->pSessionControlProxy, title, NULL); SAFE_RELEASE(tmp, CoTaskMemFree(tmp)); IAudioSessionControl_GetDisplayName(state->pSessionControlProxy, &tmp); } while (lstrcmpW(title, tmp)); SAFE_RELEASE(tmp, CoTaskMemFree(tmp)); talloc_free(title); return CONTROL_OK; } return state->opt_exclusive ? control_exclusive(ao, cmd, arg) : control_shared(ao, cmd, arg); } static void audio_reset(struct ao *ao) { set_thread_state(ao, WASAPI_THREAD_RESET); } static void audio_resume(struct ao *ao) { set_thread_state(ao, WASAPI_THREAD_RESUME); }