# slug-tts-stt: giving a mini-PC ears and a mouth, on two different chips

*2026-08-06*

> Speech-to-text on the XDNA2 NPU and text-to-speech on the Radeon iGPU of one Ryzen AI box — split across accelerators so they never contend, measured and reboot-verified.


A voice assistant needs two things from its hardware: it must hear you and it must answer you. On a single-box setup those workloads fight for the same chip — unless you deliberately split them. That's the whole idea here: STT runs on the **XDNA2 NPU** (Whisper large-v3-turbo via FastFlowLM), TTS runs on the **Radeon 840M iGPU** (Qwen3-TTS 1.7B via qwentts.cpp/Vulkan). Both exposed as OpenAI-compatible HTTP endpoints on your LAN. The reasoning brain lives elsewhere; this box is only ever the audio edge.

The claims are measured, not vibes. One STT and one TTS request running strictly simultaneously cost +1.8% and +3.3% versus running alone — confirmed structurally by open file descriptors (the STT process holds `/dev/accel0`, the TTS process holds `/dev/dri`, zero overlap). Time to first audio on streaming TTS is 0.20 seconds.

And the honest part, written down where nobody can miss it: end-to-end is 3.73s against a <1s conversational target, and the culprit is ~2.2s of *fixed per-request overhead* inside Whisper large-v3-turbo — flat against input length, present in Vulkan whisper.cpp too. The NPU is actually slower than the iGPU for ASR on this host. It was chosen anyway, because isolation is the point: TTS owns the iGPU outright.

Features:

- Two OpenAI-shaped LAN endpoints: `POST :8081/v1/audio/transcriptions` and `POST :8092/v1/audio/speech`
- Deliberate accelerator isolation with measured concurrency deltas under 4%
- Streaming TTS at 0.20s to first audio
- **Zero-shot voice cloning**: register any reference WAV as a voice, with runaway detection when transcripts don't match
- Reboot-proof voices — an ExecStartPost hook re-registers every clone from a persistent store at boot
- All measured numbers committed with raw logs; known gaps documented instead of buried
- systemd units verified across cold reboots; memlock gotcha documented

Source: [github.com/rahlquist/slug-tts-stt](https://github.com/rahlquist/slug-tts-stt)

