Voice output (TTS)
On-device neural TTS with three engines: Piper (primary, ONNX neural), Pocket-TTS (Kyutai's voice-cloning neural engine), and espeak-ng as a lightweight automatic fallback.
Engines
- Piper — a fast, local neural TTS system using ONNX models. VoxCtrl invokes the
piperbinary directly, pipes text to its stdin, and plays the raw 16-bit PCM it returns viarodio. This is the default engine. - Pocket-TTS — Kyutai's lightweight FlowLM + Mimi-codec model, ported to pure Rust on top
of Candle (no Python, no ONNX, no subprocess). Instead of fixed voice embeddings, it clones a voice from a
short reference audio clip at runtime. VoxCtrl ships a small built-in catalogue of reference clips
(Alba, Anna, Vera, Charles, Michael) and also supports custom
.wavvoice clips. It streams generated audio frame-by-frame for low perceived latency, and the model can be pre-warmed on startup with theprewarmoption. Requires a HuggingFace account that has accepted the license for the gatedkyutai/pocket-ttsmodel, plus a personal access token (hf_token). - Espeak-ng — used automatically if Piper is unavailable or no voice is downloaded. Quality is lower, but it's always available as a system package.
GPU acceleration
VoxCtrl supports GPU acceleration (CUDA) for the Piper engine only — it appends a
--cuda flag to the spawned Piper subprocess at runtime. Pocket-TTS (Candle-based) does not
currently expose a GPU toggle. If GPU initialization fails, Piper automatically and gracefully falls back to
the CPU without crashing.
Piper voice catalog
| Voice | Quality | Sample rate |
|---|---|---|
| en-us-libritts-high | High | 22050 Hz |
| en-us-ryan-high | High | 22050 Hz |
| en-us-ryan-medium | Medium | 22050 Hz |
| en-us-ryan-low | Low | 16000 Hz |
| en-us-lessac-medium (default) | Medium | 16000 Hz |
| en-us-lessac-low | Low | 16000 Hz |
| en-us-amy-low | Low | 16000 Hz |
| en-us-kathleen-low | Low | 16000 Hz |
| en-us-danny-low | Low | 16000 Hz |
| en-gb-southern_english_female-low | Low | 16000 Hz |
| en-gb-alan-low | Low | 16000 Hz |
Voices are downloaded as .tar.gz archives and extracted into the configured voice directory
(default ~/.local/share/voxctrl/piper-voices/).
Pocket-TTS voice catalog
| ID | Name |
|---|---|
alba |
Alba (Female, default) |
anna |
Anna (Female) |
vera |
Vera (Female) |
charles |
Charles (Male) |
michael |
Michael (Male) |
Drop a .wav reference clip into the configured pocket_tts.voice_dir (default
~/.local/share/voxctrl/pocket-tts-voices/) to add a custom voice — no re-encoding or extra
metadata needed. The filename (without extension) becomes the voice's id, e.g. narrator.wav
adds "Narrator (Custom)". Naming a clip after a built-in voice (e.g. alba.wav) overrides that
voice's bundled reference clip instead of adding a new entry.
Setup
- Open Settings → TTS
- Pick an engine (Piper, Pocket-TTS, or Espeak-ng) and a voice from the dropdown
- Click ⬇ Download — progress bar in-app; Piper voices land in
~/.local/share/voxctrl/piper-voices/, Pocket-TTS model weights and voice clips are cached via the HuggingFace cache (~/.cache/huggingface/hub/) - For Pocket-TTS, supply your HuggingFace token (
hf_token) to download the gated model - Click ▶ Test Voice to preview
- Toggle Enable TTS
Stop key
Default KEY_ESCAPE. Press it from any window to interrupt TTS playback instantly — playback can
be interrupted mid-generation, not just between utterances. Configure in Settings → TTS → Stop Key.
Response overlay
When enabled (tts.response_overlay), a green "SYSTEM RESPONDING" speaking pill slides up from
the bottom of the recording overlay while TTS plays, with a live mini-equalizer and the active target label —
distinct from the recording state so you always know when the app is speaking.