Spaces:
Running
Running
| import tempfile | |
| import gc | |
| import os | |
| import gradio as gr | |
| import soundfile as sf | |
| import torch | |
| from qwen_tts import Qwen3TTSModel | |
| MODEL_ID = "Cartik/Sonexa-1-TTS" | |
| # --- Определяем, доступна ли среда ZeroGPU / CUDA --- | |
| # ZeroGPU (Hugging Face Spaces с декоратором @spaces.GPU) добавляет | |
| # переменную окружения SPACES_ZERO_GPU при реальном запуске на их инфраструктуре. | |
| IS_ZERO_GPU = os.environ.get("SPACES_ZERO_GPU") == "true" | |
| HAS_CUDA = torch.cuda.is_available() | |
| USE_GPU = IS_ZERO_GPU or HAS_CUDA | |
| if USE_GPU: | |
| DEVICE = "cuda" | |
| DTYPE = torch.bfloat16 | |
| else: | |
| DEVICE = "cpu" | |
| DTYPE = torch.float32 # bfloat16 плохо/медленно работает на CPU | |
| print(f"ZeroGPU detected: {IS_ZERO_GPU}") | |
| print(f"CUDA available: {HAS_CUDA}") | |
| print(f"Using device: {DEVICE}, dtype: {DTYPE}") | |
| # --- Условный импорт декоратора spaces.GPU --- | |
| # На обычном CPU-хосте (или локально) пакет spaces может либо отсутствовать, | |
| # либо декоратор просто не нужен — тогда используем no-op заглушку. | |
| if USE_GPU: | |
| try: | |
| import spaces | |
| gpu_decorator = spaces.GPU(duration=60) | |
| except ImportError: | |
| print("Пакет 'spaces' не найден, GPU-декоратор отключен.") | |
| def gpu_decorator(fn): | |
| return fn | |
| else: | |
| def gpu_decorator(fn): | |
| return fn | |
| print("Loading Custom Voice model...") | |
| model = Qwen3TTSModel.from_pretrained( | |
| MODEL_ID, | |
| torch_dtype=DTYPE, | |
| device_map="auto" if USE_GPU else "cpu", | |
| ) | |
| print("Model loaded successfully!") | |
| SUPPORTED_SPEAKERS = [ | |
| "serena", | |
| "ryan", | |
| "aiden", | |
| "dylan", | |
| "eric", | |
| "ono_anna", | |
| "sohee", | |
| "uncle_fu", | |
| "vivian", | |
| ] | |
| def generate(text, speaker): | |
| text = (text or "").strip() | |
| if not text: | |
| raise gr.Error("Введите текст.") | |
| try: | |
| with torch.inference_mode(): | |
| wavs, sr = model.generate_custom_voice( | |
| text=text, | |
| speaker=speaker, | |
| language="Russian", | |
| max_new_tokens=256, | |
| ) | |
| tmp = tempfile.NamedTemporaryFile( | |
| suffix=".wav", | |
| delete=False, | |
| ) | |
| sf.write(tmp.name, wavs[0], sr) | |
| return tmp.name | |
| except Exception as e: | |
| raise gr.Error(str(e)) | |
| finally: | |
| gc.collect() | |
| if HAS_CUDA: | |
| torch.cuda.empty_cache() | |
| with gr.Blocks(title="Sonexa TTS") as demo: | |
| gr.Markdown("# Sonexa TTS (Custom Voice Mode)") | |
| if USE_GPU: | |
| gr.Markdown( | |
| "Генерация выполняется на GPU (ZeroGPU) — обычно занимает несколько секунд." | |
| ) | |
| else: | |
| gr.Markdown( | |
| "⚠️ GPU недоступен — генерация выполняется на CPU и может занимать значительно дольше." | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| input_text = gr.Textbox( | |
| label="Текст", | |
| placeholder="Введите текст...", | |
| lines=4, | |
| ) | |
| speaker = gr.Dropdown( | |
| choices=SUPPORTED_SPEAKERS, | |
| value="serena", | |
| label="Выберите голос", | |
| ) | |
| btn = gr.Button("Озвучить", variant="primary") | |
| with gr.Column(): | |
| output = gr.Audio( | |
| label="Результат", | |
| type="filepath", | |
| ) | |
| btn.click( | |
| fn=generate, | |
| inputs=[input_text, speaker], | |
| outputs=output, | |
| api_name="predict", | |
| ) | |
| demo.queue( | |
| max_size=32, | |
| default_concurrency_limit=1, | |
| ) | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| show_api=True, | |
| ) |