feat: integrate Silero TTS as default TTS engine
- Add SileroTTS service with GPU/CPU support - Update TTS API to support configurable engines (silero/edge) - Add TTS_ENGINE and SILERO_SPEAKER config options - Set Silero as default TTS engine - Update requirements.txt with silero dependency
This commit is contained in:
@@ -9,9 +9,15 @@ OUTPUT_DIR=./output
|
||||
SEED_VC_MODEL=seed-uvit-whisper-small-wavenet
|
||||
SEED_VC_REPO_PATH=./Seed-VC
|
||||
|
||||
# TTS Engine Configuration
|
||||
TTS_ENGINE=silero # Options: silero, edge
|
||||
|
||||
# Edge-TTS Configuration
|
||||
EDGE_TTS_VOICE=ru-RU-SvetlanaNeural
|
||||
|
||||
# Silero TTS Configuration
|
||||
SILERO_SPEAKER=v5_ru
|
||||
|
||||
# Flask Configuration
|
||||
FLASK_PORT=5000
|
||||
FLASK_DEBUG=false
|
||||
|
||||
@@ -7,6 +7,7 @@ from flask import current_app, request, send_file
|
||||
from flask_restx import Namespace, Resource, fields
|
||||
from app.models.voice import Voice
|
||||
from app.services.edge_tts_service import EdgeTTSService
|
||||
from app.services.silero_tts_service import SileroTTSService
|
||||
from app.services.seed_vc_service import SeedVCService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -110,10 +111,17 @@ class TTS(Resource):
|
||||
final_output = os.path.join(output_dir, f"{output_id}_converted.wav")
|
||||
|
||||
try:
|
||||
# Step 1: Generate TTS with Edge-TTS
|
||||
logger.info("Step 1: Generating TTS with Edge-TTS")
|
||||
edge_tts_voice = current_app.config['EDGE_TTS_VOICE']
|
||||
tts_service = EdgeTTSService(voice=edge_tts_voice)
|
||||
# Step 1: Generate TTS
|
||||
tts_engine = current_app.config.get('TTS_ENGINE', 'silero')
|
||||
logger.info(f"Step 1: Generating TTS with {tts_engine}")
|
||||
|
||||
if tts_engine == 'silero':
|
||||
silero_speaker = current_app.config.get('SILERO_SPEAKER', 'v5_ru')
|
||||
tts_service = SileroTTSService(speaker=silero_speaker)
|
||||
else:
|
||||
edge_tts_voice = current_app.config['EDGE_TTS_VOICE']
|
||||
tts_service = EdgeTTSService(voice=edge_tts_voice)
|
||||
|
||||
tts_service.text_to_speech(text, tts_output, speed=speed)
|
||||
|
||||
# Step 2: Convert voice with Seed-VC
|
||||
|
||||
@@ -18,9 +18,15 @@ class Config:
|
||||
SEED_VC_MODEL = os.getenv('SEED_VC_MODEL', 'seed-uvit-whisper-small-wavenet')
|
||||
SEED_VC_REPO_PATH = os.getenv('SEED_VC_REPO_PATH', './Seed-VC')
|
||||
|
||||
# TTS Engine
|
||||
TTS_ENGINE = os.getenv('TTS_ENGINE', 'silero') # Default to Silero
|
||||
|
||||
# Edge-TTS
|
||||
EDGE_TTS_VOICE = os.getenv('EDGE_TTS_VOICE', 'ru-RU-SvetlanaNeural')
|
||||
|
||||
# Silero TTS
|
||||
SILERO_SPEAKER = os.getenv('SILERO_SPEAKER', 'v5_ru')
|
||||
|
||||
# Flask
|
||||
FLASK_PORT = int(os.getenv('FLASK_PORT', '5000'))
|
||||
FLASK_DEBUG = os.getenv('FLASK_DEBUG', 'false').lower() == 'true'
|
||||
|
||||
87
app/services/silero_tts_service.py
Normal file
87
app/services/silero_tts_service.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""Silero TTS service for text-to-speech conversion."""
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional
|
||||
import torch
|
||||
import numpy as np
|
||||
import soundfile as sf
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SileroTTSService:
|
||||
"""Service for converting text to speech using Silero TTS."""
|
||||
|
||||
def __init__(self, device: str = 'cuda', speaker: str = 'v5_ru'):
|
||||
"""
|
||||
Initialize Silero TTS service.
|
||||
|
||||
Args:
|
||||
device: Device to use ('cuda' or 'cpu')
|
||||
speaker: Speaker model to use (e.g., 'v5_ru')
|
||||
"""
|
||||
self.device = device if torch.cuda.is_available() else 'cpu'
|
||||
self.speaker = speaker
|
||||
self.model = None
|
||||
self._load_model()
|
||||
logger.info(f"Silero TTS Service initialized with speaker: {speaker}, device: {self.device}")
|
||||
|
||||
def _load_model(self):
|
||||
"""Load Silero TTS model."""
|
||||
try:
|
||||
# Import silero here to avoid loading at module level
|
||||
from silero import silero_tts
|
||||
self.model, _ = silero_tts(
|
||||
language='ru',
|
||||
speaker=self.speaker,
|
||||
device=self.device
|
||||
)
|
||||
logger.info(f"Silero TTS model loaded successfully on {self.device}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load Silero TTS model: {e}")
|
||||
raise
|
||||
|
||||
def text_to_speech(self, text: str, output_path: str, speed: float = 1.0) -> str:
|
||||
"""
|
||||
Convert text to speech and save to file.
|
||||
|
||||
Args:
|
||||
text: Text to convert
|
||||
output_path: Path to save audio file
|
||||
speed: Speech speed multiplier (not supported by Silero, kept for API compatibility)
|
||||
|
||||
Returns:
|
||||
Path to generated audio file
|
||||
"""
|
||||
logger.info(f"Generating TTS with Silero for text: {text[:50]}...")
|
||||
|
||||
try:
|
||||
# Generate audio
|
||||
audio = self.model.apply_tts(text=text)
|
||||
|
||||
# Convert to numpy array
|
||||
audio_np = audio.numpy() if torch.is_tensor(audio) else np.array(audio)
|
||||
|
||||
# Ensure float32 format
|
||||
audio_np = audio_np.astype(np.float32)
|
||||
|
||||
# Save as WAV (Silero uses 16000 Hz by default)
|
||||
sf.write(output_path, audio_np, 16000)
|
||||
|
||||
logger.info(f"Silero TTS generated: {output_path}")
|
||||
return output_path
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Silero TTS generation failed: {e}")
|
||||
raise
|
||||
|
||||
def get_supported_voices(self) -> list:
|
||||
"""Get list of supported speakers."""
|
||||
# Silero Russian models
|
||||
voices = [
|
||||
"v5_ru", # Default Russian voice (best quality)
|
||||
"v4_ru",
|
||||
"v3_1_ru",
|
||||
"v3_ru",
|
||||
]
|
||||
return voices
|
||||
@@ -2,6 +2,7 @@ flask>=2.3.0
|
||||
flask-restx>=1.3.0
|
||||
python-dotenv>=1.0.0
|
||||
edge-tts>=6.1.0
|
||||
silero>=0.5.0
|
||||
torch>=2.0.0
|
||||
torchaudio>=2.0.0
|
||||
numpy>=1.24.0
|
||||
|
||||
Reference in New Issue
Block a user