Skip to content

bug(audio): WASAPI Exclusive should retry S24/S16 before falling back to shared #174

@InstaZDLL

Description

@InstaZDLL

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):

  1. Try IEEE_FLOAT 32-bit at the target sample rate (current behaviour)
  2. On AUDCLNT_E_UNSUPPORTED_FORMAT, try PCM 24-bit packed (wBitsPerSample = 24, nBlockAlign = nChannels * 3)
  3. On the same error, try PCM 24-bit padded (wBitsPerSample = 24, container 32-bit, nBlockAlign = nChannels * 4)
  4. On the same error, try PCM 16-bit
  5. 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

  • On a Realtek ALC1220 desktop (or any consumer device that rejects F32 in exclusive), enabling the toggle produces bit-perfect playback at the device's native bit depth instead of falling back to shared
  • If every PCM variant is rejected (very rare), behaviour matches today: log a WARN, fall back to shared mode, no crash
  • No regression on devices that already supported F32 (RME, Focusrite Scarlett, MOTU certain modes…)
  • Log line clarifies which bit depth the exclusive negotiation landed on so users can confirm in the diagnostics view

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.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingrustPull requests that update rust codescope: backendRust/Tauri backend (src-tauri/)

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions