Symptom
On Windows with the WASAPI Exclusive Mode toggle enabled, init fails on every stream open with:
WARN waveflow_lib::audio::wasapi_exclusive: wasapi exclusive init failed
err=Audio("device does not support 32-bit float in exclusive mode: Windows(Error { code: HRESULT(0x88890008), message: \"\" })")
WARN waveflow_lib::audio::output: WASAPI Exclusive init failed, falling back to shared mode
INFO waveflow_lib::audio::output: cpal output stream opened sample_rate=44100 channels=8 sample_format=F32
The fallback to cpal shared mode works fine, but the user gets two WARN log lines on every track load and never actually obtains bit-perfect playback — defeating the point of enabling the toggle.
Root cause
audio/wasapi_exclusive.rs requests the IAudioClient with WAVE_FORMAT_IEEE_FLOAT (32-bit float, what cpal uses internally). Many consumer DACs and integrated codecs (Realtek ALC, Conexant CX, MOTU's UltraLite in some modes…) only accept WAVE_FORMAT_PCM with wBitsPerSample = 16 or 24 in exclusive mode — they reject IEEE_FLOAT outright with AUDCLNT_E_UNSUPPORTED_FORMAT (0x88890008).
WASAPI Exclusive is supposed to negotiate format down — the canonical pattern is IAudioClient::IsFormatSupported with progressively narrower types until one is accepted, then we convert F32 samples to that bit depth in the decoder before pushing to the ring.
Fix sketch
In wasapi_exclusive::open (Windows):
- Try
IEEE_FLOAT 32-bit at the target sample rate (current behaviour)
- On
AUDCLNT_E_UNSUPPORTED_FORMAT, try PCM 24-bit packed (wBitsPerSample = 24, nBlockAlign = nChannels * 3)
- On the same error, try
PCM 24-bit padded (wBitsPerSample = 24, container 32-bit, nBlockAlign = nChannels * 4)
- On the same error, try
PCM 16-bit
- If all four fail, fall back to shared mode as today
The decoder thread already converts at the output boundary — extend the format conversion in audio/output.rs (or its exclusive sibling) so the same f32 pipeline can write into the alternative containers via dither + clamp.
Acceptance criteria
Notes
Phase 1.a did not touch the audio engine. Pre-existing on main, surfaced through a user report sharing diagnostic logs.
Related: docs/features/playback.md — the WASAPI Exclusive section should mention the bit-depth negotiation chain once shipped.
Symptom
On Windows with the WASAPI Exclusive Mode toggle enabled, init fails on every stream open with:
The fallback to cpal shared mode works fine, but the user gets two
WARNlog lines on every track load and never actually obtains bit-perfect playback — defeating the point of enabling the toggle.Root cause
audio/wasapi_exclusive.rsrequests the IAudioClient withWAVE_FORMAT_IEEE_FLOAT(32-bit float, what cpal uses internally). Many consumer DACs and integrated codecs (Realtek ALC, Conexant CX, MOTU's UltraLite in some modes…) only acceptWAVE_FORMAT_PCMwithwBitsPerSample = 16or24in exclusive mode — they rejectIEEE_FLOAToutright withAUDCLNT_E_UNSUPPORTED_FORMAT(0x88890008).WASAPI Exclusive is supposed to negotiate format down — the canonical pattern is
IAudioClient::IsFormatSupportedwith progressively narrower types until one is accepted, then we convert F32 samples to that bit depth in the decoder before pushing to the ring.Fix sketch
In
wasapi_exclusive::open(Windows):IEEE_FLOAT32-bit at the target sample rate (current behaviour)AUDCLNT_E_UNSUPPORTED_FORMAT, tryPCM24-bit packed (wBitsPerSample = 24,nBlockAlign = nChannels * 3)PCM24-bit padded (wBitsPerSample = 24, container 32-bit,nBlockAlign = nChannels * 4)PCM16-bitThe decoder thread already converts at the output boundary — extend the format conversion in
audio/output.rs(or its exclusive sibling) so the same f32 pipeline can write into the alternative containers via dither + clamp.Acceptance criteria
WARN, fall back to shared mode, no crashNotes
Phase 1.a did not touch the audio engine. Pre-existing on
main, surfaced through a user report sharing diagnostic logs.Related:
docs/features/playback.md— the WASAPI Exclusive section should mention the bit-depth negotiation chain once shipped.