| """ |
| FroxAI Flex-Audio — inference helper. |
| |
| Loads the fine-tuned XTTS-v2 checkpoint (model.pth + config.json + vocab.json, |
| all expected in the repo root) and exposes a single generate() function used by |
| both app.py (Gradio UI) and any other script that wants to import this directly. |
| """ |
|
|
| import os |
| import torch |
| from TTS.tts.configs.xtts_config import XttsConfig |
| from TTS.tts.models.xtts import Xtts |
|
|
| REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| VOICES_DIR = os.path.join(REPO_ROOT, "voice_refs") |
|
|
| _model = None |
| _config = None |
|
|
|
|
| def load_model(): |
| """Loads the model once and caches it. Safe to call repeatedly.""" |
| global _model, _config |
| if _model is not None: |
| return _model, _config |
|
|
| config = XttsConfig() |
| config.load_json(os.path.join(REPO_ROOT, "config.json")) |
|
|
| model = Xtts.init_from_config(config) |
| model.load_checkpoint(config, checkpoint_dir=REPO_ROOT, eval=True) |
| if torch.cuda.is_available(): |
| model.cuda() |
|
|
| _model, _config = model, config |
| return model, config |
|
|
|
|
| def list_voices(): |
| """Returns available voice names (without the .wav extension) found in voice_refs/.""" |
| if not os.path.isdir(VOICES_DIR): |
| return [] |
| return sorted( |
| os.path.splitext(f)[0] |
| for f in os.listdir(VOICES_DIR) |
| if f.lower().endswith(".wav") |
| ) |
|
|
|
|
| def generate(text: str, language: str, voice: str) -> str: |
| """Generates speech and returns the path to the written WAV file. |
| |
| Args: |
| text: the text to speak. |
| language: an XTTS-v2 language code, e.g. 'en', 'es', 'hi'. |
| voice: a voice name as returned by list_voices() (matches a file in voice_refs/). |
| """ |
| model, config = load_model() |
|
|
| voice_path = os.path.join(VOICES_DIR, f"{voice}.wav") |
| if not os.path.exists(voice_path): |
| raise FileNotFoundError(f"No reference clip found for voice '{voice}' at {voice_path}") |
|
|
| outputs = model.synthesize( |
| text, |
| config, |
| speaker_wav=voice_path, |
| language=language, |
| ) |
|
|
| out_path = "/tmp/frox_flex_audio_output.wav" |
| import soundfile as sf |
| sf.write(out_path, outputs["wav"], config.audio.sample_rate) |
| return out_path |
|
|