Skip truncated WAV payloads that cannot decode as samples.

Reject odd-length PCM and catch ValueError so a broken theme file cannot take soundd down.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1454
2026-09-02 09:26:43 -04:00
committed by firestar5683
parent cc8c0dfb16
commit b321dd71e6
2 changed files with 26 additions and 2 deletions
+6 -2
View File
@@ -192,7 +192,11 @@ class Soundd:
if length <= 0:
cloudlog.warning(f"soundd: empty audio {path}, skipping")
return None
sound = np.frombuffer(wavefile.readframes(length), dtype=np.int16).astype(np.float32) / (2**16/2)
raw = wavefile.readframes(length)
if len(raw) < 2 or (len(raw) % 2) != 0:
cloudlog.warning(f"soundd: truncated audio {path}, skipping")
return None
sound = np.frombuffer(raw, dtype=np.int16).astype(np.float32) / (2**16/2)
if sound.size == 0:
cloudlog.warning(f"soundd: empty audio {path}, skipping")
return None
@@ -208,7 +212,7 @@ class Soundd:
for path in self._sound_candidates(filename):
try:
sound_data = self._read_sound(path)
except (FileNotFoundError, OSError, EOFError, wave.Error):
except (FileNotFoundError, OSError, EOFError, ValueError, wave.Error):
cloudlog.exception(f"soundd: failed to load {path}")
continue
if sound_data is not None:
+20
View File
@@ -106,6 +106,26 @@ class TestSoundd:
assert AudibleAlert.warningImmediate in soundd.loaded_sounds
assert soundd.loaded_sounds[AudibleAlert.warningImmediate].size > 0
def test_load_sounds_falls_back_when_custom_wav_has_odd_payload(self, tmp_path):
soundd = Soundd.__new__(Soundd)
soundd.sound_directory = tmp_path / "sounds"
soundd.sound_directory.mkdir()
soundd.random_events_directory = tmp_path / "random_events"
soundd.random_events_directory.mkdir()
odd = soundd.sound_directory / "warning_immediate.wav"
with wave.open(str(odd), "w") as wav:
wav.setnchannels(1)
wav.setsampwidth(2)
wav.setframerate(48000)
wav.writeframes(b"\x00\x00\x00\x00")
odd.write_bytes(odd.read_bytes()[:-1])
soundd.load_sounds()
assert AudibleAlert.warningImmediate in soundd.loaded_sounds
assert soundd.loaded_sounds[AudibleAlert.warningImmediate].size > 0
def test_missing_goat_keeps_stock_critical_alert(self, tmp_path):
soundd = Soundd.__new__(Soundd)
soundd.sound_directory = tmp_path / "sounds"