// other wformat parameters must already be set with set_waveformat static void change_waveformat_channels(WAVEFORMATEXTENSIBLE *wformat, struct mp_chmap *channels) { wformat->Format.nChannels = channels->num; wformat->dwChannelMask = mp_chmap_to_waveext(channels); update_waveformat_datarate(wformat); }
static int try_format(struct wasapi_state *state, struct ao *const ao, int bits, int samplerate, const struct mp_chmap channels) { WAVEFORMATEXTENSIBLE wformat; set_format(&wformat, bits / 8, samplerate, channels.num, mp_chmap_to_waveext(&channels)); int af_format = format_set_bits(ao->format, bits, bits == 32); EnterCriticalSection(&state->print_lock); mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: trying %dch %s @ %dhz\n", channels.num, af_fmt_to_str(af_format), samplerate); LeaveCriticalSection(&state->print_lock); union WAVEFMT u; u.extensible = &wformat; WAVEFORMATEX *closestMatch; HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient, state->share_mode, u.ex, &closestMatch); if (closestMatch) { if (closestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { u.ex = closestMatch; wformat = *u.extensible; } else { wformat.Format = *closestMatch; } CoTaskMemFree(closestMatch); } if (hr == S_FALSE) { if (set_ao_format(state, ao, wformat)) { EnterCriticalSection(&state->print_lock); mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: accepted as %dch %s @ %dhz\n", ao->channels.num, af_fmt_to_str(ao->format), ao->samplerate); LeaveCriticalSection(&state->print_lock); return 1; } } if (hr == S_OK || (!state->opt_exclusive && hr == AUDCLNT_E_UNSUPPORTED_FORMAT)) { // AUDCLNT_E_UNSUPPORTED_FORMAT here means "works in shared, doesn't in exclusive" if (set_ao_format(state, ao, wformat)) { EnterCriticalSection(&state->print_lock); mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: %dch %s @ %dhz accepted\n", ao->channels.num, af_fmt_to_str(af_format), samplerate); LeaveCriticalSection(&state->print_lock); return 1; } } return 0; }
static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat, int format, WORD valid_bits, DWORD samplerate, struct mp_chmap *channels) { wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wformat->Format.nChannels = channels->num; wformat->Format.nSamplesPerSec = samplerate; wformat->Format.wBitsPerSample = af_fmt_to_bytes(format) * 8; wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); wformat->SubFormat = *format_to_subtype(format); wformat->Samples.wValidBitsPerSample = valid_bits ? valid_bits : wformat->Format.wBitsPerSample; wformat->dwChannelMask = mp_chmap_to_waveext(channels); update_waveformat_datarate(wformat); }
/** \brief setup sound device \param rate samplerate \param channels number of channels \param format format \param flags unused \return 0=success -1=fail */ static int init(struct ao *ao) { struct priv *p = ao->priv; int res; if (!InitDirectSound(ao)) return -1; ao->no_persistent_volume = true; p->audio_volume = 100; // ok, now create the buffers WAVEFORMATEXTENSIBLE wformat; DSBUFFERDESC dsbpridesc; DSBUFFERDESC dsbdesc; int format = af_fmt_from_planar(ao->format); int rate = ao->samplerate; if (AF_FORMAT_IS_AC3(format)) format = AF_FORMAT_AC3; else { struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_waveext(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) return -1; } switch (format) { case AF_FORMAT_AC3: case AF_FORMAT_S24_LE: case AF_FORMAT_S16_LE: case AF_FORMAT_U8: break; default: MP_VERBOSE(ao, "format %s not supported defaulting to Signed 16-bit Little-Endian\n", af_fmt_to_str(format)); format = AF_FORMAT_S16_LE; } //set our audio parameters ao->samplerate = rate; ao->format = format; ao->bps = ao->channels.num * rate * (af_fmt2bits(format) >> 3); int buffersize = ao->bps; // space for 1 sec MP_VERBOSE(ao, "Samplerate:%iHz Channels:%i Format:%s\n", rate, ao->channels.num, af_fmt_to_str(format)); MP_VERBOSE(ao, "Buffersize:%d bytes (%d msec)\n", buffersize, buffersize / ao->bps * 1000); //fill waveformatex ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE)); wformat.Format.cbSize = (ao->channels.num > 2) ? sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) : 0; wformat.Format.nChannels = ao->channels.num; wformat.Format.nSamplesPerSec = rate; if (AF_FORMAT_IS_AC3(format)) { wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF; wformat.Format.wBitsPerSample = 16; wformat.Format.nBlockAlign = 4; } else { wformat.Format.wFormatTag = (ao->channels.num > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM; wformat.Format.wBitsPerSample = af_fmt2bits(format); wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3); } // fill in primary sound buffer descriptor memset(&dsbpridesc, 0, sizeof(DSBUFFERDESC)); dsbpridesc.dwSize = sizeof(DSBUFFERDESC); dsbpridesc.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbpridesc.dwBufferBytes = 0; dsbpridesc.lpwfxFormat = NULL; // fill in the secondary sound buffer (=stream buffer) descriptor memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */ | DSBCAPS_GLOBALFOCUS /** Allows background playing */ | DSBCAPS_CTRLVOLUME; /** volume control enabled */ if (ao->channels.num > 2) { wformat.dwChannelMask = mp_chmap_to_waveext(&ao->channels); wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample; // Needed for 5.1 on emu101k - shit soundblaster dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; } wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec * wformat.Format.nBlockAlign; dsbdesc.dwBufferBytes = buffersize; dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat; p->buffer_size = dsbdesc.dwBufferBytes; p->write_offset = 0; p->min_free_space = wformat.Format.nBlockAlign; p->outburst = wformat.Format.nBlockAlign * 512; // create primary buffer and set its format res = IDirectSound_CreateSoundBuffer(p->hds, &dsbpridesc, &p->hdspribuf, NULL); if (res != DS_OK) { UninitDirectSound(ao); MP_ERR(ao, "cannot create primary buffer (%s)\n", dserr2str(res)); return -1; } res = IDirectSoundBuffer_SetFormat(p->hdspribuf, (WAVEFORMATEX *)&wformat); if (res != DS_OK) { MP_WARN(ao, "cannot set primary buffer format (%s), using " "standard setting (bad quality)", dserr2str(res)); } MP_VERBOSE(ao, "primary buffer created\n"); // now create the stream buffer res = IDirectSound_CreateSoundBuffer(p->hds, &dsbdesc, &p->hdsbuf, NULL); if (res != DS_OK) { if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) { // Try without DSBCAPS_LOCHARDWARE dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE; res = IDirectSound_CreateSoundBuffer(p->hds, &dsbdesc, &p->hdsbuf, NULL); } if (res != DS_OK) { UninitDirectSound(ao); MP_ERR(ao, "cannot create secondary (stream)buffer (%s)\n", dserr2str(res)); return -1; } } MP_VERBOSE(ao, "secondary (stream)buffer created\n"); return 0; }
static int try_passthrough(struct wasapi_state *state, struct ao *const ao) { WAVEFORMATEXTENSIBLE wformat = { .Format = { .wFormatTag = WAVE_FORMAT_EXTENSIBLE, .nChannels = ao->channels.num, .nSamplesPerSec = ao->samplerate, .nAvgBytesPerSec = (ao->samplerate) * (ao->channels.num * 2), .nBlockAlign = ao->channels.num * 2, .wBitsPerSample = 16, .cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), }, .Samples.wValidBitsPerSample = 16, .dwChannelMask = mp_chmap_to_waveext(&ao->channels), .SubFormat = local_KSDATAFORMAT_SUBTYPE_PCM, }; wformat.SubFormat.Data1 = WAVE_FORMAT_DOLBY_AC3_SPDIF; // see INIT_WAVEFORMATEX_GUID macro union WAVEFMT u; u.extensible = &wformat; MP_VERBOSE(ao, "trying passthrough for %s...\n", af_fmt_to_str((ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE)); HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient, state->share_mode, u.ex, NULL); if (!FAILED(hr)) { ao->format = (ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE;