#!/usr/bin/env python3
"""Frases cortas generadas con carrerilla, y recortadas despues.

Medido en prueba_longitud.py: un acuse de segundo y medio puntua 0,51 de
parecido, y el mismo estilo dicho en catorce segundos puntua 0,70. XTTS
arranca cada generacion en frio y no le da tiempo a asentar la voz antes de
que la frase termine. Como la cache de JARVIS son casi todo acuses de un
segundo, lo que mas se oye es lo que peor suena.

La idea: no generar "Voy, señor" a secas, sino una frase de arrastre delante,
y quedarse solo con el final. La voz llega al trozo util ya asentada.

Se prueban dos formas de cortar:
  - por silencio: XTTS deja pausa entre oraciones, asi que el ultimo tramo
    sonoro es la frase que se queria
  - repitiendo la frase tres veces y quedandose con la ultima

    python prueba_arrastre.py [cuda|cpu] [repeticiones]
"""
import os
import statistics
import sys
import tempfile

import numpy as np
import torch

DISPOSITIVO = sys.argv[1] if len(sys.argv) > 1 else "cuda"
REPES = int(sys.argv[2]) if len(sys.argv) > 2 else 4

BASE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, BASE)
from clonador import CONDICIONADO, GENERACION, FRECUENCIA, referencias

os.environ.setdefault("COQUI_TOS_AGREED", "1")
PATRON = os.path.join(BASE, "ref", "jarvis_lento_b.wav")
MEDIR = dict(gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30)

# Frase de arrastre: larga, del mismo registro, y que acaba en punto para que
# XTTS deje pausa antes de lo que viene.
ARRASTRE = ("Buenas noches, señor. He revisado el sistema y todo está en orden, "
            "sin ninguna anomalía digna de mención.")

CORTAS = ["Voy, señor.", "Enseguida, señor.", "Hecho, señor.", "A la orden, señor."]


def ultimo_tramo(wav, sr, minimo=0.35):
    """El ultimo trozo sonoro, separado por silencios."""
    import librosa
    tramos = librosa.effects.split(wav, top_db=32, frame_length=1024, hop_length=256)
    tramos = [(a, b) for a, b in tramos if (b - a) / sr >= minimo]
    if not tramos:
        return wav
    a, b = tramos[-1]
    margen = int(0.05 * sr)
    return wav[max(0, a - margen):min(len(wav), b + margen)]


def main():
    from TTS.api import TTS
    import soundfile as sf

    api = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(DISPOSITIVO)
    modelo = api.synthesizer.tts_model

    def emb(ruta):
        _, e = modelo.get_conditioning_latents(audio_path=[ruta], **MEDIR)
        return e

    patron = emb(PATRON)

    def parecido(ruta):
        return float(torch.nn.functional.cosine_similarity(
            patron.squeeze().float(), emb(ruta).squeeze().float(), dim=0))

    gpt, hablante = modelo.get_conditioning_latents(
        audio_path=referencias(), **CONDICIONADO)

    def genera(texto):
        out = modelo.inference(text=texto, language="es", gpt_cond_latent=gpt,
                               speaker_embedding=hablante,
                               enable_text_splitting=False, **GENERACION)
        return np.asarray(out["wav"], dtype=np.float32)

    metodos = {
        "directo (como ahora)": lambda f: genera(f),
        "con arrastre delante": lambda f: ultimo_tramo(
            genera(f"{ARRASTRE} {f}"), FRECUENCIA),
        "repetida x3, la ultima": lambda f: ultimo_tramo(
            genera(" ".join([f] * 3)), FRECUENCIA),
    }

    print(f"  {len(CORTAS)} frases cortas x {REPES} repeticiones\n")
    print(f"  {'metodo':24s} {'dur':>6s} {'parecido':>9s} {'desv':>7s}")
    print("  " + "-" * 50)

    with tempfile.TemporaryDirectory() as tmp:
        for nombre, hacer in metodos.items():
            puntos, duraciones = [], []
            for frase in CORTAS:
                for r in range(REPES):
                    wav = hacer(frase)
                    d = os.path.join(tmp, f"{abs(hash(nombre))}_{len(puntos)}.wav")
                    sf.write(d, wav, FRECUENCIA)
                    duraciones.append(len(wav) / FRECUENCIA)
                    puntos.append(parecido(d))
            print(f"  {nombre:24s} {statistics.mean(duraciones):5.1f}s "
                  f"{statistics.mean(puntos):9.4f} {statistics.pstdev(puntos):7.4f}",
                  flush=True)


if __name__ == "__main__":
    main()
