fix(sounds): validate sample rate + guard playback (Win11 XONAR crash)#30
Merged
Conversation
The reward chirp crashed the session on Windows: the device was found (XONAR SOUND CARD) but the hardcoded 192000 Hz from sample_rate_dict is rejected by WASAPI shared mode (PaErrorCode -9997), and the sd.play fallback raised that error inside the Bpod softcode thread, aborting the trial loop. - Validate the chosen rate with sd.check_output_settings at init; if the device rejects it, fall back to the device's reported default_samplerate (so the stream opens at a supported rate). Cards that do 192000 are unaffected. - Wrap the sd.play fallback in try/except so an audio backend error logs a warning instead of crashing the session. Tests: +1 rate fallback; 4 sound tests green.
The XONAR supports 192000, but WASAPI shared mode is locked to the Windows mixer/default-format rate, so requesting 192000 either errors (-9997) or resamples too slow (right sound, "deflated"/longer). Keep the full rate and auto-enable WASAPI exclusive on Windows when the chosen rate exceeds the device's shared default, binding the device directly at its native rate. The rate validation now runs in exclusive mode; if even that is rejected it falls back to the shared default rate (correct speed) instead of deflating. Tests: exclusive auto-enable + full-rate-kept, and shared-default fallback.
Exclusive mode opened but produced no output on the XONAR endpoint under test, so auto-enabling it for high device-native rates made the reward sound silent. Use shared mode and, when the device rejects the configured rate, fall back to its default rate (audible, correct speed) rather than going silent or playing deflated. Exclusive stays opt-in via use_wasapi_exclusive. Full 192000 on the XONAR depends on its Windows driver preset (7.1 channels / 192000), documented separately. Tests: shared-default fallback; 4 sound tests green.
The output stream can open at a different rate than requested; sounds are
generated after setup_sound_device at self.sample_rate, so if the stream
actually runs slower the buffer plays too slow ("deflated"). After opening,
adopt self._stream.samplerate as the generation rate so buffer rate always
matches playback rate (correct speed at whatever rate the device provides).
The stream-open log now reports the actual rate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to the default-output fallback: fixes a session crash on the Win11
rig and makes the reward chirp actually play.
Symptom
Device found (
Speakers (XONAR SOUND CARD), id 10) but the stream/sd.playfailed with
Invalid sample rate [PaErrorCode -9997]at 192000 Hz, and thesd.playexception propagated into the Bpod softcode thread → session aborted.Cause
sample_rate_dicthardcodes 192000 forXONAR SOUND CARD. Under WASAPI sharedmode on Windows the device is locked to its mixer format and rejects 192000
(works on Linux / exclusive mode, hence the hardcode).
sd.playfallback had no error guard, so the failure crashed the trial loop.Fix
sd.check_output_settingsat init; on rejection,fall back to the device's
default_samplerateso the stream opens. Cards thatgenuinely support 192000 are unaffected.
sd.playfallback (best-effort): an audio error logs a warning andnever crashes the session (it runs in the Bpod softcode thread).
Tests: +1 (rate fallback); 4 sound tests green; mypy clean.