1
0
mirror of https://github.com/mpv-player/mpv synced 2025-03-11 08:37:59 +00:00

ao/wasapi: rewrite format search

More clearly separate the exclusive and shared mode format discovery.
Make the exclusive mode search more systematic in particular about
channel maps (i.e., use chmap_sel). Assume that the same sample format
/ sample rates work for all channels to narrow the search space.
This commit is contained in:
Kevin Mitchell 2015-03-22 02:19:36 -07:00
parent fb61858b63
commit 41c10c3ec2
2 changed files with 307 additions and 189 deletions

View File

@ -248,13 +248,6 @@ static int init(struct ao *ao)
MP_DBG(ao, "Init wasapi\n"); MP_DBG(ao, "Init wasapi\n");
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
ao->format = af_fmt_from_planar(ao->format);
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_waveext(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
MP_ERR(ao, "Error adjusting channel map to waveext channel order\n");
return -1;
}
struct wasapi_state *state = (struct wasapi_state *)ao->priv; struct wasapi_state *state = (struct wasapi_state *)ao->priv;
state->log = ao->log; state->log = ao->log;
if(!wasapi_fill_VistaBlob(state)) if(!wasapi_fill_VistaBlob(state))

View File

@ -146,24 +146,60 @@ const char *wasapi_explain_err(const HRESULT hr)
#undef E #undef E
} }
static void update_waveformat_datarate_pcm(WAVEFORMATEXTENSIBLE *wformat)
{
WAVEFORMATEX *wf = &wformat->Format;
wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample / 8;
wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign;
}
static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat, static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
WORD bits, WORD valid_bits, bool is_float, WORD bits, WORD valid_bits, bool is_float,
DWORD samplerate, struct mp_chmap *channels) DWORD samplerate, struct mp_chmap *channels)
{ {
int block_align = channels->num * bits / 8;
wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wformat->Format.nChannels = channels->num; wformat->Format.nChannels = channels->num;
wformat->Format.nSamplesPerSec = samplerate; wformat->Format.nSamplesPerSec = samplerate;
wformat->Format.nAvgBytesPerSec = samplerate * block_align;
wformat->Format.nBlockAlign = block_align;
wformat->Format.wBitsPerSample = bits; wformat->Format.wBitsPerSample = bits;
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
if (is_float) if (is_float) {
wformat->SubFormat = mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; wformat->SubFormat = mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else } else {
wformat->SubFormat = mp_KSDATAFORMAT_SUBTYPE_PCM; wformat->SubFormat = mp_KSDATAFORMAT_SUBTYPE_PCM;
}
wformat->Samples.wValidBitsPerSample = valid_bits; wformat->Samples.wValidBitsPerSample = valid_bits;
wformat->dwChannelMask = mp_chmap_to_waveext(channels); wformat->dwChannelMask = mp_chmap_to_waveext(channels);
update_waveformat_datarate_pcm(wformat);
}
static void set_waveformat_with_ao(WAVEFORMATEXTENSIBLE *wformat, struct ao *ao)
{
// This implicitly transforms all formats to:
// interleaved / signed (except for 8 bit) / waveext channel order.
// You must still call set_ao_format() to ensure consistency.
struct mp_chmap channels = ao->channels;
mp_chmap_reorder_to_waveext(&channels);
set_waveformat(wformat, af_fmt2bits(ao->format), af_fmt2bits(ao->format),
af_fmt_is_float(ao->format), ao->samplerate, &channels);
}
static void change_waveformat_samplerate(WAVEFORMATEXTENSIBLE *wformat,
DWORD samplerate)
{
// other wformat parameters must already be set with set_waveformat
wformat->Format.nSamplesPerSec = samplerate;
update_waveformat_datarate_pcm(wformat);
}
static void change_waveformat_channels(WAVEFORMATEXTENSIBLE *wformat,
struct mp_chmap *channels)
{
// other wformat parameters must already be set with set_waveformat
wformat->Format.nChannels = channels->num;
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
update_waveformat_datarate_pcm(wformat);
} }
static WORD waveformat_valid_bits(const WAVEFORMATEX *wf) static WORD waveformat_valid_bits(const WAVEFORMATEX *wf)
@ -176,41 +212,6 @@ static WORD waveformat_valid_bits(const WAVEFORMATEX *wf)
} }
} }
static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
{
char* type = "?";
switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE:
{
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
if (IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
&wformat->SubFormat))
type = "float";
else if (IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_PCM,
&wformat->SubFormat))
type = "s";
break;
}
case WAVE_FORMAT_IEEE_FLOAT:
type = "float";
break;
case WAVE_FORMAT_PCM:
type = "s";
break;
}
int valid_bits = waveformat_valid_bits(wf);
if (valid_bits == wf->wBitsPerSample) {
snprintf(buf, buf_size, "%"PRIu16"ch %s%"PRIu16" @ %"PRIu32"hz",
wf->nChannels, type, valid_bits, (unsigned) wf->nSamplesPerSec);
} else {
snprintf(buf, buf_size, "%"PRIu16"ch %s%"PRIu16" (in %s%"PRIu16") @ %"PRIu32"hz",
wf->nChannels, type, valid_bits, type, wf->wBitsPerSample,
(unsigned) wf->nSamplesPerSec);
}
return buf;
}
#define waveformat_to_str(wf) waveformat_to_str_buf((char[40]){0}, 40, (wf))
static bool waveformat_is_float(WAVEFORMATEX *wf) static bool waveformat_is_float(WAVEFORMATEX *wf)
{ {
switch (wf->wFormatTag) { switch (wf->wFormatTag) {
@ -226,7 +227,7 @@ static bool waveformat_is_float(WAVEFORMATEX *wf)
} }
} }
static bool waveformat_is_pcm(WAVEFORMATEX *wf) static bool waveformat_is_pcm_int(WAVEFORMATEX *wf)
{ {
switch (wf->wFormatTag) { switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE: case WAVE_FORMAT_EXTENSIBLE:
@ -241,6 +242,67 @@ static bool waveformat_is_pcm(WAVEFORMATEX *wf)
} }
} }
static int format_from_waveformat(WAVEFORMATEX *wf)
{
int format = 0;
// it is an undocumented fact that 8-bit pcm in WAVEFORMATEX implies unsigned
if (waveformat_is_float(wf)) {
format = AF_FORMAT_FLOAT;
} else if (waveformat_is_pcm_int(wf)) {
format = wf->wBitsPerSample == 8 ? AF_FORMAT_U8 : AF_FORMAT_S32;
} else {
return 0;
}
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff538802%28v=vs.85%29.aspx:
// Since mpv doesn't have the notion of "valid bits", we just specify a
// format with the container size. The least significant, "invalid"
// bits will be excess precision ignored by wasapi.
return af_fmt_change_bits(format, wf->wBitsPerSample);
}
static bool chmap_from_waveformat(struct mp_chmap *channels, const WAVEFORMATEX *wf)
{
if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
mp_chmap_from_waveext(channels, wformat->dwChannelMask);
} else {
mp_chmap_from_channels(channels, wf->nChannels);
}
if (channels->num != wf->nChannels) {
mp_chmap_from_str(channels, bstr0("empty"));
return false;
}
return true;
}
static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
{
char* type = "?";
if (waveformat_is_float(wf)) {
type = "float";
} else if (waveformat_is_pcm_int(wf)) {
type = wf->wBitsPerSample == 8 ? "u" : "s";
}
unsigned valid_bits = waveformat_valid_bits(wf);
struct mp_chmap channels;
chmap_from_waveformat(&channels, wf);
if (valid_bits == wf->wBitsPerSample) {
snprintf(buf, buf_size, "%s %s%u @ %uhz",
mp_chmap_to_str(&channels), type, valid_bits,
(unsigned) wf->nSamplesPerSec);
} else {
snprintf(buf, buf_size, "%s %s%u (in %s%u) @ %uhz",
mp_chmap_to_str(&channels), type, valid_bits,
type, (unsigned) wf->wBitsPerSample,
(unsigned) wf->nSamplesPerSec);
}
return buf;
}
#define waveformat_to_str(wf) waveformat_to_str_buf((char[40]){0}, 40, (wf))
static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src) static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
{ {
if (src->wFormatTag == WAVE_FORMAT_EXTENSIBLE) if (src->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
@ -252,110 +314,230 @@ static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf) static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf)
{ {
struct wasapi_state *state = (struct wasapi_state *)ao->priv; struct wasapi_state *state = (struct wasapi_state *)ao->priv;
// explicitly disallow 8 bits - this will catch if the
// closestMatch returned by IsFormatSupported in try_format below returns
// a bit depth of less than 16
if (wf->wBitsPerSample < 16)
return false;
int format; int format = format_from_waveformat(wf);
if (waveformat_is_float(wf)) { if (!format) {
format = AF_FORMAT_FLOAT; MP_ERR(ao, "Unable to construct sample format from WAVEFORMAT %s\n",
} else if (waveformat_is_pcm(wf)) { waveformat_to_str(wf));
format = AF_FORMAT_S32;
} else {
MP_ERR(ao, "Unknown WAVEFORMAT\n");
return false; return false;
} }
// set the correct number of bits (the 32-bits assumed above may be wrong) struct mp_chmap channels;
format = af_fmt_change_bits(format, wf->wBitsPerSample); if (!chmap_from_waveformat(&channels, wf)) {
if (!format) MP_ERR(ao, "Unable to construct channel map from WAVEFORMAT %s\n",
waveformat_to_str(wf));
return false; return false;
}
ao->samplerate = wf->nSamplesPerSec; ao->samplerate = wf->nSamplesPerSec;
ao->bps = wf->nAvgBytesPerSec; ao->bps = wf->nAvgBytesPerSec;
ao->format = format; ao->format = format;
ao->channels = channels;
if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
mp_chmap_from_waveext(&ao->channels, wformat->dwChannelMask);
} else {
mp_chmap_from_channels(&ao->channels, wf->nChannels);
}
if (ao->channels.num != wf->nChannels) {
MP_ERR(ao, "Channel map doesn't match number of channels\n");
return false;
}
waveformat_copy(&state->format, wf); waveformat_copy(&state->format, wf);
return true; return true;
} }
static bool try_format(struct ao *ao, static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
int bits, int valid_bits, bool is_float,
int samplerate, struct mp_chmap *channels)
{ {
struct wasapi_state *state = (struct wasapi_state *)ao->priv; struct wasapi_state *state = (struct wasapi_state *)ao->priv;
/* If for some reason we get 8-bits, try 16-bit equivalent instead. MP_VERBOSE(ao, "Trying %s\n", waveformat_to_str(&wformat->Format));
This can happen if 8-bits is initially requested in ao->format
or in the unlikely event that GetMixFormat return 8-bits.
If someone with a "vintage" sound card or something complains
this can be reconsidered */
bits = FFMAX(16, bits);
WAVEFORMATEXTENSIBLE wformat;
set_waveformat(&wformat, bits, valid_bits, is_float, samplerate, channels);
MP_VERBOSE(ao, "Trying %s\n", waveformat_to_str(&wformat.Format));
WAVEFORMATEX *closestMatch;
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient, HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
state->share_mode, AUDCLNT_SHAREMODE_EXCLUSIVE,
&wformat.Format, &closestMatch); &wformat->Format, NULL);
if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
EXIT_ON_ERROR(hr);
if (closestMatch) { return hr == S_OK;
waveformat_copy(&wformat, closestMatch); exit_label:
CoTaskMemFree(closestMatch); MP_ERR(state, "Error testing exclusive format: %s (0x%"PRIx32")\n",
} wasapi_explain_err(hr), (uint32_t) hr);
if (hr == S_FALSE) {
if (set_ao_format(ao, &wformat.Format)) {
MP_VERBOSE(ao, "Accepted as %dch %s @ %dhz\n",
ao->channels.num, af_fmt_to_str(ao->format), ao->samplerate);
return true;
}
} else if (hr == S_OK) {
if (set_ao_format(ao, &wformat.Format)) {
MP_VERBOSE(ao, "%dch %s @ %dhz accepted\n",
ao->channels.num, af_fmt_to_str(ao->format), samplerate);
return true;
}
}
return false; return false;
} }
static bool try_mix_format(struct ao *ao) static bool search_sample_formats(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat,
int samplerate, struct mp_chmap *channels)
{
// try float
int float_bits[] = {64, 32, 0};
for (int i = 0; float_bits[i]; i++) {
set_waveformat(wformat, float_bits[i], float_bits[i], true,
samplerate, channels);
if (try_format_exclusive(ao, wformat))
return true;
}
// try int
// some common bit depths / container sizes (requests welcome)
int bits[] = {32, 24, 32, 16, 0};
int valid_bits[] = {32, 24, 24, 16, 0};
for (int i = 0; bits[i] && valid_bits[i]; i++) {
set_waveformat(wformat, bits[i], valid_bits[i], false,
samplerate, channels);
if (try_format_exclusive(ao, wformat))
return true;
}
wformat->Format.wBitsPerSample = 0;
return false;
}
static bool search_samplerates(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat,
struct mp_chmap *channels)
{
// try list of typical sample rates (requests welcome)
int try[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000,
176400, 192000, 352800, 384000, 0};
// get a list of supported rates
int n = 0;
int supported[MP_ARRAY_SIZE(try)] = {0};
wformat->Format.wBitsPerSample = 0;
for (int i = 0; try[i]; i++) {
if (!wformat->Format.wBitsPerSample) {
if (search_sample_formats(ao, wformat, try[i], channels))
supported[n++] = try[i];
} else {
change_waveformat_samplerate(wformat, try[i]);
if (try_format_exclusive(ao, wformat))
supported[n++] = try[i];
}
}
for (int i = 0; supported[i]; i++) {
// first choose the lowest integer multiple of the sample rate
if (!(supported[i] % ao->samplerate)) {
change_waveformat_samplerate(wformat, supported[i]);
return true;
}
}
// then choose the highest supported (if any)
if (n) {
change_waveformat_samplerate(wformat, supported[n-1]);
return true;
}
// otherwise, this is probably an unsupported channel map
wformat->Format.nSamplesPerSec = 0;
return false;
}
static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{ {
struct wasapi_state *state = (struct wasapi_state *)ao->priv; struct wasapi_state *state = (struct wasapi_state *)ao->priv;
WAVEFORMATEX *wf = NULL; struct mp_chmap_sel chmap_sel = {.tmp = state};
HRESULT hr = IAudioClient_GetMixFormat(state->pAudioClient, &wf); struct mp_chmap entry;
EXIT_ON_ERROR(hr); // put common layouts first so that we find sample rate/format early
char *channel_layouts[] =
{"mono", "stereo", "2.1", "4.0", "5.0", "5.1", "6.1", "7.1",
"3.0", "3.0(back)",
"quad", "quad(side)", "3.1",
"5.0(side)", "4.1",
"5.1(side)", "6.0", "6.0(front)", "hexagonal"
"6.1(back)", "6.1(front)", "7.0", "7.0(front)",
"7.1(wide)", "7.1(wide-side)", "7.1(rear)", "octagonal", NULL};
bool ret = try_format(ao, wf->wBitsPerSample, waveformat_valid_bits(wf), wformat->Format.nSamplesPerSec = 0;
waveformat_is_float(wf), wf->nSamplesPerSec, &ao->channels); for (int j = 0; channel_layouts[j]; j++) {
mp_chmap_from_str(&entry, bstr0(channel_layouts[j]));
if (!wformat->Format.nSamplesPerSec) {
if (search_samplerates(ao, wformat, &entry)) {
mp_chmap_sel_add_map(&chmap_sel, &entry);
MP_VERBOSE(ao, "%s is supported\n", waveformat_to_str(&wformat->Format));
}
} else {
change_waveformat_channels(wformat, &entry);
if (try_format_exclusive(ao, wformat)) {
mp_chmap_sel_add_map(&chmap_sel, &entry);
MP_VERBOSE(ao, "%s is supported\n", mp_chmap_to_str(&entry));
}
}
}
SAFE_RELEASE(wf, CoTaskMemFree(wf)); entry = ao->channels;
return ret; if (ao_chmap_sel_adjust(ao, &chmap_sel, &entry)){
change_waveformat_channels(wformat, &entry);
return true;
}
MP_ERR(ao, "No suitable audio format found\n");
return false;
}
static bool find_formats_exclusive(struct ao *ao)
{
WAVEFORMATEXTENSIBLE wformat;
set_waveformat_with_ao(&wformat, ao);
// If the format doesn't work as is, we have to manually try
// all possible formats in search_channels(). Nice API Microsoft.
if (!try_format_exclusive(ao, &wformat) && !search_channels(ao, &wformat))
return false;
if (!set_ao_format(ao, &wformat.Format))
return false;
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate);
return true;
}
static bool find_formats_shared(struct ao *ao)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
WAVEFORMATEXTENSIBLE wformat;
set_waveformat_with_ao(&wformat, ao);
MP_VERBOSE(ao, "Trying %s\n", waveformat_to_str(&wformat.Format));
WAVEFORMATEX *closestMatch;
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
AUDCLNT_SHAREMODE_SHARED,
&wformat.Format, &closestMatch);
if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
EXIT_ON_ERROR(hr);
switch (hr) {
case S_OK:
break;
case S_FALSE:
waveformat_copy(&wformat, closestMatch);
CoTaskMemFree(closestMatch);
MP_VERBOSE(ao, "Closest match is %s\n",
waveformat_to_str(&wformat.Format));
break;
default:
hr = IAudioClient_GetMixFormat(state->pAudioClient, &closestMatch);
EXIT_ON_ERROR(hr);
waveformat_copy(&wformat, closestMatch);
MP_VERBOSE(ao, "Fallback to mix format %s\n",
waveformat_to_str(&wformat.Format));
CoTaskMemFree(closestMatch);
}
if (!set_ao_format(ao, &wformat.Format))
return false;
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate);
return true;
exit_label: exit_label:
MP_ERR(state, "Error getting mix format: %s (0x%"PRIx32")\n", MP_ERR(state, "Error finding shared mode format: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t) hr); wasapi_explain_err(hr), (uint32_t) hr);
SAFE_RELEASE(wf, CoTaskMemFree(wf));
return false; return false;
} }
static bool try_passthrough(struct ao *ao) static bool try_passthrough(struct ao *ao)
{ {
// fixme: this will only do SPDIF AC3 and doesn't bother to check
// that the resulting waveformat is actually consistent with the ao
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd316761(v=vs.85).aspx
struct wasapi_state *state = (struct wasapi_state *)ao->priv; struct wasapi_state *state = (struct wasapi_state *)ao->priv;
WAVEFORMATEXTENSIBLE wformat = { WAVEFORMATEXTENSIBLE wformat = {
.Format = { .Format = {
.wFormatTag = WAVE_FORMAT_EXTENSIBLE, .wFormatTag = WAVE_FORMAT_EXTENSIBLE,
@ -378,29 +560,10 @@ static bool try_passthrough(struct ao *ao)
state->share_mode, state->share_mode,
&wformat.Format, NULL); &wformat.Format, NULL);
if (!FAILED(hr)) { if (!FAILED(hr)) {
ao->format = ao->format;
state->format = wformat; state->format = wformat;
return true; return true;
} }
return false; MP_ERR(ao, "Couldn't use passthrough\n");
}
static bool search_sample_formats(struct ao *ao, int samplerate, struct mp_chmap *channels)
{
// try float
for (int bits = 64; bits; bits -= 32) {
if (try_format(ao, bits, bits, true, samplerate, channels))
return true;
}
// try int
// some common bit depths / container sizes (requests welcome)
int bits[] = {32, 24, 32, 24, 32, 16};
int valid_bits[] = {32, 24, 24, 20, 20, 16};
for (int i = 0; i < MP_ARRAY_SIZE(bits); i++) {
if (try_format(ao, bits[i], valid_bits[i], false, samplerate, channels))
return true;
}
return false; return false;
} }
@ -408,56 +571,18 @@ static bool find_formats(struct ao *ao)
{ {
struct wasapi_state *state = (struct wasapi_state *)ao->priv; struct wasapi_state *state = (struct wasapi_state *)ao->priv;
if (AF_FORMAT_IS_IEC61937(ao->format)) { if (state->opt_exclusive){
if (try_passthrough(ao)) // https://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx
return true; // "Many audio devices support both PCM and non-PCM stream formats.
// However, the audio engine can mix only PCM streams. Thus, only
MP_ERR(ao, "Couldn't use passthrough"); // exclusive-mode streams can have non-PCM formats.
if (!state->opt_exclusive) if (AF_FORMAT_IS_IEC61937(ao->format))
MP_ERR(ao, " (try exclusive mode)"); return try_passthrough(ao);
MP_ERR(ao, "\n"); else
return false; return find_formats_exclusive(ao);
} }
/* See if the format works as-is */ return find_formats_shared(ao);
int bits = af_fmt2bits(ao->format);
if (try_format(ao, bits, bits, af_fmt_is_float(ao->format),
ao->samplerate, &ao->channels))
return true;
if (!state->opt_exclusive) {
/* shared mode, we can use the system default mix format. */
if (try_mix_format(ao))
return true;
MP_WARN(ao, "Couldn't use default mix format\n");
}
/* Exclusive mode, we have to guess. */
while (true) {
int samplerate = ao->samplerate;
// try integer multiples of requested sample rate first
while (samplerate <= 384000) {
if (search_sample_formats(ao, samplerate, &ao->channels))
return true;
samplerate += ao->samplerate;
}
// now try list of typical sample rates (requests welcome)
int samplerates[] = {384000, 352800, 192000, 176400, 96000, 88200,
48000, 44100, 32000, 22050, 16000, 11025,
8000};
for (int i = 0; i < MP_ARRAY_SIZE(samplerates); i++) {
if (search_sample_formats(ao, samplerates[i], &ao->channels))
return true;
}
if (ao->channels.num > 1) {
mp_chmap_from_channels(&ao->channels, ao->channels.num - 1);
} else {
MP_ERR(ao, "Couldn't find acceptable audio format\n");
return false;
}
}
} }
static HRESULT init_clock(struct wasapi_state *state) { static HRESULT init_clock(struct wasapi_state *state) {