diff --git a/speech.py b/speech.py index 1a16a9e..84a6ae8 100755 --- a/speech.py +++ b/speech.py @@ -22,7 +22,67 @@ import uvicorn @contextlib.asynccontextmanager async def lifespan(app): + # Startup: Initialize voice caches in each worker process + global voice_to_model_cache, voices_cache + + default_exists('config/pre_process_map.yaml') + default_exists('config/voice_to_speaker.yaml') + + # Build voice-to-model cache for fast lookups + with open('config/voice_to_speaker.yaml', 'r', encoding='utf8') as file: + voice_map = yaml.safe_load(file) + for model_id, voices in voice_map.items(): + if isinstance(voices, dict): + for voice_name in voices.keys(): + # First match wins (for duplicate voice names across models) + if voice_name not in voice_to_model_cache: + voice_to_model_cache[voice_name] = model_id + print(f"Voice-to-model cache initialized with {len(voice_to_model_cache)} voices") + + # Build voices cache for /v1/voices endpoint + models_data = [] + for model_id, voices in voice_map.items(): + if isinstance(voices, dict): + voice_list = list(voices.keys()) + + model_info = { + "id": model_id, + "object": "model", + "created": 1700000000, + "owned_by": "uncloseai", + "voices": voice_list, + "voice_count": len(voice_list) + } + + # Add engine-specific metadata + if model_id == 'tts-1': + model_info["engine"] = "piper" + model_info["description"] = "Fast neural TTS with 100+ voices" + model_info["sample_rate"] = 22050 + elif model_id == 'tts-1-hd': + model_info["engine"] = "xtts" + model_info["description"] = "High-quality voice cloning TTS" + model_info["sample_rate"] = 24000 + elif model_id == 'tts-1-silero': + model_info["engine"] = "silero" + model_info["description"] = "Fast multilingual TTS (en, ru, de, es, fr)" + model_info["sample_rate"] = 48000 + elif model_id == 'tts-1-kokoro': + model_info["engine"] = "kokoro" + model_info["description"] = "Lightweight decoder-only TTS (82M params)" + model_info["sample_rate"] = 24000 + + models_data.append(model_info) + + voices_cache = { + "object": "list", + "data": models_data + } + print(f"/v1/voices cache initialized with {len(models_data)} models") + yield + + # Shutdown: Cleanup gc.collect() try: import torch @@ -743,61 +803,6 @@ if __name__ == "__main__": args = parser.parse_args() - default_exists('config/pre_process_map.yaml') - default_exists('config/voice_to_speaker.yaml') - - # Build voice-to-model cache for fast lookups - with open('config/voice_to_speaker.yaml', 'r', encoding='utf8') as file: - voice_map = yaml.safe_load(file) - for model_id, voices in voice_map.items(): - if isinstance(voices, dict): - for voice_name in voices.keys(): - # First match wins (for duplicate voice names across models) - if voice_name not in voice_to_model_cache: - voice_to_model_cache[voice_name] = model_id - print(f"Voice-to-model cache initialized with {len(voice_to_model_cache)} voices") - - # Build voices cache for /v1/voices endpoint - models_data = [] - for model_id, voices in voice_map.items(): - if isinstance(voices, dict): - voice_list = list(voices.keys()) - - model_info = { - "id": model_id, - "object": "model", - "created": 1700000000, - "owned_by": "uncloseai", - "voices": voice_list, - "voice_count": len(voice_list) - } - - # Add engine-specific metadata - if model_id == 'tts-1': - model_info["engine"] = "piper" - model_info["description"] = "Fast neural TTS with 100+ voices" - model_info["sample_rate"] = 22050 - elif model_id == 'tts-1-hd': - model_info["engine"] = "xtts" - model_info["description"] = "High-quality voice cloning TTS" - model_info["sample_rate"] = 24000 - elif model_id == 'tts-1-silero': - model_info["engine"] = "silero" - model_info["description"] = "Fast multilingual TTS (en, ru, de, es, fr)" - model_info["sample_rate"] = 48000 - elif model_id == 'tts-1-kokoro': - model_info["engine"] = "kokoro" - model_info["description"] = "Lightweight decoder-only TTS (82M params)" - model_info["sample_rate"] = 24000 - - models_data.append(model_info) - - voices_cache = { - "object": "list", - "data": models_data - } - print(f"/v1/voices cache initialized with {len(models_data)} models") - logger.remove() logger.add(sink=sys.stderr, level=args.log_level)