49 lines
1.5 KiB
Python
49 lines
1.5 KiB
Python
"""Abstraction des moteurs TTS (backend pluggable).
|
|
|
|
Deux implementations : Kokoro (rapide, voix preglees -> previews) et Qwen3-TTS
|
|
(qualite + clonage par audio de reference -> rendu final). Toutes deux renvoient
|
|
de l'audio mono float32 + une frequence d'echantillonnage.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
import numpy as np
|
|
|
|
|
|
@dataclass
|
|
class VoiceSpec:
|
|
"""Decrit la voix a utiliser pour une synthese.
|
|
|
|
- `preset` : nom d'une voix preglee (Kokoro: "ff_siwis" ; Qwen3: "Chelsie").
|
|
- `ref_audio` / `ref_text` : clip de reference pour le clonage (Qwen3).
|
|
"""
|
|
preset: Optional[str] = None
|
|
ref_audio: Optional[str] = None
|
|
ref_text: Optional[str] = None
|
|
speed: float = 1.0
|
|
|
|
|
|
class TTSBackend(ABC):
|
|
"""Interface commune a tous les moteurs TTS."""
|
|
|
|
name: str = "base"
|
|
|
|
@abstractmethod
|
|
def synthesize(self, text: str, voice: VoiceSpec) -> tuple[np.ndarray, int]:
|
|
"""Synthetise `text` et renvoie (audio mono float32, sample_rate)."""
|
|
|
|
def default_voice(self) -> VoiceSpec:
|
|
return VoiceSpec()
|
|
|
|
|
|
def to_mono_float32(audio) -> np.ndarray:
|
|
"""Normalise une sortie de modele (mx.array / np / list) en mono float32."""
|
|
arr = np.asarray(audio, dtype=np.float32)
|
|
if arr.ndim > 1:
|
|
# (channels, n) ou (n, channels) -> moyenne sur l'axe des canaux.
|
|
arr = arr.mean(axis=0) if arr.shape[0] < arr.shape[-1] else arr.mean(axis=-1)
|
|
return np.ascontiguousarray(arr.reshape(-1))
|