Go to file
mARTin-B78 2013157bda Document the three-book casting validation (v1.20.35)
Re-cast all three volumes end to end and compared each against its existing
cast. Book 03 finished at 9 unknown, below the 25-line cast it replaced, and
its fused multi-turn segments — one voice reading both sides of an exchange —
fell from 60 to 6. Speaker counts stayed level with the hand-corrected books
instead of inflating, confirming the phantom-speaker filter and the label
consolidation both hold.

Agreement with the hand-corrected books is 81-82%. The largest remaining
error is systematic ping-pong inversion, where a whole exchange alternates
onto the wrong speaker, followed by epithets that share no tokens with the
canonical name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 19:44:47 +02:00
claude studio Update voice library UI: convert table layout to card grid, change network to dgx_net 2026-05-25 20:31:27 +02:00
config Guarantee attribution rules server-side, fix highlight corruption (v1.12.92) 2026-07-04 18:19:01 +02:00
core Fix bundle-breaking TDZ throw, use English emotion instructs, forward Fish gen params (v1.20.6) 2026-08-11 22:00:56 +02:00
docker/qwen3-tts-streaming Update voice library UI: convert table layout to card grid, change network to dgx_net 2026-05-25 20:31:27 +02:00
docs chore: Commit all pending local changes 2026-06-22 14:59:37 +02:00
logs Replace named Docker volume with local bind-mount folders 2026-05-29 15:31:46 +02:00
routes Fix Fish-Speech 0-voices bug, add inline emotion-tag reference to Try It Out (v1.20.8) 2026-08-12 00:08:36 +02:00
scripts Add one-command runner for the book01 manual-vs-automated cast comparison 2026-08-12 11:15:35 +02:00
static Reject phantom speakers invented from German noun phrases (v1.20.35) 2026-08-12 16:52:27 +02:00
support/faster-qwen3-tts-config Update voice library UI: convert table layout to card grid, change network to dgx_net 2026-05-25 20:31:27 +02:00
tests Release v1.6.0: a11y (WCAG AA), i18n (DE), PWA, perf, tests, Cast UX 2026-06-03 14:23:35 +02:00
.gitignore release: v1.12.52 2026-06-30 13:53:08 +02:00
CHANGELOG.md Document the three-book casting validation (v1.20.35) 2026-08-12 19:44:47 +02:00
docker-compose.yml chore: Commit all pending local changes 2026-06-22 14:59:37 +02:00
Dockerfile Add routing voice-picker, restore voice filters, fix pedalboard (v1.14.3) 2026-07-08 21:05:45 +02:00
guided_installer.py Studio Notebook redesign: warm cream theme, sidebar, multi-file static structure 2026-05-25 19:53:01 +02:00
package.json Release v1.6.0: a11y (WCAG AA), i18n (DE), PWA, perf, tests, Cast UX 2026-06-03 14:23:35 +02:00
playwright.config.js Release v1.6.0: a11y (WCAG AA), i18n (DE), PWA, perf, tests, Cast UX 2026-06-03 14:23:35 +02:00
portainer-stack.yml chore: Commit all pending local changes 2026-06-22 14:59:37 +02:00
README.md Improve voice workflows and add user manual 2026-06-04 08:23:18 +02:00
requirements.txt Add pedalboard to requirements for audio effects 2026-06-09 03:41:30 +02:00
server.py Add unified Studio casting workflow and fix voice/casting pipeline bugs 2026-07-26 02:03:56 +02:00
start-guided-installer.bat Studio Notebook redesign: warm cream theme, sidebar, multi-file static structure 2026-05-25 19:53:01 +02:00
start-guided-installer.command Studio Notebook redesign: warm cream theme, sidebar, multi-file static structure 2026-05-25 19:53:01 +02:00
start-guided-installer.sh Studio Notebook redesign: warm cream theme, sidebar, multi-file static structure 2026-05-25 19:53:01 +02:00
ttsvc_cli.py Studio Notebook redesign: warm cream theme, sidebar, multi-file static structure 2026-05-25 19:53:01 +02:00
VERSION Reject phantom speakers invented from German noun phrases (v1.20.35) 2026-08-12 16:52:27 +02:00

TTS Voice Creator — Clone & Design

A browser-based voice management studio running in Docker. Clone real voices, design new ones from text descriptions, manage your voice library, and connect any app or AI agent via REST or MCP.


User Manual

For a step-by-step app tutorial, see docs/USER_MANUAL.md.


What it does

[ Drop file / YouTube URL / Record mic ]   or   [ Describe voice in text ]
                     ↓                                       ↓
      [ Interactive waveform trimmer ]          [ Qwen3-TTS VoiceDesign API ]
                     ↓                                       ↓
            [ 24 kHz · mono · 16-bit PCM WAV  +  reference.txt ]
                                   ↓
                     [ Named voice saved to disk ]
                         ↓                  ↓
              [ TTS preview / Library ]   [ /speak · /mcp · /v1 proxy ]

Core workflows:

  • Voice cloning — record or import audio, trim a clean segment, auto-transcribe, normalize loudness, and save as a TTS reference voice.
  • Voice design — describe a voice in plain language; Qwen3-TTS VoiceDesign synthesises one from scratch. Export to the clone library or use live as a virtual voice.
  • Character persona — attach a personality description to any voice; a local LLM rewrites incoming text in that character's style before synthesis.
  • Speak APIPOST /speak for any script, app, or agent, with per-client voice bindings and optional persona rewrite.
  • Native MCP server — built-in at POST /mcp; exposes speak, transcribe, list_captures, and list_profiles as MCP tools for Claude Code, Cursor, or any MCP-aware agent.
  • Global hotkey daemon — run hotkey_daemon.py on the host for push-to-talk dictation that types transcribed text into any focused window.

Requirements

Requirement Notes
Docker Engine + Docker Compose v2 docs.docker.com/get-docker
A Docker network shared with your TTS/STT containers docker network create dgx_net
A Whisper-compatible transcription server See Recommended backends
A TTS server See Recommended backends
ARM64 (aarch64) or x86-64 Image based on python:3.11-slim-bookworm
Internet access during build pip packages; WaveSurfer.js loaded from CDN at runtime

Installation & Setup

1 — Clone the repository

git clone https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2.git
cd tts-voice-creator-clone-and-design-2

2 — Create or reuse a Docker network

docker network create dgx_net   # skip if you already have one

3 — Configure docker-compose.yml

Set the host path for voices and the network name:

volumes:
  - /your/voices/directory:/voices:rw
networks:
  dgx_net:
    external: true

4 — Build and start

docker compose up -d --build

5 — Open the UI

http://<host-ip>:7890

6 — Configure API endpoints

Settings → Connections — fill in at minimum:

Field Example
Voice Clone / Base URL http://host.docker.internal:8020
Voice Design URL http://host.docker.internal:8021
Whisper URL http://host.docker.internal:8010
LLM base URL http://localhost:11434/v1 (for persona rewrite)

Settings persist to a named Docker volume across restarts.


Speech-to-Text

Option Notes
faster-whisper-server Best local option. GPU-accelerated, OpenAI-compatible.
Whisper.cpp server CPU-only, no Python.
Groq Whisper API Fast cloud, generous free tier.
NVIDIA Parakeet http://host.docker.internal:8092 — 200× realtime on GPU.

Text-to-Speech

Option Backend ID Notes
faster-qwen3-tts voice_clone Recommended. Docker wrapper for Qwen3-TTS with auto voice loading.
Qwen3-TTS Voice Design voice_design Prompt-based voice synthesis. Needs Qwen3-TTS-12Hz-1.7B-VoiceDesign.
CustomVoice customvoice Style-controlled synthesis with pre-configured speakers.
Streaming streaming Progressive low-latency WAV playback.
Kokoro FastAPI kokoro OpenAI-compatible. 82M model, ~300 MB RAM, CPU-friendly.
VibeVoice vibevoice Simple POST /tts service. Default: http://192.168.178.8:8027.
NVIDIA Magpie TTS nvidia_magpie Fixed-speaker GPU TTS. http://host.docker.internal:8091.
NVIDIA Magpie Zeroshot nvidia_zeroshot WAV-prompted zero-shot clone NIM.
NVIDIA Magpie Flow nvidia_flow WAV + transcript high-fidelity clone NIM.
ElevenLabs Cloud; browse the shared library in the Get Voices Online tab.

