CrispTTS is a versatile command-line Text-to-Speech (TTS) tool designed for synthesizing German speech using a variety of popular local and cloud-based TTS engines. Its modular architecture allows for easy maintenance and straightforward addition of new TTS handlers.
| Project | Role |
|---|---|
| Susurrus | Python GUI + CLI — 30+ ASR, 12 TTS, translation |
| CrispASR | C++ ASR/TTS engine — 26+ ASR, 18+ TTS backends, ggml inference |
| CrispTTS | This repo — Python TTS CLI with 35+ handlers |
| CrisperWeaver | Flutter transcription app — desktop + mobile |
NOTE: This is in experimental / work in progress state. Some Python-only models may be broken due to dependency conflicts. The CrispASR-based handlers (crispasr_*) are the most reliable — they use native C++ inference with no Python ML dependencies.
- 35+ TTS Engine Support:
- CrispASR native C++ engines (14 backends, auto-download, no Python ML deps):
- Kokoro (multilingual, Apache 2.0)
- Orpheus + Kartoffel-Orpheus DE (19 German speakers, llama3.2 license)
- Qwen3-TTS (voice cloning + voice design, Apache 2.0)
- Chatterbox (CFM synthesis, MIT)
- VibeVoice TTS (voice cloning)
- IndexTTS (zero-shot cloning, Apache 2.0)
- VoxCPM2 (48 kHz, 30 languages, Apache 2.0)
- F5-TTS (flow-matching, voice cloning, Apache 2.0)
- MeloTTS (VITS2, 44.1 kHz, MIT)
- Piper (250+ community voices, 30+ languages — faster than Python Piper)
- BananaMind-TTS (Tacotron-lite + HiFi-GAN, en/de)
- Dots.TTS (Qwen2.5 LLM + DiT + BigVGAN, 48 kHz, CAM++ voice cloning)
- CosyVoice3 (multi-GGUF: LLM+flow+CAM+++HiFT, voice cloning)
- CSM/Sesame (Llama backbone + Mimi codec, causal mode, voice cloning)
- Microsoft Edge TTS (cloud-based, requires
edge-tts) - Coqui TTS (XTTS v2, VITS, etc.)
- Piper (local ONNX, requires
piper-tts) - Orpheus GGUF (local, requires
llama-cpp-python) - Orpheus via LM Studio / Ollama API
- OuteTTS (LlamaCPP or HF backend)
- SpeechT5 (German fine-tune via HF Transformers)
- FastPitch (German via NeMo)
- mlx-audio (Bark, Kokoro, Dia — Apple Silicon)
- LLaSA (hybrid, German, multilingual variants)
- F5-TTS (MLX/PyTorch)
- Kokoro ONNX (lightweight)
- TTS.cpp (GGUF models)
- Zonos (acoustic conditioning)
- Chatterbox Python (Kartoffelbox)
- CrispASR native C++ engines (14 backends, auto-download, no Python ML deps):
- AI Audio Watermarking & Provenance:
- WavMark neural watermark (MIT license — code + model weights;
pip install wavmark) - Spread-spectrum watermark (always on, imperceptible, ~38 dB SNR)
- AudioSeal neural watermark (optional upgrade via
pip install audiosealor CrispASR GGUF) - WAV LIST/INFO, MP3 ID3v2, FLAC Vorbis comment, and Opus/OGG metadata marking audio as AI-generated
- C2PA content credentials signing (optional,
pip install c2pa-python) - Voice-cloning consent gate (
--i-have-rightsCLI /"i_have_rights": trueAPI) - Spoken AI disclaimer prepended to voice-cloned audio
- Persistent consent audit log at
~/.cache/crisptts/consent_audit.log
- WavMark neural watermark (MIT license — code + model weights;
- CrispASR Integration:
--verify: ASR roundtrip verification of TTS output quality--translate: Pre-synthesis translation (EN→DE via m2m100/MadLad)--speech-speed: Rate multiplier (maps to CrispASR--pace)--trim-silence: Remove leading/trailing silence from output--tts-steps: Diffusion model inference steps (quality vs speed)--tts-language: Override language for multilingual models--pitch-shift: Pitch shift in Hz for FastPitch backends--instruct: Natural-language voice descriptions (Qwen3-TTS VoiceDesign)--stream: Stream audio playback during synthesis--output-sample-rate: Resample output to target sample rate
- OpenAI-Compatible API Server (
--server):POST /v1/audio/speech— drop-in replacement for OpenAI TTSGET /v1/audio/models— list all configured models- Voice-cloning consent gate (returns 403 if
i_have_rightsmissing) - All responses watermarked + metadata-injected
- Text Input Flexibility: Synthesize from CLI,
.txt,.md,.html,.pdf,.epub - Smart Text Chunking: Automatic sentence-boundary splitting for long texts
- Customizable Output: Save audio to
.wav,.mp3,.flac, or.opus - Direct Playback: Play synthesized audio immediately
- Voice Selection: Override default voices/speakers for most models
- Model Parameter Tuning: JSON-formatted parameters for fine-tuning
- Comprehensive Testing:
--test-all: Test all models with default voices--test-all-speakers: Test all models with all configured voices- 254+ unit and live tests
- Modular Design:
config.py+utils.py+handlers/+main.py - Logging: Configurable logging levels
- Automatic Patching: Runtime monkeypatches for library compatibility
crisptts_project/
├── main.py # Main CLI application script
├── config.py # Model configurations and global constants
├── utils.py # Shared utility functions and classes
├── watermark.py # Audio watermarking, metadata, consent gate, C2PA
├── chunking.py # Smart sentence-boundary text splitting
├── server.py # OpenAI-compatible HTTP API server
├── decoder.py # User-provided decoder for Orpheus models (if used)
├── handlers/ # Package for individual TTS engine handlers
│ ├── __init__.py # Makes 'handlers' a package, exports handler functions
│ ├── crispasr_handler.py # CrispASR native C++ TTS (10 backends)
│ ├── edge_handler.py # Edge TTS cloud service handler
│ ├── piper_handler.py # Piper TTS (ONNX) handler
│ ├── orpheus_gguf_handler.py # Local Orpheus GGUF model handler
│ ├── orpheus_api_handler.py # Handlers for LM Studio and Ollama API
│ ├── outetts_handler.py # OuteTTS model handler
│ ├── speecht5_handler.py # SpeechT5 model handler
│ ├── nemo_handler.py # NeMo FastPitch handler
│ ├── coqui_tts_handler.py # Coqui TTS handler (for XTTS, VITS etc.)
│ ├── kartoffel_handler.py # Orpheus "Kartoffel" Transformers handler
│ ├── kokoro_onnx_handler.py # Kokoro (multilingual but no German) ONNX handler
│ ├── llasa_hybrid_handler.py # LLaSA Hybrid handler
│ ├── tts_cpp_handler.py # TTS.cpp handler supporting GGUF models
│ ├── f5_tts_handler.py # F5-TTS handler (MLX/PyTorch)
│ ├── zonos_handler.py # Zonos acoustic conditioning handler
│ ├── chatterbox_handler.py # Chatterbox/Kartoffelbox handler
│ └── mlx_audio_handler.py # Handler for mlx-audio library (e.g., Bark)
├── tests/ # Unit and integration tests
├── requirements.txt # Python package dependencies
├── pyproject.toml # Project metadata and build config
└── README.md # This documentation file
- Python 3.10+ and
pipfor installing packages - For
mlx-audiobased models: Apple Silicon Mac is required for GPU acceleration - For
TTS.cppa C++ compiler and CMake are required to build the engine
-
Clone/Download Files
git clone https://github.com/CrispStrobe/CrispTTS
-
Create a Virtual Environment (Recommended):
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install Dependencies:
pip install -r requirements.txt
Optional feature groups:
pip install crisptts[watermark-mit] # WavMark neural watermark (MIT license) pip install crisptts[metadata] # FLAC/Opus metadata via mutagen pip install crisptts[provenance] # C2PA content credentials pip install crisptts[dev] # ruff, bandit, pytest
Note: Some libraries like PyTorch, NeMo, LlamaCPP, and
mlx-audiocan have specific installation needs depending on your OS and hardware (e.g., CUDA for Nvidia GPUs, Metal for Apple Silicon). Please refer to their official documentation if you encounter issues. Ensure you haveffmpeginstalled and available in your system's PATH if you encounter issues with audio file format conversions or direct playback (some underlying libraries might need it). -
Install and Build Engine-Specific Dependencies (required for certain handlers):
For TTS.cpp: Clone and build the TTS.cpp project separately.
git clone https://github.com/mmwillet/TTS.cpp.git cd TTS.cpp cmake -B build cmake --build build --config Release cd ..
For kokoro-onnx: Install the Python package and download model files.
pip install kokoro-onnx
-
Environment Variables (Optional but Recommended):
HF_TOKEN: If you need to download models from gated or private Hugging Face repositories, set this environment variable with your Hugging Face API token:export HF_TOKEN="your_huggingface_token_here"
GGML_METAL_NDEBUG=1: Set automatically bymain.pyto reduce verbose Metal logs fromllama-cpp-pythonon macOS.
The config.py file is central to defining which TTS models are available and their default settings.
-
GERMAN_TTS_MODELSDictionary: This is the primary configuration structure. Each key is a uniqueMODEL_IDused in the CLI. The value is a dictionary containing:"handler_function_key"(Optional, defaults toMODEL_ID): The key used to look up the synthesis function inhandlers.ALL_HANDLERS- Specific parameters for that model (e.g.,
model_repo_id,default_voice_id, API URLs,onnx_repo_id, etc.) "notes": A brief description of the model
-
mlx-audioBark Configuration Example: To use themlx-audioBark model, your configuration might look like this, enabling the dual-source strategy for voice prompts (main model frommlx-community, voice NPYs fromsuno):"mlx_audio_bark_de": { "handler_function_key": "mlx_audio", "mlx_model_path": "mlx-community/bark-small", # Main MLX model # Voice prompts will be fetched by the patched handler from "suno/bark-small" "default_voice_id": "v2/de_speaker_3", "available_voices": ["v2/de_speaker_0", "v2/de_speaker_1", "v2/de_speaker_3", "..."], "lang_code": "de", "sample_rate": 24000, "notes": "mlx-audio (Bark) with main model from mlx-community/bark-small and voices from suno/bark-small (via patch)." },
-
Global Constants: API URLs, default voice names, and sample rates are also defined here
-
Adding/Modifying Models: To add a new variation of an existing engine or a completely new engine (after creating its handler), you would add a new entry to
GERMAN_TTS_MODELS
All interactions are done through main.py from your project's root directory.
python main.py [ACTION_FLAG | --model-id <MODEL_ID> [OPTIONS]]
# or using the --backend shortcut for CrispASR engines:
python main.py --backend kokoro --input-text "Hello" --output-file out.wav| Flag | Description |
|---|---|
--list-models |
List all configured TTS models with their notes |
--voice-info MODEL_ID |
Show available voices/speakers for a model |
--test-all |
Test all models with default voices (requires --input-text or --input-file) |
--test-all-speakers |
Test all models with ALL configured voices |
--skip-models M1 M2 ... |
Skip specific model IDs during --test-all / --test-all-speakers |
--detect-watermark FILE |
Detect AI-generated watermark in a WAV file and report confidence |
--server |
Run as HTTP server with OpenAI-compatible endpoints |
--check |
With --list-models: probe CrispASR backends for availability |
| Flag | Default | Description |
|---|---|---|
--model-id MODEL_ID |
— | TTS model to use (see --list-models for choices) |
--backend NAME |
— | Shortcut for CrispASR backends (e.g., kokoro, piper, dots-tts) |
--input-text TEXT |
— | Text to synthesize (mutually exclusive with --input-file) |
--input-file PATH |
— | Input file: .txt, .md, .html, .pdf, .epub |
--output-file PATH |
— | Save audio to file (format detected from extension: .wav, .mp3, .flac, .opus) |
--output-dir DIR |
tts_test_outputs |
Output directory for --test-all / --test-all-speakers |
--play-direct |
off | Play audio immediately after synthesis |
--german-voice-id ID |
model default | Override voice/speaker (name, ID, or path to .wav for cloning) |
--model-params JSON |
— | JSON string of model-specific parameters, e.g. '{"temperature":0.7}' |
--speech-speed FLOAT |
1.0 |
Speech rate multiplier (>1 = faster, <1 = slower) |
--trim-silence |
off | Remove leading/trailing silence from output |
--tts-steps N |
backend default | Diffusion/flow-matching inference steps (quality vs. speed) |
--tts-language LANG |
model default | Override language code for multilingual models (e.g. de, en, zh, ja) |
--pitch-shift HZ |
0 |
Pitch offset in Hz (positive = higher, negative = lower) |
--instruct TEXT |
— | Natural-language voice description for VoiceDesign models (Qwen3-TTS) |
--output-sample-rate HZ |
native | Resample output to target sample rate (e.g. 16000, 22050, 44100) |
--stream |
off | Stream audio playback during synthesis (CrispASR backends only) |
--ref-text TEXT |
— | Transcript of reference voice audio for inline voice cloning (TADA, dots-tts) |
--no-spoken-disclaimer |
off | Skip the AI-disclosure spoken prefix on voice-cloned audio |
--lexicon TSV_PATH |
— | Custom word→phoneme TSV file for CrispASR pronunciation |
--batch |
off | Split input at blank lines, produce numbered output files |
| Flag | Default | Description |
|---|---|---|
--verify |
off | Run ASR on output for roundtrip quality verification |
--verify-backend NAME |
parakeet |
ASR backend for --verify (e.g. parakeet, whisper) |
--translate |
off | Translate input text before synthesis |
--translate-from LANG |
en |
Source language for translation |
--translate-to LANG |
de |
Target language for translation |
--translate-backend NAME |
m2m100 |
Translation backend (m2m100 or madlad) |
| Flag | Default | Description |
|---|---|---|
--no-watermark |
off | Disable watermarking (debug only — not recommended) |
--watermark-model PATH |
— | Path to AudioSeal GGUF model for neural watermarking |
--i-have-rights |
off | Consent attestation for voice-cloning models (required) |
--c2pa-cert PEM |
— | X.509 PEM certificate for C2PA content credentials |
--c2pa-key PEM |
— | PEM private key for C2PA content credentials |
| Flag | Default | Description |
|---|---|---|
--server |
off | Start the HTTP API server |
--server-host ADDR |
127.0.0.1 |
Server bind address |
--server-port PORT |
8880 |
Server port |
--rate-limit N |
10 |
Max synthesis requests per minute per IP (0=unlimited) |
Parameters are passed as a JSON string. Available keys depend on the backend:
| Key | Backends | Description |
|---|---|---|
temperature |
Most LLM-based | Sampling temperature (higher = more varied) |
seed |
All CrispASR | Random seed for reproducible output |
top_p |
LLM-based | Nucleus sampling threshold |
repetition_penalty |
LLM-based | Penalize token repetition |
tts_steps |
Diffusion/flow | Number of inference steps |
speech_speed |
CrispASR | Rate multiplier (same as --speech-speed) |
pitch_shift |
FastPitch | Hz offset (same as --pitch-shift) |
top_k |
LLM-based | Top-K candidates |
min_p |
LLM-based | Min-P threshold |
cfg_weight |
Chatterbox | Classifier-free guidance weight |
cfg_scale |
Chatterbox, F5, TADA | CFG scale for acoustic conditioning |
exaggeration |
Chatterbox | Emotion exaggeration factor |
length_scale |
VITS | Duration scaling factor |
speaker_name |
Multi-speaker | Speaker name override |
speaker_id |
Piper | Multi-speaker model ID |
do_sample |
TADA | 0=greedy, 1=sample talker |
num_candidates |
TADA | Acoustic flow-matching candidates |
num_steps |
TADA, flow-matching | FM/diffusion inference steps |
noise_temp |
TADA | FM noise temperature |
noise_scale |
Piper | VITS variance |
noise_w |
Piper | Stochastic duration predictor |
max_speech_tokens |
Chatterbox | Max AR tokens |
Example:
python main.py --model-id crispasr_chatterbox \
--model-params '{"cfg_weight": 3.0, "exaggeration": 0.7, "temperature": 0.8}' \
--input-text "Emotional speech test." --output-file chatterbox.wavList all available models:
python main.py --list-modelsGet information about voices for a specific model:
python main.py --voice-info edge
python main.py --voice-info mlx_audio_bark_deSynthesize text using a specific model:
python main.py --model-id edge --input-text "Hallo, wie geht es Ihnen heute?" --output-file hallo_edge.mp3 --play-directSynthesize text using mlx-audio Bark (German):
python main.py --model-id mlx_audio_bark_de --input-text "Das ist ein Test mit Bark auf Apple Silicon." --output-file bark_test_de.wavUse a specific German voice (if supported by the model):
python main.py --model-id edge --input-text "Ein Test mit einer anderen Stimme." --german-voice-id de-DE-ConradNeural --output-file conrad_test.mp3Check --voice-info <MODEL_ID> for available voice IDs/formats for that model.
Synthesize text from a file:
python main.py --model-id piper_local --input-file ./my_text.txt --output-file piper_output.wavSupported input file types: .txt, .md, .html, .pdf, .epub.
Use model-specific parameters (as a JSON string):
python main.py --model-id orpheus_gguf --input-text "Ein Test." --model-params "{\"temperature\": 0.8, \"n_gpu_layers\": -1}" --output-file orpheus_custom.wavTest all configured models with default voices:
python main.py --input-text "Dies ist ein kurzer Test für alle Modelle." --test-all --output-dir ./test_resultsTest all models with all their configured available voices/speakers:
python main.py --input-text "Ein Test für alle Stimmen." --test-all-speakers --output-dir ./test_results_all_speakersSpeech speed and pitch control:
python main.py --model-id crispasr_kokoro --input-text "Schneller sprechen." --speech-speed 1.3 --output-file fast.wav
python main.py --model-id crispasr_kokoro --input-text "Höher." --pitch-shift 50 --output-file high.wavSilence trimming and resampling:
python main.py --model-id crispasr_kokoro --input-text "Test." --trim-silence --output-sample-rate 16000 --output-file trimmed_16k.wavVoiceDesign — generate voices from text descriptions:
python main.py --model-id crispasr_qwen3_tts_voicedesign --instruct "A calm elderly man" --input-text "Hallo" --output-file calm.wavStreaming playback (hear audio while it generates):
python main.py --model-id crispasr_kokoro --input-text "Dies wird sofort abgespielt." --streamRun as OpenAI-compatible API server:
python main.py --server --server-port 8880
# Then: curl -X POST http://localhost:8880/v1/audio/speech \
# -H "Content-Type: application/json" \
# -d '{"model":"crispasr_kokoro","input":"Hallo Welt","voice":"af_heart"}' \
# --output speech.wavVoice cloning (with consent attestation):
# CLI
python main.py --model-id coqui_xtts_v2_de_clone --i-have-rights \
--input-text "Hallo" --german-voice-id ref_voice.wav --output-file cloned.wav
# API (include i_have_rights in request body)
curl -X POST http://localhost:8880/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"crispasr_f5_tts","input":"Hallo","voice":"ref.wav","i_have_rights":true}' \
--output cloned.wavChange Logging Level (for debugging):
python main.py --model-id edge --input-text "Debug Test." --loglevel DEBUGOverride API URLs (for API-based models like Orpheus LM Studio/Ollama):
python main.py --model-id orpheus_lm_studio --input-text "Hallo API" --lm-studio-api-url http://localhost:5000/v1/completions
python main.py --model-id orpheus_ollama --input-text "Hallo Ollama" --ollama-api-url http://localhost:11223/api/generate --ollama-model-name my-orpheus-ollama-modelRefer to the output of python main.py --list-models for the currently configured models and their notes. The script supports integration with:
- CrispASR native C++ (14 backends: Kokoro, Orpheus, Qwen3-TTS, Chatterbox, VibeVoice, IndexTTS, VoxCPM2, F5-TTS, MeloTTS, Piper, BananaMind, Dots.TTS, CosyVoice3, CSM/Sesame)
- Microsoft Edge TTS
- Piper TTS
- Orpheus GGUF (via llama-cpp-python)
- Orpheus via LM Studio API
- Orpheus via Ollama API
- OuteTTS (LlamaCPP and Hugging Face ONNX backends)
- SpeechT5 (Hugging Face Transformers)
- FastPitch (NeMo / Hugging Face)
- Coqui TTS (XTTS, VITS, etc.)
- Orpheus "Kartoffel" (Transformers-based)
- LLaSA Hybrid (Experimental MLX + PyTorch)
- mlx-audio (e.g., Bark for Apple Silicon)
- F5-TTS (MLX/PyTorch)
- Zonos (acoustic conditioning)
- Chatterbox/Kartoffelbox (Python)
The modular design makes it easy to add support for new TTS engines:
-
Create a New Handler File: In the
handlers/directory, create a new Python file (e.g.,my_new_tts_handler.py) -
Implement Synthesis Function: Inside this file, write a function that takes the standard arguments:
model_config,text,voice_id_override,model_params_override,output_file_str,play_direct. This function should handle all aspects of using the new TTS engine. -
Update
handlers/__init__.py: Import your new function and add it to theALL_HANDLERSdictionary. -
Update
config.py: Add a new entry toGERMAN_TTS_MODELSfor your new engine.
For all Orpheus-based models (GGUF local, LM Studio API, Ollama API, Kartoffel), this project relies on a user-provided decoder.py file located in the project's root directory. This file must contain a function:
def convert_to_audio(multiframe_tokens: list[int], total_token_count: int) -> bytes | None:
# Your implementation here to convert Orpheus token IDs to raw PCM audio bytes
# (16-bit, 24000 Hz, mono)
# Return audio frame bytes, or None/empty bytes on error.
passIf this file or function is missing, Orpheus models will not produce audible output, and a placeholder will be used.
CrispTTS is a synthesis tool — it does not bundle or redistribute
any voice/model weights. Each model is downloaded at runtime from its
upstream repository into a local cache (Piper voices from
rhasspy/piper-voices,
Coqui models via the TTS library, etc.). You obtain the weights directly
from the source, under that source's terms.
You are responsible for honouring each voice's license for whatever
you produce. Licenses vary per voice and are not uniform across
rhasspy/piper-voices — check the upstream MODEL_CARD (and, where it
only says "See URL", the underlying dataset), because the card fields are
self-reported. Notable cases among the German Piper voices CrispTTS lists:
- thorsten, kerstin — CC0 (public domain).
- eva_k, karlsson, ramona — M-AILABS, BSD-style (commercial OK; retain the copyright notice).
- mls — CC-BY 4.0 (attribution required).
- pavoque — CC BY-NC-SA 4.0 (non-commercial) — do not use the output commercially.
For a redistributable, pre-curated permissive-only GGUF set (the same
voices minus the non-commercial/restricted ones, converted for the
CrispASR/CrisperWeaver native runtime), see
cstr/piper-voices-GGUF.
CrispTTS automatically marks all synthesized audio as AI-generated using a multi-layered provenance system.
All outputs are watermarked — CLI, --test-all, and API server responses. CrispASR C++ backends watermark at the binary level; all other handlers are watermarked in Python post-synthesis.
| Layer | What | Status | Install |
|---|---|---|---|
| WavMark | Neural watermark (MIT license, 16-bit payload, >38 dB SNR) | Auto-detected (preferred) | pip install wavmark |
| Spread-spectrum | Frequency-domain watermark (32 bins, alpha=0.08, ~38 dB SNR) | Always active | Built-in (numpy) |
| AudioSeal | Neural watermark (Meta, 16-bit message, sample-rate aware) | Auto-detected | pip install audioseal |
| WAV/MP3/FLAC/Opus metadata | LIST/INFO, ID3v2, Vorbis comments — AI_GENERATED=true |
Always active | Built-in (FLAC/Opus: pip install mutagen) |
| C2PA credentials | Signed provenance manifests (trainedAlgorithmicMedia) |
Opt-in | pip install c2pa-python |
| Spoken disclaimer | AI disclosure prepended to voice-cloned audio | Auto for cloning | Built-in |
| Consent gate | Voice-cloning attestation + persistent audit logging | Required for cloning | Built-in |
Watermark backend priority: WavMark (MIT) > AudioSeal (Python) > CrispASR GGUF > spread-spectrum (always-on fallback). Neural backends are lazy-loaded on first synthesis — --list-models and --help remain instant.
Voice-cloning models require explicit consent attestation before synthesis is allowed:
- CLI:
--i-have-rightsflag required (synthesis blocked without it) - API:
"i_have_rights": truein request body (returns 403 without it) - Detection: triggered by handler key, model ID keywords (
clone,xtts,zeroshot,vibevoice,indextts,voxcpm2,qwen3_tts,f5_tts,zonos,chatterbox), or.wavvoice path - Audit log: written to stderr AND
~/.cache/crisptts/consent_audit.log - Spoken disclaimer: "This audio was generated by artificial intelligence." prepended to cloned output (generated via CrispASR kokoro, Edge TTS fallback, beep marker last resort)
| Feature | CrispTTS | CrispASR | CrisperWeaver |
|---|---|---|---|
| Spread-spectrum watermark | numpy (Python) | C++ header-only | Dart LSB + native FFI |
| WavMark neural watermark (MIT) | Python (wavmark) | — | — |
| AudioSeal neural watermark | Python + crispasr GGUF | C++ ggml (GGUF) | via CrispASR FFI |
| WAV LIST/INFO metadata | ISFT + ICMT | ISFT + ICMT | ISFT + ICMT + IART + ICRD |
| MP3 ID3v2 tags | TXXX (AI_GENERATED) | TXXX (AI_GENERATED) | TXXX (AI_GENERATED) |
| FLAC/Opus metadata | Vorbis comments (mutagen) | — | — |
| C2PA content credentials | c2pa-python (optional) | c2pa-c (compile-time) | — |
| Spoken AI disclaimer | CrispASR kokoro / Edge TTS / beep | Native TTS (cached) | Beep marker |
| Voice-cloning consent gate | CLI + API (403) | CLI + server JSON | GDPR Art. 9(2)(a) consent files |
| Consent audit logging | stderr + consent_audit.log |
[CONSENT] stderr |
[CONSENT] log + .consent.json |
| Post-embed verification | detect after save | detect after save | detect after embed |
| Watermark detection CLI | --detect-watermark |
--detect-watermark |
detect in service |
| Cross-project detection | Yes (shared PRNG key) | Yes (shared PRNG key) | Yes (via CrispASR FFI) |
# Default: spread-spectrum watermark + metadata (no extra deps)
python main.py --model-id edge --input-text "Hallo" --output-file out.mp3
# With WavMark neural watermark (MIT, preferred)
pip install wavmark
python main.py --model-id edge --input-text "Hallo" --output-file out.mp3
# With C2PA content credentials
pip install c2pa-python
python main.py --c2pa-cert cert.pem --c2pa-key key.pem --model-id edge --input-text "Hallo" --output-file out.mp3
# Voice-cloning models require consent attestation (spoken disclaimer auto-prepended)
python main.py --model-id coqui_xtts_v2_de_clone --i-have-rights --input-text "Hallo" --output-file out.wav
# Detect watermark in existing audio
python main.py --detect-watermark out.wav
# Disable watermarking (debug only)
python main.py --no-watermark --model-id edge --input-text "Hallo" --output-file out.mp3from watermark import watermark_detect
import soundfile as sf
pcm, sr = sf.read("out.wav", dtype="float32")
confidence = watermark_detect(pcm, sample_rate=sr)
print(f"Watermark confidence: {confidence:.3f}") # >0.65 = AI-generatedThe spread-spectrum watermark uses the same PRNG seed (0x437269737041535F), FFT parameters, and bin selection as CrispASR's C++ implementation and CrisperWeaver's native FFI path. Audio watermarked by any project in the ecosystem can be detected by the others.
CrispTTS includes an OpenAI-compatible HTTP server for integration with applications that use the OpenAI TTS SDK.
# Start the server
python main.py --server --server-port 8880
# Or run directly
python server.py --host 0.0.0.0 --port 8880| Method | Path | Description |
|---|---|---|
| POST | /v1/audio/speech |
Synthesize audio (OpenAI-compatible) |
| GET | /v1/audio/models |
List available models and voices |
| GET | /health |
Health check |
{
"model": "crispasr_kokoro",
"input": "Hallo, wie geht es Ihnen?",
"voice": "af_heart",
"response_format": "wav",
"speed": 1.0,
"i_have_rights": false
}The i_have_rights field is required (and must be true) for voice-cloning models. Omit it or set to false for non-cloning models.
Response: audio bytes with Content-Type and Content-Disposition: attachment headers. Features:
- All output watermarked (
X-CrispTTS-Watermarked: true) - Voice-cloning models return 403 unless
i_have_rightsis set - Concurrent requests handled via threaded server
- Rate limiting: 10 requests/minute/IP (configurable via
--rate-limit) - Synthesis caching: identical requests served from cache (
X-CrispTTS-Cache: hit) - Enhanced
/health: reports loaded handlers, memory RSS, registered backends
espeak-ng for Kokoro: The Kokoro backend requires espeak-ng for phonemization. Install via:
pip install py-espeak-ng # installs espeak-ng CLI to ~/.local/bin
# or system-wide: apt install espeak-ngCrispASR voice paths: The CrispASR binary auto-downloads models but voice packs need full paths for older binary versions. Use the cached path directly:
python main.py --model-id crispasr_kokoro \
--german-voice-id ~/.cache/crispasr/kokoro-voice-af_heart.gguf \
--input-text "Test" --output-file out.wavMissing Libraries: If a specific TTS engine fails, ensure you have installed all its required libraries via pip install -r requirements.txt and any extra steps mentioned in their documentation.
mlx-audio Bark Specifics:
- This handler currently requires the main MLX model to be from a repository like
mlx-community/bark-small(which should provide MLX-compatible.safetensorsor model files) - The voice prompts (speaker embeddings) are fetched from
suno/bark-smallby default (due to an included monkey patch inmlx_audio_handler.py) which has a comprehensive set of speaker prompts as separate.npyfiles. This dual-source setup is necessary becausemlx-community/bark-smallhas limited voice prompt files in the required format - If mlx-audio's
load_modelfunction reports "No safetensors found" for the mainmlx_model_path, you may need to convert the target Bark model to MLX format usingpython -m mlx_audio.tts.convertand pointmlx_model_pathto the local converted directory. The voice prompt patch in the handler is designed to work with either an HF repo ID or a local path formlx_model_pathwhen determining how to fetch/locate the.npyprompts fromsuno/bark-smallor aspeaker_embeddingssubfolder
API Keys/Servers: API-based models require the respective servers (LM Studio, Ollama) to be running and accessible.
Model Downloads: First-time use of a model that needs to be downloaded from Hugging Face Hub might take some time. Ensure you have an internet connection. Set HF_TOKEN for gated models.
Verbose Output: Use --loglevel DEBUG for detailed diagnostic information if you encounter issues.
RAM Usage: Local GGUF and large Transformer models can be memory-intensive. Ensure your system has sufficient RAM.
Paths: When providing paths for --input-file, --output-file, or speaker WAV files (--german-voice-id), use appropriate relative or absolute paths.