Make Qwen3-TTS the default engine, add CPU-only docker support
- Switch default TTS engine from Piper to Qwen3-TTS (1.7B params) - Upgrade to Python 3.12 - Add docker-compose.cpu.yml for CPU-only deployments - Improve GPU configuration with NVIDIA environment variables - Comment out optional engines (Piper, XTTS, Silero, Kokoro) in requirements - Update Makefile with local/local-cpu targets and venv support - Simplify voice_to_speaker.default.yaml for Qwen3-TTS voices - Update docs/MODELS.md with Qwen3-TTS documentation - Add git commit guidelines to CLAUDE.md
This commit is contained in:
parent
4a019cf897
commit
b315659be6
9 changed files with 572 additions and 951 deletions
|
|
@ -79,7 +79,13 @@ owned_by = "UncloseAI"
|
|||
|
||||
**Never view or log secrets** - source them and use them.
|
||||
|
||||
### 4. Documentation Requirements
|
||||
### 4. Git Commit Guidelines
|
||||
|
||||
- **Never add AI attribution** - Do not use `Co-Authored-By: Claude` or similar in commit messages
|
||||
- Write clear, concise commit messages describing what changed and why
|
||||
- Use imperative mood ("Add feature" not "Added feature")
|
||||
|
||||
### 5. Documentation Requirements
|
||||
|
||||
When adding features, update ALL relevant docs:
|
||||
- `Makefile` help text
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.11-slim
|
||||
FROM python:3.12-slim
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/pip pip install -U pip
|
||||
|
||||
|
|
|
|||
145
Makefile
145
Makefile
|
|
@ -14,41 +14,48 @@ REMOTE_USER ?= $(USER)
|
|||
REMOTE_PATH ?= ~/uncloseai-speech
|
||||
CONTAINER_NAME ?= uncloseai-speech-server-1
|
||||
|
||||
.PHONY: help deploy sync restart logs test clean stop start voices voices-piper voices-xtts voices-kokoro test-kokoro voices-silero test-silero voices-chatterbox test-chatterbox push-all hydrate load-test
|
||||
.PHONY: help deploy sync restart logs test clean stop start voices voices-qwen voices-piper voices-xtts voices-kokoro test-kokoro voices-silero test-silero voices-chatterbox test-chatterbox push-all hydrate load-test test-qwen venv venv-run local local-cpu
|
||||
|
||||
help:
|
||||
@echo "🦝 Raccoon TTS Mission - Development Commands"
|
||||
@echo ""
|
||||
@echo "Deployment:"
|
||||
@echo "Quick Start (Docker with GPU):"
|
||||
@echo " make local - Build and run locally with GPU support"
|
||||
@echo " make local-cpu - Build and run locally (CPU only, slower)"
|
||||
@echo " make test - Test Qwen3-TTS endpoint"
|
||||
@echo " make logs - Tail container logs"
|
||||
@echo ""
|
||||
@echo "Quick Start (No Docker):"
|
||||
@echo " make venv - Create Python virtual environment"
|
||||
@echo " make venv-run - Run server in virtual environment"
|
||||
@echo ""
|
||||
@echo "Remote Deployment:"
|
||||
@echo " make deploy - Full deploy: sync files, restart container"
|
||||
@echo " make sync - Sync local files to remote server"
|
||||
@echo " make restart - Restart the Docker container"
|
||||
@echo ""
|
||||
@echo "Development:"
|
||||
@echo " make logs - Tail container logs"
|
||||
@echo " make test - Test TTS endpoint (Piper)"
|
||||
@echo " make test-xtts - Test XTTS HD endpoint"
|
||||
@echo " make voices - Download all voices (Piper + XTTS)"
|
||||
@echo " make voices-piper - Download Piper voices only"
|
||||
@echo " make voices-xtts - Download XTTS voices and samples"
|
||||
@echo " make voices-kokoro - Download Kokoro models"
|
||||
@echo " make test-kokoro - Test Kokoro fast TTS"
|
||||
@echo " make voices-silero - Download Silero models (en, ru, de, es, fr)"
|
||||
@echo " make test-silero - Test Silero TTS endpoint"
|
||||
@echo " make voices-chatterbox - Download Chatterbox models"
|
||||
@echo " make test-chatterbox - Test Chatterbox TTS with emotion control"
|
||||
@echo ""
|
||||
@echo "Testing:"
|
||||
@echo " make hydrate - Hydrate all models by testing ALL 227 voices"
|
||||
@echo " make load-test - Load test with concurrent random voice requests"
|
||||
@echo " make test - Test TTS endpoint (Qwen3-TTS)"
|
||||
@echo " make test-qwen - Test Qwen3-TTS voice cloning"
|
||||
@echo " make hydrate - Test all configured voices"
|
||||
@echo " make load-test - Concurrent load test"
|
||||
@echo ""
|
||||
@echo "Container:"
|
||||
@echo "Other Engines (disabled by default):"
|
||||
@echo " make test-xtts - Test XTTS HD endpoint"
|
||||
@echo " make test-kokoro - Test Kokoro fast TTS"
|
||||
@echo " make test-silero - Test Silero TTS endpoint"
|
||||
@echo " make voices-piper - Download Piper voices"
|
||||
@echo " make voices-xtts - Download XTTS voices"
|
||||
@echo " make voices-kokoro - Download Kokoro models"
|
||||
@echo " make voices-silero - Download Silero models"
|
||||
@echo ""
|
||||
@echo "Container Management:"
|
||||
@echo " make start - Start Docker container"
|
||||
@echo " make stop - Stop Docker container"
|
||||
@echo " make clean - Stop and remove container"
|
||||
@echo ""
|
||||
@echo "Git:"
|
||||
@echo " make push-all - Push to all git remotes (origin + github)"
|
||||
@echo " make push-all - Push to all git remotes"
|
||||
|
||||
sync:
|
||||
@echo "📦 Syncing files to $(REMOTE_HOST)..."
|
||||
|
|
@ -61,6 +68,78 @@ sync:
|
|||
deploy: sync restart
|
||||
@echo "✅ Deployment complete!"
|
||||
|
||||
# ============================================================================
|
||||
# Local Development (Docker)
|
||||
# ============================================================================
|
||||
|
||||
local:
|
||||
@echo "🐳 Building and running with GPU support..."
|
||||
@[ -f speech.env ] || cp sample.env speech.env
|
||||
docker compose up -d --build
|
||||
@echo ""
|
||||
@echo "✅ Container started! Qwen3-TTS model will download on first request (~3.4GB)"
|
||||
@echo " Test with: make test"
|
||||
@echo " View logs: make logs"
|
||||
|
||||
local-cpu:
|
||||
@echo "🐳 Building and running (CPU only - slower inference)..."
|
||||
@[ -f speech.env ] || cp sample.env speech.env
|
||||
docker compose -f docker-compose.cpu.yml up -d --build
|
||||
@echo ""
|
||||
@echo "✅ Container started (CPU mode)!"
|
||||
@echo " Note: Qwen3-TTS is ~10x slower on CPU"
|
||||
@echo " Test with: make test"
|
||||
|
||||
# ============================================================================
|
||||
# Local Development (Python venv - no Docker)
|
||||
# ============================================================================
|
||||
|
||||
VENV_DIR := .venv
|
||||
PYTHON := python3
|
||||
|
||||
venv:
|
||||
@echo "🐍 Creating Python virtual environment..."
|
||||
@if [ ! -d "$(VENV_DIR)" ]; then \
|
||||
$(PYTHON) -m venv $(VENV_DIR); \
|
||||
echo "✅ Virtual environment created at $(VENV_DIR)"; \
|
||||
else \
|
||||
echo "ℹ️ Virtual environment already exists at $(VENV_DIR)"; \
|
||||
fi
|
||||
@echo ""
|
||||
@echo "📦 Installing dependencies..."
|
||||
$(VENV_DIR)/bin/pip install --upgrade pip
|
||||
$(VENV_DIR)/bin/pip install -r requirements.txt
|
||||
@echo ""
|
||||
@echo "✅ Setup complete!"
|
||||
@echo ""
|
||||
@echo "To activate manually:"
|
||||
@echo " source $(VENV_DIR)/bin/activate"
|
||||
@echo ""
|
||||
@echo "To run the server:"
|
||||
@echo " make venv-run"
|
||||
@echo ""
|
||||
@echo "Or run directly:"
|
||||
@echo " $(VENV_DIR)/bin/python speech.py"
|
||||
|
||||
venv-run:
|
||||
@echo "🚀 Starting uncloseai-speech server..."
|
||||
@if [ ! -d "$(VENV_DIR)" ]; then \
|
||||
echo "❌ Virtual environment not found. Run 'make venv' first."; \
|
||||
exit 1; \
|
||||
fi
|
||||
@[ -d "config" ] || mkdir -p config
|
||||
@[ -d "voices" ] || mkdir -p voices
|
||||
@echo ""
|
||||
@echo "Server starting on http://localhost:8000"
|
||||
@echo "Qwen3-TTS model will download on first request (~3.4GB)"
|
||||
@echo ""
|
||||
$(VENV_DIR)/bin/python speech.py
|
||||
|
||||
venv-clean:
|
||||
@echo "🧹 Removing virtual environment..."
|
||||
rm -rf $(VENV_DIR)
|
||||
@echo "✅ Virtual environment removed"
|
||||
|
||||
restart:
|
||||
@echo "🔄 Rebuilding and restarting container on $(REMOTE_HOST)..."
|
||||
ssh $(REMOTE_USER)@$(REMOTE_HOST) "cd $(REMOTE_PATH) && docker compose up -d --build"
|
||||
|
|
@ -81,17 +160,29 @@ logs:
|
|||
@echo "📋 Tailing logs from $(REMOTE_HOST)..."
|
||||
ssh $(REMOTE_USER)@$(REMOTE_HOST) "docker logs -f $(CONTAINER_NAME)"
|
||||
|
||||
test:
|
||||
@echo "🧪 Testing TTS endpoint..."
|
||||
test: test-qwen
|
||||
|
||||
test-qwen:
|
||||
@echo "🧪 Testing Qwen3-TTS endpoint (default model)..."
|
||||
curl -X POST http://$(REMOTE_HOST):8000/v1/audio/speech \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"tts-1","voice":"alloy","input":"Raccoon mission TTS test"}' \
|
||||
-o /tmp/raccoon_test.mp3
|
||||
-d '{"model":"tts-1-qwen","voice":"alloy","input":"Raccoon mission TTS test with Qwen three"}' \
|
||||
-o /tmp/qwen_test.mp3
|
||||
@echo "✅ Test complete! Playing audio..."
|
||||
@firefox /tmp/raccoon_test.mp3 || mpv /tmp/raccoon_test.mp3 || echo "Install firefox or mpv to play audio"
|
||||
@firefox /tmp/qwen_test.mp3 || mpv /tmp/qwen_test.mp3 || echo "Install firefox or mpv to play audio"
|
||||
|
||||
voices: voices-piper voices-xtts voices-silero
|
||||
@echo "✅ All voices downloaded (Piper, XTTS, Silero)!"
|
||||
voices: voices-qwen
|
||||
@echo "✅ Qwen3-TTS ready (model downloads automatically on first use)"
|
||||
|
||||
voices-qwen:
|
||||
@echo "🎤 Qwen3-TTS models download automatically on first use"
|
||||
@echo " Model: Qwen/Qwen3-TTS-12Hz-1.7B-Base (~3.4GB)"
|
||||
@echo " The model will be cached in /app/voices/hub/"
|
||||
@echo ""
|
||||
@echo "To pre-download, run: make test-qwen"
|
||||
|
||||
voices-all: voices-qwen voices-piper voices-xtts voices-silero
|
||||
@echo "✅ All voices downloaded (Qwen, Piper, XTTS, Silero)!"
|
||||
|
||||
voices-piper:
|
||||
@echo "🎤 Downloading all Piper voices..."
|
||||
|
|
|
|||
18
docker-compose.cpu.yml
Normal file
18
docker-compose.cpu.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# CPU-only configuration (no GPU)
|
||||
# Use this if you don't have an NVIDIA GPU
|
||||
# Note: Qwen3-TTS is ~10x slower on CPU
|
||||
|
||||
services:
|
||||
server:
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
image: uncloseai-speech:local
|
||||
env_file: speech.env
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./voices:/app/voices
|
||||
- ./config:/app/config
|
||||
environment:
|
||||
- CUDA_VISIBLE_DEVICES= # Disable CUDA
|
||||
restart: unless-stopped
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
# GPU configuration (NVIDIA CUDA)
|
||||
# Requires: nvidia-container-toolkit installed on host
|
||||
# Install: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html
|
||||
|
||||
services:
|
||||
server:
|
||||
build:
|
||||
|
|
@ -9,13 +13,16 @@ services:
|
|||
volumes:
|
||||
- ./voices:/app/voices
|
||||
- ./config:/app/config
|
||||
# To install as a service
|
||||
environment:
|
||||
- NVIDIA_VISIBLE_DEVICES=all
|
||||
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
#device_ids: ['0', '1'] # Select a gpu, or
|
||||
# Uncomment to select specific GPU(s):
|
||||
# device_ids: ['0']
|
||||
count: all
|
||||
capabilities: [gpu]
|
||||
|
|
|
|||
104
docs/MODELS.md
104
docs/MODELS.md
|
|
@ -2,6 +2,22 @@
|
|||
|
||||
**Raccoon Mission:** Rescue abandoned open-source TTS models and integrate them into uncloseai-speech
|
||||
|
||||
## Default Model: Qwen3-TTS
|
||||
|
||||
**🎯 Qwen3-TTS is now the default and only enabled model.**
|
||||
|
||||
All other models (Piper, XTTS, Silero, Kokoro) are disabled by default. To enable them, uncomment their sections in `voice_to_speaker.yaml`.
|
||||
|
||||
### Quick Start
|
||||
```bash
|
||||
# Test Qwen3-TTS (default)
|
||||
make test
|
||||
|
||||
# The model downloads automatically on first use (~3.4GB)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documentation Index
|
||||
|
||||
### Comprehensive Research
|
||||
|
|
@ -33,7 +49,83 @@ Each model has detailed documentation covering technical specs, integration stat
|
|||
|
||||
## Currently Integrated
|
||||
|
||||
### 1. Piper TTS ✅
|
||||
### 0. Qwen3-TTS ✅ (DEFAULT)
|
||||
|
||||
**Status:** INTEGRATED as tts-1-qwen (DEFAULT MODEL)
|
||||
**Project:** Qwen/Qwen3-TTS (Alibaba, actively maintained)
|
||||
**License:** Apache 2.0
|
||||
**Model:** Qwen3-TTS-12Hz-1.7B-Base
|
||||
|
||||
**Why Default:**
|
||||
- State-of-the-art quality with 1.7B parameters
|
||||
- Extremely low latency (97ms first packet)
|
||||
- Voice cloning from 3-second samples
|
||||
- 10 languages: Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian
|
||||
- Apache 2.0 license (commercial-friendly)
|
||||
- Actively maintained by Alibaba
|
||||
|
||||
**Features:**
|
||||
- Universal end-to-end architecture (no cascading errors)
|
||||
- 12Hz acoustic tokenizer for efficient compression
|
||||
- Dual-track streaming/non-streaming generation
|
||||
- High-fidelity speech reconstruction
|
||||
- Natural language instruction control
|
||||
- Supports both GPU and CPU inference
|
||||
|
||||
**Model Specs:**
|
||||
- Parameters: 1.7B
|
||||
- Sample Rate: ~24kHz
|
||||
- Input: Text + Reference Audio (3+ seconds)
|
||||
- Languages: 10 (zh, en, ja, ko, de, fr, ru, pt, es, it)
|
||||
- Size: ~3.4GB
|
||||
|
||||
**Model Source:**
|
||||
- HuggingFace: `Qwen/Qwen3-TTS-12Hz-1.7B-Base`
|
||||
- Auto-downloaded on first use via huggingface-hub
|
||||
- Cached in `/app/voices/hub/`
|
||||
|
||||
**Integration:**
|
||||
- Used for `tts-1-qwen` model (default)
|
||||
- Voice cloning with reference audio + transcript
|
||||
- Pre-configured with Qwen's demo voice
|
||||
|
||||
**Example Config:**
|
||||
```yaml
|
||||
tts-1-qwen:
|
||||
alloy:
|
||||
ref_audio: https://example.com/reference.wav
|
||||
ref_text: "The exact text spoken in the reference audio"
|
||||
language: English
|
||||
```
|
||||
|
||||
**Custom Voice Setup:**
|
||||
1. Record 3+ seconds of clear speech
|
||||
2. Transcribe the audio exactly
|
||||
3. Add to `voice_to_speaker.yaml`:
|
||||
```yaml
|
||||
tts-1-qwen:
|
||||
my_voice:
|
||||
ref_audio: voices/my_voice_sample.wav
|
||||
ref_text: "Hello, this is my voice sample for cloning."
|
||||
language: English
|
||||
```
|
||||
|
||||
**Makefile Targets:**
|
||||
```bash
|
||||
make test # Test Qwen3-TTS (default)
|
||||
make test-qwen # Test Qwen3-TTS explicitly
|
||||
```
|
||||
|
||||
**Hardware Requirements:**
|
||||
- GPU: NVIDIA with 8GB+ VRAM (recommended)
|
||||
- CPU: Works but slower (~10x)
|
||||
- FlashAttention 2 recommended for lower memory
|
||||
|
||||
**Raccoon Priority:** ⭐⭐⭐⭐⭐ (State-of-the-art, actively maintained, Apache 2.0)
|
||||
|
||||
---
|
||||
|
||||
### 1. Piper TTS (disabled by default) ✅
|
||||
|
||||
> 📖 **See [detailed documentation](models/piper-tts.md)** for comprehensive technical specs and integration guide
|
||||
|
||||
|
|
@ -505,8 +597,8 @@ The following models have detailed documentation but are not yet integrated or p
|
|||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-11-09
|
||||
**Raccoon Status:** 🦝 4 models rescued! Silero and Kokoro TTS integrated successfully
|
||||
**Integration Status:** ✅ Piper (55 voices), XTTS (8 voices), Silero (148 voices), Kokoro (34 voices) | 🎯 Next: Chatterbox, StyleTTS2
|
||||
**API Endpoints:** tts-1, tts-1-hd, tts-1-silero, tts-1-kokoro | /v1/models for discovery
|
||||
**Documentation Status:** 📚 10 models fully documented, 1 comprehensive research overview
|
||||
**Last Updated:** 2026-01-26
|
||||
**Raccoon Status:** 🦝 5 models rescued! Qwen3-TTS is now the default model
|
||||
**Integration Status:** ✅ Qwen3-TTS (default, unlimited voices via cloning) | Disabled: Piper (55), XTTS (8), Silero (148), Kokoro (34)
|
||||
**API Endpoints:** tts-1-qwen (default) | Others available: tts-1, tts-1-hd, tts-1-silero, tts-1-kokoro
|
||||
**Documentation Status:** 📚 11 models fully documented, 1 comprehensive research overview
|
||||
|
|
|
|||
|
|
@ -1,28 +1,31 @@
|
|||
fastapi
|
||||
uvicorn
|
||||
loguru
|
||||
# Qwen3-TTS - state-of-the-art TTS with voice cloning (Apache 2.0)
|
||||
# 1.7B params, 10 languages, 97ms latency, 12Hz tokenizer
|
||||
qwen-tts>=0.1.0
|
||||
# OHF-Voice fork doesn't have installable Python package yet
|
||||
# Stick with PyPI piper-tts but use absolute paths in config
|
||||
piper-tts>=1.2.0
|
||||
# piper-tts>=1.2.0
|
||||
# 🦝 RACCOON TODO: Create our own PyPI package from OHF-Voice fork
|
||||
# git+https://github.com/OHF-Voice/piper1-gpl.git@v1.3.0#subdirectory=src/python_run
|
||||
coqui-tts[languages]
|
||||
# coqui-tts[languages]
|
||||
# Silero TTS - actively maintained, small efficient models
|
||||
# Note: Silero models are loaded via torch.hub, no package install needed
|
||||
# Models: ~50-100MB each, CPU-friendly, real-time capable
|
||||
omegaconf # Required by Silero TTS
|
||||
# omegaconf # Required by Silero TTS
|
||||
# Chatterbox - emotion control, 23 languages (Resemble AI)
|
||||
# Install from git since no PyPI package exists yet
|
||||
# 🦝 RACCOON NOTE: Disabled due to dependency conflict with Coqui TTS
|
||||
# gradio 5.44.1 requires typer<1.0 and >=0.12, but spacy 3.6.x requires typer<0.10.0
|
||||
# TODO: Test Chatterbox in isolated environment or wait for dependency updates
|
||||
# git+https://github.com/resemble-ai/chatterbox.git
|
||||
langdetect
|
||||
# langdetect
|
||||
pyyaml
|
||||
# Kokoro TTS - fast decoder-only architecture
|
||||
# Lightweight decoder-only TTS, 82M params, 24kHz output
|
||||
kokoro>=0.9.2
|
||||
soundfile # Required by Kokoro for audio output
|
||||
# kokoro>=0.9.2
|
||||
soundfile # Required by Qwen3-TTS and Kokoro for audio output
|
||||
transformers>=4.35.0
|
||||
# Hugging Face Hub for model downloads
|
||||
huggingface-hub[cli]
|
||||
|
|
|
|||
267
speech.py
267
speech.py
|
|
@ -38,6 +38,20 @@ except ImportError:
|
|||
split_sentence = None
|
||||
detect = None
|
||||
|
||||
# Try to import Qwen3-TTS dependencies
|
||||
try:
|
||||
import torch
|
||||
from qwen_tts import Qwen3TTSModel
|
||||
QWEN_TTS_AVAILABLE = True
|
||||
except ImportError:
|
||||
QWEN_TTS_AVAILABLE = False
|
||||
if torch is None:
|
||||
try:
|
||||
import torch
|
||||
except ImportError:
|
||||
torch = None
|
||||
Qwen3TTSModel = None
|
||||
|
||||
@contextlib.asynccontextmanager
|
||||
async def lifespan(app):
|
||||
# Startup: Initialize voice caches in each worker process
|
||||
|
|
@ -89,6 +103,10 @@ async def lifespan(app):
|
|||
model_info["engine"] = "kokoro"
|
||||
model_info["description"] = "Lightweight decoder-only TTS (82M params)"
|
||||
model_info["sample_rate"] = 24000
|
||||
elif model_id == 'tts-1-qwen':
|
||||
model_info["engine"] = "qwen3-tts"
|
||||
model_info["description"] = "State-of-the-art TTS with voice cloning (1.7B params, 10 languages)"
|
||||
model_info["sample_rate"] = 24000
|
||||
|
||||
models_data.append(model_info)
|
||||
|
||||
|
|
@ -125,6 +143,8 @@ silero_model = None
|
|||
silero_speakers = {}
|
||||
kokoro_pipeline = None
|
||||
kokoro_lang = None
|
||||
qwen_model = None
|
||||
qwen_voice_prompts = {} # Cache for voice clone prompts
|
||||
|
||||
# Default args for worker processes (will be overridden in __main__)
|
||||
class DefaultArgs:
|
||||
|
|
@ -159,6 +179,7 @@ voices_cache = None
|
|||
# Semaphores to limit concurrent model loading (prevent thread pool exhaustion)
|
||||
silero_load_semaphore = asyncio.Semaphore(1) # Only one Silero model load at a time
|
||||
kokoro_load_semaphore = asyncio.Semaphore(1) # Only one Kokoro model load at a time
|
||||
qwen_load_semaphore = asyncio.Semaphore(1) # Only one Qwen model load at a time
|
||||
|
||||
def unload_model():
|
||||
import torch, gc
|
||||
|
|
@ -350,6 +371,126 @@ class kokoro_wrapper():
|
|||
logger.error(f"Kokoro TTS generation failed: {e}")
|
||||
raise
|
||||
|
||||
class qwen3_wrapper():
|
||||
"""Wrapper for Qwen3-TTS model
|
||||
|
||||
Qwen3-TTS is a state-of-the-art TTS with voice cloning:
|
||||
- 1.7B parameters, 12Hz tokenizer
|
||||
- 10 languages: zh, en, ja, ko, de, fr, ru, pt, es, it
|
||||
- 97ms first-packet latency
|
||||
- 3-second rapid voice cloning
|
||||
Output: Variable sample rate (typically 24kHz)
|
||||
"""
|
||||
def __init__(self, model_name='Qwen/Qwen3-TTS-12Hz-1.7B-Base', device='cuda', dtype=None):
|
||||
self.model_name = model_name
|
||||
self.device = device
|
||||
self.sample_rate = None # Set after first generation
|
||||
self.voice_prompts = {} # Cache for reusable voice clone prompts
|
||||
|
||||
logger.info(f"Loading Qwen3-TTS model '{model_name}' on device '{device}'")
|
||||
|
||||
try:
|
||||
import torch
|
||||
from qwen_tts import Qwen3TTSModel
|
||||
|
||||
# Determine dtype
|
||||
if dtype is None:
|
||||
if device == 'cuda' and torch.cuda.is_available():
|
||||
dtype = torch.bfloat16
|
||||
else:
|
||||
dtype = torch.float32
|
||||
|
||||
# Try to use flash attention if available
|
||||
try:
|
||||
self.model = Qwen3TTSModel.from_pretrained(
|
||||
model_name,
|
||||
device_map=device,
|
||||
dtype=dtype,
|
||||
attn_implementation="flash_attention_2",
|
||||
)
|
||||
logger.info(f"Loaded Qwen3-TTS with FlashAttention 2")
|
||||
except Exception as fa_error:
|
||||
logger.warning(f"FlashAttention 2 not available ({fa_error}), using default attention")
|
||||
self.model = Qwen3TTSModel.from_pretrained(
|
||||
model_name,
|
||||
device_map=device,
|
||||
dtype=dtype,
|
||||
)
|
||||
|
||||
logger.info(f"Successfully loaded Qwen3-TTS model on {device}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load Qwen3-TTS model: {e}")
|
||||
raise
|
||||
|
||||
def create_voice_prompt(self, ref_audio, ref_text, x_vector_only_mode=False):
|
||||
"""Create a reusable voice clone prompt from reference audio/text.
|
||||
|
||||
Args:
|
||||
ref_audio: Path to reference audio file, URL, or (numpy_array, sample_rate) tuple
|
||||
ref_text: Text spoken in the reference audio
|
||||
x_vector_only_mode: If True, use only speaker embedding (faster but lower quality)
|
||||
|
||||
Returns:
|
||||
Voice clone prompt items for reuse
|
||||
"""
|
||||
logger.info(f"Creating voice clone prompt from ref_audio={ref_audio}, ref_text={ref_text[:50]}...")
|
||||
return self.model.create_voice_clone_prompt(
|
||||
ref_audio=ref_audio,
|
||||
ref_text=ref_text,
|
||||
x_vector_only_mode=x_vector_only_mode,
|
||||
)
|
||||
|
||||
def tts(self, text, language='English', ref_audio=None, ref_text=None, voice_prompt=None):
|
||||
"""Generate speech from text using voice cloning.
|
||||
|
||||
Args:
|
||||
text: Text to synthesize (string or list of strings)
|
||||
language: Language of the text
|
||||
ref_audio: Path/URL to reference audio (if voice_prompt not provided)
|
||||
ref_text: Text in reference audio (if voice_prompt not provided)
|
||||
voice_prompt: Pre-computed voice clone prompt (for efficiency)
|
||||
|
||||
Returns:
|
||||
Audio data as bytes (float32 PCM)
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
logger.info(f"Qwen3-TTS generating: text length={len(text)}, language={language}")
|
||||
|
||||
try:
|
||||
if voice_prompt is not None:
|
||||
# Use pre-computed voice prompt
|
||||
wavs, sr = self.model.generate_voice_clone(
|
||||
text=text,
|
||||
language=language,
|
||||
voice_clone_prompt=voice_prompt,
|
||||
)
|
||||
elif ref_audio is not None and ref_text is not None:
|
||||
# Generate with inline reference
|
||||
wavs, sr = self.model.generate_voice_clone(
|
||||
text=text,
|
||||
language=language,
|
||||
ref_audio=ref_audio,
|
||||
ref_text=ref_text,
|
||||
)
|
||||
else:
|
||||
raise ValueError("Either voice_prompt or (ref_audio + ref_text) must be provided")
|
||||
|
||||
self.sample_rate = sr
|
||||
logger.info(f"Qwen3-TTS generated {len(wavs)} audio segment(s) at {sr}Hz")
|
||||
|
||||
# Concatenate all wav segments and convert to bytes
|
||||
if len(wavs) > 1:
|
||||
full_audio = np.concatenate(wavs)
|
||||
else:
|
||||
full_audio = wavs[0]
|
||||
|
||||
return full_audio.astype(np.float32).tobytes()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Qwen3-TTS generation failed: {e}")
|
||||
raise
|
||||
|
||||
def default_exists(filename: str):
|
||||
if not os.path.exists(filename):
|
||||
fpath, ext = os.path.splitext(filename)
|
||||
|
|
@ -462,33 +603,41 @@ def build_ffmpeg_args(response_format, input_format, sample_rate):
|
|||
async def list_models():
|
||||
"""List all available TTS models (OpenAI-compatible format)"""
|
||||
# Return minimal OpenAI-compatible model list (no extra fields)
|
||||
# Only tts-1-qwen is enabled by default
|
||||
return {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "tts-1",
|
||||
"object": "model",
|
||||
"created": 1700000000,
|
||||
"owned_by": "uncloseai"
|
||||
},
|
||||
{
|
||||
"id": "tts-1-hd",
|
||||
"object": "model",
|
||||
"created": 1700000000,
|
||||
"owned_by": "uncloseai"
|
||||
},
|
||||
{
|
||||
"id": "tts-1-silero",
|
||||
"object": "model",
|
||||
"created": 1700000000,
|
||||
"owned_by": "uncloseai"
|
||||
},
|
||||
{
|
||||
"id": "tts-1-kokoro",
|
||||
"id": "tts-1-qwen",
|
||||
"object": "model",
|
||||
"created": 1700000000,
|
||||
"owned_by": "uncloseai"
|
||||
}
|
||||
# Other models disabled by default:
|
||||
# {
|
||||
# "id": "tts-1",
|
||||
# "object": "model",
|
||||
# "created": 1700000000,
|
||||
# "owned_by": "uncloseai"
|
||||
# },
|
||||
# {
|
||||
# "id": "tts-1-hd",
|
||||
# "object": "model",
|
||||
# "created": 1700000000,
|
||||
# "owned_by": "uncloseai"
|
||||
# },
|
||||
# {
|
||||
# "id": "tts-1-silero",
|
||||
# "object": "model",
|
||||
# "created": 1700000000,
|
||||
# "owned_by": "uncloseai"
|
||||
# },
|
||||
# {
|
||||
# "id": "tts-1-kokoro",
|
||||
# "object": "model",
|
||||
# "created": 1700000000,
|
||||
# "owned_by": "uncloseai"
|
||||
# }
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -542,6 +691,10 @@ async def list_voices():
|
|||
model_info["engine"] = "kokoro"
|
||||
model_info["description"] = "Lightweight decoder-only TTS (82M params)"
|
||||
model_info["sample_rate"] = 24000
|
||||
elif model_id == 'tts-1-qwen':
|
||||
model_info["engine"] = "qwen3-tts"
|
||||
model_info["description"] = "State-of-the-art TTS with voice cloning (1.7B params, 10 languages)"
|
||||
model_info["sample_rate"] = 24000
|
||||
|
||||
models_data.append(model_info)
|
||||
|
||||
|
|
@ -597,6 +750,8 @@ async def generate_speech(request: GenerateSpeechRequest):
|
|||
media_type = "audio/pcm;rate=48000"
|
||||
elif model == 'tts-1-kokoro': # kokoro
|
||||
media_type = "audio/pcm;rate=24000"
|
||||
elif model == 'tts-1-qwen': # qwen3-tts
|
||||
media_type = "audio/pcm;rate=24000"
|
||||
else:
|
||||
raise BadRequestError(f"Invalid response_format: '{response_format}'", param='response_format')
|
||||
|
||||
|
|
@ -912,9 +1067,70 @@ async def generate_speech(request: GenerateSpeechRequest):
|
|||
out_writer_worker = threading.Thread(target=out_writer, daemon=True)
|
||||
out_writer_worker.start()
|
||||
|
||||
return StreamingResponse(content=ffmpeg_proc.stdout, media_type=media_type)
|
||||
# Use Qwen3-TTS for tts-1-qwen
|
||||
elif model == 'tts-1-qwen':
|
||||
global qwen_model, qwen_voice_prompts
|
||||
|
||||
if not QWEN_TTS_AVAILABLE:
|
||||
raise ServiceUnavailableError("Qwen3-TTS is not available. Install with: pip install qwen-tts")
|
||||
|
||||
voice_map = map_voice_to_speaker(voice, 'tts-1-qwen')
|
||||
ref_audio = voice_map.get('ref_audio')
|
||||
ref_text = voice_map.get('ref_text')
|
||||
language = voice_map.get('language', 'English')
|
||||
|
||||
# Load Qwen model if not already loaded
|
||||
if qwen_model is None:
|
||||
async with qwen_load_semaphore:
|
||||
if qwen_model is None:
|
||||
device = args.xtts_device if args.xtts_device != 'none' else 'cpu'
|
||||
logger.info(f"Loading Qwen3-TTS model on device '{device}'")
|
||||
qwen_model = await asyncio.to_thread(
|
||||
qwen3_wrapper,
|
||||
model_name='Qwen/Qwen3-TTS-12Hz-1.7B-Base',
|
||||
device=device
|
||||
)
|
||||
|
||||
# Create or retrieve cached voice prompt
|
||||
voice_prompt = None
|
||||
if ref_audio and ref_text:
|
||||
cache_key = f"{voice}_{ref_audio}"
|
||||
if cache_key not in qwen_voice_prompts:
|
||||
logger.info(f"Creating voice prompt for '{voice}'")
|
||||
qwen_voice_prompts[cache_key] = await asyncio.to_thread(
|
||||
qwen_model.create_voice_prompt,
|
||||
ref_audio=ref_audio,
|
||||
ref_text=ref_text
|
||||
)
|
||||
voice_prompt = qwen_voice_prompts[cache_key]
|
||||
else:
|
||||
raise BadRequestError(f"Voice '{voice}' requires ref_audio and ref_text configuration", param='voice')
|
||||
|
||||
# Generate audio
|
||||
audio_data = await asyncio.to_thread(
|
||||
qwen_model.tts,
|
||||
text=input_text,
|
||||
language=language,
|
||||
voice_prompt=voice_prompt
|
||||
)
|
||||
|
||||
# Qwen outputs float32 PCM at ~24kHz (sample rate from model)
|
||||
sample_rate = str(qwen_model.sample_rate or 24000)
|
||||
ffmpeg_args = build_ffmpeg_args(response_format, input_format="f32le", sample_rate=sample_rate)
|
||||
|
||||
# Apply speed adjustment if needed
|
||||
if speed != 1.0:
|
||||
ffmpeg_args.extend(["-af", f"atempo={speed}"])
|
||||
|
||||
ffmpeg_args.extend(["-"])
|
||||
ffmpeg_proc = subprocess.Popen(ffmpeg_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
ffmpeg_proc.stdin.write(audio_data)
|
||||
ffmpeg_proc.stdin.close()
|
||||
|
||||
return StreamingResponse(content=ffmpeg_proc.stdout, media_type=media_type)
|
||||
else:
|
||||
raise BadRequestError("No such model, must be tts-1, tts-1-hd, tts-1-silero, or tts-1-kokoro.", param='model')
|
||||
raise BadRequestError("No such model, must be tts-1-qwen (default), tts-1, tts-1-hd, tts-1-silero, or tts-1-kokoro.", param='model')
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
|
|
@ -941,10 +1157,13 @@ if __name__ == "__main__":
|
|||
elif args.preload:
|
||||
xtts = xtts_wrapper(args.preload, device=args.xtts_device, unload_timer=args.unload_timer)
|
||||
|
||||
app.register_model('tts-1')
|
||||
app.register_model('tts-1-hd')
|
||||
app.register_model('tts-1-silero')
|
||||
app.register_model('tts-1-kokoro')
|
||||
# Register only Qwen by default (other models disabled)
|
||||
app.register_model('tts-1-qwen')
|
||||
# To enable other models, uncomment below:
|
||||
# app.register_model('tts-1')
|
||||
# app.register_model('tts-1-hd')
|
||||
# app.register_model('tts-1-silero')
|
||||
# app.register_model('tts-1-kokoro')
|
||||
|
||||
# Use multiple workers for true concurrency (each worker = separate process with own GIL)
|
||||
# This prevents thread pool exhaustion and allows concurrent model loading
|
||||
|
|
|
|||
|
|
@ -1,892 +1,77 @@
|
|||
tts-1:
|
||||
# OpenAI-compatible voice aliases (backward compatibility)
|
||||
alloy:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 79 # 64, 79, 80, 101, 130
|
||||
echo:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 134 # 52, 102, 134
|
||||
fable:
|
||||
model: /app/voices/en/en_GB/northern_english_male/medium/en_GB-northern_english_male-medium.onnx
|
||||
speaker: # default speaker
|
||||
onyx:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 159 # 55, 90, 132, 136, 137, 159
|
||||
nova:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 107 # 57, 61, 107, 150, 162
|
||||
shimmer:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 163
|
||||
# uncloseai-speech Voice Configuration
|
||||
# Only tts-1-qwen is enabled by default
|
||||
|
||||
# English US voices - all available Piper models
|
||||
# libritts_r has 904 speakers (multi-speaker model)
|
||||
en_us_libritts_r_0:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 0
|
||||
en_us_libritts_r_52:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 52
|
||||
en_us_libritts_r_55:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 55
|
||||
en_us_libritts_r_57:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 57
|
||||
en_us_libritts_r_61:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 61
|
||||
en_us_libritts_r_64:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 64
|
||||
en_us_libritts_r_79:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 79
|
||||
en_us_libritts_r_80:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 80
|
||||
en_us_libritts_r_90:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 90
|
||||
en_us_libritts_r_101:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 101
|
||||
en_us_libritts_r_102:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 102
|
||||
en_us_libritts_r_107:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 107
|
||||
en_us_libritts_r_130:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 130
|
||||
en_us_libritts_r_132:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 132
|
||||
en_us_libritts_r_134:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 134
|
||||
en_us_libritts_r_136:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 136
|
||||
en_us_libritts_r_137:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 137
|
||||
en_us_libritts_r_150:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 150
|
||||
en_us_libritts_r_159:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 159
|
||||
en_us_libritts_r_162:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 162
|
||||
en_us_libritts_r_163:
|
||||
model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
speaker: 163
|
||||
tts-1-qwen:
|
||||
# OpenAI-compatible voice aliases
|
||||
# Each voice requires ref_audio (reference audio) and ref_text (transcript of the audio)
|
||||
# Language: Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian
|
||||
|
||||
# Single-speaker Piper voices (to be downloaded)
|
||||
en_us_amy:
|
||||
model: /app/voices/en/en_US/amy/medium/en_US-amy-medium.onnx
|
||||
speaker: # default
|
||||
en_us_arctic:
|
||||
model: /app/voices/en/en_US/arctic/medium/en_US-arctic-medium.onnx
|
||||
speaker: # default
|
||||
en_us_bryce:
|
||||
model: /app/voices/en/en_US/bryce/medium/en_US-bryce-medium.onnx
|
||||
speaker: # default
|
||||
en_us_danny:
|
||||
model: /app/voices/en/en_US/danny/low/en_US-danny-low.onnx
|
||||
speaker: # default
|
||||
en_us_hfc_female:
|
||||
model: /app/voices/en/en_US/hfc_female/medium/en_US-hfc_female-medium.onnx
|
||||
speaker: # default
|
||||
en_us_hfc_male:
|
||||
model: /app/voices/en/en_US/hfc_male/medium/en_US-hfc_male-medium.onnx
|
||||
speaker: # default
|
||||
en_us_joe:
|
||||
model: /app/voices/en/en_US/joe/medium/en_US-joe-medium.onnx
|
||||
speaker: # default
|
||||
en_us_john:
|
||||
model: /app/voices/en/en_US/john/medium/en_US-john-medium.onnx
|
||||
speaker: # default
|
||||
en_us_kathleen:
|
||||
model: /app/voices/en/en_US/kathleen/low/en_US-kathleen-low.onnx
|
||||
speaker: # default
|
||||
en_us_kristin:
|
||||
model: /app/voices/en/en_US/kristin/medium/en_US-kristin-medium.onnx
|
||||
speaker: # default
|
||||
en_us_kusal:
|
||||
model: /app/voices/en/en_US/kusal/medium/en_US-kusal-medium.onnx
|
||||
speaker: # default
|
||||
en_us_l2arctic:
|
||||
model: /app/voices/en/en_US/l2arctic/medium/en_US-l2arctic-medium.onnx
|
||||
speaker: # default (multi-speaker model)
|
||||
en_us_lessac:
|
||||
model: /app/voices/en/en_US/lessac/medium/en_US-lessac-medium.onnx
|
||||
speaker: # default (multi-speaker model)
|
||||
en_us_libritts:
|
||||
model: /app/voices/en/en_US/libritts/high/en_US-libritts-high.onnx
|
||||
speaker: # default (multi-speaker model)
|
||||
en_us_ljspeech:
|
||||
model: /app/voices/en/en_US/ljspeech/medium/en_US-ljspeech-medium.onnx
|
||||
speaker: # default
|
||||
en_us_norman:
|
||||
model: /app/voices/en/en_US/norman/medium/en_US-norman-medium.onnx
|
||||
speaker: # default
|
||||
en_us_reza_ibrahim:
|
||||
model: /app/voices/en/en_US/reza_ibrahim/medium/en_US-reza_ibrahim-medium.onnx
|
||||
speaker: # default
|
||||
en_us_ryan:
|
||||
model: /app/voices/en/en_US/ryan/high/en_US-ryan-high.onnx
|
||||
speaker: # default
|
||||
en_us_sam:
|
||||
model: /app/voices/en/en_US/sam/medium/en_US-sam-medium.onnx
|
||||
speaker: # default
|
||||
# Default voice - using Qwen's example clone audio
|
||||
alloy:
|
||||
ref_audio: https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone.wav
|
||||
ref_text: "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it!"
|
||||
language: English
|
||||
|
||||
# English GB voices
|
||||
en_gb_alan:
|
||||
model: /app/voices/en/en_GB/alan/medium/en_GB-alan-medium.onnx
|
||||
speaker: # default
|
||||
en_gb_alba:
|
||||
model: /app/voices/en/en_GB/alba/medium/en_GB-alba-medium.onnx
|
||||
speaker: # default
|
||||
en_gb_aru:
|
||||
model: /app/voices/en/en_GB/aru/medium/en_GB-aru-medium.onnx
|
||||
speaker: # default (multi-speaker model)
|
||||
en_gb_cori:
|
||||
model: /app/voices/en/en_GB/cori/medium/en_GB-cori-medium.onnx
|
||||
speaker: # default (multi-speaker model)
|
||||
en_gb_jenny_dioco:
|
||||
model: /app/voices/en/en_GB/jenny_dioco/medium/en_GB-jenny_dioco-medium.onnx
|
||||
speaker: # default
|
||||
en_gb_northern_english_male:
|
||||
model: /app/voices/en/en_GB/northern_english_male/medium/en_GB-northern_english_male-medium.onnx
|
||||
speaker: # default
|
||||
en_gb_semaine:
|
||||
model: /app/voices/en/en_GB/semaine/medium/en_GB-semaine-medium.onnx
|
||||
speaker: # default
|
||||
en_gb_southern_english_female:
|
||||
model: /app/voices/en/en_GB/southern_english_female/low/en_GB-southern_english_female-low.onnx
|
||||
speaker: # default
|
||||
en_gb_vctk:
|
||||
model: /app/voices/en/en_GB/vctk/medium/en_GB-vctk-medium.onnx
|
||||
speaker: # default (multi-speaker model)
|
||||
tts-1-hd:
|
||||
alloy-alt:
|
||||
model: xtts
|
||||
speaker: voices/alloy-alt.wav
|
||||
alloy:
|
||||
model: xtts
|
||||
speaker: voices/alloy.wav
|
||||
# Echo - same sample, different name for compatibility
|
||||
echo:
|
||||
model: xtts
|
||||
speaker: voices/echo.wav
|
||||
ref_audio: https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone.wav
|
||||
ref_text: "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it!"
|
||||
language: English
|
||||
|
||||
# Fable - same sample
|
||||
fable:
|
||||
model: xtts
|
||||
speaker: voices/fable.wav
|
||||
ref_audio: https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone.wav
|
||||
ref_text: "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it!"
|
||||
language: English
|
||||
|
||||
# Onyx - same sample
|
||||
onyx:
|
||||
model: xtts
|
||||
speaker: voices/onyx.wav
|
||||
ref_audio: https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone.wav
|
||||
ref_text: "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it!"
|
||||
language: English
|
||||
|
||||
# Nova - same sample
|
||||
nova:
|
||||
model: xtts
|
||||
speaker: voices/nova.wav
|
||||
ref_audio: https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone.wav
|
||||
ref_text: "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it!"
|
||||
language: English
|
||||
|
||||
# Shimmer - same sample
|
||||
shimmer:
|
||||
model: xtts
|
||||
speaker: voices/shimmer.wav
|
||||
me:
|
||||
model: xtts_v2.0.2 # you can specify an older xtts version
|
||||
speaker: voices/me.wav # this could be you
|
||||
language: auto
|
||||
enable_text_splitting: True
|
||||
length_penalty: 1.0
|
||||
repetition_penalty: 10
|
||||
speed: 1.0
|
||||
temperature: 0.75
|
||||
top_k: 50
|
||||
top_p: 0.85
|
||||
comment: You can add a comment here also, which will be persistent and otherwise ignored.
|
||||
tts-1-silero:
|
||||
# All 118 Silero v3_en voices with proper names
|
||||
# NOTE: Use native Silero voice names (en_0, en_1, etc.) for Silero
|
||||
# OpenAI voice names (alloy, echo, etc.) are reserved for tts-1 and tts-1-hd only
|
||||
en_0:
|
||||
language: en
|
||||
speaker: en_0
|
||||
silero_speaker: v3_en
|
||||
en_1:
|
||||
language: en
|
||||
speaker: en_1
|
||||
silero_speaker: v3_en
|
||||
en_2:
|
||||
language: en
|
||||
speaker: en_2
|
||||
silero_speaker: v3_en
|
||||
en_3:
|
||||
language: en
|
||||
speaker: en_3
|
||||
silero_speaker: v3_en
|
||||
en_4:
|
||||
language: en
|
||||
speaker: en_4
|
||||
silero_speaker: v3_en
|
||||
en_5:
|
||||
language: en
|
||||
speaker: en_5
|
||||
silero_speaker: v3_en
|
||||
en_6:
|
||||
language: en
|
||||
speaker: en_6
|
||||
silero_speaker: v3_en
|
||||
en_7:
|
||||
language: en
|
||||
speaker: en_7
|
||||
silero_speaker: v3_en
|
||||
en_8:
|
||||
language: en
|
||||
speaker: en_8
|
||||
silero_speaker: v3_en
|
||||
en_9:
|
||||
language: en
|
||||
speaker: en_9
|
||||
silero_speaker: v3_en
|
||||
en_10:
|
||||
language: en
|
||||
speaker: en_10
|
||||
silero_speaker: v3_en
|
||||
en_11:
|
||||
language: en
|
||||
speaker: en_11
|
||||
silero_speaker: v3_en
|
||||
en_12:
|
||||
language: en
|
||||
speaker: en_12
|
||||
silero_speaker: v3_en
|
||||
en_13:
|
||||
language: en
|
||||
speaker: en_13
|
||||
silero_speaker: v3_en
|
||||
en_14:
|
||||
language: en
|
||||
speaker: en_14
|
||||
silero_speaker: v3_en
|
||||
en_15:
|
||||
language: en
|
||||
speaker: en_15
|
||||
silero_speaker: v3_en
|
||||
en_16:
|
||||
language: en
|
||||
speaker: en_16
|
||||
silero_speaker: v3_en
|
||||
en_17:
|
||||
language: en
|
||||
speaker: en_17
|
||||
silero_speaker: v3_en
|
||||
en_18:
|
||||
language: en
|
||||
speaker: en_18
|
||||
silero_speaker: v3_en
|
||||
en_19:
|
||||
language: en
|
||||
speaker: en_19
|
||||
silero_speaker: v3_en
|
||||
en_20:
|
||||
language: en
|
||||
speaker: en_20
|
||||
silero_speaker: v3_en
|
||||
en_21:
|
||||
language: en
|
||||
speaker: en_21
|
||||
silero_speaker: v3_en
|
||||
en_22:
|
||||
language: en
|
||||
speaker: en_22
|
||||
silero_speaker: v3_en
|
||||
en_23:
|
||||
language: en
|
||||
speaker: en_23
|
||||
silero_speaker: v3_en
|
||||
en_24:
|
||||
language: en
|
||||
speaker: en_24
|
||||
silero_speaker: v3_en
|
||||
en_25:
|
||||
language: en
|
||||
speaker: en_25
|
||||
silero_speaker: v3_en
|
||||
en_26:
|
||||
language: en
|
||||
speaker: en_26
|
||||
silero_speaker: v3_en
|
||||
en_27:
|
||||
language: en
|
||||
speaker: en_27
|
||||
silero_speaker: v3_en
|
||||
en_28:
|
||||
language: en
|
||||
speaker: en_28
|
||||
silero_speaker: v3_en
|
||||
en_29:
|
||||
language: en
|
||||
speaker: en_29
|
||||
silero_speaker: v3_en
|
||||
en_30:
|
||||
language: en
|
||||
speaker: en_30
|
||||
silero_speaker: v3_en
|
||||
en_31:
|
||||
language: en
|
||||
speaker: en_31
|
||||
silero_speaker: v3_en
|
||||
en_32:
|
||||
language: en
|
||||
speaker: en_32
|
||||
silero_speaker: v3_en
|
||||
en_33:
|
||||
language: en
|
||||
speaker: en_33
|
||||
silero_speaker: v3_en
|
||||
en_34:
|
||||
language: en
|
||||
speaker: en_34
|
||||
silero_speaker: v3_en
|
||||
en_35:
|
||||
language: en
|
||||
speaker: en_35
|
||||
silero_speaker: v3_en
|
||||
en_36:
|
||||
language: en
|
||||
speaker: en_36
|
||||
silero_speaker: v3_en
|
||||
en_37:
|
||||
language: en
|
||||
speaker: en_37
|
||||
silero_speaker: v3_en
|
||||
en_38:
|
||||
language: en
|
||||
speaker: en_38
|
||||
silero_speaker: v3_en
|
||||
en_39:
|
||||
language: en
|
||||
speaker: en_39
|
||||
silero_speaker: v3_en
|
||||
en_40:
|
||||
language: en
|
||||
speaker: en_40
|
||||
silero_speaker: v3_en
|
||||
en_41:
|
||||
language: en
|
||||
speaker: en_41
|
||||
silero_speaker: v3_en
|
||||
en_42:
|
||||
language: en
|
||||
speaker: en_42
|
||||
silero_speaker: v3_en
|
||||
en_43:
|
||||
language: en
|
||||
speaker: en_43
|
||||
silero_speaker: v3_en
|
||||
en_44:
|
||||
language: en
|
||||
speaker: en_44
|
||||
silero_speaker: v3_en
|
||||
en_45:
|
||||
language: en
|
||||
speaker: en_45
|
||||
silero_speaker: v3_en
|
||||
en_46:
|
||||
language: en
|
||||
speaker: en_46
|
||||
silero_speaker: v3_en
|
||||
en_47:
|
||||
language: en
|
||||
speaker: en_47
|
||||
silero_speaker: v3_en
|
||||
en_48:
|
||||
language: en
|
||||
speaker: en_48
|
||||
silero_speaker: v3_en
|
||||
en_49:
|
||||
language: en
|
||||
speaker: en_49
|
||||
silero_speaker: v3_en
|
||||
en_50:
|
||||
language: en
|
||||
speaker: en_50
|
||||
silero_speaker: v3_en
|
||||
en_51:
|
||||
language: en
|
||||
speaker: en_51
|
||||
silero_speaker: v3_en
|
||||
en_52:
|
||||
language: en
|
||||
speaker: en_52
|
||||
silero_speaker: v3_en
|
||||
en_53:
|
||||
language: en
|
||||
speaker: en_53
|
||||
silero_speaker: v3_en
|
||||
en_54:
|
||||
language: en
|
||||
speaker: en_54
|
||||
silero_speaker: v3_en
|
||||
en_55:
|
||||
language: en
|
||||
speaker: en_55
|
||||
silero_speaker: v3_en
|
||||
en_56:
|
||||
language: en
|
||||
speaker: en_56
|
||||
silero_speaker: v3_en
|
||||
en_57:
|
||||
language: en
|
||||
speaker: en_57
|
||||
silero_speaker: v3_en
|
||||
en_58:
|
||||
language: en
|
||||
speaker: en_58
|
||||
silero_speaker: v3_en
|
||||
en_59:
|
||||
language: en
|
||||
speaker: en_59
|
||||
silero_speaker: v3_en
|
||||
en_60:
|
||||
language: en
|
||||
speaker: en_60
|
||||
silero_speaker: v3_en
|
||||
en_61:
|
||||
language: en
|
||||
speaker: en_61
|
||||
silero_speaker: v3_en
|
||||
en_62:
|
||||
language: en
|
||||
speaker: en_62
|
||||
silero_speaker: v3_en
|
||||
en_63:
|
||||
language: en
|
||||
speaker: en_63
|
||||
silero_speaker: v3_en
|
||||
en_64:
|
||||
language: en
|
||||
speaker: en_64
|
||||
silero_speaker: v3_en
|
||||
en_65:
|
||||
language: en
|
||||
speaker: en_65
|
||||
silero_speaker: v3_en
|
||||
en_66:
|
||||
language: en
|
||||
speaker: en_66
|
||||
silero_speaker: v3_en
|
||||
en_67:
|
||||
language: en
|
||||
speaker: en_67
|
||||
silero_speaker: v3_en
|
||||
en_68:
|
||||
language: en
|
||||
speaker: en_68
|
||||
silero_speaker: v3_en
|
||||
en_69:
|
||||
language: en
|
||||
speaker: en_69
|
||||
silero_speaker: v3_en
|
||||
en_70:
|
||||
language: en
|
||||
speaker: en_70
|
||||
silero_speaker: v3_en
|
||||
en_71:
|
||||
language: en
|
||||
speaker: en_71
|
||||
silero_speaker: v3_en
|
||||
en_72:
|
||||
language: en
|
||||
speaker: en_72
|
||||
silero_speaker: v3_en
|
||||
en_73:
|
||||
language: en
|
||||
speaker: en_73
|
||||
silero_speaker: v3_en
|
||||
en_74:
|
||||
language: en
|
||||
speaker: en_74
|
||||
silero_speaker: v3_en
|
||||
en_75:
|
||||
language: en
|
||||
speaker: en_75
|
||||
silero_speaker: v3_en
|
||||
en_76:
|
||||
language: en
|
||||
speaker: en_76
|
||||
silero_speaker: v3_en
|
||||
en_77:
|
||||
language: en
|
||||
speaker: en_77
|
||||
silero_speaker: v3_en
|
||||
en_78:
|
||||
language: en
|
||||
speaker: en_78
|
||||
silero_speaker: v3_en
|
||||
en_79:
|
||||
language: en
|
||||
speaker: en_79
|
||||
silero_speaker: v3_en
|
||||
en_80:
|
||||
language: en
|
||||
speaker: en_80
|
||||
silero_speaker: v3_en
|
||||
en_81:
|
||||
language: en
|
||||
speaker: en_81
|
||||
silero_speaker: v3_en
|
||||
en_82:
|
||||
language: en
|
||||
speaker: en_82
|
||||
silero_speaker: v3_en
|
||||
en_83:
|
||||
language: en
|
||||
speaker: en_83
|
||||
silero_speaker: v3_en
|
||||
en_84:
|
||||
language: en
|
||||
speaker: en_84
|
||||
silero_speaker: v3_en
|
||||
en_85:
|
||||
language: en
|
||||
speaker: en_85
|
||||
silero_speaker: v3_en
|
||||
en_86:
|
||||
language: en
|
||||
speaker: en_86
|
||||
silero_speaker: v3_en
|
||||
en_87:
|
||||
language: en
|
||||
speaker: en_87
|
||||
silero_speaker: v3_en
|
||||
en_88:
|
||||
language: en
|
||||
speaker: en_88
|
||||
silero_speaker: v3_en
|
||||
en_89:
|
||||
language: en
|
||||
speaker: en_89
|
||||
silero_speaker: v3_en
|
||||
en_90:
|
||||
language: en
|
||||
speaker: en_90
|
||||
silero_speaker: v3_en
|
||||
en_91:
|
||||
language: en
|
||||
speaker: en_91
|
||||
silero_speaker: v3_en
|
||||
en_92:
|
||||
language: en
|
||||
speaker: en_92
|
||||
silero_speaker: v3_en
|
||||
en_93:
|
||||
language: en
|
||||
speaker: en_93
|
||||
silero_speaker: v3_en
|
||||
en_94:
|
||||
language: en
|
||||
speaker: en_94
|
||||
silero_speaker: v3_en
|
||||
en_95:
|
||||
language: en
|
||||
speaker: en_95
|
||||
silero_speaker: v3_en
|
||||
en_96:
|
||||
language: en
|
||||
speaker: en_96
|
||||
silero_speaker: v3_en
|
||||
en_97:
|
||||
language: en
|
||||
speaker: en_97
|
||||
silero_speaker: v3_en
|
||||
en_98:
|
||||
language: en
|
||||
speaker: en_98
|
||||
silero_speaker: v3_en
|
||||
en_99:
|
||||
language: en
|
||||
speaker: en_99
|
||||
silero_speaker: v3_en
|
||||
en_100:
|
||||
language: en
|
||||
speaker: en_100
|
||||
silero_speaker: v3_en
|
||||
en_101:
|
||||
language: en
|
||||
speaker: en_101
|
||||
silero_speaker: v3_en
|
||||
en_102:
|
||||
language: en
|
||||
speaker: en_102
|
||||
silero_speaker: v3_en
|
||||
en_103:
|
||||
language: en
|
||||
speaker: en_103
|
||||
silero_speaker: v3_en
|
||||
en_104:
|
||||
language: en
|
||||
speaker: en_104
|
||||
silero_speaker: v3_en
|
||||
en_105:
|
||||
language: en
|
||||
speaker: en_105
|
||||
silero_speaker: v3_en
|
||||
en_106:
|
||||
language: en
|
||||
speaker: en_106
|
||||
silero_speaker: v3_en
|
||||
en_107:
|
||||
language: en
|
||||
speaker: en_107
|
||||
silero_speaker: v3_en
|
||||
en_108:
|
||||
language: en
|
||||
speaker: en_108
|
||||
silero_speaker: v3_en
|
||||
en_109:
|
||||
language: en
|
||||
speaker: en_109
|
||||
silero_speaker: v3_en
|
||||
en_110:
|
||||
language: en
|
||||
speaker: en_110
|
||||
silero_speaker: v3_en
|
||||
en_111:
|
||||
language: en
|
||||
speaker: en_111
|
||||
silero_speaker: v3_en
|
||||
en_112:
|
||||
language: en
|
||||
speaker: en_112
|
||||
silero_speaker: v3_en
|
||||
en_113:
|
||||
language: en
|
||||
speaker: en_113
|
||||
silero_speaker: v3_en
|
||||
en_114:
|
||||
language: en
|
||||
speaker: en_114
|
||||
silero_speaker: v3_en
|
||||
en_115:
|
||||
language: en
|
||||
speaker: en_115
|
||||
silero_speaker: v3_en
|
||||
en_116:
|
||||
language: en
|
||||
speaker: en_116
|
||||
silero_speaker: v3_en
|
||||
en_117:
|
||||
language: en
|
||||
speaker: en_117
|
||||
silero_speaker: v3_en
|
||||
random:
|
||||
language: en
|
||||
speaker: random
|
||||
silero_speaker: v3_en
|
||||
# Russian voices (v3_ru/ru_v3 model) - use ru_v3 for better compatibility
|
||||
ru_aidar:
|
||||
language: ru
|
||||
speaker: aidar
|
||||
silero_speaker: ru_v3
|
||||
ru_baya:
|
||||
language: ru
|
||||
speaker: baya
|
||||
silero_speaker: ru_v3
|
||||
ru_kseniya:
|
||||
language: ru
|
||||
speaker: kseniya
|
||||
silero_speaker: ru_v3
|
||||
ru_xenia:
|
||||
language: ru
|
||||
speaker: xenia
|
||||
silero_speaker: ru_v3
|
||||
ru_eugene:
|
||||
language: ru
|
||||
speaker: eugene
|
||||
silero_speaker: ru_v3
|
||||
ru_random:
|
||||
language: ru
|
||||
speaker: random
|
||||
silero_speaker: ru_v3
|
||||
# German voices (v3_de model)
|
||||
de_bernd_ungerer:
|
||||
language: de
|
||||
speaker: bernd_ungerer
|
||||
silero_speaker: v3_de
|
||||
de_eva_k:
|
||||
language: de
|
||||
speaker: eva_k
|
||||
silero_speaker: v3_de
|
||||
de_friedrich:
|
||||
language: de
|
||||
speaker: friedrich
|
||||
silero_speaker: v3_de
|
||||
de_hokuspokus:
|
||||
language: de
|
||||
speaker: hokuspokus
|
||||
silero_speaker: v3_de
|
||||
de_karlsson:
|
||||
language: de
|
||||
speaker: karlsson
|
||||
silero_speaker: v3_de
|
||||
de_random:
|
||||
language: de
|
||||
speaker: random
|
||||
silero_speaker: v3_de
|
||||
# Spanish voices (v3_es model)
|
||||
es_0:
|
||||
language: es
|
||||
speaker: es_0
|
||||
silero_speaker: v3_es
|
||||
es_1:
|
||||
language: es
|
||||
speaker: es_1
|
||||
silero_speaker: v3_es
|
||||
es_2:
|
||||
language: es
|
||||
speaker: es_2
|
||||
silero_speaker: v3_es
|
||||
es_random:
|
||||
language: es
|
||||
speaker: random
|
||||
silero_speaker: v3_es
|
||||
# French voices (v3_fr model)
|
||||
fr_0:
|
||||
language: fr
|
||||
speaker: fr_0
|
||||
silero_speaker: v3_fr
|
||||
fr_1:
|
||||
language: fr
|
||||
speaker: fr_1
|
||||
silero_speaker: v3_fr
|
||||
fr_2:
|
||||
language: fr
|
||||
speaker: fr_2
|
||||
silero_speaker: v3_fr
|
||||
fr_3:
|
||||
language: fr
|
||||
speaker: fr_3
|
||||
silero_speaker: v3_fr
|
||||
fr_4:
|
||||
language: fr
|
||||
speaker: fr_4
|
||||
silero_speaker: v3_fr
|
||||
fr_5:
|
||||
language: fr
|
||||
speaker: fr_5
|
||||
silero_speaker: v3_fr
|
||||
fr_random:
|
||||
language: fr
|
||||
speaker: random
|
||||
silero_speaker: v3_fr
|
||||
tts-1-kokoro:
|
||||
# OpenAI-compatible voice aliases (Kokoro's intentional OpenAI-themed voices)
|
||||
alloy:
|
||||
lang_code: a
|
||||
kokoro_voice: af_alloy
|
||||
echo:
|
||||
lang_code: a
|
||||
kokoro_voice: am_echo
|
||||
fable:
|
||||
lang_code: b
|
||||
kokoro_voice: bm_fable
|
||||
onyx:
|
||||
lang_code: a
|
||||
kokoro_voice: am_onyx
|
||||
nova:
|
||||
lang_code: a
|
||||
kokoro_voice: af_nova
|
||||
shimmer:
|
||||
lang_code: a
|
||||
kokoro_voice: af_sky
|
||||
# Female American voices
|
||||
af_heart:
|
||||
lang_code: a
|
||||
kokoro_voice: af_heart
|
||||
af_bella:
|
||||
lang_code: a
|
||||
kokoro_voice: af_bella
|
||||
af_nicole:
|
||||
lang_code: a
|
||||
kokoro_voice: af_nicole
|
||||
af_aoede:
|
||||
lang_code: a
|
||||
kokoro_voice: af_aoede
|
||||
af_kore:
|
||||
lang_code: a
|
||||
kokoro_voice: af_kore
|
||||
af_sarah:
|
||||
lang_code: a
|
||||
kokoro_voice: af_sarah
|
||||
af_nova:
|
||||
lang_code: a
|
||||
kokoro_voice: af_nova
|
||||
af_sky:
|
||||
lang_code: a
|
||||
kokoro_voice: af_sky
|
||||
af_alloy:
|
||||
lang_code: a
|
||||
kokoro_voice: af_alloy
|
||||
af_jessica:
|
||||
lang_code: a
|
||||
kokoro_voice: af_jessica
|
||||
af_river:
|
||||
lang_code: a
|
||||
kokoro_voice: af_river
|
||||
# Male American voices
|
||||
am_michael:
|
||||
lang_code: a
|
||||
kokoro_voice: am_michael
|
||||
am_fenrir:
|
||||
lang_code: a
|
||||
kokoro_voice: am_fenrir
|
||||
am_puck:
|
||||
lang_code: a
|
||||
kokoro_voice: am_puck
|
||||
am_echo:
|
||||
lang_code: a
|
||||
kokoro_voice: am_echo
|
||||
am_eric:
|
||||
lang_code: a
|
||||
kokoro_voice: am_eric
|
||||
am_liam:
|
||||
lang_code: a
|
||||
kokoro_voice: am_liam
|
||||
am_onyx:
|
||||
lang_code: a
|
||||
kokoro_voice: am_onyx
|
||||
am_santa:
|
||||
lang_code: a
|
||||
kokoro_voice: am_santa
|
||||
am_adam:
|
||||
lang_code: a
|
||||
kokoro_voice: am_adam
|
||||
# Female British voices
|
||||
bf_emma:
|
||||
lang_code: b
|
||||
kokoro_voice: bf_emma
|
||||
bf_isabella:
|
||||
lang_code: b
|
||||
kokoro_voice: bf_isabella
|
||||
bf_alice:
|
||||
lang_code: b
|
||||
kokoro_voice: bf_alice
|
||||
bf_lily:
|
||||
lang_code: b
|
||||
kokoro_voice: bf_lily
|
||||
# Male British voices
|
||||
bm_george:
|
||||
lang_code: b
|
||||
kokoro_voice: bm_george
|
||||
bm_fable:
|
||||
lang_code: b
|
||||
kokoro_voice: bm_fable
|
||||
bm_lewis:
|
||||
lang_code: b
|
||||
kokoro_voice: bm_lewis
|
||||
bm_daniel:
|
||||
lang_code: b
|
||||
kokoro_voice: bm_daniel
|
||||
ref_audio: https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone.wav
|
||||
ref_text: "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it!"
|
||||
language: English
|
||||
|
||||
# Custom voice example - add your own reference audio
|
||||
# my_voice:
|
||||
# ref_audio: voices/my_voice_sample.wav
|
||||
# ref_text: "The exact text spoken in the reference audio file"
|
||||
# language: English
|
||||
|
||||
# Other models are disabled by default. Uncomment to enable.
|
||||
# See the git history for full voice configurations.
|
||||
|
||||
# tts-1:
|
||||
# # Piper TTS voices (fast CPU inference)
|
||||
# alloy:
|
||||
# model: /app/voices/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx
|
||||
# speaker: 79
|
||||
|
||||
# tts-1-hd:
|
||||
# # XTTS voice cloning
|
||||
# alloy:
|
||||
# model: xtts
|
||||
# speaker: voices/alloy.wav
|
||||
|
||||
# tts-1-silero:
|
||||
# # Silero TTS (CPU-friendly)
|
||||
# en_0:
|
||||
# language: en
|
||||
# speaker: en_0
|
||||
# silero_speaker: v3_en
|
||||
|
||||
# tts-1-kokoro:
|
||||
# # Kokoro TTS (lightweight 82M params)
|
||||
# alloy:
|
||||
# lang_code: a
|
||||
# kokoro_voice: af_alloy
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue