ao_wasapi: try mix format except for chmap

In shared mode, we previously tried to feed the full native format to
IsFormatSupported in the hopes that the "closest match" returned was
actually that.

Turns out, IsFormatSupported will always return the mix format if we
don't use the mix format's sample rate. This will also clobber our
choice of channel map with the mix format channel map even if our
desired channel map is supported due to surround emulation.

The solution is to not bother trying to use anything other than the mix
format sample rate. While we're at it, we might as well use the mix
format PCM sample format (always float32) since this conversion will
happen anyway and may avoid unecessary dithering to intermediate
integer formats if we are already resampling or channel mixing.
This commit is contained in:
Kevin Mitchell 2020-03-19 00:45:30 -07:00 committed by Jan Ekström
parent 609d0ef478
commit 3aad89829f
1 changed files with 36 additions and 11 deletions

View File

@ -427,32 +427,57 @@ static bool find_formats_shared(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{ {
struct wasapi_state *state = ao->priv; struct wasapi_state *state = ao->priv;
WAVEFORMATEX *closestMatch; struct mp_chmap channels;
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient, if (!chmap_from_waveformat(&channels, &wformat->Format)) {
AUDCLNT_SHAREMODE_SHARED, MP_ERR(ao, "Error converting channel map\n");
&wformat->Format, return false;
&closestMatch); }
HRESULT hr;
WAVEFORMATEX *mix_format;
hr = IAudioClient_GetMixFormat(state->pAudioClient, &mix_format);
EXIT_ON_ERROR(hr);
// WASAPI doesn't do any sample rate conversion on its own and
// will typically only accept the mix format samplerate. Although
// it will accept any PCM sample format, everything gets converted
// to the mix format anyway (pretty much always float32), so just
// use that.
WAVEFORMATEXTENSIBLE try_format;
waveformat_copy(&try_format, mix_format);
CoTaskMemFree(mix_format);
// WASAPI may accept channel maps other than the mix format
// if a surround emulator is enabled.
change_waveformat_channels(&try_format, &channels);
hr = IAudioClient_IsFormatSupported(state->pAudioClient,
AUDCLNT_SHAREMODE_SHARED,
&try_format.Format,
&mix_format);
MP_VERBOSE(ao, "Trying %s (shared) -> %s\n", MP_VERBOSE(ao, "Trying %s (shared) -> %s\n",
waveformat_to_str(&wformat->Format), mp_format_res_str(hr)); waveformat_to_str(&try_format.Format), mp_format_res_str(hr));
if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
EXIT_ON_ERROR(hr); EXIT_ON_ERROR(hr);
switch (hr) { switch (hr) {
case S_OK: case S_OK:
waveformat_copy(wformat, &try_format.Format);
break; break;
case S_FALSE: case S_FALSE:
waveformat_copy(wformat, closestMatch); waveformat_copy(wformat, mix_format);
CoTaskMemFree(closestMatch); CoTaskMemFree(mix_format);
MP_VERBOSE(ao, "Closest match is %s\n", MP_VERBOSE(ao, "Closest match is %s\n",
waveformat_to_str(&wformat->Format)); waveformat_to_str(&wformat->Format));
break; break;
default: default:
hr = IAudioClient_GetMixFormat(state->pAudioClient, &closestMatch); hr = IAudioClient_GetMixFormat(state->pAudioClient, &mix_format);
EXIT_ON_ERROR(hr); EXIT_ON_ERROR(hr);
waveformat_copy(wformat, closestMatch); waveformat_copy(wformat, mix_format);
CoTaskMemFree(mix_format);
MP_VERBOSE(ao, "Fallback to mix format %s\n", MP_VERBOSE(ao, "Fallback to mix format %s\n",
waveformat_to_str(&wformat->Format)); waveformat_to_str(&wformat->Format));
CoTaskMemFree(closestMatch);
} }
return true; return true;