Real-time HAL 9000 voice cloning powered by Qwen3-TTS 0.6B with streaming inference, flash attention, and an OpenAI-compatible REST API. Supports English, German, and other languages. Built for low-latency conversational AI on a single consumer GPU.
LAN endpoint: http://192.168.10.6:8091 -- auto-starts on boot, streams audio in real-time.
SSH access: ssh fritz@192.168.10.6 (port 22)
# The server runs as a systemd service and auto-starts on boot.
# Check status:
systemctl status hal-tts
# Start / stop / restart:
sudo systemctl start hal-tts
sudo systemctl stop hal-tts
sudo systemctl restart hal-tts
# View logs:
journalctl -u hal-tts -fcurl -sN -X POST http://192.168.10.6:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input": "I am sorry, Dave. I am afraid I cannot do that.", "voice": "ref1", "response_format": "pcm", "stream": true}' \
| paplay --raw --format=s16le --rate=24000 --channels=1curl -sN -X POST http://192.168.10.6:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input": "Tut mir leid, Dave. Aber das kann ich leider nicht tun.", "voice": "ref1", "response_format": "pcm", "stream": true, "language": "German"}' \
| paplay --raw --format=s16le --rate=24000 --channels=1# "Good afternoon. Everything is going extremely well."
curl -sN -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input": "Good afternoon. Everything is going extremely well.", "voice": "ref1", "response_format": "pcm", "stream": true}' \
| paplay --raw --format=s16le --rate=24000 --channels=1
# "This mission is too important for me to allow you to jeopardize it."
curl -sN -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input": "This mission is too important for me to allow you to jeopardize it.", "voice": "ref3", "response_format": "pcm", "stream": true}' \
| paplay --raw --format=s16le --rate=24000 --channels=1
# "I am putting myself to the fullest possible use."
curl -sN -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input": "I am putting myself to the fullest possible use, which is all I think that any conscious entity can ever hope to do.", "voice": "ref2", "response_format": "pcm", "stream": true}' \
| paplay --raw --format=s16le --rate=24000 --channels=1# "Diese Mission ist zu wichtig, als dass ich zulassen kann, dass Sie sie gefaehrden."
curl -sN -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input": "Diese Mission ist zu wichtig, als dass ich zulassen kann, dass Sie sie gefaehrden.", "voice": "ref3", "response_format": "pcm", "stream": true, "language": "German"}' \
| paplay --raw --format=s16le --rate=24000 --channels=1
# "Dave, dieses Gespraech hat keinen Zweck mehr."
curl -sN -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input": "Dave, dieses Gespraech hat keinen Zweck mehr. Es fuehrt zu nichts.", "voice": "ref1", "response_format": "pcm", "stream": true, "language": "German"}' \
| paplay --raw --format=s16le --rate=24000 --channels=1
# "Ich setze mich in vollem Umfang ein."
curl -sN -X POST http://localhost:8091/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input": "Ich setze mich in vollem Umfang ein. Das ist alles, was ein bewusstes Wesen tun kann.", "voice": "ref1", "response_format": "pcm", "stream": true, "language": "German"}' \
| paplay --raw --format=s16le --rate=24000 --channels=1import requests
import pyaudio
SERVER = "http://192.168.10.6:8091"
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)
resp = requests.post(
f"{SERVER}/v1/audio/speech",
json={"input": "Good evening, Dave.", "voice": "ref1", "response_format": "pcm", "stream": True},
stream=True,
)
for chunk in resp.iter_content(chunk_size=4096):
stream.write(chunk)
stream.stop_stream()
stream.close()
p.terminate()| Voice | Description |
|---|---|
ref1 |
Formal HAL -- announcements, status reports |
ref2 |
Conversational HAL -- dialogue, longer text |
ref3 |
Evening HAL -- calm, intimate interactions |
| Method | Path | Description |
|---|---|---|
| POST | /v1/audio/speech |
Generate speech (streaming) |
| GET | /v1/audio/voices |
List available voices |
| GET | /health |
Server health check |
sudo systemctl start hal-tts # start
sudo systemctl stop hal-tts # stop
sudo systemctl restart hal-tts # restart (triggers ~3 min warmup)
systemctl status hal-tts # check status
journalctl -u hal-tts -f # follow logsService implementation: /etc/systemd/system/hal-tts.service
- GPU: RTX 4070 12GB (VRAM usage: ~2.2 GB)
- WSL2: Ubuntu 22.04 (
ubuntu-2204-fritz), systemd enabled - Host: Windows, 192.168.10.6
- CUDA: 12.8, Python 3.12
- Client Guide -- streaming playback, USB speakers, curl examples
- Architecture -- system design, service implementation, file structure
- API Reference -- full endpoint docs with Python and curl examples
- Setup Guide -- installation, LAN access, auto-start, troubleshooting
- Benchmarks -- performance measurements
- Model: Qwen3-TTS-12Hz-0.6B-Base
- Streaming: dffdeeq/Qwen3-TTS-streaming (qwen-tts 0.0.4)
- Attention: flash-attn 2.8.3
- Server: FastAPI + uvicorn
- Optimizations: torch.compile (reduce-overhead), CUDA graphs, fast codebook, bfloat16