Go to file
mARTin-B78 813e06c914 feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine
Magazine P1:
- Images shown automatically during playback (assign_images_to_articles
  links each figure to the article whose column it falls in; the client
  shows the last image reached in reading order as playback advances).
- "Play all articles" queues every approved article and advances
  automatically at the end of each rather than stopping.
- Word-level highlighting, explicitly documented as an ESTIMATE (no
  forced-alignment/timestamp source exists): each word's share of the
  sentence's real measured audio duration is proportional to its
  character length. A checkbox turns it off; sentence highlighting
  (measured, not estimated) is unaffected.
- Driving mode: full-screen overlay, two oversized controls, reuses
  whatever is already playing/queued.
- Sidebars/pull-out boxes excluded from narration automatically
  (mark_sidebars: an anomalously narrow column vs. the page's regular
  grid). Fixed a real defect found while building this: a full-width
  headline was left in the column-width analysis and could bridge two
  genuinely separate columns into one, hiding a real sidebar column
  entirely -- caught by a failing test before the fix.

Second OCR engine + fallback ladder:
- KDL Frontier Parser nano wired in via vision_ocr_page_chain (ordered
  engine list, first usable result wins, tesseract last resort).
  Confirmed live: ~3x faster than Baidu (~12s vs ~40s/page) but returns
  plain text with NO coordinate markers, unlike Baidu's labeled regions.
  _whole_page_region treats the whole response as one ambiguous
  full-page block (gated by ocr_quality_score) rather than discarding
  usable text just because it lacks layout structure.

OCR engines page + a real UI bug:
- The OCR & Document Parsing page had no .s-engines-page[data-page="ocr"]
  markup at all -- renderLocalContainers already supported an OCR grid
  generically, but $('dc-grid-ocr') was always null, so the card grid
  silently never rendered. Added an Active OCR Engine panel mirroring
  Active Language Model exactly, plus the Local grid -- both configured
  engines now appear as ready-made cards, no "Add Custom" dialog needed.
- Fixed the audiobook pipeline's "Previous/Next" nav bar bleeding into
  every .section-head in the document (confirmed live on the Engines
  page: a "Cast Audiobook" button and "STEP 1/8" hint that made no sense
  there). Changed from a blocklist (naming each unrelated section as
  noticed) to an allowlist of the pipeline's three actual destinations.

Verified live end-to-end: play-all queue building and manual advance,
word-span markup, driving-mode toggle, OCR page showing both engines as
local cards, zero stray nav bars on the Engines page, no console errors.

20 new backend tests (156 total in the magazine module).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 23:39:34 +02:00
.github/workflows feat: add request correlation diagnostics 2026-09-04 10:58:46 +02:00
architecture feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +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 feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +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 test: preserve dialogue-only recast merges 2026-09-04 15:31:45 +02:00
logs Replace named Docker volume with local bind-mount folders 2026-05-29 15:31:46 +02:00
routes feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +02:00
scratch chore: release 1.21.1 - Testing and scratch scripts 2026-08-23 09:27:34 +02:00
scripts perf: lazy-load section feature modules 2026-09-04 10:23:20 +02:00
static feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +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 feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +02:00
workflows Release v1.22.2 2026-09-03 16:12:02 +02:00
.gitignore chore: ignore local Test-Magazin sample scans 2026-09-06 15:44:02 +02:00
2026-07-16 inventory.md chore: release 1.21.1 - Testing and scratch scripts 2026-08-23 09:27:34 +02:00
CHANGELOG.md feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +02:00
CLAUDE.md docs: add agent instructions (CLAUDE.md) 2026-08-26 13:33:39 +02:00
docker-compose.yml security: isolate docker socket behind proxy 2026-09-04 07:55:00 +02:00
Dockerfile feat: add magazine projects with layout-aware article extraction 2026-09-05 12:46:54 +02:00
guided_installer.py Studio Notebook redesign: warm cream theme, sidebar, multi-file static structure 2026-05-25 19:53:01 +02:00
Manual.md feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +02:00
package.json test: add frontend quality gate 2026-09-04 09:26:01 +02:00
playwright.config.js Stabilize browser smoke tests for current UI 2026-08-19 16:59:39 +02:00
portainer-stack.yml Release v1.22.2 2026-09-03 16:12:02 +02:00
PROJECT_MAP.md feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +02:00
README.md Improve voice workflows and add user manual 2026-06-04 08:23:18 +02:00
requirements.txt feat: add magazine projects with layout-aware article extraction 2026-09-05 12:46:54 +02:00
server.py feat: add magazine projects with layout-aware article extraction 2026-09-05 12:46:54 +02:00
sonar-project.properties Release v1.22.2 2026-09-03 16:12:02 +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
test_flux_portrait.png chore: release 1.21.1 - Testing and scratch scripts 2026-08-23 09:27:34 +02:00
test_pollinations_portrait.png chore: release 1.21.1 - Testing and scratch scripts 2026-08-23 09:27:34 +02:00
TODO.md feat: complete magazine P1 (images, play-all, karaoke, driving mode, sidebar exclusion) + OCR engines page + second OCR engine 2026-09-06 23:39:34 +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 fix: preserve custom OCR engine cards 2026-09-05 08:11:57 +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 API — POST /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 5–20 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.