From cc8c0dfb16059cc16c6903f10a8521d4f637885e Mon Sep 17 00:00:00 2001 From: 1454 <19884453+1454@users.noreply.github.com> Date: Wed, 2 Sep 2026 09:23:43 -0400 Subject: [PATCH] Keep stock critical alerts if goat or a theme WAV fails. Catch truncated files that raise EOFError, and only replace a critical chime with goat when that clip actually loaded. Co-authored-by: Cursor --- selfdrive/ui/soundd.py | 14 +++++++++++--- selfdrive/ui/tests/test_soundd.py | 32 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index 24aefa158..101118967 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -208,7 +208,7 @@ class Soundd: for path in self._sound_candidates(filename): try: sound_data = self._read_sound(path) - except (FileNotFoundError, OSError, wave.Error): + except (FileNotFoundError, OSError, EOFError, wave.Error): cloudlog.exception(f"soundd: failed to load {path}") continue if sound_data is not None: @@ -263,6 +263,12 @@ class Soundd: self.bluetooth_audio = None sink.close() + def select_critical_alert(self, stock_alert, goat_scream_critical): + goat_alert = starpilot_alert_key(StarPilotAudibleAlert.goat) + if goat_scream_critical and goat_alert in self.loaded_sounds: + return goat_alert + return stock_alert + def update_alert(self, new_alert): if new_alert != AudibleAlert.none and new_alert not in self.loaded_sounds: new_alert = AudibleAlert.none @@ -295,8 +301,10 @@ class Soundd: critical_full_alert = sm['selfdriveState'].alertStatus == log.SelfdriveState.AlertStatus.critical critical_full_alert &= sm['selfdriveState'].alertSize == log.SelfdriveState.AlertSize.full - if self.starpilot_toggles.goat_scream_critical_alerts and critical_full_alert: - new_alert = starpilot_alert_key(StarPilotAudibleAlert.goat) + new_alert = self.select_critical_alert( + new_alert, + self.starpilot_toggles.goat_scream_critical_alerts and critical_full_alert, + ) new_starpilot_alert = sm['starpilotSelfdriveState'].alertSound.raw if new_alert == AudibleAlert.none and new_starpilot_alert != StarPilotAudibleAlert.none: diff --git a/selfdrive/ui/tests/test_soundd.py b/selfdrive/ui/tests/test_soundd.py index 0355e3a18..52e81fb2b 100644 --- a/selfdrive/ui/tests/test_soundd.py +++ b/selfdrive/ui/tests/test_soundd.py @@ -92,6 +92,38 @@ class TestSoundd: assert AudibleAlert.engage in soundd.loaded_sounds assert soundd.loaded_sounds[AudibleAlert.engage].size > 0 + def test_load_sounds_falls_back_when_custom_wav_is_truncated(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() + + (soundd.sound_directory / "warning_immediate.wav").write_bytes(b"RIFF") + + 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" + soundd.sound_directory.mkdir() + soundd.random_events_directory = tmp_path / "random_events" + soundd.random_events_directory.mkdir() + soundd.load_sounds() + + goat_alert = starpilot_alert_key(StarPilotAudibleAlert.goat) + assert goat_alert not in soundd.loaded_sounds + assert AudibleAlert.warningImmediate in soundd.loaded_sounds + + assert soundd.select_critical_alert(AudibleAlert.warningImmediate, True) == AudibleAlert.warningImmediate + + soundd.loaded_sounds[goat_alert] = soundd.loaded_sounds[AudibleAlert.warningImmediate] + assert soundd.select_critical_alert(AudibleAlert.warningImmediate, True) == goat_alert + assert soundd.select_critical_alert(AudibleAlert.warningImmediate, False) == AudibleAlert.warningImmediate + def test_bluetooth_audio_mutes_local_only_while_healthy(self): soundd = Soundd.__new__(Soundd) samples = np.array([0.25, -0.5], dtype=np.float32)