first commit
This commit is contained in:
34
.dockerignore
Normal file
34
.dockerignore
Normal file
@ -0,0 +1,34 @@
|
||||
# Git / version control
|
||||
.git
|
||||
.gitignore
|
||||
.dockerignore
|
||||
|
||||
# Virtual environment
|
||||
.venv
|
||||
venv/
|
||||
.env/
|
||||
|
||||
# IDE / Editor folders
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Python cache
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
# Build artifacts
|
||||
build/
|
||||
dist/
|
||||
*.egg-info
|
||||
|
||||
# Test artifacts
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
|
||||
# Docker files
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
|
||||
# Local environment settings
|
||||
.env
|
||||
7
.env.example
Normal file
7
.env.example
Normal file
@ -0,0 +1,7 @@
|
||||
# Comma-separated list of engines to activate.
|
||||
# Available options (potentially): piper, styletts, f5_tts, chattts
|
||||
ACTIVE_ENGINES='["piper", "styletts"]'
|
||||
|
||||
# Server configuration
|
||||
HOST=0.0.0.0
|
||||
PORT=8000
|
||||
52
.gitignore
vendored
Normal file
52
.gitignore
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
# .gitignore für Python-Projekt
|
||||
# Version: v1.1
|
||||
# Erstellt: 2025-07-25
|
||||
|
||||
# Bytecode & Caches
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# Virtuelle Umgebungen
|
||||
venv/
|
||||
.env/
|
||||
.virtualenv/
|
||||
.venv/
|
||||
|
||||
# IDE/Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Test- & Coverage-Dateien
|
||||
htmlcov/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.pytest_cache/
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
|
||||
# Build & Distribution
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
pip-wheel-metadata/
|
||||
|
||||
# Logs & Temp
|
||||
*.log
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
.DS_Store
|
||||
|
||||
# Umgebungsvariablen
|
||||
.env
|
||||
|
||||
# Sprachmodelle (TTS)
|
||||
tts_models/
|
||||
voice_models/
|
||||
*.pt
|
||||
*.onnx
|
||||
*.bin
|
||||
*.safetensors
|
||||
asset/
|
||||
42
Dockerfile
Normal file
42
Dockerfile
Normal file
@ -0,0 +1,42 @@
|
||||
# Stage 1: Builder
|
||||
FROM python:3.11 as builder
|
||||
WORKDIR /opt/venv
|
||||
|
||||
# Create a virtual environment and install dependencies
|
||||
COPY requirements.txt .
|
||||
RUN python -m venv . && . /opt/venv/bin/activate && pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Stage 2: Runner (The final image)
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Install system dependencies needed at runtime
|
||||
# ffmpeg is required for audio conversion
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ffmpeg \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create an unprivileged user
|
||||
RUN useradd --create-home --shell /bin/bash appuser
|
||||
|
||||
# Set working directory for the application code
|
||||
WORKDIR /home/appuser
|
||||
|
||||
# Copy the virtual environment from the builder stage
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
|
||||
# Copy the application code into the container
|
||||
COPY app/ ./app
|
||||
|
||||
# Set the PATH to include the virtual environment's bin directory
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Expose the application port
|
||||
EXPOSE 8000
|
||||
|
||||
# Run as the unprivileged user
|
||||
USER appuser
|
||||
|
||||
# Command to run the application using gunicorn for production
|
||||
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-w", "2", "-b", "0.0.0.0:8000", "app.main:app"]
|
||||
|
||||
|
||||
150
Makefile
Normal file
150
Makefile
Normal file
@ -0,0 +1,150 @@
|
||||
# --- Configuration ---
|
||||
IMAGE_NAME := audio-engine-hub
|
||||
TAG := latest
|
||||
# Default internal application port (exposed to Traefik)
|
||||
PORT := 8000
|
||||
# Traefik's external entrypoints
|
||||
TRAEFIK_WEB_PORT := 80
|
||||
TRAEFIK_DASHBOARD_PORT := 8080
|
||||
|
||||
# --- Port Checking Functions ---
|
||||
|
||||
# Function to check if required Traefik ports are free
|
||||
# Exits if any are busy, does not suggest alternatives.
|
||||
define check_traefik_ports_free
|
||||
@echo "Checking if Traefik ports ($(TRAEFIK_WEB_PORT}, $(TRAEFIK_DASHBOARD_PORT}) are free..." >&2
|
||||
@local busy_ports=""; \
|
||||
if ss -tulnp | grep ":$(TRAEFIK_WEB_PORT) " > /dev/null; then \
|
||||
busy_ports="$$busy_ports $(TRAEFIK_WEB_PORT)"; \
|
||||
fi; \
|
||||
if ss -tulnp | grep ":$(TRAEFIK_DASHBOARD_PORT) " > /dev/null; then \
|
||||
busy_ports="$$busy_ports $(TRAEFIK_DASHBOARD_PORT)"; \
|
||||
fi; \
|
||||
if [ -n "$$busy_ports" ]; then \
|
||||
echo "ERROR: The following Traefik ports are already in use: $$busy_ports. Please free them or stop Traefik if already running." >&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
@echo "Traefik ports are free." >&2
|
||||
endef
|
||||
|
||||
# Function to find next free host port for the app and ask user
|
||||
# This function will echo the chosen port if successful, or exit with an error.
|
||||
define check_app_port_free
|
||||
@local start_port=$(PORT); \
|
||||
local found_port=$$start_port; \
|
||||
local is_free=false; \
|
||||
\
|
||||
if ! ss -tulnp | grep ":$$start_port " > /dev/null; then \
|
||||
echo "$$start_port"; \
|
||||
exit 0; \
|
||||
fi; \
|
||||
\
|
||||
echo "Port $$start_port is busy. Searching for a free port for the app..." >&2; \
|
||||
while ! $$is_free; do \
|
||||
if ! ss -tulnp | grep ":$$found_port " > /dev/null; then \
|
||||
is_free=true; \
|
||||
else \
|
||||
((found_port++)); \
|
||||
if [ "$$found_port" -gt 65535 ]; then \
|
||||
echo "ERROR: No free ports found up to 65535. Aborting." >&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi; \
|
||||
done; \
|
||||
\
|
||||
echo "Port $$start_port is busy. I found port $$found_port to be free for the app." >&2; \
|
||||
read -p "Do you want to use port $$found_port for the app? (y/N): " choice; \
|
||||
case "$$choice" in \
|
||||
y|Y ) \
|
||||
echo "$$found_port"; \
|
||||
;; \
|
||||
* ) \
|
||||
echo "Operation cancelled by user." >&2; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac;
|
||||
endef
|
||||
|
||||
# --- Docker Commands ---
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
@echo "Building Docker image: $(IMAGE_NAME):$(TAG)"
|
||||
docker build -t $(IMAGE_NAME):$(TAG) .
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
@export SELECTED_HOST_PORT=$$(bash -c 'func() { $(check_app_port_free) }; func') && \
|
||||
echo "Using host port $$SELECTED_HOST_PORT for single app container" && \
|
||||
docker run -d -p $$SELECTED_HOST_PORT:$(PORT) --name $(IMAGE_NAME) $(IMAGE_NAME):$(TAG)
|
||||
|
||||
.PHONY: stop
|
||||
stop:
|
||||
@echo "Stopping Docker container: $(IMAGE_NAME)"
|
||||
docker stop $(IMAGE_NAME) || true
|
||||
docker rm $(IMAGE_NAME) || true
|
||||
|
||||
.PHONY: logs
|
||||
logs:
|
||||
@echo "Showing logs for container: $(IMAGE_NAME)"
|
||||
docker logs -f $(IMAGE_NAME)
|
||||
|
||||
.PHONY: shell
|
||||
shell:
|
||||
@echo "Accessing shell in container: $(IMAGE_NAME)"
|
||||
docker exec -it $(IMAGE_NAME) /bin/bash
|
||||
|
||||
# --- Docker Compose Commands ---
|
||||
|
||||
.PHONY: up
|
||||
up:
|
||||
$(call check_traefik_ports_free) # Check Traefik ports before starting
|
||||
@echo "Starting development environment with Docker Compose (Traefik enabled)..."
|
||||
docker-compose up --build -d
|
||||
|
||||
.PHONY: down
|
||||
down:
|
||||
@echo "Stopping development environment with Docker Compose..."
|
||||
docker-compose down
|
||||
|
||||
# --- Image Management ---
|
||||
|
||||
.PHONY: tag
|
||||
tag:
|
||||
@echo "Tagging image $(IMAGE_NAME):$(TAG) as $(REGISTRY)/$(IMAGE_NAME):$(TAG)"
|
||||
docker tag $(IMAGE_NAME):$(TAG) $(REGISTRY)/$(IMAGE_NAME):$(TAG)
|
||||
|
||||
.PHONY: push
|
||||
push: tag
|
||||
@echo "Pushing image $(REGISTRY)/$(IMAGE_NAME):$(TAG) to registry..."
|
||||
docker push $(REGISTRY)/$(IMAGE_NAME):$(TAG)
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@echo "Running tests with coverage..."
|
||||
pytest --cov=. app/ tests/
|
||||
|
||||
# --- Cleanup ---
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@echo "Cleaning up stopped containers and dangling images..."
|
||||
docker container prune -f
|
||||
docker image prune -f
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Available commands:"
|
||||
@echo " build - Build the Docker image"
|
||||
@echo " run - Run the Docker container (single app, no Traefik)"
|
||||
@echo " stop - Stop and remove the Docker container"
|
||||
@echo " logs - Follow the logs of the container"
|
||||
@echo " shell - Get a shell inside the running container"
|
||||
@echo " up - Start the dev environment with docker-compose (with Traefik)"
|
||||
@echo " down - Stop the dev environment with docker-compose"
|
||||
@echo " tag - Tag the image for a registry"
|
||||
@echo " push - Push the image to a registry (after tagging)"
|
||||
@echo " clean - Clean up unused containers and images"
|
||||
@echo " help - Show this help message"
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
222
README.md
Normal file
222
README.md
Normal file
@ -0,0 +1,222 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
main.py
|
||||
Version: v0.0.7
|
||||
|
||||
Description:
|
||||
Adds /speakers endpoint to list speakers for a given engine/model.
|
||||
Returns list of available speakers from engine.list_voices(model).
|
||||
All previous endpoints and logic included.
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: main.py
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Query
|
||||
from fastapi.responses import JSONResponse, FileResponse
|
||||
from pydantic import BaseModel
|
||||
import os
|
||||
import base64
|
||||
from engines.piper import PiperEngine
|
||||
from engines.styletts import StyleTTSEngine
|
||||
from engines.chattts import ChatTTSEngine
|
||||
import shutil
|
||||
import uuid
|
||||
import hashlib
|
||||
import tempfile
|
||||
import ffmpeg
|
||||
|
||||
app = FastAPI(
|
||||
title="NovaAi – TTS-Engine-Hub",
|
||||
version="0.0.7",
|
||||
description="Local-first, modular multi-engine TTS server for your homelab and automation."
|
||||
)
|
||||
|
||||
ENGINE_REGISTRY = {
|
||||
"piper": PiperEngine(),
|
||||
"styletts": StyleTTSEngine(),
|
||||
"chattts": ChatTTSEngine(),
|
||||
}
|
||||
|
||||
AUDIO_OUT_DIR = "/tmp/tts_output"
|
||||
CACHE_DIR = "/tmp/tts_cache"
|
||||
os.makedirs(AUDIO_OUT_DIR, exist_ok=True)
|
||||
os.makedirs(CACHE_DIR, exist_ok=True)
|
||||
|
||||
class TTSRequest(BaseModel):
|
||||
text: str
|
||||
engine: str
|
||||
model: str = None
|
||||
speaker: str = None
|
||||
format: str = "ogg"
|
||||
chunking: bool = False
|
||||
|
||||
|
||||
def build_cache_key(req: TTSRequest) -> str:
|
||||
data = f"{req.text}|{req.engine}|{req.model}|{req.speaker}|{req.format}|{req.chunking}"
|
||||
return hashlib.sha256(data.encode()).hexdigest()
|
||||
|
||||
def chunk_text(text, maxlen=250):
|
||||
import re
|
||||
sentences = re.split(r'([.!?]\s)', text)
|
||||
chunks = []
|
||||
buf = ""
|
||||
for s in sentences:
|
||||
if len(buf) + len(s) > maxlen:
|
||||
if buf:
|
||||
chunks.append(buf.strip())
|
||||
buf = ""
|
||||
buf += s
|
||||
if buf.strip():
|
||||
chunks.append(buf.strip())
|
||||
return [c for c in chunks if c.strip()]
|
||||
|
||||
def concat_audio(files, fmt):
|
||||
if len(files) == 1:
|
||||
return files[0]
|
||||
output_file = tempfile.mktemp(suffix=f'.{fmt}', prefix="chunked_", dir="/tmp")
|
||||
if fmt == "wav":
|
||||
import wave
|
||||
data = []
|
||||
params = None
|
||||
for f in files:
|
||||
with wave.open(f, 'rb') as wf:
|
||||
if params is None:
|
||||
params = wf.getparams()
|
||||
data.append(wf.readframes(wf.getnframes()))
|
||||
with wave.open(output_file, 'wb') as wf:
|
||||
wf.setparams(params)
|
||||
for d in data:
|
||||
wf.writeframes(d)
|
||||
else:
|
||||
with tempfile.NamedTemporaryFile("w", delete=False) as tf:
|
||||
for f in files:
|
||||
tf.write(f"file '{f}'\n")
|
||||
tf.flush()
|
||||
(
|
||||
ffmpeg
|
||||
.input(tf.name, format='concat', safe=0)
|
||||
.output(output_file, acodec='copy')
|
||||
.run(overwrite_output=True, quiet=True)
|
||||
)
|
||||
os.unlink(tf.name)
|
||||
return output_file
|
||||
|
||||
@app.post("/tts")
|
||||
def tts_endpoint(req: TTSRequest, as_base64: bool = Query(False, alias="as")):
|
||||
cache_key = build_cache_key(req)
|
||||
ext = f'.{req.format.lower()}'
|
||||
cached_file = os.path.join(CACHE_DIR, f"tts_{cache_key}{ext}")
|
||||
if os.path.isfile(cached_file):
|
||||
fname = f"tts_{cache_key}{ext}"
|
||||
dest = os.path.join(AUDIO_OUT_DIR, fname)
|
||||
shutil.copy(cached_file, dest)
|
||||
if as_base64:
|
||||
with open(cached_file, "rb") as f:
|
||||
audio_b64 = base64.b64encode(f.read()).decode("utf-8")
|
||||
return JSONResponse({
|
||||
"engine": req.engine,
|
||||
"model": req.model,
|
||||
"speaker": req.speaker,
|
||||
"format": req.format,
|
||||
"audio_base64": audio_b64,
|
||||
"chunking": req.chunking,
|
||||
"message": "Audio from cache, base64 included"
|
||||
})
|
||||
return JSONResponse({
|
||||
"engine": req.engine,
|
||||
"model": req.model,
|
||||
"speaker": req.speaker,
|
||||
"format": req.format,
|
||||
"audio_url": f"/audio/{fname}",
|
||||
"cached": True,
|
||||
"chunking": req.chunking,
|
||||
"message": "Audio served from cache. Download from audio_url"
|
||||
})
|
||||
engine = ENGINE_REGISTRY.get(req.engine.lower())
|
||||
if not engine:
|
||||
raise HTTPException(status_code=404, detail=f"Engine '{req.engine}' not found.")
|
||||
if req.chunking and len(req.text) > 250:
|
||||
chunks = chunk_text(req.text, maxlen=250)
|
||||
chunk_files = [engine.synthesize(c, speaker=req.speaker, model=req.model, fmt=req.format) for c in chunks]
|
||||
audio_path = concat_audio(chunk_files, req.format.lower())
|
||||
else:
|
||||
audio_path = engine.synthesize(req.text, speaker=req.speaker, model=req.model, fmt=req.format)
|
||||
shutil.copy(audio_path, cached_file)
|
||||
fname = f"tts_{cache_key}{ext}"
|
||||
dest = os.path.join(AUDIO_OUT_DIR, fname)
|
||||
shutil.copy(audio_path, dest)
|
||||
if as_base64:
|
||||
with open(cached_file, "rb") as f:
|
||||
audio_b64 = base64.b64encode(f.read()).decode("utf-8")
|
||||
return JSONResponse({
|
||||
"engine": req.engine,
|
||||
"model": req.model,
|
||||
"speaker": req.speaker,
|
||||
"format": req.format,
|
||||
"audio_base64": audio_b64,
|
||||
"chunking": req.chunking,
|
||||
"message": "Audio from synth, base64 included"
|
||||
})
|
||||
return JSONResponse({
|
||||
"engine": req.engine,
|
||||
"model": req.model,
|
||||
"speaker": req.speaker,
|
||||
"format": req.format,
|
||||
"audio_url": f"/audio/{fname}",
|
||||
"cached": False,
|
||||
"chunking": req.chunking,
|
||||
"message": "Synthesized new audio. Download from audio_url"
|
||||
})
|
||||
|
||||
@app.get("/audio/{filename}")
|
||||
def audio_file(filename: str):
|
||||
fpath = os.path.join(AUDIO_OUT_DIR, filename)
|
||||
if not os.path.isfile(fpath):
|
||||
raise HTTPException(status_code=404, detail="Audio file not found")
|
||||
media_type = "audio/wav" if filename.endswith(".wav") else (
|
||||
"audio/ogg" if filename.endswith(".ogg") else "audio/mpeg"
|
||||
)
|
||||
return FileResponse(fpath, media_type=media_type, filename=filename)
|
||||
|
||||
@app.get("/engines")
|
||||
def engines_endpoint():
|
||||
engines = {}
|
||||
for name, engine in ENGINE_REGISTRY.items():
|
||||
engines[name] = engine.healthcheck()
|
||||
return engines
|
||||
|
||||
@app.get("/models")
|
||||
def models_endpoint():
|
||||
result = {}
|
||||
for name, engine in ENGINE_REGISTRY.items():
|
||||
try:
|
||||
result[name] = engine.list_models()
|
||||
except Exception as e:
|
||||
result[name] = []
|
||||
return result
|
||||
|
||||
@app.get("/speakers")
|
||||
def speakers_endpoint(engine: str, model: str = None):
|
||||
e = ENGINE_REGISTRY.get(engine.lower())
|
||||
if not e:
|
||||
raise HTTPException(status_code=404, detail=f"Engine '{engine}' not found.")
|
||||
try:
|
||||
speakers = e.list_voices(model)
|
||||
except Exception as err:
|
||||
speakers = []
|
||||
return {"engine": engine, "model": model, "speakers": speakers}
|
||||
|
||||
@app.get("/version")
|
||||
def version():
|
||||
return {"version": app.version}
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
status = {name: engine.healthcheck()["status"] for name, engine in ENGINE_REGISTRY.items()}
|
||||
return {"status": status, "detail": "API and engines loaded"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
|
||||
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
204
app/main.py
Normal file
204
app/main.py
Normal file
@ -0,0 +1,204 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
main.py
|
||||
Version: v0.1.0
|
||||
|
||||
Description:
|
||||
Refactored main.py: uses utils modules for chunking, concat, and cache key generation.
|
||||
All endpoints, features, and logic as before, but cleaner and more modular.
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: main.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from fastapi import FastAPI, HTTPException, Query
|
||||
from fastapi.responses import JSONResponse, FileResponse
|
||||
from pydantic import BaseModel
|
||||
import os
|
||||
import base64
|
||||
import shutil
|
||||
import uvicorn
|
||||
|
||||
from config import settings
|
||||
from engines.piper import PiperEngine
|
||||
from engines.styletts import StyleTTSEngine
|
||||
from engines.chattts import ChatTTSEngine
|
||||
from engines.f5_tts import F5TTSEngine
|
||||
from utils.text import chunk_text
|
||||
from utils.audio import concat_audio
|
||||
from utils.cache import build_cache_key
|
||||
|
||||
# --- Master list of all possible engine classes. ---
|
||||
ALL_ENGINES = {
|
||||
"piper": PiperEngine,
|
||||
"styletts": StyleTTSEngine,
|
||||
"chattts": ChatTTSEngine,
|
||||
"f5-tts": F5TTSEngine,
|
||||
}
|
||||
|
||||
def create_app():
|
||||
app = FastAPI(
|
||||
title="NovaAi – TTS-Engine-Hub",
|
||||
version="0.3.0",
|
||||
description="Local-first, modular multi-engine TTS server for your homelab and automation."
|
||||
)
|
||||
|
||||
# Dynamically build the registry of active engines based on settings.
|
||||
# This registry is local to the app instance created by this function.
|
||||
app.ENGINE_REGISTRY = {}
|
||||
for engine_name in settings.ACTIVE_ENGINES:
|
||||
if engine_name in ALL_ENGINES:
|
||||
print(f"Activating engine: {engine_name}")
|
||||
app.ENGINE_REGISTRY[engine_name] = ALL_ENGINES[engine_name]()
|
||||
else:
|
||||
print(f"Warning: Engine '{engine_name}' requested in config but not found in ALL_ENGINES.")
|
||||
|
||||
# Ensure the audio asset/cache directory exists.
|
||||
os.makedirs(settings.AUDIO_CACHE_DIR, exist_ok=True)
|
||||
|
||||
class TTSRequest(BaseModel):
|
||||
text: str
|
||||
engine: str
|
||||
model: str = None
|
||||
speaker: str = None
|
||||
format: str = "ogg"
|
||||
chunking: bool = False
|
||||
|
||||
@app.post("/tts")
|
||||
async def tts_endpoint(req: TTSRequest, as_base64: bool = Query(False, alias="as")):
|
||||
# --- 1. Check for engine and handle health ---
|
||||
engine = app.ENGINE_REGISTRY.get(req.engine.lower())
|
||||
if not engine:
|
||||
raise HTTPException(status_code=404, detail=f"Engine '{req.engine}' not found.")
|
||||
|
||||
health = engine.healthcheck()
|
||||
if health.get("status") != "ok":
|
||||
raise HTTPException(status_code=503, detail=f"Engine '{req.engine}' is not available. Status: {health.get('status')}")
|
||||
|
||||
# --- 2. Input validation ---
|
||||
available_models = engine.list_models()
|
||||
if req.model and available_models and req.model not in available_models:
|
||||
raise HTTPException(status_code=400, detail=f"Model '{req.model}' not found for engine '{req.engine}'. Available models: {available_models}")
|
||||
|
||||
available_voices = engine.list_voices(req.model)
|
||||
if req.speaker and available_voices and req.speaker not in available_voices:
|
||||
raise HTTPException(status_code=400, detail=f"Speaker '{req.speaker}' not found for model '{req.model}'. Available speakers: {available_voices}")
|
||||
|
||||
# --- 3. Check cache ---
|
||||
cache_key = build_cache_key(req)
|
||||
ext = f'.{req.format.lower()}'
|
||||
output_filename = f"tts_{cache_key}{ext}"
|
||||
output_filepath = os.path.join(settings.AUDIO_CACHE_DIR, output_filename)
|
||||
|
||||
is_cached = await asyncio.to_thread(os.path.isfile, output_filepath)
|
||||
if is_cached:
|
||||
if as_base64:
|
||||
audio_bytes = await asyncio.to_thread(lambda: open(output_filepath, "rb").read())
|
||||
audio_b64 = base64.b64encode(audio_bytes).decode("utf-8")
|
||||
return JSONResponse({
|
||||
"engine": req.engine, "model": req.model, "speaker": req.speaker, "format": req.format,
|
||||
"audio_base64": audio_b64, "chunking": req.chunking, "message": "Audio from cache, base64 included"
|
||||
})
|
||||
return JSONResponse({
|
||||
"engine": req.engine, "model": req.model, "speaker": req.speaker, "format": req.format,
|
||||
"audio_url": f"/audio/{output_filename}", "cached": True, "chunking": req.chunking,
|
||||
"message": "Audio served from cache. Download from audio_url"
|
||||
})
|
||||
|
||||
# --- 4. Synthesize audio ---
|
||||
try:
|
||||
if req.chunking and len(req.text) > 250:
|
||||
chunks = chunk_text(req.text, maxlen=250)
|
||||
synthesis_tasks = [engine.synthesize(c, speaker=req.speaker, model=req.model, fmt=req.format) for c in chunks]
|
||||
chunk_files = await asyncio.gather(*synthesis_tasks)
|
||||
synthesized_path = await asyncio.to_thread(concat_audio, chunk_files, req.format.lower())
|
||||
else:
|
||||
synthesized_path = await engine.synthesize(req.text, speaker=req.speaker, model=req.model, fmt=req.format)
|
||||
except (RuntimeError, ValueError, FileNotFoundError) as e:
|
||||
raise HTTPException(status_code=500, detail=f"Error during synthesis: {e}")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {e}")
|
||||
|
||||
# --- 5. Cache and return result ---
|
||||
await asyncio.to_thread(shutil.copy, synthesized_path, output_filepath)
|
||||
|
||||
# If the synth created a temp file in a different directory, clean it up
|
||||
if settings.AUDIO_CACHE_DIR not in os.path.abspath(synthesized_path):
|
||||
await asyncio.to_thread(os.remove, synthesized_path)
|
||||
|
||||
if as_base64:
|
||||
audio_bytes = await asyncio.to_thread(lambda: open(output_filepath, "rb").read())
|
||||
audio_b64 = base64.b64encode(audio_bytes).decode("utf-8")
|
||||
return JSONResponse({
|
||||
"engine": req.engine, "model": req.model, "speaker": req.speaker, "format": req.format,
|
||||
"audio_base64": audio_b64, "chunking": req.chunking, "message": "Audio from synth, base64 included"
|
||||
})
|
||||
|
||||
return JSONResponse({
|
||||
"engine": req.engine, "model": req.model, "speaker": req.speaker, "format": req.format,
|
||||
"audio_url": f"/audio/{output_filename}", "cached": False, "chunking": req.chunking,
|
||||
"message": "Synthesized new audio. Download from audio_url"
|
||||
})
|
||||
|
||||
@app.get("/audio/{filename}")
|
||||
async def audio_file(filename: str): # Made async
|
||||
fpath = os.path.join(settings.AUDIO_CACHE_DIR, filename)
|
||||
if not await asyncio.to_thread(os.path.isfile, fpath) or not await asyncio.to_thread(lambda: fpath.startswith(os.path.abspath(settings.AUDIO_CACHE_DIR))):
|
||||
raise HTTPException(status_code=404, detail="Audio file not found")
|
||||
media_type = "audio/wav" if filename.endswith(".wav") else (
|
||||
"audio/ogg" if filename.endswith(".ogg") else "audio/mpeg"
|
||||
)
|
||||
return FileResponse(fpath, media_type=media_type, filename=filename)
|
||||
|
||||
@app.get("/engines")
|
||||
def engines_endpoint():
|
||||
engines = {}
|
||||
for name, engine in app.ENGINE_REGISTRY.items():
|
||||
engines[name] = engine.healthcheck()
|
||||
return engines
|
||||
|
||||
@app.get("/models")
|
||||
def models_endpoint():
|
||||
result = {}
|
||||
for name, engine in app.ENGINE_REGISTRY.items():
|
||||
try:
|
||||
result[name] = engine.list_models()
|
||||
except Exception as e:
|
||||
result[name] = {"error": str(e)}
|
||||
return result
|
||||
|
||||
@app.get("/speakers")
|
||||
def speakers_endpoint(engine: str, model: str = None):
|
||||
e = app.ENGINE_REGISTRY.get(engine.lower())
|
||||
if not e:
|
||||
raise HTTPException(status_code=404, detail=f"Engine '{engine}' not found.")
|
||||
try:
|
||||
speakers = e.list_voices(model)
|
||||
except Exception as err:
|
||||
speakers = []
|
||||
return {"engine": engine, "model": model, "speakers": speakers}
|
||||
|
||||
@app.get("/version")
|
||||
def version():
|
||||
return {"version": app.version}
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
status = {name: engine.healthcheck()["status"] for name, engine in app.ENGINE_REGISTRY.items()}
|
||||
return {"status": status, "detail": "API and engines loaded"}
|
||||
|
||||
return app
|
||||
|
||||
# If main.py is executed directly, create the app and run uvicorn
|
||||
if __name__ == "__main__":
|
||||
app_instance = create_app()
|
||||
uvicorn.run(
|
||||
app_instance, # Pass the app instance
|
||||
host=settings.HOST,
|
||||
port=settings.PORT,
|
||||
reload=True
|
||||
)
|
||||
|
||||
|
||||
25
config.py
Normal file
25
config.py
Normal file
@ -0,0 +1,25 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
config.py
|
||||
Version: v0.0.1
|
||||
|
||||
Description:
|
||||
Centralized configuration management using pydantic-settings.
|
||||
Loads settings from a .env file and environment variables.
|
||||
"""
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from typing import List, Set
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# Server Configuration
|
||||
HOST: str = "0.0.0.0"
|
||||
PORT: int = 8000
|
||||
|
||||
# Application Configuration
|
||||
ACTIVE_ENGINES: Set[str] = {"piper"}
|
||||
ASSET_DIR: str = "asset"
|
||||
AUDIO_CACHE_DIR: str = "asset/audio"
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding='utf-8')
|
||||
|
||||
settings = Settings()
|
||||
44
docker-compose.yml
Normal file
44
docker-compose.yml
Normal file
@ -0,0 +1,44 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
container_name: audio_engine_hub_app
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
# Mount local app directory for hot-reloading in dev
|
||||
- ./app:/home/appuser/app
|
||||
# Mount models directory to provide models to the container
|
||||
- ./models:/home/appuser/models
|
||||
# Mount asset directory to persist generated audio files
|
||||
- ./asset:/home/appuser/asset
|
||||
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
env_file:
|
||||
- .env
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.app-router.rule=Host(`localhost`)"
|
||||
- "traefik.http.routers.app-router.entrypoints=web"
|
||||
- "traefik.http.services.app-service.loadbalancer.server.port=8000"
|
||||
networks:
|
||||
- web
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.10"
|
||||
container_name: traefik_proxy
|
||||
command:
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80"
|
||||
ports:
|
||||
- "80:80" # The HTTP port Traefik listens on
|
||||
- "8080:8080" # The Traefik Web UI (Dashboard)
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro" # Traefik needs access to the Docker daemon
|
||||
networks:
|
||||
- web
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: false
|
||||
48
engines/chattts.py
Normal file
48
engines/chattts.py
Normal file
@ -0,0 +1,48 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
engines/chattts.py
|
||||
Version: v0.0.1
|
||||
|
||||
Description:
|
||||
ChatTTS engine adapter.
|
||||
Implements TTSEngineBase interface for ChatTTS integration (dummy implementation).
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: chattts.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from engines.engine_base import TTSEngineBase
|
||||
|
||||
class ChatTTSEngine(TTSEngineBase):
|
||||
async def synthesize(self, text: str, speaker: str = None, model: str = None, fmt: str = "mp3"):
|
||||
# Dummy implementation: returns an empty string as it doesn't produce a file.
|
||||
print("Warning: ChatTTSEngine.synthesize is a dummy and does not produce audio.")
|
||||
return ""
|
||||
|
||||
def list_models(self):
|
||||
# Dummy implementation
|
||||
return ["chattts-v1", "chattts-v2"]
|
||||
|
||||
def list_voices(self, model: str = None):
|
||||
# Dummy implementation
|
||||
return ["default", "custom1", "custom2"]
|
||||
|
||||
def healthcheck(self):
|
||||
# Dummy implementation
|
||||
return {"status": "ok", "engine": "chattts"}
|
||||
|
||||
async def selftest(self):
|
||||
# Dummy implementation
|
||||
return {"selftest": True, "engine": "chattts"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
async def main():
|
||||
engine = ChatTTSEngine()
|
||||
print("Selftest:", await engine.selftest())
|
||||
print("Models:", engine.list_models())
|
||||
print("Voices:", engine.list_voices())
|
||||
print("Healthcheck:", engine.healthcheck())
|
||||
|
||||
asyncio.run(main())
|
||||
45
engines/engine_base.py
Normal file
45
engines/engine_base.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
engine_base.py
|
||||
Version: v0.0.1
|
||||
|
||||
Description:
|
||||
Abstract base class for all TTS engine modules.
|
||||
Defines the required interface for engine adapters.
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: engine_base.py
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import asyncio
|
||||
|
||||
class TTSEngineBase(ABC):
|
||||
@abstractmethod
|
||||
async def synthesize(self, text: str, speaker: str = None, model: str = None, fmt: str = "ogg"):
|
||||
"""
|
||||
Asynchronously generate speech audio from text input.
|
||||
Returns path to audio file.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def list_models(self):
|
||||
"""Return a list of available models."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def list_voices(self, model: str = None):
|
||||
"""Return a list of available voices for a model."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def healthcheck(self):
|
||||
"""Return health/status info for this engine."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
async def selftest(self):
|
||||
"""Asynchronously run internal self-test (basic functionality check)."""
|
||||
raise NotImplementedError
|
||||
1
engines/f5-tts-voices/default.txt
Normal file
1
engines/f5-tts-voices/default.txt
Normal file
@ -0,0 +1 @@
|
||||
Some call me nature, others call me mother nature.
|
||||
143
engines/f5_tts.py
Normal file
143
engines/f5_tts.py
Normal file
@ -0,0 +1,143 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
f5_tts.py
|
||||
Version: v0.0.2
|
||||
|
||||
Description:
|
||||
F5-TTS engine module.
|
||||
Implements the TTSEngineBase for F5-TTS text-to-speech synthesis.
|
||||
Now with robust speaker handling.
|
||||
|
||||
Author: Your Name (or leave as generated)
|
||||
Date: 2025-12-03
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import torch
|
||||
import torchaudio
|
||||
import numpy as np
|
||||
import soundfile as sf
|
||||
import asyncio
|
||||
from .engine_base import TTSEngineBase
|
||||
from importlib.resources import files
|
||||
|
||||
try:
|
||||
from f5_tts.api import F5TTS
|
||||
except ImportError:
|
||||
print("Warning: F5TTS could not be imported. F5-TTS engine will not be available.")
|
||||
F5TTS = None
|
||||
|
||||
class F5TTSEngine(TTSEngineBase):
|
||||
def __init__(self):
|
||||
# Initialize F5-TTS specific resources, models, etc.
|
||||
print("F5-TTS Engine Initializing...")
|
||||
self.speakers = {}
|
||||
self.model = None
|
||||
|
||||
if F5TTS:
|
||||
try:
|
||||
self.model = F5TTS(model="F5TTS_v1_Base")
|
||||
print("F5-TTS Engine Initialized.")
|
||||
self._load_speakers()
|
||||
except Exception as e:
|
||||
print(f"Error initializing F5-TTS Engine: {e}")
|
||||
self.model = None
|
||||
else:
|
||||
print("F5-TTS Engine not initialized because F5TTS is not available.")
|
||||
|
||||
def _load_speakers(self):
|
||||
# Add the default speaker
|
||||
default_wav = str(files("f5_tts").joinpath("infer/examples/basic/basic_ref_en.wav"))
|
||||
default_txt = "engines/f5-tts-voices/default.txt"
|
||||
if os.path.exists(default_txt):
|
||||
self.speakers["default"] = {"wav": default_wav, "txt": default_txt}
|
||||
|
||||
# Scan for custom speakers
|
||||
voices_dir = "engines/f5-tts-voices"
|
||||
if not os.path.isdir(voices_dir):
|
||||
return
|
||||
for file in os.listdir(voices_dir):
|
||||
if file.endswith(".wav"):
|
||||
speaker_name = file.rsplit('.', 1)[0]
|
||||
wav_path = os.path.join(voices_dir, file)
|
||||
txt_path = os.path.join(voices_dir, f"{speaker_name}.txt")
|
||||
if os.path.exists(txt_path):
|
||||
self.speakers[speaker_name] = {"wav": wav_path, "txt": txt_path}
|
||||
print(f"Found custom speaker: {speaker_name}")
|
||||
|
||||
def _blocking_synthesize(self, text: str, speaker: str, fmt: str):
|
||||
"""The actual blocking synthesis logic."""
|
||||
speaker_data = self.speakers[speaker]
|
||||
ref_file = speaker_data["wav"]
|
||||
|
||||
with open(speaker_data["txt"], 'r') as f:
|
||||
ref_text = f.read()
|
||||
|
||||
print(f"F5-TTS: Synthesizing '{text}' with reference voice from '{ref_file}'.")
|
||||
|
||||
wav, sr, spec = self.model.infer(
|
||||
ref_file=ref_file,
|
||||
ref_text=ref_text,
|
||||
gen_text=text,
|
||||
)
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix=f".{fmt}") as temp_file:
|
||||
if fmt == "wav":
|
||||
torchaudio.save(temp_file.name, torch.from_numpy(wav).unsqueeze(0), sr, format="wav")
|
||||
else:
|
||||
# Convert to float32 for soundfile
|
||||
wav_float = wav.astype(np.float32) / np.iinfo(wav.dtype).max
|
||||
sf.write(temp_file.name, wav_float, sr)
|
||||
return temp_file.name
|
||||
|
||||
async def synthesize(self, text: str, speaker: str = None, model: str = None, fmt: str = "ogg"):
|
||||
"""
|
||||
Asynchronously generate speech audio from text input using F5-TTS.
|
||||
"""
|
||||
if not self.model:
|
||||
raise RuntimeError("F5-TTS Engine not initialized.")
|
||||
|
||||
speaker_to_use = speaker if speaker in self.speakers else "default"
|
||||
if speaker and speaker not in self.speakers:
|
||||
print(f"Warning: Speaker '{speaker}' not found. Falling back to default speaker.")
|
||||
|
||||
if speaker_to_use not in self.speakers:
|
||||
raise RuntimeError("No default speaker found for F5-TTS. Please add a 'default.wav' and 'default.txt' to the 'engines/f5-tts-voices' directory.")
|
||||
|
||||
try:
|
||||
# Run the blocking synthesis in a separate thread
|
||||
return await asyncio.to_thread(self._blocking_synthesize, text, speaker_to_use, fmt)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"F5-TTS synthesis failed: {e}")
|
||||
|
||||
def list_models(self):
|
||||
"""Return a list of available F5-TTS models."""
|
||||
if not self.model:
|
||||
return []
|
||||
return ["F5TTS_v1_Base"]
|
||||
|
||||
def list_voices(self, model: str = None):
|
||||
"""Return a list of available F5-TTS voices for a model."""
|
||||
return list(self.speakers.keys())
|
||||
|
||||
def healthcheck(self):
|
||||
"""Return health/status info for F5-TTS engine."""
|
||||
if self.model:
|
||||
return {"status": "ok", "message": "F5-TTS engine is ready"}
|
||||
else:
|
||||
return {"status": "error", "message": "F5-TTS engine failed to initialize"}
|
||||
|
||||
async def selftest(self):
|
||||
"""Run internal self-test for F5-TTS."""
|
||||
if not self.model:
|
||||
return {"status": "failed", "message": "F5-TTS Engine not initialized."}
|
||||
try:
|
||||
# Await the async synthesize method
|
||||
audio_file = await self.synthesize("this is a test.")
|
||||
selftest_passed = os.path.exists(audio_file) and os.path.getsize(audio_file) > 0
|
||||
if selftest_passed:
|
||||
os.remove(audio_file)
|
||||
return {"status": "passed" if selftest_passed else "failed", "message": "F5-TTS self-test successful"}
|
||||
except Exception as e:
|
||||
return {"status": "failed", "message": f"F5-TTS self-test failed: {e}"}
|
||||
163
engines/piper.py
Normal file
163
engines/piper.py
Normal file
@ -0,0 +1,163 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
engines/piper.py
|
||||
Version: v0.1.1
|
||||
|
||||
Description:
|
||||
Piper TTS engine adapter: real CLI invocation + output as WAV, OGG, or MP3.
|
||||
Synthesizes WAV via Piper, converts to OGG/MP3 via ffmpeg-python if needed.
|
||||
Uses dynamic model path: ./models/piper/[model]/model.onnx
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: piper.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import subprocess
|
||||
import tempfile
|
||||
import os
|
||||
import shutil
|
||||
import json
|
||||
from engines.engine_base import TTSEngineBase
|
||||
import ffmpeg
|
||||
|
||||
class PiperEngine(TTSEngineBase):
|
||||
def __init__(self):
|
||||
self.piper_executable = shutil.which("piper")
|
||||
self.ffmpeg_executable = shutil.which("ffmpeg")
|
||||
|
||||
def _load_config(self, model: str):
|
||||
"""Load the model config JSON file to get speaker mappings."""
|
||||
model_dir = f"./models/piper/{model}"
|
||||
config_file = os.path.join(model_dir, f"{model}.onnx.json")
|
||||
if os.path.isfile(config_file):
|
||||
with open(config_file, 'r') as f:
|
||||
return json.load(f)
|
||||
return {}
|
||||
|
||||
def _get_speaker_id(self, speaker: str, model: str):
|
||||
"""Convert speaker name to speaker ID using the model's config."""
|
||||
if not speaker or speaker == "default":
|
||||
return None
|
||||
if speaker.isdigit():
|
||||
return speaker
|
||||
config = self._load_config(model)
|
||||
speaker_id_map = config.get('speaker_id_map', {})
|
||||
return str(speaker_id_map.get(speaker))
|
||||
|
||||
def _run_ffmpeg_blocking(self, input_path, output_path):
|
||||
"""Wrapper for the blocking ffmpeg call."""
|
||||
(
|
||||
ffmpeg
|
||||
.input(input_path)
|
||||
.output(output_path)
|
||||
.run(overwrite_output=True, quiet=True)
|
||||
)
|
||||
|
||||
async def synthesize(self, text: str, speaker: str = None, model: str = None, fmt: str = "ogg"):
|
||||
if not self.piper_executable:
|
||||
raise RuntimeError("Piper executable not found. Please install it and ensure it's in your PATH.")
|
||||
if not model:
|
||||
raise ValueError("Model must be specified for Piper.")
|
||||
model_dir = f"./models/piper/{model}"
|
||||
model_file = os.path.join(model_dir, f"{model}.onnx")
|
||||
if not os.path.isfile(model_file):
|
||||
raise FileNotFoundError(f"Piper model not found: {model_file}")
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".wav", prefix="piper_", delete=False) as wav_file:
|
||||
output_wav_path = wav_file.name
|
||||
|
||||
cmd = [self.piper_executable, "--model", model_file, "--output_file", output_wav_path, "--stdin_text"]
|
||||
|
||||
if speaker:
|
||||
speaker_id = self._get_speaker_id(speaker, model)
|
||||
if speaker_id:
|
||||
cmd += ["--speaker", speaker_id]
|
||||
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate(input=text.encode('utf-8'))
|
||||
|
||||
if process.returncode != 0:
|
||||
os.remove(output_wav_path)
|
||||
raise RuntimeError(f"Piper synth failed: {stderr.decode()}")
|
||||
|
||||
fmt = (fmt or "ogg").lower()
|
||||
if fmt == "wav":
|
||||
return output_wav_path
|
||||
|
||||
if not self.ffmpeg_executable:
|
||||
os.remove(output_wav_path)
|
||||
raise RuntimeError("ffmpeg not found, cannot convert audio format.")
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=f'.{fmt}', prefix="piper_conv_", delete=False) as converted_file:
|
||||
output_other_path = converted_file.name
|
||||
|
||||
try:
|
||||
await asyncio.to_thread(self._run_ffmpeg_blocking, output_wav_path, output_other_path)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"ffmpeg conversion failed: {e}")
|
||||
finally:
|
||||
os.remove(output_wav_path)
|
||||
|
||||
return output_other_path
|
||||
|
||||
def list_models(self):
|
||||
models_dir = "./models/piper/"
|
||||
if not os.path.isdir(models_dir):
|
||||
return []
|
||||
return [name for name in os.listdir(models_dir)
|
||||
if os.path.isdir(os.path.join(models_dir, name))]
|
||||
|
||||
def list_voices(self, model: str = None):
|
||||
if not model:
|
||||
return ["default"]
|
||||
config = self._load_config(model)
|
||||
speaker_id_map = config.get('speaker_id_map', {})
|
||||
if speaker_id_map:
|
||||
return ["default"] + sorted(speaker_id_map.keys())
|
||||
return ["default"]
|
||||
|
||||
def healthcheck(self):
|
||||
status = "ok"
|
||||
if not self.piper_executable:
|
||||
status = "missing_piper_executable"
|
||||
return {"status": status, "engine": "piper"}
|
||||
|
||||
async def selftest(self):
|
||||
if not self.piper_executable:
|
||||
return {"selftest": False, "error": "Piper executable not found.", "engine": "piper"}
|
||||
try:
|
||||
models = self.list_models()
|
||||
if not models:
|
||||
return {"selftest": False, "error": "No Piper models found.", "engine": "piper"}
|
||||
|
||||
test_text = "This is a selftest."
|
||||
first_model = models[0]
|
||||
voices = self.list_voices(first_model)
|
||||
test_voice = voices[0] if voices else None
|
||||
|
||||
audio_file = await self.synthesize(test_text, speaker=test_voice, model=first_model, fmt="wav")
|
||||
|
||||
selftest_passed = os.path.exists(audio_file) and os.path.getsize(audio_file) > 0
|
||||
if selftest_passed:
|
||||
os.remove(audio_file)
|
||||
|
||||
return {"selftest": selftest_passed, "models": models, "engine": "piper"}
|
||||
except Exception as e:
|
||||
return {"selftest": False, "error": str(e), "engine": "piper"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
async def main():
|
||||
engine = PiperEngine()
|
||||
print("Selftest:", await engine.selftest())
|
||||
print("Models:", engine.list_models())
|
||||
print("Voices:", engine.list_voices(engine.list_models()[0]))
|
||||
print("Healthcheck:", engine.healthcheck())
|
||||
|
||||
asyncio.run(main())
|
||||
48
engines/styletts.py
Normal file
48
engines/styletts.py
Normal file
@ -0,0 +1,48 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
engines/styletts.py
|
||||
Version: v0.0.1
|
||||
|
||||
Description:
|
||||
StyleTTS engine adapter.
|
||||
Implements TTSEngineBase interface for StyleTTS integration (dummy implementation).
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: styletts.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from engines.engine_base import TTSEngineBase
|
||||
|
||||
class StyleTTSEngine(TTSEngineBase):
|
||||
async def synthesize(self, text: str, speaker: str = None, model: str = None, fmt: str = "mp3"):
|
||||
# Dummy implementation: returns an empty string as it doesn't produce a file.
|
||||
print("Warning: StyleTTSEngine.synthesize is a dummy and does not produce audio.")
|
||||
return ""
|
||||
|
||||
def list_models(self):
|
||||
# Dummy implementation
|
||||
return ["styletts_v2_de", "styletts_v2_en"]
|
||||
|
||||
def list_voices(self, model: str = None):
|
||||
# Dummy implementation
|
||||
return ["neutral", "emotional", "female"]
|
||||
|
||||
def healthcheck(self):
|
||||
# Dummy implementation
|
||||
return {"status": "ok", "engine": "styletts"}
|
||||
|
||||
async def selftest(self):
|
||||
# Dummy implementation
|
||||
return {"selftest": True, "engine": "styletts"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
async def main():
|
||||
engine = StyleTTSEngine()
|
||||
print("Selftest:", await engine.selftest())
|
||||
print("Models:", engine.list_models())
|
||||
print("Voices:", engine.list_voices())
|
||||
print("Healthcheck:", engine.healthcheck())
|
||||
|
||||
asyncio.run(main())
|
||||
296
guide.md
Normal file
296
guide.md
Normal file
@ -0,0 +1,296 @@
|
||||
# Leitfaden: Best Practices zur Containerisierung von Python-Backends
|
||||
|
||||
Dieser Leitfaden zeigt einen professionellen Workflow zur Containerisierung einer Python-Backend-Anwendung mit Docker, inklusive Integration eines Reverse Proxys (Traefik) für die lokale Entwicklung. Wir verwenden eine minimale FastAPI-Anwendung als Beispiel.
|
||||
|
||||
Die vorgestellten Methoden umfassen:
|
||||
- Optimierte, sichere Docker-Images durch Multi-Stage-Builds.
|
||||
- Einen einfachen Workflow durch die Verwendung eines `Makefile`.
|
||||
- Trennung von Entwicklungs- und Produktionsumgebungen.
|
||||
- Korrekte Verwaltung von Code und Images mit Git und einer Container Registry.
|
||||
- Integration von Traefik für dynamisches Routing in der lokalen Entwicklung.
|
||||
|
||||
## 1. Projektstruktur
|
||||
|
||||
Wir haben die folgende Struktur erstellt:
|
||||
|
||||
```
|
||||
/
|
||||
├── .dockerignore # Listet Dateien auf, die Docker ignorieren soll.
|
||||
├── .gitignore # Listet Dateien auf, die Git ignorieren soll.
|
||||
├── Dockerfile # Die Blaupause für unser produktives Docker-Image.
|
||||
├── Makefile # Vereinfacht die Ausführung von Docker-Befehlen.
|
||||
├── app/
|
||||
│ ├── __init__.py # Macht 'app' zu einem Python-Paket.
|
||||
│ └── main.py # Unser FastAPI-Anwendungscode.
|
||||
├── docker-compose.yml # Definiert die lokale Entwicklungsumgebung mit Traefik.
|
||||
├── guide.md # Dieser Leitfaden.
|
||||
└── requirements.txt # Liste der Python-Abhängigkeiten.
|
||||
```
|
||||
|
||||
## 2. Die Komponenten im Detail
|
||||
|
||||
### `requirements.txt`
|
||||
Hier definieren wir die Python-Bibliotheken, die unser Projekt benötigt.
|
||||
```
|
||||
fastapi
|
||||
uvicorn[standard]
|
||||
gunicorn
|
||||
```
|
||||
- `fastapi`: Das Web-Framework.
|
||||
- `uvicorn`: Ein schneller ASGI-Server, ideal für die Entwicklung.
|
||||
- `gunicorn`: Ein robuster WSGI-Produktionsserver, der Uvicorn-Worker zur Ausführung unserer ASGI-Anwendung nutzt.
|
||||
|
||||
### `.gitignore` & `.dockerignore`
|
||||
- **`.gitignore`**: Verhindert, dass sensible Daten (`.env`), temporäre Dateien (`__pycache__`) oder lokale Konfigurationen (`.vscode/`) in Git eingecheckt werden.
|
||||
- **`.dockerignore`**: Verhindert, dass unnötige oder sensible Dateien in das Docker-Image kopiert werden. Dies hält das Image klein und sicher. Wir schließen z.B. das `.git`-Verzeichnis, das `Dockerfile` selbst und lokale Umgebungsdateien aus.
|
||||
|
||||
### `app/main.py`
|
||||
Eine minimale FastAPI-Anwendung mit zwei Endpunkten.
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"message": "Hello World"}
|
||||
|
||||
@app.get("/health")
|
||||
def health_check():
|
||||
return {"status": "ok"}
|
||||
```
|
||||
|
||||
### `Dockerfile` (Multi-Stage-Build)
|
||||
Dies ist das Herzstück unserer Containerisierung. Ein Multi-Stage-Build trennt die Build-Umgebung von der Laufzeitumgebung.
|
||||
|
||||
**Stufe 1: `builder`**
|
||||
```dockerfile
|
||||
FROM python:3.11 as builder
|
||||
WORKDIR /opt/venv
|
||||
RUN python -m venv .
|
||||
COPY requirements.txt .
|
||||
RUN . /opt/venv/bin/activate && pip install --no-cache-dir -r requirements.txt
|
||||
```
|
||||
- Wir starten mit einem vollständigen Python-Image.
|
||||
- Erstellen ein virtuelles Environment (`venv`) und installieren die Abhängigkeiten hinein.
|
||||
- Das Ergebnis ist ein Ordner `/opt/venv` mit einer sauberen Python-Umgebung.
|
||||
|
||||
**Stufe 2: `runner` (Das finale Image)**
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
RUN useradd --create-home --shell /bin/bash appuser
|
||||
WORKDIR /home/appuser/app
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
COPY app/ .
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
EXPOSE 8000
|
||||
USER appuser
|
||||
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-w", "2", "-b", "0.0.0.0:8000", "main:app"]
|
||||
```
|
||||
- Wir starten mit einem minimalen `-slim`-Image.
|
||||
- **Sicherheit**: Wir erstellen einen unprivilegierten Benutzer `appuser`. Die Anwendung wird als dieser Benutzer ausgeführt.
|
||||
- Wir kopieren das `venv` aus der `builder`-Stufe und unseren App-Code.
|
||||
- **Produktions-Server**: Wir starten die App mit `gunicorn` und 2 `uvicorn`-Workern. Dies ist ein stabiles Setup für die Produktion.
|
||||
|
||||
### `docker-compose.yml` (Für die lokale Entwicklung mit Traefik)
|
||||
Diese Datei definiert unsere lokale Entwicklungsumgebung, die nun auch Traefik als Reverse Proxy enthält.
|
||||
|
||||
**Was ist Traefik?**
|
||||
Traefik ist ein moderner Edge Router und Reverse Proxy, der dynamisch Dienste auf der Grundlage ihrer Konfiguration (z.B. Docker-Labels) entdeckt. Er leitet Anfragen an die richtigen Container weiter, ohne dass man manuelle Konfigurationen in einer separaten Datei vornehmen muss.
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
volumes:
|
||||
- ./app:/home/appuser/app
|
||||
command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# Definiert einen Router für den App-Dienst
|
||||
- "traefik.http.routers.app-router.rule=Host(`localhost`)"
|
||||
- "traefik.http.routers.app-router.entrypoints=web"
|
||||
# Definiert den internen Port des Dienstes (im Container)
|
||||
- "traefik.http.services.app-service.loadbalancer.server.port=8000"
|
||||
networks:
|
||||
- web
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.10"
|
||||
command:
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false" # Nur Dienste mit traefik.enable=true exposen
|
||||
- "--entrypoints.web.address=:80"
|
||||
ports:
|
||||
- "80:80" # Der HTTP-Port, über den Traefik lauscht
|
||||
- "8080:8080" # Das Web UI (Dashboard) von Traefik
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro" # Traefik braucht Zugriff auf den Docker-Daemon
|
||||
networks:
|
||||
- web
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: false
|
||||
```
|
||||
- **`app` Service**: Die direkte Port-Exponierung wurde entfernt. Stattdessen nutzt Traefik die `labels`, um das Routing zu konfigurieren.
|
||||
- `traefik.enable=true`: Aktiviert die Erkennung durch Traefik.
|
||||
- `traefik.http.routers.app-router.rule=Host('localhost')`: Sagt Traefik, dass Anfragen an die Host `localhost` an diesen Dienst geleitet werden sollen.
|
||||
- `traefik.http.routers.app-router.entrypoints=web`: Verknüpft diesen Router mit dem `web`-Entrypoint von Traefik (Port 80).
|
||||
- `traefik.http.services.app-service.loadbalancer.server.port=8000`: Informiert Traefik über den internen Port des FastAPI-Dienstes.
|
||||
- **`traefik` Service**: Dies ist der Traefik-Container selbst.
|
||||
- Er konfiguriert das Dashboard (`--api.dashboard=true`) und aktiviert den Docker-Provider (`--providers.docker=true`), der Container anhand ihrer Labels entdeckt.
|
||||
- Er lauscht auf Port 80 (HTTP) und 8080 (Dashboard).
|
||||
- `/var/run/docker.sock`: Ermöglicht Traefik die Kommunikation mit dem Docker-Daemon, um Container und deren Labels zu erkennen.
|
||||
- **`networks: web`**: Erstellt ein gemeinsames Netzwerk, in dem Traefik und die `app` kommunizieren können.
|
||||
|
||||
### `Makefile` (Der Kommando-Runner)
|
||||
Das `Makefile` gibt uns einfache Befehle für komplexe Aktionen. Es wurde angepasst, um die Traefik-Ports zu berücksichtigen.
|
||||
|
||||
```makefile
|
||||
# --- Configuration ---
|
||||
# Default internal application port (exposed to Traefik)
|
||||
PORT := 8000
|
||||
# Traefik's external entrypoints
|
||||
TRAEFIK_WEB_PORT := 80
|
||||
TRAEFIK_DASHBOARD_PORT := 8080
|
||||
|
||||
# --- Port Checking Functions ---
|
||||
|
||||
# Function to check if required Traefik ports are free
|
||||
# Exits if any are busy, does not suggest alternatives.
|
||||
define check_traefik_ports_free
|
||||
@echo "Checking if Traefik ports ($(TRAEFIK_WEB_PORT}, $(TRAEFIK_DASHBOARD_PORT}) are free..." >&2
|
||||
@local busy_ports=""; \
|
||||
if ss -tulnp | grep ":$(TRAEFIK_WEB_PORT) " > /dev/null; then \
|
||||
busy_ports="$$busy_ports $(TRAEFIK_WEB_PORT)"; \
|
||||
fi; \
|
||||
if ss -tulnp | grep ":$(TRAEFIK_DASHBOARD_PORT) " > /dev/null; then \
|
||||
busy_ports="$$busy_ports $(TRAEFIK_DASHBOARD_PORT)"; \
|
||||
fi; \
|
||||
if [ -n "$$busy_ports" ]; then \
|
||||
echo "ERROR: The following Traefik ports are already in use: $$busy_ports. Please free them or stop Traefik if already running." >&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
@echo "Traefik ports are free." >&2
|
||||
endef
|
||||
|
||||
# Function to find next free host port for the app and ask user
|
||||
# This function will echo the chosen port if successful, or exit with an error.
|
||||
define check_app_port_free
|
||||
@local start_port=$(PORT); \
|
||||
local found_port=$$start_port; \
|
||||
local is_free=false; \
|
||||
\
|
||||
if ! ss -tulnp | grep ":$$start_port " > /dev/null; then \
|
||||
echo "$$start_port"; \
|
||||
exit 0; \
|
||||
fi; \
|
||||
\
|
||||
echo "Port $$start_port is busy. Searching for a free port for the app..." >&2; \
|
||||
while ! $$is_free; do \
|
||||
if ! ss -tulnp | grep ":$$found_port " > /dev/null; then \
|
||||
is_free=true; \
|
||||
else \
|
||||
((found_port++)); \
|
||||
if [ "$$found_port" -gt 65535 ]; then \
|
||||
echo "ERROR: No free ports found up to 65535. Aborting." >&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi; \
|
||||
done; \
|
||||
\
|
||||
echo "Port $$start_port is busy. I found port $$found_port to be free for the app." >&2; \
|
||||
read -p "Do you want to use port $$found_port for the app? (y/N): " choice; \
|
||||
case "$$choice" in \
|
||||
y|Y ) \
|
||||
echo "$$found_port"; \
|
||||
;; \
|
||||
* ) \
|
||||
echo "Operation cancelled by user." >&2; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac;
|
||||
endef
|
||||
|
||||
# --- Docker Commands ---
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
@echo "Building Docker image: $(IMAGE_NAME):$(TAG)"
|
||||
docker build -t $(IMAGE_NAME):$(TAG) .
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
@export SELECTED_HOST_PORT=$$(bash -c 'func() { $(check_app_port_free) }; func') && \
|
||||
echo "Using host port $$SELECTED_HOST_PORT for single app container" && \
|
||||
docker run -d -p $$SELECTED_HOST_PORT:$(PORT) --name $(IMAGE_NAME) $(IMAGE_NAME):$(TAG)
|
||||
|
||||
.PHONY: stop
|
||||
stop:
|
||||
@echo "Stopping Docker container: $(IMAGE_NAME)"
|
||||
docker stop $(IMAGE_NAME) || true
|
||||
docker rm $(IMAGE_NAME) || true
|
||||
|
||||
.PHONY: logs
|
||||
logs:
|
||||
@echo "Showing logs for container: $(IMAGE_NAME)"
|
||||
docker logs -f $(IMAGE_NAME)
|
||||
|
||||
.PHONY: shell
|
||||
shell:
|
||||
@echo "Accessing shell in container: $(IMAGE_NAME)"
|
||||
docker exec -it $(IMAGE_NAME) /bin/bash
|
||||
|
||||
# --- Docker Compose Commands ---
|
||||
|
||||
.PHONY: up
|
||||
up:
|
||||
$(call check_traefik_ports_free) # Check Traefik ports before starting
|
||||
@echo "Starting development environment with Docker Compose (Traefik enabled)..."
|
||||
docker-compose up --build -d
|
||||
|
||||
.PHONY: down
|
||||
down:
|
||||
@echo "Stopping development environment with Docker Compose..."
|
||||
docker-compose down
|
||||
|
||||
# --- Image Management ---
|
||||
|
||||
.PHONY: tag
|
||||
tag:
|
||||
@echo "Tagging image $(IMAGE_NAME):$(TAG) as $(REGISTRY)/$(IMAGE_NAME):$(TAG)"
|
||||
docker tag $(IMAGE_NAME):$(TAG) $(REGISTRY)/$(IMAGE_NAME):$(TAG)
|
||||
|
||||
.PHONY: push
|
||||
push: tag
|
||||
@echo "Pushing image $(REGISTRY)/$(IMAGE_NAME):$(TAG) to registry..."
|
||||
docker push $(REGISTRY)/$(IMAGE_NAME):$(TAG)
|
||||
|
||||
# --- Cleanup ---
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@echo "Cleaning up stopped containers and dangling images..."
|
||||
docker container prune -f
|
||||
docker image prune -f
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Available commands:"
|
||||
@echo " build - Build the Docker image"
|
||||
@echo " run - Run the Docker container (single app, no Traefik)"
|
||||
@echo " stop - Stop and remove the Docker container"
|
||||
@echo " logs - Follow the logs of the container"
|
||||
@echo " shell - Get a shell inside the running container"
|
||||
@echo " up - Start the dev environment with docker-compose (with Traefik)"
|
||||
@echo " down - Stop the dev environment with docker-compose"
|
||||
@echo " tag - Tag the image for a registry"
|
||||
@echo " push - Push the image to a registry (after tagging)"
|
||||
@echo " clean - Clean up unused containers and images"
|
||||
@echo " help - Show this help message"
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
493
models/de_DE-thorsten-high/de_DE-thorsten-high.onnx.json
Normal file
493
models/de_DE-thorsten-high/de_DE-thorsten-high.onnx.json
Normal file
@ -0,0 +1,493 @@
|
||||
{
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "high"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "de"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 1,
|
||||
"speaker_id_map": {},
|
||||
"piper_version": "1.0.0",
|
||||
"language": {
|
||||
"code": "de_DE",
|
||||
"family": "de",
|
||||
"region": "DE",
|
||||
"name_native": "Deutsch",
|
||||
"name_english": "German",
|
||||
"country_english": "Germany"
|
||||
},
|
||||
"dataset": "thorsten"
|
||||
}
|
||||
@ -0,0 +1,502 @@
|
||||
{
|
||||
"piper_version": "1.1.0",
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "medium"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "de"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 8,
|
||||
"speaker_id_map": {
|
||||
"amused": 0,
|
||||
"angry": 1,
|
||||
"disgusted": 2,
|
||||
"drunk": 3,
|
||||
"neutral": 4,
|
||||
"sleepy": 5,
|
||||
"surprised": 6,
|
||||
"whisper": 7
|
||||
},
|
||||
"language": {
|
||||
"code": "de_DE",
|
||||
"family": "de",
|
||||
"region": "DE",
|
||||
"name_native": "Deutsch",
|
||||
"name_english": "German",
|
||||
"country_english": "Germany"
|
||||
},
|
||||
"dataset": "thorsten_emotional"
|
||||
}
|
||||
502
models/en_GB-cori-high/en_GB-cori-high.onnx.json
Normal file
502
models/en_GB-cori-high/en_GB-cori-high.onnx.json
Normal file
@ -0,0 +1,502 @@
|
||||
{
|
||||
"dataset": "cori",
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "high"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "en"
|
||||
},
|
||||
"language": {
|
||||
"code": "en_GB",
|
||||
"family": "en",
|
||||
"region": "GB",
|
||||
"name_native": "English",
|
||||
"name_english": "English",
|
||||
"country_english": "Great Britain"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"X": [
|
||||
156
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"g": [
|
||||
154
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʦ": [
|
||||
155
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 1,
|
||||
"speaker_id_map": {},
|
||||
"piper_version": "1.0.0"
|
||||
}
|
||||
603
models/en_GB-vctk-medium/en_GB-vctk-medium.onnx.json
Normal file
603
models/en_GB-vctk-medium/en_GB-vctk-medium.onnx.json
Normal file
@ -0,0 +1,603 @@
|
||||
{
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "medium"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "en-gb-x-rp"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.333,
|
||||
"length_scale": 1.4,
|
||||
"noise_w": 0.333
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 109,
|
||||
"speaker_id_map": {
|
||||
"p239": 0,
|
||||
"p236": 1,
|
||||
"p264": 2,
|
||||
"p250": 3,
|
||||
"p259": 4,
|
||||
"p247": 5,
|
||||
"p261": 6,
|
||||
"p263": 7,
|
||||
"p283": 8,
|
||||
"p286": 9,
|
||||
"p274": 10,
|
||||
"p276": 11,
|
||||
"p270": 12,
|
||||
"p281": 13,
|
||||
"p277": 14,
|
||||
"p231": 15,
|
||||
"p271": 16,
|
||||
"p238": 17,
|
||||
"p257": 18,
|
||||
"p273": 19,
|
||||
"p284": 20,
|
||||
"p329": 21,
|
||||
"p361": 22,
|
||||
"p287": 23,
|
||||
"p360": 24,
|
||||
"p374": 25,
|
||||
"p376": 26,
|
||||
"p310": 27,
|
||||
"p304": 28,
|
||||
"p334": 29,
|
||||
"p340": 30,
|
||||
"p323": 31,
|
||||
"p347": 32,
|
||||
"p330": 33,
|
||||
"p308": 34,
|
||||
"p314": 35,
|
||||
"p317": 36,
|
||||
"p339": 37,
|
||||
"p311": 38,
|
||||
"p294": 39,
|
||||
"p305": 40,
|
||||
"p266": 41,
|
||||
"p335": 42,
|
||||
"p318": 43,
|
||||
"p351": 44,
|
||||
"p333": 45,
|
||||
"p313": 46,
|
||||
"p316": 47,
|
||||
"p244": 48,
|
||||
"p307": 49,
|
||||
"p363": 50,
|
||||
"p336": 51,
|
||||
"p297": 52,
|
||||
"p312": 53,
|
||||
"p267": 54,
|
||||
"p275": 55,
|
||||
"p295": 56,
|
||||
"p258": 57,
|
||||
"p288": 58,
|
||||
"p301": 59,
|
||||
"p232": 60,
|
||||
"p292": 61,
|
||||
"p272": 62,
|
||||
"p280": 63,
|
||||
"p278": 64,
|
||||
"p341": 65,
|
||||
"p268": 66,
|
||||
"p298": 67,
|
||||
"p299": 68,
|
||||
"p279": 69,
|
||||
"p285": 70,
|
||||
"p326": 71,
|
||||
"p300": 72,
|
||||
"s5": 73,
|
||||
"p230": 74,
|
||||
"p345": 75,
|
||||
"p254": 76,
|
||||
"p269": 77,
|
||||
"p293": 78,
|
||||
"p252": 79,
|
||||
"p262": 80,
|
||||
"p243": 81,
|
||||
"p227": 82,
|
||||
"p343": 83,
|
||||
"p255": 84,
|
||||
"p229": 85,
|
||||
"p240": 86,
|
||||
"p248": 87,
|
||||
"p253": 88,
|
||||
"p233": 89,
|
||||
"p228": 90,
|
||||
"p282": 91,
|
||||
"p251": 92,
|
||||
"p246": 93,
|
||||
"p234": 94,
|
||||
"p226": 95,
|
||||
"p260": 96,
|
||||
"p245": 97,
|
||||
"p241": 98,
|
||||
"p303": 99,
|
||||
"p265": 100,
|
||||
"p306": 101,
|
||||
"p237": 102,
|
||||
"p249": 103,
|
||||
"p256": 104,
|
||||
"p302": 105,
|
||||
"p364": 106,
|
||||
"p225": 107,
|
||||
"p362": 108
|
||||
},
|
||||
"piper_version": "1.0.0",
|
||||
"language": {
|
||||
"code": "en_GB",
|
||||
"family": "en",
|
||||
"region": "GB",
|
||||
"name_native": "English",
|
||||
"name_english": "English",
|
||||
"country_english": "Great Britain"
|
||||
},
|
||||
"dataset": "vctk"
|
||||
}
|
||||
508
models/en_US-hfc_female-medium/en_US-hfc_female-medium.onnx.json
Normal file
508
models/en_US-hfc_female-medium/en_US-hfc_female-medium.onnx.json
Normal file
@ -0,0 +1,508 @@
|
||||
{
|
||||
"dataset": "hfc_female",
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "medium"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "en-us"
|
||||
},
|
||||
"language": {
|
||||
"code": "en_US",
|
||||
"family": "en",
|
||||
"region": "US",
|
||||
"name_native": "English",
|
||||
"name_english": "English",
|
||||
"country_english": "United States"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"X": [
|
||||
156
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"g": [
|
||||
154
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʦ": [
|
||||
155
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̊": [
|
||||
158
|
||||
],
|
||||
"̝": [
|
||||
157
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 1,
|
||||
"speaker_id_map": {},
|
||||
"piper_version": "1.0.0"
|
||||
}
|
||||
502
models/en_US-kristin-medium/en_US-kristin-medium.onnx.json
Normal file
502
models/en_US-kristin-medium/en_US-kristin-medium.onnx.json
Normal file
@ -0,0 +1,502 @@
|
||||
{
|
||||
"dataset": "kristin",
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "medium"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "en"
|
||||
},
|
||||
"language": {
|
||||
"code": "en_US",
|
||||
"family": "en",
|
||||
"region": "US",
|
||||
"name_native": "English",
|
||||
"name_english": "English",
|
||||
"country_english": "United States"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"X": [
|
||||
156
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"g": [
|
||||
154
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʦ": [
|
||||
155
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 1,
|
||||
"speaker_id_map": {},
|
||||
"piper_version": "1.0.0"
|
||||
}
|
||||
493
models/piper/de_DE-thorsten-high/de_DE-thorsten-high.onnx.json
Normal file
493
models/piper/de_DE-thorsten-high/de_DE-thorsten-high.onnx.json
Normal file
@ -0,0 +1,493 @@
|
||||
{
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "high"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "de"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 1,
|
||||
"speaker_id_map": {},
|
||||
"piper_version": "1.0.0",
|
||||
"language": {
|
||||
"code": "de_DE",
|
||||
"family": "de",
|
||||
"region": "DE",
|
||||
"name_native": "Deutsch",
|
||||
"name_english": "German",
|
||||
"country_english": "Germany"
|
||||
},
|
||||
"dataset": "thorsten"
|
||||
}
|
||||
@ -0,0 +1,502 @@
|
||||
{
|
||||
"piper_version": "1.1.0",
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "medium"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "de"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 8,
|
||||
"speaker_id_map": {
|
||||
"amused": 0,
|
||||
"angry": 1,
|
||||
"disgusted": 2,
|
||||
"drunk": 3,
|
||||
"neutral": 4,
|
||||
"sleepy": 5,
|
||||
"surprised": 6,
|
||||
"whisper": 7
|
||||
},
|
||||
"language": {
|
||||
"code": "de_DE",
|
||||
"family": "de",
|
||||
"region": "DE",
|
||||
"name_native": "Deutsch",
|
||||
"name_english": "German",
|
||||
"country_english": "Germany"
|
||||
},
|
||||
"dataset": "thorsten_emotional"
|
||||
}
|
||||
502
models/piper/en_GB-cori-high/en_GB-cori-high.onnx.json
Normal file
502
models/piper/en_GB-cori-high/en_GB-cori-high.onnx.json
Normal file
@ -0,0 +1,502 @@
|
||||
{
|
||||
"dataset": "cori",
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "high"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "en"
|
||||
},
|
||||
"language": {
|
||||
"code": "en_GB",
|
||||
"family": "en",
|
||||
"region": "GB",
|
||||
"name_native": "English",
|
||||
"name_english": "English",
|
||||
"country_english": "Great Britain"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"X": [
|
||||
156
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"g": [
|
||||
154
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʦ": [
|
||||
155
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 1,
|
||||
"speaker_id_map": {},
|
||||
"piper_version": "1.0.0"
|
||||
}
|
||||
603
models/piper/en_GB-vctk-medium/en_GB-vctk-medium.onnx.json
Normal file
603
models/piper/en_GB-vctk-medium/en_GB-vctk-medium.onnx.json
Normal file
@ -0,0 +1,603 @@
|
||||
{
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "medium"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "en-gb-x-rp"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.333,
|
||||
"length_scale": 1.4,
|
||||
"noise_w": 0.333
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 109,
|
||||
"speaker_id_map": {
|
||||
"p239": 0,
|
||||
"p236": 1,
|
||||
"p264": 2,
|
||||
"p250": 3,
|
||||
"p259": 4,
|
||||
"p247": 5,
|
||||
"p261": 6,
|
||||
"p263": 7,
|
||||
"p283": 8,
|
||||
"p286": 9,
|
||||
"p274": 10,
|
||||
"p276": 11,
|
||||
"p270": 12,
|
||||
"p281": 13,
|
||||
"p277": 14,
|
||||
"p231": 15,
|
||||
"p271": 16,
|
||||
"p238": 17,
|
||||
"p257": 18,
|
||||
"p273": 19,
|
||||
"p284": 20,
|
||||
"p329": 21,
|
||||
"p361": 22,
|
||||
"p287": 23,
|
||||
"p360": 24,
|
||||
"p374": 25,
|
||||
"p376": 26,
|
||||
"p310": 27,
|
||||
"p304": 28,
|
||||
"p334": 29,
|
||||
"p340": 30,
|
||||
"p323": 31,
|
||||
"p347": 32,
|
||||
"p330": 33,
|
||||
"p308": 34,
|
||||
"p314": 35,
|
||||
"p317": 36,
|
||||
"p339": 37,
|
||||
"p311": 38,
|
||||
"p294": 39,
|
||||
"p305": 40,
|
||||
"p266": 41,
|
||||
"p335": 42,
|
||||
"p318": 43,
|
||||
"p351": 44,
|
||||
"p333": 45,
|
||||
"p313": 46,
|
||||
"p316": 47,
|
||||
"p244": 48,
|
||||
"p307": 49,
|
||||
"p363": 50,
|
||||
"p336": 51,
|
||||
"p297": 52,
|
||||
"p312": 53,
|
||||
"p267": 54,
|
||||
"p275": 55,
|
||||
"p295": 56,
|
||||
"p258": 57,
|
||||
"p288": 58,
|
||||
"p301": 59,
|
||||
"p232": 60,
|
||||
"p292": 61,
|
||||
"p272": 62,
|
||||
"p280": 63,
|
||||
"p278": 64,
|
||||
"p341": 65,
|
||||
"p268": 66,
|
||||
"p298": 67,
|
||||
"p299": 68,
|
||||
"p279": 69,
|
||||
"p285": 70,
|
||||
"p326": 71,
|
||||
"p300": 72,
|
||||
"s5": 73,
|
||||
"p230": 74,
|
||||
"p345": 75,
|
||||
"p254": 76,
|
||||
"p269": 77,
|
||||
"p293": 78,
|
||||
"p252": 79,
|
||||
"p262": 80,
|
||||
"p243": 81,
|
||||
"p227": 82,
|
||||
"p343": 83,
|
||||
"p255": 84,
|
||||
"p229": 85,
|
||||
"p240": 86,
|
||||
"p248": 87,
|
||||
"p253": 88,
|
||||
"p233": 89,
|
||||
"p228": 90,
|
||||
"p282": 91,
|
||||
"p251": 92,
|
||||
"p246": 93,
|
||||
"p234": 94,
|
||||
"p226": 95,
|
||||
"p260": 96,
|
||||
"p245": 97,
|
||||
"p241": 98,
|
||||
"p303": 99,
|
||||
"p265": 100,
|
||||
"p306": 101,
|
||||
"p237": 102,
|
||||
"p249": 103,
|
||||
"p256": 104,
|
||||
"p302": 105,
|
||||
"p364": 106,
|
||||
"p225": 107,
|
||||
"p362": 108
|
||||
},
|
||||
"piper_version": "1.0.0",
|
||||
"language": {
|
||||
"code": "en_GB",
|
||||
"family": "en",
|
||||
"region": "GB",
|
||||
"name_native": "English",
|
||||
"name_english": "English",
|
||||
"country_english": "Great Britain"
|
||||
},
|
||||
"dataset": "vctk"
|
||||
}
|
||||
@ -0,0 +1,508 @@
|
||||
{
|
||||
"dataset": "hfc_female",
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "medium"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "en-us"
|
||||
},
|
||||
"language": {
|
||||
"code": "en_US",
|
||||
"family": "en",
|
||||
"region": "US",
|
||||
"name_native": "English",
|
||||
"name_english": "English",
|
||||
"country_english": "United States"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"X": [
|
||||
156
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"g": [
|
||||
154
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʦ": [
|
||||
155
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̊": [
|
||||
158
|
||||
],
|
||||
"̝": [
|
||||
157
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 1,
|
||||
"speaker_id_map": {},
|
||||
"piper_version": "1.0.0"
|
||||
}
|
||||
502
models/piper/en_US-kristin-medium/en_US-kristin-medium.onnx.json
Normal file
502
models/piper/en_US-kristin-medium/en_US-kristin-medium.onnx.json
Normal file
@ -0,0 +1,502 @@
|
||||
{
|
||||
"dataset": "kristin",
|
||||
"audio": {
|
||||
"sample_rate": 22050,
|
||||
"quality": "medium"
|
||||
},
|
||||
"espeak": {
|
||||
"voice": "en"
|
||||
},
|
||||
"language": {
|
||||
"code": "en_US",
|
||||
"family": "en",
|
||||
"region": "US",
|
||||
"name_native": "English",
|
||||
"name_english": "English",
|
||||
"country_english": "United States"
|
||||
},
|
||||
"inference": {
|
||||
"noise_scale": 0.667,
|
||||
"length_scale": 1,
|
||||
"noise_w": 0.8
|
||||
},
|
||||
"phoneme_type": "espeak",
|
||||
"phoneme_map": {},
|
||||
"phoneme_id_map": {
|
||||
" ": [
|
||||
3
|
||||
],
|
||||
"!": [
|
||||
4
|
||||
],
|
||||
"\"": [
|
||||
150
|
||||
],
|
||||
"#": [
|
||||
149
|
||||
],
|
||||
"$": [
|
||||
2
|
||||
],
|
||||
"'": [
|
||||
5
|
||||
],
|
||||
"(": [
|
||||
6
|
||||
],
|
||||
")": [
|
||||
7
|
||||
],
|
||||
",": [
|
||||
8
|
||||
],
|
||||
"-": [
|
||||
9
|
||||
],
|
||||
".": [
|
||||
10
|
||||
],
|
||||
"0": [
|
||||
130
|
||||
],
|
||||
"1": [
|
||||
131
|
||||
],
|
||||
"2": [
|
||||
132
|
||||
],
|
||||
"3": [
|
||||
133
|
||||
],
|
||||
"4": [
|
||||
134
|
||||
],
|
||||
"5": [
|
||||
135
|
||||
],
|
||||
"6": [
|
||||
136
|
||||
],
|
||||
"7": [
|
||||
137
|
||||
],
|
||||
"8": [
|
||||
138
|
||||
],
|
||||
"9": [
|
||||
139
|
||||
],
|
||||
":": [
|
||||
11
|
||||
],
|
||||
";": [
|
||||
12
|
||||
],
|
||||
"?": [
|
||||
13
|
||||
],
|
||||
"X": [
|
||||
156
|
||||
],
|
||||
"^": [
|
||||
1
|
||||
],
|
||||
"_": [
|
||||
0
|
||||
],
|
||||
"a": [
|
||||
14
|
||||
],
|
||||
"b": [
|
||||
15
|
||||
],
|
||||
"c": [
|
||||
16
|
||||
],
|
||||
"d": [
|
||||
17
|
||||
],
|
||||
"e": [
|
||||
18
|
||||
],
|
||||
"f": [
|
||||
19
|
||||
],
|
||||
"g": [
|
||||
154
|
||||
],
|
||||
"h": [
|
||||
20
|
||||
],
|
||||
"i": [
|
||||
21
|
||||
],
|
||||
"j": [
|
||||
22
|
||||
],
|
||||
"k": [
|
||||
23
|
||||
],
|
||||
"l": [
|
||||
24
|
||||
],
|
||||
"m": [
|
||||
25
|
||||
],
|
||||
"n": [
|
||||
26
|
||||
],
|
||||
"o": [
|
||||
27
|
||||
],
|
||||
"p": [
|
||||
28
|
||||
],
|
||||
"q": [
|
||||
29
|
||||
],
|
||||
"r": [
|
||||
30
|
||||
],
|
||||
"s": [
|
||||
31
|
||||
],
|
||||
"t": [
|
||||
32
|
||||
],
|
||||
"u": [
|
||||
33
|
||||
],
|
||||
"v": [
|
||||
34
|
||||
],
|
||||
"w": [
|
||||
35
|
||||
],
|
||||
"x": [
|
||||
36
|
||||
],
|
||||
"y": [
|
||||
37
|
||||
],
|
||||
"z": [
|
||||
38
|
||||
],
|
||||
"æ": [
|
||||
39
|
||||
],
|
||||
"ç": [
|
||||
40
|
||||
],
|
||||
"ð": [
|
||||
41
|
||||
],
|
||||
"ø": [
|
||||
42
|
||||
],
|
||||
"ħ": [
|
||||
43
|
||||
],
|
||||
"ŋ": [
|
||||
44
|
||||
],
|
||||
"œ": [
|
||||
45
|
||||
],
|
||||
"ǀ": [
|
||||
46
|
||||
],
|
||||
"ǁ": [
|
||||
47
|
||||
],
|
||||
"ǂ": [
|
||||
48
|
||||
],
|
||||
"ǃ": [
|
||||
49
|
||||
],
|
||||
"ɐ": [
|
||||
50
|
||||
],
|
||||
"ɑ": [
|
||||
51
|
||||
],
|
||||
"ɒ": [
|
||||
52
|
||||
],
|
||||
"ɓ": [
|
||||
53
|
||||
],
|
||||
"ɔ": [
|
||||
54
|
||||
],
|
||||
"ɕ": [
|
||||
55
|
||||
],
|
||||
"ɖ": [
|
||||
56
|
||||
],
|
||||
"ɗ": [
|
||||
57
|
||||
],
|
||||
"ɘ": [
|
||||
58
|
||||
],
|
||||
"ə": [
|
||||
59
|
||||
],
|
||||
"ɚ": [
|
||||
60
|
||||
],
|
||||
"ɛ": [
|
||||
61
|
||||
],
|
||||
"ɜ": [
|
||||
62
|
||||
],
|
||||
"ɞ": [
|
||||
63
|
||||
],
|
||||
"ɟ": [
|
||||
64
|
||||
],
|
||||
"ɠ": [
|
||||
65
|
||||
],
|
||||
"ɡ": [
|
||||
66
|
||||
],
|
||||
"ɢ": [
|
||||
67
|
||||
],
|
||||
"ɣ": [
|
||||
68
|
||||
],
|
||||
"ɤ": [
|
||||
69
|
||||
],
|
||||
"ɥ": [
|
||||
70
|
||||
],
|
||||
"ɦ": [
|
||||
71
|
||||
],
|
||||
"ɧ": [
|
||||
72
|
||||
],
|
||||
"ɨ": [
|
||||
73
|
||||
],
|
||||
"ɪ": [
|
||||
74
|
||||
],
|
||||
"ɫ": [
|
||||
75
|
||||
],
|
||||
"ɬ": [
|
||||
76
|
||||
],
|
||||
"ɭ": [
|
||||
77
|
||||
],
|
||||
"ɮ": [
|
||||
78
|
||||
],
|
||||
"ɯ": [
|
||||
79
|
||||
],
|
||||
"ɰ": [
|
||||
80
|
||||
],
|
||||
"ɱ": [
|
||||
81
|
||||
],
|
||||
"ɲ": [
|
||||
82
|
||||
],
|
||||
"ɳ": [
|
||||
83
|
||||
],
|
||||
"ɴ": [
|
||||
84
|
||||
],
|
||||
"ɵ": [
|
||||
85
|
||||
],
|
||||
"ɶ": [
|
||||
86
|
||||
],
|
||||
"ɸ": [
|
||||
87
|
||||
],
|
||||
"ɹ": [
|
||||
88
|
||||
],
|
||||
"ɺ": [
|
||||
89
|
||||
],
|
||||
"ɻ": [
|
||||
90
|
||||
],
|
||||
"ɽ": [
|
||||
91
|
||||
],
|
||||
"ɾ": [
|
||||
92
|
||||
],
|
||||
"ʀ": [
|
||||
93
|
||||
],
|
||||
"ʁ": [
|
||||
94
|
||||
],
|
||||
"ʂ": [
|
||||
95
|
||||
],
|
||||
"ʃ": [
|
||||
96
|
||||
],
|
||||
"ʄ": [
|
||||
97
|
||||
],
|
||||
"ʈ": [
|
||||
98
|
||||
],
|
||||
"ʉ": [
|
||||
99
|
||||
],
|
||||
"ʊ": [
|
||||
100
|
||||
],
|
||||
"ʋ": [
|
||||
101
|
||||
],
|
||||
"ʌ": [
|
||||
102
|
||||
],
|
||||
"ʍ": [
|
||||
103
|
||||
],
|
||||
"ʎ": [
|
||||
104
|
||||
],
|
||||
"ʏ": [
|
||||
105
|
||||
],
|
||||
"ʐ": [
|
||||
106
|
||||
],
|
||||
"ʑ": [
|
||||
107
|
||||
],
|
||||
"ʒ": [
|
||||
108
|
||||
],
|
||||
"ʔ": [
|
||||
109
|
||||
],
|
||||
"ʕ": [
|
||||
110
|
||||
],
|
||||
"ʘ": [
|
||||
111
|
||||
],
|
||||
"ʙ": [
|
||||
112
|
||||
],
|
||||
"ʛ": [
|
||||
113
|
||||
],
|
||||
"ʜ": [
|
||||
114
|
||||
],
|
||||
"ʝ": [
|
||||
115
|
||||
],
|
||||
"ʟ": [
|
||||
116
|
||||
],
|
||||
"ʡ": [
|
||||
117
|
||||
],
|
||||
"ʢ": [
|
||||
118
|
||||
],
|
||||
"ʦ": [
|
||||
155
|
||||
],
|
||||
"ʰ": [
|
||||
145
|
||||
],
|
||||
"ʲ": [
|
||||
119
|
||||
],
|
||||
"ˈ": [
|
||||
120
|
||||
],
|
||||
"ˌ": [
|
||||
121
|
||||
],
|
||||
"ː": [
|
||||
122
|
||||
],
|
||||
"ˑ": [
|
||||
123
|
||||
],
|
||||
"˞": [
|
||||
124
|
||||
],
|
||||
"ˤ": [
|
||||
146
|
||||
],
|
||||
"̃": [
|
||||
141
|
||||
],
|
||||
"̧": [
|
||||
140
|
||||
],
|
||||
"̩": [
|
||||
144
|
||||
],
|
||||
"̪": [
|
||||
142
|
||||
],
|
||||
"̯": [
|
||||
143
|
||||
],
|
||||
"̺": [
|
||||
152
|
||||
],
|
||||
"̻": [
|
||||
153
|
||||
],
|
||||
"β": [
|
||||
125
|
||||
],
|
||||
"ε": [
|
||||
147
|
||||
],
|
||||
"θ": [
|
||||
126
|
||||
],
|
||||
"χ": [
|
||||
127
|
||||
],
|
||||
"ᵻ": [
|
||||
128
|
||||
],
|
||||
"↑": [
|
||||
151
|
||||
],
|
||||
"↓": [
|
||||
148
|
||||
],
|
||||
"ⱱ": [
|
||||
129
|
||||
]
|
||||
},
|
||||
"num_symbols": 256,
|
||||
"num_speakers": 1,
|
||||
"speaker_id_map": {},
|
||||
"piper_version": "1.0.0"
|
||||
}
|
||||
22
requirements.txt
Normal file
22
requirements.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# NovaAi – TTS-Engine-Hub
|
||||
# requirements.txt
|
||||
# Version: v0.0.1
|
||||
|
||||
fastapi
|
||||
uvicorn
|
||||
gunicorn
|
||||
pydantic
|
||||
pydantic-settings
|
||||
ffmpeg-python
|
||||
piper-tts
|
||||
|
||||
|
||||
f5-tts
|
||||
torch
|
||||
torchaudio
|
||||
|
||||
# Development & Testing
|
||||
pytest-cov
|
||||
pytest-asyncio
|
||||
|
||||
|
||||
21
run.sh
Executable file
21
run.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
# NovaAi – TTS-Engine-Hub
|
||||
# run.sh
|
||||
# Version: v0.0.1
|
||||
#
|
||||
# Activates venv and runs main.py via uvicorn (with reload for dev convenience).
|
||||
# Author: Abby (ChatGPT)
|
||||
# Date: 2025-07-23
|
||||
# Canvas: run.sh
|
||||
|
||||
if [ ! -d ".venv" ]; then
|
||||
echo "Virtual environment not found! Please run setup_env.sh first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source .venv/bin/activate
|
||||
|
||||
export PYTHONPATH=$(pwd)
|
||||
|
||||
python main.py
|
||||
|
||||
34
setup_env.sh
Normal file
34
setup_env.sh
Normal file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# NovaAi – TTS-Engine-Hub
|
||||
# setup_env.sh
|
||||
# Version: v0.0.1
|
||||
#
|
||||
# Deletes old venv (if exists), creates new one, and installs dependencies from requirements.txt.
|
||||
# Author: Abby (ChatGPT)
|
||||
# Date: 2025-07-23
|
||||
# Canvas: setup_env.sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ -d ".venv" ]; then
|
||||
echo "Removing existing venv..."
|
||||
rm -rf .venv
|
||||
fi
|
||||
|
||||
echo "Creating new virtual environment..."
|
||||
python3 -m venv .venv
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
source .venv/bin/activate
|
||||
|
||||
if [ ! -f requirements.txt ]; then
|
||||
echo "requirements.txt not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing requirements..."
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Setup complete!"
|
||||
|
||||
34
tests/conftest.py
Normal file
34
tests/conftest.py
Normal file
@ -0,0 +1,34 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from main import create_app # Import the app factory function
|
||||
from config import settings # Import settings to monkeypatch
|
||||
import os
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_audio_dir(tmp_path):
|
||||
"""Provides a temporary directory for audio caching for each test."""
|
||||
audio_dir = tmp_path / "test_audio_cache"
|
||||
audio_dir.mkdir()
|
||||
return audio_dir
|
||||
|
||||
@pytest.fixture
|
||||
def app_instance(tmp_audio_dir, monkeypatch):
|
||||
"""
|
||||
Provides a fresh FastAPI application instance for each test,
|
||||
with its AUDIO_CACHE_DIR redirected to a temporary location.
|
||||
"""
|
||||
monkeypatch.setattr(settings, "AUDIO_CACHE_DIR", str(tmp_audio_dir))
|
||||
app = create_app()
|
||||
return app
|
||||
|
||||
@pytest.fixture
|
||||
def app_client(app_instance):
|
||||
"""Provides a TestClient instance for the FastAPI application."""
|
||||
with TestClient(app_instance) as client:
|
||||
yield client
|
||||
|
||||
@pytest.fixture
|
||||
def piper_engine(app_instance):
|
||||
"""Provides the PiperEngine instance from the registry of the fresh app instance."""
|
||||
# Access the engine registry from the app_instance
|
||||
return app_instance.ENGINE_REGISTRY.get("piper")
|
||||
123
tests/test_api.py
Normal file
123
tests/test_api.py
Normal file
@ -0,0 +1,123 @@
|
||||
from fastapi.testclient import TestClient
|
||||
import pytest
|
||||
# from main import app, ENGINE_REGISTRY # No longer needed, using fixtures
|
||||
import shutil
|
||||
import asyncio
|
||||
|
||||
TEST_TEXT = "Dies ist ein NovaAi Test."
|
||||
# Ensure the engine being tested is in the default .env config
|
||||
ENGINE = "piper"
|
||||
MODEL = "de_DE-thorsten-high"
|
||||
FORMAT = "ogg"
|
||||
|
||||
# === Happy Path Tests ===
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tts_link_success(app_client):
|
||||
"""Tests successful synthesis returning a URL."""
|
||||
resp = app_client.post("/tts", json={
|
||||
"text": TEST_TEXT,
|
||||
"engine": ENGINE,
|
||||
"model": MODEL,
|
||||
"format": FORMAT
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert "audio_url" in data
|
||||
|
||||
# Also test the audio download endpoint
|
||||
audio_url = data['audio_url']
|
||||
audio_resp = app_client.get(audio_url)
|
||||
assert audio_resp.status_code == 200
|
||||
assert len(audio_resp.content) > 1000
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tts_base64_success(app_client):
|
||||
"""Tests successful synthesis returning base64 data."""
|
||||
resp = app_client.post("/tts?as=true", json={
|
||||
"text": TEST_TEXT,
|
||||
"engine": ENGINE,
|
||||
"model": MODEL,
|
||||
"format": FORMAT
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert "audio_base64" in data
|
||||
assert len(data["audio_base64"]) > 1000
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_and_engines_endpoints(app_client):
|
||||
"""Tests the /health and /engines endpoints for correct responses."""
|
||||
resp_engines = app_client.get("/engines")
|
||||
assert resp_engines.status_code == 200
|
||||
engines = resp_engines.json()
|
||||
assert ENGINE in engines
|
||||
|
||||
resp_health = app_client.get("/health")
|
||||
assert resp_health.status_code == 200
|
||||
health = resp_health.json()
|
||||
assert health["status"][ENGINE] == "ok"
|
||||
|
||||
# === Failure Path Tests ===
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tts_fails_with_invalid_engine(app_client):
|
||||
"""Tests that a request with a non-existent engine fails with HTTP 404."""
|
||||
response = app_client.post("/tts", json={
|
||||
"text": TEST_TEXT,
|
||||
"engine": "non_existent_engine",
|
||||
"model": MODEL,
|
||||
})
|
||||
assert response.status_code == 404
|
||||
assert "Engine 'non_existent_engine' not found" in response.json()["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tts_fails_with_invalid_model(app_client):
|
||||
"""Tests that a request with a non-existent model fails with HTTP 400."""
|
||||
response = app_client.post("/tts", json={
|
||||
"text": TEST_TEXT,
|
||||
"engine": ENGINE,
|
||||
"model": "non_existent_model",
|
||||
})
|
||||
assert response.status_code == 400
|
||||
assert "Model 'non_existent_model' not found" in response.json()["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tts_fails_with_invalid_speaker(app_client, piper_engine):
|
||||
"""Tests that a request with a non-existent speaker fails with HTTP 400."""
|
||||
# This model has speakers, so requesting a non-existent one should fail.
|
||||
# We need to pick a model that is known to have multiple speakers
|
||||
# The default piper engine models should have this.
|
||||
model_with_speakers = "en_US-kristin-medium" # Example model with speakers
|
||||
|
||||
# First, check if the model is actually available to test against
|
||||
if model_with_speakers not in piper_engine.list_models():
|
||||
pytest.skip(f"Model '{model_with_speakers}' not available for '{ENGINE}' to test against.")
|
||||
|
||||
response = app_client.post("/tts", json={
|
||||
"text": "This is a test.",
|
||||
"engine": ENGINE,
|
||||
"model": model_with_speakers,
|
||||
"speaker": "non_existent_speaker"
|
||||
})
|
||||
assert response.status_code == 400
|
||||
assert "Speaker 'non_existent_speaker' not found" in response.json()["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tts_fails_with_unavailable_engine(app_client, piper_engine, monkeypatch):
|
||||
"""Tests that a request fails with HTTP 503 if an engine is unhealthy."""
|
||||
# Simulate the piper executable being not found by monkeypatching the specific instance
|
||||
monkeypatch.setattr(piper_engine, "piper_executable", None)
|
||||
|
||||
response = app_client.post("/tts", json={
|
||||
"text": TEST_TEXT,
|
||||
"engine": ENGINE,
|
||||
"model": MODEL,
|
||||
})
|
||||
|
||||
assert response.status_code == 503
|
||||
assert "is not available" in response.json()["detail"]
|
||||
|
||||
|
||||
|
||||
|
||||
70
tests/test_f5_tts.py
Normal file
70
tests/test_f5_tts.py
Normal file
@ -0,0 +1,70 @@
|
||||
import unittest
|
||||
import os
|
||||
import shutil
|
||||
import asyncio
|
||||
from importlib.resources import files
|
||||
from engines.f5_tts import F5TTSEngine
|
||||
|
||||
class TestF5TTSEngine(unittest.TestCase):
|
||||
async def asyncSetUp(self):
|
||||
self.engine = F5TTSEngine()
|
||||
self.voices_dir = "engines/f5-tts-voices"
|
||||
self.test_speaker_name = "test_speaker"
|
||||
self.test_speaker_wav = os.path.join(self.voices_dir, f"{self.test_speaker_name}.wav")
|
||||
self.test_speaker_txt = os.path.join(self.voices_dir, f"{self.test_speaker_name}.txt")
|
||||
|
||||
# Create a dummy speaker for testing
|
||||
if not await asyncio.to_thread(os.path.exists, self.test_speaker_wav):
|
||||
default_wav_path = str(files("f5_tts").joinpath("infer/examples/basic/basic_ref_en.wav"))
|
||||
await asyncio.to_thread(shutil.copy, default_wav_path, self.test_speaker_wav)
|
||||
if not await asyncio.to_thread(os.path.exists, self.test_speaker_txt):
|
||||
await asyncio.to_thread(lambda: open(self.test_speaker_txt, "w").write("Some call me nature, others call me mother nature."))
|
||||
|
||||
# Reload speakers to include the new test speaker
|
||||
self.engine._load_speakers()
|
||||
|
||||
async def asyncTearDown(self):
|
||||
# Clean up the dummy speaker files
|
||||
if await asyncio.to_thread(os.path.exists, self.test_speaker_wav):
|
||||
await asyncio.to_thread(os.remove, self.test_speaker_wav)
|
||||
if await asyncio.to_thread(os.path.exists, self.test_speaker_txt):
|
||||
await asyncio.to_thread(os.remove, self.test_speaker_txt)
|
||||
|
||||
async def test_synthesize_default_wav(self):
|
||||
if not self.engine.model:
|
||||
self.skipTest("F5-TTS model not initialized.")
|
||||
|
||||
text = "Hello, this is a test with the default voice."
|
||||
audio_file = await self.engine.synthesize(text, fmt="wav")
|
||||
|
||||
self.assertTrue(await asyncio.to_thread(os.path.exists, audio_file))
|
||||
self.assertTrue(await asyncio.to_thread(os.path.getsize, audio_file) > 0)
|
||||
|
||||
await asyncio.to_thread(os.remove, audio_file)
|
||||
|
||||
async def test_synthesize_custom_speaker_wav(self):
|
||||
if not self.engine.model:
|
||||
self.skipTest("F5-TTS model not initialized.")
|
||||
|
||||
text = "Hello, this is a test with a custom voice."
|
||||
audio_file = await self.engine.synthesize(text, speaker=self.test_speaker_name, fmt="wav")
|
||||
|
||||
self.assertTrue(await asyncio.to_thread(os.path.exists, audio_file))
|
||||
self.assertTrue(await asyncio.to_thread(os.path.getsize, audio_file) > 0)
|
||||
|
||||
await asyncio.to_thread(os.remove, audio_file)
|
||||
|
||||
async def test_synthesize_ogg(self):
|
||||
if not self.engine.model:
|
||||
self.skipTest("F5-TTS model not initialized.")
|
||||
|
||||
text = "Hello, this is a test in ogg format."
|
||||
audio_file = await self.engine.synthesize(text, fmt="ogg")
|
||||
|
||||
self.assertTrue(await asyncio.to_thread(os.path.exists, audio_file))
|
||||
self.assertTrue(await asyncio.to_thread(os.path.getsize, audio_file) > 0)
|
||||
|
||||
await asyncio.to_thread(os.remove, audio_file)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
76
utils/audio.py
Normal file
76
utils/audio.py
Normal file
@ -0,0 +1,76 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
utils/audio.py
|
||||
Version: v0.0.1
|
||||
|
||||
Description:
|
||||
Audio processing utilities: concat, merging chunks, etc.
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: utils/audio.py
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
import os
|
||||
import ffmpeg
|
||||
import shutil
|
||||
|
||||
def check_ffmpeg():
|
||||
"""Check if ffmpeg is installed and available in the system's PATH."""
|
||||
return shutil.which("ffmpeg") is not None
|
||||
|
||||
def concat_audio(files, fmt):
|
||||
"""
|
||||
Concatenate a list of audio files into a single file of the given format.
|
||||
Supports: wav, ogg, mp3
|
||||
"""
|
||||
if len(files) == 1:
|
||||
# If there's only one file, just return it, no cleanup needed here.
|
||||
return files[0]
|
||||
|
||||
# Securely create a temporary file for the output
|
||||
with tempfile.NamedTemporaryFile(suffix=f'.{fmt}', prefix="chunked_", delete=False) as temp_output_file:
|
||||
output_file_path = temp_output_file.name
|
||||
|
||||
try:
|
||||
if fmt == "wav":
|
||||
import wave
|
||||
data = []
|
||||
params = None
|
||||
for f in files:
|
||||
with wave.open(f, 'rb') as wf:
|
||||
if params is None:
|
||||
params = wf.getparams()
|
||||
data.append(wf.readframes(wf.getnframes()))
|
||||
with wave.open(output_file_path, 'wb') as wf:
|
||||
wf.setparams(params)
|
||||
for d in data:
|
||||
wf.writeframes(d)
|
||||
else:
|
||||
if not check_ffmpeg():
|
||||
raise RuntimeError("ffmpeg not found. Please install ffmpeg and ensure it is in your PATH.")
|
||||
|
||||
with tempfile.NamedTemporaryFile("w", delete=False) as tf:
|
||||
list_file_path = tf.name
|
||||
for f in files:
|
||||
tf.write(f"file '{os.path.abspath(f)}'\\n")
|
||||
tf.flush()
|
||||
|
||||
try:
|
||||
(
|
||||
ffmpeg
|
||||
.input(list_file_path, format='concat', safe=0)
|
||||
.output(output_file_path, acodec='copy')
|
||||
.run(overwrite_output=True, quiet=True)
|
||||
)
|
||||
finally:
|
||||
os.unlink(list_file_path)
|
||||
|
||||
finally:
|
||||
# Clean up the input chunk files
|
||||
for f in files:
|
||||
if os.path.exists(f):
|
||||
os.remove(f)
|
||||
|
||||
return output_file_path
|
||||
22
utils/cache.py
Normal file
22
utils/cache.py
Normal file
@ -0,0 +1,22 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
utils/cache.py
|
||||
Version: v0.0.1
|
||||
|
||||
Description:
|
||||
Caching utilities for TTS requests (cache key generation, etc).
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: utils/cache.py
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
|
||||
# Für die API: Wichtig! req muss mindestens die Felder .text, .engine, .model, .speaker, .format, .chunking haben.
|
||||
def build_cache_key(req) -> str:
|
||||
"""
|
||||
Build a cache key from all relevant TTS request parameters.
|
||||
"""
|
||||
data = f"{req.text}|{req.engine}|{req.model}|{req.speaker}|{req.format}|{getattr(req, 'chunking', False)}"
|
||||
return hashlib.sha256(data.encode()).hexdigest()
|
||||
30
utils/text.py
Normal file
30
utils/text.py
Normal file
@ -0,0 +1,30 @@
|
||||
"""
|
||||
NovaAi – TTS-Engine-Hub
|
||||
utils/text.py
|
||||
Version: v0.0.1
|
||||
|
||||
Description:
|
||||
Text processing utilities: chunking, splitting, etc.
|
||||
|
||||
Author: Abby (ChatGPT)
|
||||
Date: 2025-07-23
|
||||
Canvas: utils/text.py
|
||||
"""
|
||||
|
||||
def chunk_text(text, maxlen=250):
|
||||
"""
|
||||
Split text into chunks of roughly maxlen (split at sentence boundaries if possible).
|
||||
"""
|
||||
import re
|
||||
sentences = re.split(r'([.!?]\s)', text)
|
||||
chunks = []
|
||||
buf = ""
|
||||
for s in sentences:
|
||||
if len(buf) + len(s) > maxlen:
|
||||
if buf:
|
||||
chunks.append(buf.strip())
|
||||
buf = ""
|
||||
buf += s
|
||||
if buf.strip():
|
||||
chunks.append(buf.strip())
|
||||
return [c for c in chunks if c.strip()]
|
||||
Reference in New Issue
Block a user