feat(audio): add offline local neural voice via Piper (sv + en)
Announcements can use Piper, a free local neural TTS, instead of the browser's system voice. No account, no paid service, and it keeps working with no internet at the track. - npm run tts:install (or Settings -> Audio -> Install) downloads the standalone Piper release for Linux/Windows/macOS plus Swedish and English voices into piper/ once - server/tts.js generates each line once, one at a time, and caches it as WAV under data/tts/; a new line takes ~0.3 s with the standalone build - src/piper_speech.js plays it and falls back to the browser voice if Piper is missing, errors, or takes over 2.5 s, so no announcement is lost and timing is never held up - "Pre-generate phrases and driver names" renders every fixed phrase and name in both languages before race day Browser voice stays the default. Generation routes fall under the admin PIN. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -230,6 +230,15 @@ Owns:
|
||||
- `middleware`: once a PIN exists, rejects unauthenticated non-GET `/api/*` requests and `GET /api/ammc/config` with `401 { error: "auth_required" }`; applies `RC_ADMIN_ALLOWED_CIDRS` (403 `ip_not_allowed`) to the same routes regardless of PIN
|
||||
- routes wired in `server.js`: `GET /api/auth/status`, `POST /api/auth/login`, `/logout`, `/pin`, `/disable`
|
||||
|
||||
### `server/tts.js`, `server/tts_install.js`, `scripts/install-piper.js`
|
||||
Role:
|
||||
- local neural text-to-speech via Piper (CommonJS, required by `server.js`)
|
||||
|
||||
Owns:
|
||||
- `tts.js`: finds the Piper binary (`RC_PIPER_BIN`, then `<project>/piper/`, then PATH, then `~/.local/bin`) and voices (`RC_PIPER_VOICES_DIR`, `data/piper-voices`, `<project>/piper/voices`, `~/.local/share/piper-voices`); generates one line at a time into a WAV cache under `data/tts/` keyed by voice + speed + text; dedupes identical in-flight requests; background pre-generation with progress
|
||||
- `tts_install.js`: downloads the standalone Piper release for the OS and the `sv_SE-nst-medium` / `en_US-lessac-medium` voices into `<project>/piper/`, skipping anything present; used by both `npm run tts:install` (`scripts/install-piper.js`) and `POST /api/tts/install`
|
||||
- routes in `server.js`: `GET /api/tts/status` (open), `POST /api/tts/speech`, `POST /api/tts/pregenerate`, `POST /api/tts/install` (all PIN-protected once a PIN is set)
|
||||
|
||||
### `scripts/record-video-guides.mjs` and `scripts/capture-screenshots.mjs`
|
||||
Role:
|
||||
- documentation tooling - not run in production, not imported by `server.js` or any `src/*.js` module
|
||||
@@ -551,6 +560,14 @@ Owns:
|
||||
- `showPinDialog`: the login modal
|
||||
- `mountAuthSettings`: the "Admin PIN" panel in Settings -> System & backup (enable, change, log out, disable)
|
||||
|
||||
### `src/piper_speech.js` and `src/tts_settings_view.js`
|
||||
Type:
|
||||
- local voice playback and its settings panel
|
||||
|
||||
Owns:
|
||||
- `piper_speech.js`: `speakWithPiper` fetches the server's WAV and plays it; on any error or after 2.5 s it calls the browser-voice fallback instead. `speakTextHelper` (`runtime_services.js`) routes through it when `settings.ttsEngine === "piper"`, so legacy speaker cues and the race-audio engine both use it
|
||||
- `tts_settings_view.js`: the "Voice engine" panel in Settings -> Audio (engine choice, sv/en install status, install / pre-generate / test buttons)
|
||||
|
||||
### `src/misc_views.js`
|
||||
Type:
|
||||
- misc page renderers
|
||||
@@ -2081,6 +2098,7 @@ Practical shape (field names as actually read/written in `src/settings.js` and `
|
||||
speechVoiceUri: string,
|
||||
speechRate: number,
|
||||
speechPitch: number,
|
||||
ttsEngine: "browser" | "piper", // voice engine; Piper falls back to the browser voice on any failure
|
||||
speakerPassingCueEnabled: boolean,
|
||||
speakerLeaderCueEnabled: boolean,
|
||||
speakerFinishCueEnabled: boolean,
|
||||
@@ -2717,12 +2735,6 @@ The suite uses Node's built-in `node:test` runner and does not add a test-framew
|
||||
Closed out 2026-09-12: `resultsBySession`'s competitor row schema (15.7), a full `settings` schema snapshot (15.9), and the SQLite table contract (15.10) are now documented; the browser regression suite (section 18) already covers every critical create/edit/save workflow (classes, drivers, cars, sponsor events, the race wizard, team management, Race Day start/pause/resume/finish, session-type-specific start sequences, Settings tabs, Event Manager tabs, the schedule panel, OBS popup, decoder reconnect).
|
||||
|
||||
If the project grows further, the next useful additions to this document are:
|
||||
- optional neural TTS provider integration for natural start, finish, and driver-name announcements (deferred at the user's request pending a chosen provider - see ROADMAP.md, "More natural race speech")
|
||||
- backend-only provider credentials with no API key material in frontend state, exports, or public overlays
|
||||
- server-side speech generation and content-addressed cache under `data/tts/` so race playback never waits for a cloud request
|
||||
- cached audio invalidation when language, voice, phrase, or a driver's `speechName` changes
|
||||
- offline fallback order: cached neural audio -> uploaded WAV/MP3 -> browser system voice -> passing beep
|
||||
- provider health/status and a pre-generate-all-names action in Settings before race day
|
||||
- a full controller-to-view ID map for every form/button (large - the doc currently documents this per-feature as each area is touched, e.g. the manage-tab targets in 2.2's `event_manager_controller.js` entry, rather than as one exhaustive cross-reference)
|
||||
- permissions/authentication design once that work actually starts (ROADMAP.md, "Permissions and mobile Race Control" - deferred, no identity system exists yet)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user