diff options
Diffstat (limited to 'audio/out/ao_wasapi.c')
-rw-r--r-- | audio/out/ao_wasapi.c | 147 |
1 files changed, 96 insertions, 51 deletions
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c index da4a937c7d..34c3b6193f 100644 --- a/audio/out/ao_wasapi.c +++ b/audio/out/ao_wasapi.c @@ -36,7 +36,8 @@ static UINT64 uint64_scale(UINT64 x, UINT64 num, UINT64 den) + ((x % den) * (num % den)) / den; } -static HRESULT get_device_delay(struct wasapi_state *state, double *delay_us) { +static HRESULT get_device_delay(struct wasapi_state *state, double *delay_ns) +{ UINT64 sample_count = atomic_load(&state->sample_count); UINT64 position, qpc_position; HRESULT hr; @@ -54,7 +55,7 @@ static HRESULT get_device_delay(struct wasapi_state *state, double *delay_us) { state->format.Format.nSamplesPerSec, state->clock_frequency); INT64 diff = sample_count - sample_position; - *delay_us = diff * 1e6 / state->format.Format.nSamplesPerSec; + *delay_ns = diff * 1e9 / state->format.Format.nSamplesPerSec; // Correct for any delay in IAudioClock_GetPosition above. // This should normally be very small (<1 us), but just in case. . . @@ -65,16 +66,16 @@ static HRESULT get_device_delay(struct wasapi_state *state, double *delay_us) { // ignore the above calculation if it yields more than 10 seconds (due to // possible overflow inside IAudioClock_GetPosition) if (qpc_diff < 10 * 10000000) { - *delay_us -= qpc_diff / 10.0; // convert to us + *delay_ns -= qpc_diff * 100.0; // convert to ns } else { MP_VERBOSE(state, "Insane qpc delay correction of %g seconds. " "Ignoring it.\n", qpc_diff / 10000000.0); } - if (sample_count > 0 && *delay_us <= 0) { - MP_WARN(state, "Under-run: Device delay: %g us\n", *delay_us); + if (sample_count > 0 && *delay_ns <= 0) { + MP_WARN(state, "Under-run: Device delay: %g ns\n", *delay_ns); } else { - MP_TRACE(state, "Device delay: %g us\n", *delay_us); + MP_TRACE(state, "Device delay: %g ns\n", *delay_ns); } return S_OK; @@ -116,11 +117,11 @@ static bool thread_feed(struct ao *ao) MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n", frame_count, padding); - double delay_us; - hr = get_device_delay(state, &delay_us); + double delay_ns; + hr = get_device_delay(state, &delay_ns); EXIT_ON_ERROR(hr); // add the buffer delay - delay_us += frame_count * 1e6 / state->format.Format.nSamplesPerSec; + delay_ns += frame_count * 1e9 / state->format.Format.nSamplesPerSec; BYTE *pData; hr = IAudioRenderClient_GetBuffer(state->pRenderClient, @@ -131,7 +132,7 @@ static bool thread_feed(struct ao *ao) ao_read_data_converted(ao, &state->convert_format, (void **)data, frame_count, - mp_time_us() + (int64_t)llrint(delay_us)); + mp_time_ns() + (int64_t)llrint(delay_ns)); // note, we can't use ao_read_data return value here since we already // committed to frame_count above in the GetBuffer call @@ -149,14 +150,32 @@ exit_label: return false; } +static void thread_pause(struct ao *ao) +{ + struct wasapi_state *state = ao->priv; + MP_DBG(state, "Thread Pause\n"); + HRESULT hr = IAudioClient_Stop(state->pAudioClient); + if (FAILED(hr)) + MP_ERR(state, "IAudioClient_Stop returned: %s\n", mp_HRESULT_to_str(hr)); +} + +static void thread_unpause(struct ao *ao) +{ + struct wasapi_state *state = ao->priv; + MP_DBG(state, "Thread Unpause\n"); + HRESULT hr = IAudioClient_Start(state->pAudioClient); + if (FAILED(hr)) { + MP_ERR(state, "IAudioClient_Start returned %s\n", + mp_HRESULT_to_str(hr)); + } +} + static void thread_reset(struct ao *ao) { struct wasapi_state *state = ao->priv; HRESULT hr; MP_DBG(state, "Thread Reset\n"); - hr = IAudioClient_Stop(state->pAudioClient); - if (FAILED(hr)) - MP_ERR(state, "IAudioClient_Stop returned: %s\n", mp_HRESULT_to_str(hr)); + thread_pause(ao); hr = IAudioClient_Reset(state->pAudioClient); if (FAILED(hr)) @@ -171,19 +190,14 @@ static void thread_resume(struct ao *ao) MP_DBG(state, "Thread Resume\n"); thread_reset(ao); thread_feed(ao); - - HRESULT hr = IAudioClient_Start(state->pAudioClient); - if (FAILED(hr)) { - MP_ERR(state, "IAudioClient_Start returned %s\n", - mp_HRESULT_to_str(hr)); - } + thread_unpause(ao); } static void thread_wakeup(void *ptr) { struct ao *ao = ptr; struct wasapi_state *state = ao->priv; - SetEvent(state->hWake); + SetEvent(state->hUserWake); } static void set_thread_state(struct ao *ao, @@ -198,7 +212,7 @@ static DWORD __stdcall AudioThread(void *lpParameter) { struct ao *ao = lpParameter; struct wasapi_state *state = ao->priv; - mpthread_set_name("wasapi event"); + mp_thread_set_name("ao/wasapi"); CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); state->init_ok = wasapi_thread_init(ao); @@ -208,17 +222,25 @@ static DWORD __stdcall AudioThread(void *lpParameter) MP_DBG(ao, "Entering dispatch loop\n"); while (true) { - if (WaitForSingleObject(state->hWake, INFINITE) != WAIT_OBJECT_0) - MP_ERR(ao, "Unexpected return value from WaitForSingleObject\n"); + HANDLE handles[] = {state->hWake, state->hUserWake}; + switch (WaitForMultipleObjects(MP_ARRAY_SIZE(handles), handles, FALSE, INFINITE)) { + case WAIT_OBJECT_0: + // fill twice on under-full buffer (see comment in thread_feed) + if (thread_feed(ao) && thread_feed(ao)) + MP_ERR(ao, "Unable to fill buffer fast enough\n"); + continue; + case WAIT_OBJECT_0 + 1: + break; + default: + MP_ERR(ao, "Unexpected return value from WaitForMultipleObjects\n"); + break; + } mp_dispatch_queue_process(state->dispatch, 0); int thread_state = atomic_load(&state->thread_state); switch (thread_state) { case WASAPI_THREAD_FEED: - // fill twice on under-full buffer (see comment in thread_feed) - if (thread_feed(ao) && thread_feed(ao)) - MP_ERR(ao, "Unable to fill buffer fast enough\n"); break; case WASAPI_THREAD_RESET: thread_reset(ao); @@ -229,6 +251,12 @@ static DWORD __stdcall AudioThread(void *lpParameter) case WASAPI_THREAD_SHUTDOWN: thread_reset(ao); goto exit_label; + case WASAPI_THREAD_PAUSE: + thread_pause(ao); + break; + case WASAPI_THREAD_UNPAUSE: + thread_unpause(ao); + break; default: MP_ERR(ao, "Unhandled thread state: %d\n", thread_state); } @@ -248,7 +276,7 @@ static void uninit(struct ao *ao) { MP_DBG(ao, "Uninit wasapi\n"); struct wasapi_state *state = ao->priv; - if (state->hWake) + if (state->hWake && state->hUserWake) set_thread_state(ao, WASAPI_THREAD_SHUTDOWN); if (state->hAudioThread && @@ -260,6 +288,7 @@ static void uninit(struct ao *ao) SAFE_DESTROY(state->hInitDone, CloseHandle(state->hInitDone)); SAFE_DESTROY(state->hWake, CloseHandle(state->hWake)); + SAFE_DESTROY(state->hUserWake, CloseHandle(state->hUserWake)); SAFE_DESTROY(state->hAudioThread,CloseHandle(state->hAudioThread)); wasapi_change_uninit(ao); @@ -293,7 +322,8 @@ static int init(struct ao *ao) state->hInitDone = CreateEventW(NULL, FALSE, FALSE, NULL); state->hWake = CreateEventW(NULL, FALSE, FALSE, NULL); - if (!state->hInitDone || !state->hWake) { + state->hUserWake = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!state->hInitDone || !state->hWake || !state->hUserWake) { MP_FATAL(ao, "Error creating events\n"); uninit(ao); return -1; @@ -348,13 +378,10 @@ static int thread_control_exclusive(struct ao *ao, enum aocontrol cmd, void *arg case AOCONTROL_GET_VOLUME: IAudioEndpointVolume_GetMasterVolumeLevelScalar( state->pEndpointVolume, &volume); - *(ao_control_vol_t *)arg = (ao_control_vol_t){ - .left = 100.0f * volume, - .right = 100.0f * volume, - }; + *(float *)arg = volume * 100.f; return CONTROL_OK; case AOCONTROL_SET_VOLUME: - volume = ((ao_control_vol_t *)arg)->left / 100.f; + volume = (*(float *)arg) / 100.f; IAudioEndpointVolume_SetMasterVolumeLevelScalar( state->pEndpointVolume, volume, NULL); return CONTROL_OK; @@ -381,13 +408,10 @@ static int thread_control_shared(struct ao *ao, enum aocontrol cmd, void *arg) switch(cmd) { case AOCONTROL_GET_VOLUME: ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume, &volume); - *(ao_control_vol_t *)arg = (ao_control_vol_t){ - .left = 100.0f * volume, - .right = 100.0f * volume, - }; + *(float *)arg = volume * 100.f; return CONTROL_OK; case AOCONTROL_SET_VOLUME: - volume = ((ao_control_vol_t *)arg)->left / 100.f; + volume = (*(float *)arg) / 100.f; ISimpleAudioVolume_SetMasterVolume(state->pAudioVolume, volume, NULL); return CONTROL_OK; case AOCONTROL_GET_MUTE: @@ -412,20 +436,28 @@ static int thread_control(struct ao *ao, enum aocontrol cmd, void *arg) if (!state->pSessionControl) 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->pSessionControl, title, NULL); - - SAFE_DESTROY(tmp, CoTaskMemFree(tmp)); - IAudioSessionControl_GetDisplayName(state->pSessionControl, &tmp); - } while (wcscmp(title, tmp)); - SAFE_DESTROY(tmp, CoTaskMemFree(tmp)); + wchar_t *title = mp_from_utf8(NULL, (const char *)arg); + HRESULT hr = IAudioSessionControl_SetDisplayName(state->pSessionControl, + title,NULL); talloc_free(title); - return CONTROL_OK; + + if (SUCCEEDED(hr)) + return CONTROL_OK; + + MP_WARN(ao, "Error setting audio session name: %s\n", + mp_HRESULT_to_str(hr)); + + assert(ao->client_name); + if (!ao->client_name) + return CONTROL_ERROR; + + // Fallback to client name + title = mp_from_utf8(NULL, ao->client_name); + IAudioSessionControl_SetDisplayName(state->pSessionControl, + title, NULL); + talloc_free(title); + + return CONTROL_ERROR; } return state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE ? @@ -461,6 +493,12 @@ static void audio_resume(struct ao *ao) set_thread_state(ao, WASAPI_THREAD_RESUME); } +static bool audio_set_pause(struct ao *ao, bool paused) +{ + set_thread_state(ao, paused ? WASAPI_THREAD_PAUSE : WASAPI_THREAD_UNPAUSE); + return true; +} + static void hotplug_uninit(struct ao *ao) { MP_DBG(ao, "Hotplug uninit\n"); @@ -494,8 +532,15 @@ const struct ao_driver audio_out_wasapi = { .control = control, .reset = audio_reset, .start = audio_resume, + .set_pause = audio_set_pause, .list_devs = wasapi_list_devs, .hotplug_init = hotplug_init, .hotplug_uninit = hotplug_uninit, .priv_size = sizeof(wasapi_state), + .options_prefix = "wasapi", + .options = (const struct m_option[]) { + {"exclusive-buffer", OPT_CHOICE(opt_exclusive_buffer, + {"default", 0}, {"min", -1}), M_RANGE(1, 2000000)}, + {0} + }, }; |