Sintetizzatore vocale per DLQ
  • Python 56.3%
  • Shell 43.7%
Find a file
2026-05-20 13:19:53 +02:00
.gitignore update .gitignore 2026-05-19 09:01:05 +02:00
abaddon.py fex bug durante generazione audio 2026-05-19 09:47:48 +02:00
abaddon.service altro fix 2026-05-18 18:24:19 +02:00
abaddon_logo.svg initial commit — Abaddon voice daemon 2026-05-18 15:22:10 +02:00
config.py update config.py 2026-05-19 09:02:08 +02:00
install_abaddon.sh update permessi per creazione file audio 2026-05-20 13:19:53 +02:00
README.md altro fix 2026-05-18 18:24:19 +02:00

Abaddon — Voice Daemon

Abaddon logo

Servizio TTS offline per Raspberry Pi / totem x86. Sintetizza testo in MP3 e lo salva su disco.

Stack: Piper TTS · ffmpeg · mpg123 · Python stdlib (no dipendenze esterne)


Requisiti

  • Linux x86_64 o Raspberry Pi 3/4 (aarch64/armv7l)
  • Python 3.9+
  • Connessione internet solo durante il setup — poi tutto offline

Installazione

git clone <repo> && cd abaddon
bash setup.sh

Lo script installa automaticamente:

  • ffmpeg e mpg123 tramite apt
  • Piper TTS binario in ./piper/
  • Voce italiana it_IT-paola-medium (~50 MB) in ./piper/
  • Voce inglese en_US-hfc_female-medium in ./piper/
  • Systemd user service per l'utente kipanga con il Python rilevato automaticamente (avvio automatico)

Durante il setup viene chiesta la cartella di salvataggio MP3:

==> Cartella di salvataggio file MP3 (cache)
   Percorso [/var/www/kipanga/audios]:

Premi Invio per accettare il default o inserisci un percorso custom. La cartella viene creata automaticamente e scritta in config.py.

Configurazione

Modifica config.py per cambiare i parametri dopo l'installazione:

Parametro Default Note
PORT 9966 porta <1024 richiede sudo
CACHE_DIR /var/www/kipanga/audios impostato da setup.sh
PIPER_BIN ./piper/piper installato da setup.sh
MODEL_PATH ./piper/it_IT-paola-medium.onnx
PIPER_HZ 22050 sample rate del modello
PAUSE_MS 150 durata pausa |P| in millisecondi
MP3_HZ 48000 sample rate file MP3 di output
MP3_BITRATE 320k bitrate file MP3 di output

Avvio

Manuale

python3 abaddon.py
# [TTS] Pronto  →  http://0.0.0.0:9966
# [TTS] Cache   →  /var/www/kipanga/audios  (0 file)

Automatico all'avvio (systemd)

Gestito da setup.sh, che rileva il Python installato e genera il service con il path corretto nel profilo systemd user di kipanga. Per gestione manuale:

sudo -u kipanga XDG_RUNTIME_DIR=/run/user/$(id -u kipanga) systemctl --user status abaddon
sudo -u kipanga XDG_RUNTIME_DIR=/run/user/$(id -u kipanga) systemctl --user restart abaddon
sudo -u kipanga XDG_RUNTIME_DIR=/run/user/$(id -u kipanga) journalctl --user -u abaddon -f

setup.sh abilita automaticamente il linger (loginctl enable-linger) così il servizio parte anche senza login attivo (headless/SSH).


API

Sintesi frase

GET /speak?text=<testo>[&lang=it|en][&folder=<percorso>]

Sintetizza il testo, salva il file MP3 nella cartella indicata (o in CACHE_DIR se omessa) e restituisce l'hash del file.

# Cartella default (CACHE_DIR), lingua italiana
curl "http://localhost:9966/speak?text=Serviamo+il+numero+A1+allo+sportello+3&lang=it"

# Lingua inglese
curl "http://localhost:9966/speak?text=Now+serving+A1+at+counter+3&lang=en"

# Cartella custom
curl "http://localhost:9966/speak?text=Serviamo+il+numero+A1&folder=/var/www/audio"

Risposta:

{ "hash": "33f40c2a...", "file": "33f40c2a....mp3" }

Il client conosce la cartella e recupera il file direttamente dal disco. Se il file è già in cache la risposta è istantanea (~5ms).

Marker di pausa |P|

Inserisci |P| nel testo per aggiungere silenzio (durata: PAUSE_MS in config.py):

curl "http://localhost:9966/speak?text=Serviamo+il+numero+%7CP%7C+A1+%7CP%7C+allo+sportello+3"

Il marker è case-insensitive. Frasi con pause diverse producono file MP3 distinti.

File in cache

curl http://localhost:9966/cache
{ "cached_files": 42, "folder": "/var/www/kipanga/audios" }

Cache

Ogni frase viene sintetizzata una sola volta:

<folder>/<sha1-del-testo>.mp3

La cache sopravvive ai riavvii. Per pre-generare tutte le frasi note:

python3 - <<'EOF'
from abaddon import synth_mp3
frasi = [l.strip() for l in open("frasi.txt") if l.strip()]
for i, f in enumerate(frasi, 1):
    print(f"[{i}/{len(frasi)}] {f}")
    synth_mp3(f)
print("Fatto.")
EOF

Troubleshooting

Piper non trovato / permission denied

ls -la ./piper/piper          # deve mostrare -rwxr-xr-x
chmod +x ./piper/piper        # se manca il bit eseguibile

Porta già in uso

ss -tlnp | grep 9966
# Cambia PORT in config.py

Log del daemon

sudo -u kipanga XDG_RUNTIME_DIR=/run/user/$(id -u kipanga) journalctl --user -u abaddon -f