diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index 101118967..a10811f6f 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -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: diff --git a/selfdrive/ui/tests/test_soundd.py b/selfdrive/ui/tests/test_soundd.py index 52e81fb2b..4b35c2f92 100644 --- a/selfdrive/ui/tests/test_soundd.py +++ b/selfdrive/ui/tests/test_soundd.py @@ -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"