ANI-BG / vocoder_server.py
ssasio's picture
Update vocoder_server.py
766b890 verified
Raw
History Blame Contribute Delete
4.25 kB
import torch
import numpy as np
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import Response
import soundfile as sf
import io
import json
import os
app = FastAPI(title="AniTTS Vocoder Server")
# Зареждане на MioCodec - опит с различни методи
codec = None
# Метод 1: Оригинален MioCodec
try:
from miocodec import MioCodecModel
codec = MioCodecModel.from_pretrained("Aratako/MioCodec-25Hz-24kHz")
codec = codec.eval()
print("✅ MioCodec loaded (method 1)")
except Exception as e:
print(f"⚠️ Method 1 failed: {e}")
# Метод 2: CodecV6 от BgTTS
if codec is None:
try:
# Добавяне на текущата директория
import sys
sys.path.append(os.getcwd())
from codec import CodecV6
codec = CodecV6(device="cpu")
codec.model.eval()
print("✅ CodecV6 loaded (method 2)")
except Exception as e:
print(f"⚠️ Method 2 failed: {e}")
# Метод 3: Dummy vocoder (винаги работи, но само бип)
if codec is None:
print("⚠️ Using dummy vocoder - will return beep only")
codec = "dummy"
@app.post("/vocoder")
async def vocoder_endpoint(request: Request):
global codec
# Вземане на данните от заявката
try:
body = await request.body()
body_str = body.decode('utf-8')
# Парсване на form data
tokens_str = None
embedding_str = None
for part in body_str.split('&'):
if '=' in part:
key, val = part.split('=', 1)
if key == 'tokens':
tokens_str = val
elif key == 'embedding':
embedding_str = val
if tokens_str is None or embedding_str is None:
raise HTTPException(status_code=400, detail="Missing tokens or embedding")
# URL декодиране
import urllib.parse
tokens_str = urllib.parse.unquote(tokens_str)
embedding_str = urllib.parse.unquote(embedding_str)
tokens = json.loads(tokens_str)
speaker_emb = json.loads(embedding_str)
except Exception as e:
print(f"Parse error: {e}")
raise HTTPException(status_code=400, detail=f"Invalid request: {e}")
# Dummy vocoder (бип)
if codec == "dummy" or codec is None:
print(f"⚠️ Dummy vocoder for {len(tokens)} tokens")
duration = 0.3
sample_rate = 24000
t = np.linspace(0, duration, int(sample_rate * duration))
beep = 0.3 * np.sin(2 * np.pi * 440 * t)
beep = beep * np.hanning(len(beep))
buffer = io.BytesIO()
sf.write(buffer, beep, sample_rate, format='wav')
buffer.seek(0)
return Response(content=buffer.read(), media_type="audio/wav")
# Реален vocoder
try:
tokens_tensor = torch.tensor(tokens, dtype=torch.long)
speaker_emb_tensor = torch.tensor(speaker_emb, dtype=torch.float32)
print(f"Processing {len(tokens)} tokens...")
with torch.no_grad():
waveform = codec.decode(
global_embedding=speaker_emb_tensor,
content_token_indices=tokens_tensor
)
if torch.is_tensor(waveform):
waveform = waveform.cpu().numpy()
if waveform.ndim > 1:
waveform = waveform.squeeze()
# Нормализиране
max_val = np.abs(waveform).max()
if max_val > 0:
waveform = waveform / max_val * 0.95
buffer = io.BytesIO()
sf.write(buffer, waveform, 24000, format='wav')
buffer.seek(0)
return Response(content=buffer.read(), media_type="audio/wav")
except Exception as e:
print(f"Decode error: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health():
return {"status": "ok", "codec_available": codec is not None}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)