Application Sections

Voices — Clone library

Manage WAV reference voices. Crop and normalize audio, auto-transcribe, set metadata (language, gender, rating, note), and attach a character persona for LLM-based text rewriting.

Feature Details
Add voice Drag-and-drop, URL/YouTube import, microphone recording
Waveform editor Crop to clean segment, normalize loudness to target dBFS
Auto-transcribe Sends audio to the configured STT backend
Character persona Plain-language personality prompt; LLM rewrites text before synthesis
Active / hidden Toggle moves voice between active_voices and hidden_voices
Sync / Calculate dB / Benchmark Bulk tools for library health

Clone a Voice

Step-by-step wizard: import audio → trim → transcribe → save to library.

Design a Voice

Describe a voice in natural language; Qwen3-TTS VoiceDesign generates it from scratch. Export to the clone library or use as a live vd_... virtual voice.

Includes curated sample prompts: acoustic attribute control, age control, timbre reuse, multi-speaker dialogue.

Studio — Get Voices Online

Browse public voice clip sources, scrape and preview direct audio files, import from URLs, and browse the ElevenLabs shared voice library.

Try It Out — TTS Playground

Generate speech from any backend and voice. Features:

  • Backend and voice selector with live availability check
  • Style instruction field (emotion, pacing, tone)
  • Apply character persona toggle — LLM rewrites input text through the voice's personality before synthesis
  • Chunked generation for long texts (split at sentence boundaries)
  • Audio effects: reverb, compressor, chorus, pitch shift
  • Generation history (last 20 clips, in-memory)
  • Playlist with drag-to-reorder and merged WAV export

Also includes an STT → TTS workspace: record or upload speech → transcribe → LLM-refine → re-synthesize with any voice.

Performance — Benchmark

Run latency and quality benchmarks across backends. Batch mode tests every active voice against a configurable sentence set. Results (RTSL, duration, per-voice) are shown in a sortable table and saved to voice metadata.

App Routing

Route incoming OpenAI-compatible TTS requests by app name, voice, and detected language. Rules map generic voice names (default, assistant) to specific library voices. Before/after sounds and per-route backend selection are supported.

Connect Your Apps

Ready-to-copy integration snippets for SillyTavern, Open WebUI, Home Assistant, generic curl, virtual VoiceDesign voices, streaming TTS, and the native MCP server.

Settings

Organized into sub-pages: Connections (all backend URLs including VibeVoice, NVIDIA stack), STT, Generation (extra TTS params per backend), LLM (URL for persona rewrite and text refinement), Voice (scan directory, normalization targets), Output, Captures, General, About (MCP connection commands, /speak examples, hotkey daemon setup).


Character Persona

Each voice can have a personality description stored in its .meta.json sidecar:

{ "persona": "Gruff Scottish engineer, dry wit, technical vocabulary, measured pace." }

When persona rewrite is active, a local LLM at the configured LLM URL rephrases the input text in that character's voice before TTS synthesis. This runs through the same OpenAI-compatible endpoint used for text refinement.

Activate persona rewrite:

  • In the playground: check Apply character persona before generating.
  • Via API: pass "apply_persona": true in the request body to /api/tts-preview or /speak.
  • Via MCP: pass "apply_persona": true to the speak tool.

Edit a voice's persona in the voice inspector (Voices tab → select voice → Character persona section).


Speak API

POST /speak — generate audio from any script or app without needing routing rules.

# Bind a client ID to a voice (persisted)
curl -X PUT http://localhost:7890/speak/bindings/my-script \
  -H "Content-Type: application/json" \
  -d '{"voice": "EN_F_Anna"}'

# Generate speech — voice from binding, optional persona rewrite
curl -X POST http://localhost:7890/speak \
  -H "Content-Type: application/json" \
  -H "X-Voice-Creator-Client-Id: my-script" \
  -d '{"text": "Hello world", "apply_persona": true}' \
  --output speech.wav

