# sg-announcer – utrop på høyttaleranlegget (Sauda Gathering) Liten Flask-app: tekst inn i nettleseren → forhåndslytt → «Send ut på anlegget» spiller lyden lokalt på **denne** maskinen med `pw-play`. TTS er Piper (`piper-tts`, onnx, helt lokalt). Alt ligger i to filer: `announcer.py` (server + kø) og `index.html` (sida). Dokumentasjon for Kent: Athena-notatet `30 Ressurser/Teknologi/Sauda Gathering - announcer.md`. Oppdater det, ikke denne fila, når noe som er verdt å huske endres. ## Kjøring Tjenesten er en systemd-brukerenhet: `~/.config/systemd/user/sg-announcer.service` (enabled, port 8090). `systemctl --user restart sg-announcer` etter endringer i koden. Manuelt: `uv run announcer.py --port 8090`. Ny maskin (Debian/Ubuntu/Mint): `git clone https://git.1x1.no/kent/sg-announcer.git` og `./install.sh` gjør alt (apt, uv, Python 3.13, stemmer, enhet, ufw). Kan kjøres på nytt. Avspilling bruker `pw-play` når PipeWire kjører, ellers `paplay` (`player_cmd()`), så Mint 21 med PulseAudio virker også. Miljøvariabler (settes i enheten): `ANNOUNCER_PORT`, `ANNOUNCER_PIN` (tom = ingen PIN), `ANNOUNCER_SINK` (pw-play `--target`; tom = standard utgang). ## Hvordan det henger sammen - `/api/preview` syntetiserer og returnerer WAV til nettleseren. Ingen lyd på PC-en. - `/api/announce` legger meldingen i én FIFO-kø; en bakgrunnstråd spiller én om gangen med `pw-play`. `/api/stop` dreper gjeldende avspilling, `/api/clear` tømmer køen. - Knappen «Send ut» er låst til nøyaktig den teksten/stemmen/hastigheten som sist ble forhåndslyttet. Det er med vilje: ingen skal sende noe de ikke har hørt. - Ferdige WAV-er bufres i `cache/` etter innholdshash. Trygt å slette. - Jingler: de innebygde (`BUILTIN_CHIMES`) genereres i Python med `_tone`/ `_mix`/`_overlay`, ingen lydfiler. Egne filer i `chimes/` (wav/mp3/ogg/flac) dukker opp i menyen som `file:`; ffmpeg kutter til 6 s, normaliserer til -18 LUFS og fader ut. Mappa leses ved hver forespørsel, så nye filer vises uten restart. Toppnivå på de innebygde ligger mellom -4 og -10 dBFS, talen går til 0 dBFS, så jinglen skal aldri være høyere enn stemmen. ## Stemmer og motorer `VOICES_LIST` i `announcer.py` er eneste sted en stemme legges til. To motorer: - **edge** (`_edge`): Microsofts nevrale stemmer via `edge-tts`-pakken. Gratis, ingen nøkkel, men krever internett og er en uoffisiell bruk av Edge sin «les høyt»-tjeneste – kan slutte å virke uten varsel. Norsk: bare `nb-NO-FinnNeural` og `nb-NO-PernilleNeural`. `*MultilingualNeural`-stemmene kan lese norsk med engelsk aksent. Liste: `uv run edge-tts --list-voices`. Leverer MP3 24 kHz; dekodes med ffmpeg til PCM i `edge_pcm()`. - **piper** (`_piper`): lokalt, robotaktig, reserve når nettet er nede. Modellen lastes ned ved oppstart til `voices/` (gitignored). Norsk finnes bare som `talesyntese-medium` og `nvcc-medium` (ti talere, `speaker`-indeks). Full liste: `https://huggingface.co/rhasspy/piper-voices/resolve/main/voices.json`. Vil Kent ha enda bedre lyd: ElevenLabs (nøkkel, best norsk) eller OpenAI `gpt-4o-mini-tts` er neste trinn; legg til som ny motor i `synthesize()`. ## Feller - **ufw**: appen kjører nativt, så Docker-smutthullet gjelder ikke. Porten må åpnes eksplisitt for LAN-et (`sudo ufw allow from 10.20.70.0/23 to any port 8090 proto tcp`). - `pkill -f announcer.py` fra et skall som selv har `announcer.py` i kommandolinja dreper skallet. Bruk `systemctl --user`. - Piper 1.8 bruker `SynthesisConfig(length_scale=…)`; lavere = raskere. - Forhåndslytt svarer 502 med feilmelding hvis edge-tts feiler (ingen nett). Køen markerer meldingen som `failed`. Bytt til en lokal reserve-stemme.