feat(audio): passing tone only during races and remove legacy audio

Adds 'Calls during a race' (default: passing tone only, like timing
systems) that drops name/personal best/leader/position calls in race
sessions; practice keeps them. Removes the legacy audio panel and
fallbacks, including its 'Sound on' switch that silently muted every
race-audio voice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
larssand
2026-09-30 21:47:40 +02:00
parent 5bfb67ea02
commit aaca5e8c63
13 changed files with 108 additions and 205 deletions

View File

@@ -361,14 +361,8 @@ export function selectSpeechVoice(voices, voiceUri, language) {
|| null;
}
export function getSessionAnnouncementText(session, phase, { t }) {
const practice = ["open_practice", "free_practice", "practice"].includes(String(session?.type || "").toLowerCase());
if (phase === "start") return t(practice ? "timing.practice_started_voice" : "timing.race_started_voice");
return t(practice ? "timing.practice_finished_voice" : "timing.race_finished_voice");
}
export function speakTextHelper(text, { state, currentLanguage }, options = {}) {
if (!state.settings.audioEnabled || !text) return false;
if (!text) return false;
if (state.settings.ttsEngine === "piper" && !options.skipPiper) {
const piperLanguage = String(options.language || currentLanguage()).toLowerCase().startsWith("sv") ? "sv" : "en";
return speakWithPiper(text, {
@@ -402,35 +396,15 @@ export function speakTextHelper(text, { state, currentLanguage }, options = {})
return true;
}
export function announcePassingHelper(entry, { state, t, playPassingBeep, speakText }) {
if (!state.settings.audioEnabled) return;
if (state.settings.passingSoundMode === "beep") { playPassingBeep(); return; }
if (state.settings.passingSoundMode === "name") {
const spoken = speakText(entry?.speechName || entry?.displayName || entry?.driverName || t("common.unknown_driver"), { interrupt: false });
if (!spoken) playPassingBeep();
}
}
export function announceRaceStartedHelper(session, { state, t, pushOverlayEvent, playStartCue, speakText }) {
// Race audio (race_audio.js) owns every start/finish sound; these only log the overlay event.
export function announceRaceStartedHelper(session, { t, pushOverlayEvent }) {
if (!session) return;
pushOverlayEvent("start", `${session.name} • ${t("timing.start")}`, { silentCue: true });
if (state.settings.raceAudio?.enabled) return;
if (!state.settings.audioEnabled) return;
if (state.settings.startSoundEnabled !== false) playStartCue();
if (state.settings.finishVoiceEnabled !== false) {
setTimeout(() => speakText(getSessionAnnouncementText(session, "start", { t }), { interrupt: true }), state.settings.startSoundEnabled === false ? 0 : 650);
}
}
export function announceRaceFinishedHelper({ state, t, getActiveSession, pushOverlayEvent, playFinishSiren, speakText }) {
export function announceRaceFinishedHelper({ t, getActiveSession, pushOverlayEvent }) {
const session = getActiveSession();
if (session) pushOverlayEvent("finish", `${session.name} • ${t("timing.race_finished")}`, { silentCue: true });
if (state.settings.raceAudio?.enabled) return;
if (!session || !state.settings.audioEnabled) return;
if (state.settings.finishSoundEnabled !== false) playFinishSiren();
if (state.settings.finishVoiceEnabled !== false) {
setTimeout(() => speakText(getSessionAnnouncementText(session, "finish", { t }), { interrupt: true }), state.settings.finishSoundEnabled === false ? 0 : 1150);
}
}
export function handleSessionTimerTickHelper({ getActiveSession, isUntimedSession, getSessionTiming, saveState, getLastFinishAnnouncementSessionId, setLastFinishAnnouncementSessionId, announceRaceFinished, announceRaceClockEvent = () => {} }) {