# Or specify voice directly
curl -X POST http://localhost:7890/speak \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello world", "voice": "EN_F_Anna", "backend": "voice_clone"}' \
  --output speech.wav

Voice resolution order: explicit voice param → X-Voice-Creator-Client-Id binding → captures_default_voice in Settings.

Binding endpoints:

Method Path Description
GET /speak/bindings List all per-client voice bindings
PUT /speak/bindings/{client_id} Set or update a binding
DELETE /speak/bindings/{client_id} Remove a binding

Native MCP Server

The app ships a built-in MCP server at /mcp (JSON-RPC 2.0, Streamable HTTP transport). No external Python script or extra packages required.

Tools:

Tool Arguments Returns
speak text, voice?, apply_persona? Base64 WAV audio + confirmation text
transcribe audio_base64 Transcribed text
list_captures 20 most recent output WAV files
list_profiles All voice profiles with id, lang, persona, enabled

Connect Claude Code:

claude mcp add voice-creator \
  --transport http \
  --url http://localhost:7890/mcp \
  --header "X-Voice-Creator-Client-Id: claude-code"

Any HTTP MCP client (Cursor, Windsurf, VS Code, etc.):

{
  "mcpServers": {
    "voice-creator": {
      "url": "http://localhost:7890/mcp",
      "headers": { "X-Voice-Creator-Client-Id": "my-agent" }
    }
  }
}

The X-Voice-Creator-Client-Id header binds this client to a voice via PUT /speak/bindings/{id}. Per-client bindings persist in Settings.


Global Hotkey Daemon

hotkey_daemon.py is a standalone host-side script (runs outside Docker) for push-to-talk dictation. Hold Ctrl+Shift+Space to record, release to transcribe and type the result into any focused window.

# Install dependencies (host Python, not Docker)
pip install pynput sounddevice soundfile pyperclip requests

# Linux: install xdotool for direct key injection (falls back to clipboard paste)
sudo apt install xdotool

# Run
python hotkey_daemon.py --server http://localhost:7890
Option Default Notes
--server http://localhost:7890 Voice Creator URL
--backend configured STT backend to use

Platform notes:

  • Linux X11: uses xdotool type for direct injection; clipboard fallback on Wayland.
  • macOS: clipboard paste via Cmd+V; grant Accessibility + Microphone in System Preferences.
  • Windows: clipboard paste via Ctrl+V.

OpenAI-Compatible Proxy

This app exposes a /v1 proxy that any OpenAI-compatible client can use:

Base URL: http://YOUR_HOST:7890/v1
API key:  dummy   (any non-empty value)
Model:    tts-1
Voice:    EN_F_Anna   (or any active voice ID, or vd_<PresetName> for virtual voices)

Routing rules (App Routing tab) run on every request through this proxy.


API Reference

TTS endpoints

Endpoint Description
POST /v1/audio/speech OpenAI-compatible TTS with routing
POST /speak Direct TTS with per-client voice binding
POST /api/tts-preview Preview any backend/voice combination
POST /api/tts-preview with apply_persona:true Preview with LLM persona rewrite
GET /v1/audio/voices List active voice IDs
GET /v1/models List TTS models

STT endpoints

Endpoint Description
POST /v1/audio/transcriptions OpenAI-compatible transcription
POST /api/transcribe-bytes Multipart upload → transcription (used by hotkey daemon)

MCP endpoint

Endpoint Description
POST /mcp JSON-RPC 2.0 handler (initialize, tools/list, tools/call)
GET /mcp SSE keep-alive stream

Voice library

Endpoint Description
GET /api/voices List all voices with metadata
POST /api/save Save a voice from a temp file
POST /api/update-voice-meta Update note, rating, flag, gender, persona
POST /api/voices/calculate-db Normalize dBFS for all active voices
POST /api/voices/benchmark Run latency/quality benchmarks
GET /api/tts-backends List all backends with availability and capabilities

/speak bindings

Endpoint Description
GET /speak/bindings List all per-client voice bindings
PUT /speak/bindings/{id} Set a per-client voice binding
DELETE /speak/bindings/{id} Remove a binding

Voice File Format

