Read-aloud: natural neural voice via local Piper TTS
Replace the browser's robotic Web Speech API (espeak on Chromium) as the
primary read-aloud path with server-side Piper neural TTS, served by petal
and kept fully offline on millenia next to Ollama.
- internal/tts: proxy short passages to Piper, transcode WAV -> mp3/opus via
ffmpeg, content-addressed disk cache (instant re-taps). Each Piper instance
loads one voice, so language routes to its own endpoint:{voice}. Unknown
language -> 404 so the client falls back to Web Speech. UTF-8-safe truncation
for multibyte (Chinese) text.
- config: TTS_ENDPOINT / TTS_ENDPOINT_ZH / TTS_VOICE_EN / TTS_VOICE_ZH /
TTS_CACHE_DIR / TTS_TIMEOUT / TTS_AUDIO_FORMAT. Route mounts only when
TTS_ENDPOINT is set; otherwise unchanged behavior.
- web/audio/speech.ts: speak() hits /api/tts first, falls back to Web Speech on
any failure; rapid-tap-safe via a request token. Call sites unchanged.
- deploy/: Piper user systemd units (EN :5005, ZH :5006), setup script, README.
English (en_US-amy-medium) and Chinese (zh_CN-huayan-medium) are both live.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
This commit is contained in:
43
deploy/setup-piper.sh
Executable file
43
deploy/setup-piper.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
# One-time Piper TTS setup on millenia as a *user* systemd service (no sudo).
|
||||
# Idempotent — safe to re-run. Run on the box:
|
||||
# bash setup-piper.sh
|
||||
# To survive logout/reboot, also (once, needs root):
|
||||
# sudo loginctl enable-linger "$USER"
|
||||
set -euo pipefail
|
||||
|
||||
PIPER_HOME="$HOME/piper"
|
||||
VENV="$PIPER_HOME/venv"
|
||||
VOICES="$PIPER_HOME/voices"
|
||||
VOICE=en_US-amy-medium
|
||||
UNIT_DIR="$HOME/.config/systemd/user"
|
||||
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
|
||||
|
||||
echo ">> ffmpeg check (petal transcodes Piper WAV -> mp3 with it):"
|
||||
command -v ffmpeg >/dev/null || { echo "ffmpeg not found on PATH"; exit 1; }
|
||||
|
||||
echo ">> venv + piper-tts[http]"
|
||||
[ -d "$VENV" ] || python3 -m venv "$VENV"
|
||||
"$VENV/bin/pip" install --upgrade pip >/dev/null
|
||||
"$VENV/bin/pip" install "piper-tts[http]"
|
||||
|
||||
echo ">> download voice $VOICE"
|
||||
mkdir -p "$VOICES"
|
||||
"$VENV/bin/python" -m piper.download_voices "$VOICE" --data-dir "$VOICES"
|
||||
|
||||
echo ">> install user service"
|
||||
mkdir -p "$UNIT_DIR"
|
||||
install -m 0644 "$(dirname "$0")/piper.service" "$UNIT_DIR/piper.service"
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now piper.service
|
||||
|
||||
echo ">> wait + status"
|
||||
sleep 2
|
||||
systemctl --user --no-pager --full status piper.service | head -n 6 || true
|
||||
|
||||
echo ">> smoke test"
|
||||
curl -sf -X POST localhost:5005/ \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"text\":\"hello there\",\"voice\":\"$VOICE\"}" -o /tmp/piper-test.wav \
|
||||
&& echo "OK: /tmp/piper-test.wav ($(stat -c%s /tmp/piper-test.wav) bytes)" \
|
||||
|| { echo "smoke test FAILED"; journalctl --user -u piper.service --no-pager | tail -n 20; exit 1; }
|
||||
Reference in New Issue
Block a user