is_supported_exclusive_with_quirks falls back to querying the format as WAVEFORMATEX for mono and stereo formats. That fallback is not safe for 24 bit formats.
WAVEFORMATEX has no wValidBitsPerSample. The two 24 bit layouts can still be told apart by nBlockAlign, 6 bytes per stereo frame for packed and 8 for padded, but that is not a reliable way to specify them. For 8, 16 and 32 bit int and for f32 there is only one possible layout, so the simple structure is unambiguous. 24 bit is the only case with two options.
Old drivers handle this badly. Reported in HEnquist/camilladsp#509, where a Realtek ALC236 with a 2017 driver correctly rejects 3-byte packed as WAVEFORMATEXTENSIBLE and then accepts the same format as WAVEFORMATEX:
api.rs:878> Repeating query with format as WAVEFORMATEX
api.rs:884> The requested format is supported as WAVEFORMATEX
Initialize succeeds as well, so nothing catches it. The driver seems to map "24 bit" onto its native 24-in-32 mode and ignore nBlockAlign, so it reads 8 byte frames from a buffer filled with 6 byte ones. The result is loud noise and constant underruns. Switching to the Microsoft inbox HDAudio driver fixes it.
PortAudio avoids this by always using WAVEFORMATEXTENSIBLE for anything that is not 8 or 16 bits, see MakeWaveFormatFromParams in pa_win_wasapi.c.
Fix: skip the WAVEFORMATEX fallback unless the container is 8, 16 or 32 bits and wValidBitsPerSample equals wBitsPerSample.
Two related things in to_waveformatex:
- it drops
wValidBitsPerSample without checking it, so a 24-in-32 format silently becomes plain 32 bit int
- the
.unwrap() on the call in is_supported_exclusive_with_quirks can panic, since to_waveformatex returns an error for subformats other than PCM and float
is_supported_exclusive_with_quirksfalls back to querying the format asWAVEFORMATEXfor mono and stereo formats. That fallback is not safe for 24 bit formats.WAVEFORMATEXhas nowValidBitsPerSample. The two 24 bit layouts can still be told apart bynBlockAlign, 6 bytes per stereo frame for packed and 8 for padded, but that is not a reliable way to specify them. For 8, 16 and 32 bit int and for f32 there is only one possible layout, so the simple structure is unambiguous. 24 bit is the only case with two options.Old drivers handle this badly. Reported in HEnquist/camilladsp#509, where a Realtek ALC236 with a 2017 driver correctly rejects 3-byte packed as
WAVEFORMATEXTENSIBLEand then accepts the same format asWAVEFORMATEX:Initializesucceeds as well, so nothing catches it. The driver seems to map "24 bit" onto its native 24-in-32 mode and ignorenBlockAlign, so it reads 8 byte frames from a buffer filled with 6 byte ones. The result is loud noise and constant underruns. Switching to the Microsoft inbox HDAudio driver fixes it.PortAudio avoids this by always using
WAVEFORMATEXTENSIBLEfor anything that is not 8 or 16 bits, seeMakeWaveFormatFromParamsinpa_win_wasapi.c.Fix: skip the
WAVEFORMATEXfallback unless the container is 8, 16 or 32 bits andwValidBitsPerSampleequalswBitsPerSample.Two related things in
to_waveformatex:wValidBitsPerSamplewithout checking it, so a 24-in-32 format silently becomes plain 32 bit int.unwrap()on the call inis_supported_exclusive_with_quirkscan panic, sinceto_waveformatexreturns an error for subformats other than PCM and float