Property Value
Format WAV (PCM, uncompressed)
Sample rate 24 000 Hz
Channels Mono
Bit depth 16-bit
Duration 520 seconds
Companion file <id>.reference.txt — verbatim transcript

The app converts all source audio automatically using pydub + ffmpeg.

Sidecar files

File Contents
<id>.reference.txt Reference transcript (required for cloning)
<id>.meta.json Note, rating, flag, gender, enabled, persona, loudness, benchmark
<id>.jpg / .png / .webp Profile picture shown in the library

Naming Convention

{LANG}_{GENDER}_{Name}.wav

Examples: EN_F_Anna.wav, DE_M_Christoph.wav, ZH_F_Mei.wav

Language codes: EN DE ZH FR ES JA KO IT PT RU AR PL NL SV TR HI
Gender codes: F (female) · M (male) · N (neutral)

Custom IDs like narrator_calm are equally valid.


Architecture

Browser (any device on your local network)
  └── port 7890 ──► FastAPI + uvicorn  (server.py)
                         ├── /v1          OpenAI-compatible TTS/STT proxy + routing
                         ├── /speak       Direct TTS with per-client bindings
                         ├── /mcp         Native MCP server (JSON-RPC 2.0)
                         ├── /api/...     Voice library, settings, benchmarks
                         ├── pydub+ffmpeg audio conversion & trimming
                         ├── yt-dlp       YouTube / URL audio extraction
                         ├── Whisper API  transcription (multiple backends)
                         ├── TTS backends voice_clone · voice_design · customvoice
                         │               streaming · kokoro · vibevoice
                         │               nvidia_magpie · nvidia_zeroshot · nvidia_flow
                         └── LLM API      persona rewrite, text refinement

Host machine (optional)
  └── hotkey_daemon.py   global Ctrl+Shift+Space → record → transcribe → type

Frontend (static HTML + Vanilla JS, no build step)
  ├── WaveSurfer.js 7    interactive waveform + region trimmer
  ├── MediaRecorder API  browser microphone recording
  └── MDI v7.4.47        icons

CLI

ttsvc_cli.py talks to the running app over HTTP:

./ttsvc_cli.py settings show
./ttsvc_cli.py settings set --tts-url http://host.docker.internal:8020
./ttsvc_cli.py voices list --all
./ttsvc_cli.py voices enable EN_F_Anna
./ttsvc_cli.py routes openwebui-defaults --en EN_F_Anna --de DE_M_Max
./ttsvc_cli.py tts speak --voice EN_F_Anna --text "Hello world" --out test.wav
./ttsvc_cli.py create-voice --file sample.wav --voice-id EN_F_Anna --transcript "Exact words."

Use --base-url http://HOST:7890 for remote instances.


Guided Installer

python3 guided_installer.py
# or: start-guided-installer.sh  /  .command  /  .bat

Checks Docker, configures containers, generates Compose files, and runs deployment with visible logs.


Volume Mounts

Host path Container path Purpose
${VOICE_HOST_DIR:-./voices} /voices Voice collection (read-write)
Named volume tts-voice-creator-clone-and-design-2 /home/app/.config/tts-voice-creator Settings and presets
./static /app/static Frontend (live-reload without rebuild)
./server.py /app/server.py Backend (live-reload without rebuild)

Troubleshooting

"Cannot connect to TTS/STT API" — ensure both containers share the same Docker network; use container hostnames, not localhost.

Microphone not working — browsers block getUserMedia on non-localhost origins without HTTPS. Use a reverse proxy with TLS for remote access.

Persona rewrite not working — check the LLM URL in Settings → LLM server. The endpoint must be OpenAI-compatible (e.g. Ollama at http://localhost:11434/v1).

MCP tools not appearing — run claude mcp list and confirm voice-creator is shown. Restart Claude Code after adding.

Hotkey daemon not typing — on Linux, install xdotool for X11; on Wayland it falls back to clipboard paste (Ctrl+V). On macOS, grant Accessibility permission.

Voice not appearing after save — restart the TTS container (docker restart <name>). Most engines only scan the voice directory on startup.

Transcription garbled — use large-v3 Whisper model; manually correct the transcript before saving.