#!/usr/bin/env python3
"""Segunda vuelta: que referencia condiciona mejor, medido sobre varias frases.

La primera vuelta (comparar_ajustes.py) comparo parametros con UNA frase. Una
sola medida engaña: XTTS muestrea, y dos generaciones de lo mismo no puntuan
igual. Aqui cada candidata se mide sobre varias frases y se promedia.

Lo que se compara es de donde sale el timbre:
  - una sola referencia larga (la mejor de las normalizadas)
  - todas las normalizadas juntas, promediadas por XTTS
  - un unico wav con todo el material pegado, para que el condicionado vea
    mas fonemas del mismo hablante de una vez
  - solo las tomas de la MISMA sesion de grabacion (las dos "Buenas noches"),
    por si mezclar sesiones con tono distinto es lo que diluye

    python afinar_referencia.py [cuda|cpu]
"""
import glob
import os
import subprocess
import sys
import tempfile

import torch

DISPOSITIVO = sys.argv[1] if len(sys.argv) > 1 else "cuda"
BASE = os.path.dirname(os.path.abspath(__file__))
REF = os.path.join(BASE, "ref")
DESCARGAS = os.path.expanduser("~/Downloads")

os.environ.setdefault("COQUI_TOS_AGREED", "1")
LARGO = dict(gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30)

FRASES = [
    "Buenas noches, señor. Soy su asistente personal.",
    "Un momento, lo miro.",
    "He encontrado catorce ficheros, señor. Dígame cuál abro.",
]


def parecido(a, b):
    return float(torch.nn.functional.cosine_similarity(
        a.squeeze().float(), b.squeeze().float(), dim=0))


def pega(entradas, destino):
    """Concatena varios audios en un wav mono 24k."""
    lista = destino + ".txt"
    with open(lista, "w") as f:
        for e in entradas:
            f.write(f"file '{e}'\n")
    subprocess.run(["ffmpeg", "-y", "-loglevel", "error", "-f", "concat",
                    "-safe", "0", "-i", lista, "-ar", "24000", "-ac", "1",
                    destino], check=True)
    return destino


def main():
    from TTS.api import TTS
    print(f"cargando XTTS en {DISPOSITIVO}...", flush=True)
    api = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(DISPOSITIVO)
    modelo = api.synthesizer.tts_model
    import soundfile as sf

    def emb(rutas):
        _, e = modelo.get_conditioning_latents(audio_path=list(rutas), **LARGO)
        return e

    patron = emb([os.path.join(REF, "jarvis_lento_b.wav")])

    with tempfile.TemporaryDirectory() as tmp:
        normalizadas = sorted(glob.glob(os.path.join(REF, "*.wav")))
        pegadas = pega(normalizadas, os.path.join(tmp, "todo.wav"))

        misma_sesion = sorted(glob.glob(
            os.path.join(DESCARGAS, "Jarvis-*Buenas-noches*.mp3")))
        sesion_wavs = []
        for i, m in enumerate(misma_sesion):
            d = os.path.join(tmp, f"s{i}.wav")
            subprocess.run(["ffmpeg", "-y", "-loglevel", "error", "-i", m,
                            "-ar", "24000", "-ac", "1", d], check=True)
            sesion_wavs.append(d)
        sesion_pegada = pega(sesion_wavs, os.path.join(tmp, "sesion.wav")) \
            if sesion_wavs else None

        candidatas = [
            ("una_larga", [os.path.join(REF, "jarvis_lento_b.wav")]),
            ("tres_normalizadas", [os.path.join(REF, n) for n in
                                   ("jarvis_lento_b.wav", "jarvis_lento_a.wav",
                                    "jarvis_ref.wav")]),
            ("todo_pegado", [pegadas]),
        ]
        if sesion_pegada:
            candidatas.append(("misma_sesion_pegada", [sesion_pegada]))

        print(f"\n  cada candidata sobre {len(FRASES)} frases:\n")
        tabla = []
        for nombre, refs in candidatas:
            gpt, hablante = modelo.get_conditioning_latents(
                audio_path=refs, **LARGO)
            puntos = []
            for j, frase in enumerate(FRASES):
                salida = modelo.inference(
                    text=frase, language="es", gpt_cond_latent=gpt,
                    speaker_embedding=hablante, temperature=0.65,
                    repetition_penalty=2.0, speed=1.0,
                    enable_text_splitting=True)
                d = os.path.join(tmp, f"{nombre}_{j}.wav")
                sf.write(d, salida["wav"], 24000)
                puntos.append(parecido(patron, emb([d])))
            media = sum(puntos) / len(puntos)
            tabla.append((media, nombre, puntos))
            detalle = " ".join(f"{p:.3f}" for p in puntos)
            print(f"    {media:.4f}  {nombre:22s} [{detalle}]", flush=True)

        print("\n  mejor:", max(tabla)[1])


if __name__ == "__main__":
    main()
