import os import io import traceback import numpy as np import scipy.io.wavfile as wavfile from fastapi import FastAPI, HTTPException from fastapi.responses import Response from pydantic import BaseModel from supertonic import TTS import uvicorn app = FastAPI(title="Supertonic TTS API") class TTSRequest(BaseModel): text: str lang: str = "ru" voice: str = "M2" print("Загрузка модели Supertonic TTS...") tts = TTS(auto_download=True) default_style = tts.get_voice_style(voice_name="M2") print("Модель успешно загружена и готова к работе!") @app.get("/") async def root(): return { "status": "ok", "message": "Supertonic TTS API is running", "docs": "/docs", "usage": "POST /api/tts с JSON: {'text': 'ваш текст', 'lang': 'ru', 'voice': 'M2'}" } @app.post("/api/tts") async def synthesize(request: TTSRequest): try: # 1. Получаем стиль голоса if request.voice == "M2": style = default_style else: style = tts.get_voice_style(voice_name=request.voice) # 2. Синтез wav, duration = tts.synthesize(request.text, voice_style=style, lang=request.lang) # 3. Конвертация аудио в numpy (если модель вернула тензор PyTorch) if hasattr(wav, 'cpu'): wav = wav.cpu().numpy() elif hasattr(wav, 'numpy'): wav = wav.numpy() wav = np.asarray(wav) # 4. Убираем лишние измерения (например, если форма (1, 48000) -> (48000,)) wav = wav.squeeze() # 5. Нормализация и конвертация в int16 (стандарт для WAV) wav = wav.astype(np.float32) max_val = np.max(np.abs(wav)) if max_val > 1.0: wav = wav / max_val # Конвертируем в int16 (от -32768 до 32767) wav_int16 = (wav * 32767).astype(np.int16) # 6. Получаем sample rate sample_rate = getattr(tts, 'sample_rate', 24000) # 7. Записываем в память через scipy out = io.BytesIO() wavfile.write(out, sample_rate, wav_int16) audio_bytes = out.getvalue() # 8. ИСПРАВЛЕНИЕ: Превращаем duration из numpy массива в обычный float # .item() безопасно извлекает скалярное значение из numpy array duration_float = float(np.asarray(duration).item()) # 9. Возвращаем аудио return Response( content=audio_bytes, media_type='audio/wav', headers={ "Content-Disposition": "attachment; filename=speech.wav", "X-Audio-Duration": str(round(duration_float, 2)) } ) except Exception as e: traceback.print_exc() raise HTTPException(status_code=500, detail=f"Ошибка генерации: {str(e)}") if __name__ == '__main__': port = int(os.environ.get('PORT', 7860)) uvicorn.run(app, host='0.0.0.0', port=port)