#!/usr/bin/env python3 """Compara ajustes de clonado y mide cuanto se parece cada uno al original. El problema: "se parece pero no acaba de ser el". Juzgarlo solo de oido es lento y poco fiable —dos escuchas seguidas ya no opinan igual—, asi que aqui se genera la misma frase con varias configuraciones y se puntua cada resultado contra la referencia con el propio codificador de hablante de XTTS. La puntuacion es coseno entre embeddings de hablante: 1.0 seria identico. No es la verdad absoluta —el oido manda— pero ordena las opciones y evita perder la tarde comparando cosas que objetivamente van peor. El hallazgo que motiva esto: la API de alto nivel usa `gpt_cond_len=6`, o sea que de una referencia de 25 segundos solo condiciona con los 6 primeros. Lo demas se tira. Subirlo es lo primero que hay que probar. python comparar_ajustes.py [cuda|cpu] """ import os import sys import time import torch DISPOSITIVO = sys.argv[1] if len(sys.argv) > 1 else "cuda" BASE = os.path.dirname(os.path.abspath(__file__)) REF_DIR = os.path.join(BASE, "ref") SALIDA = os.path.join(BASE, "comparativa") os.environ.setdefault("COQUI_TOS_AGREED", "1") # La misma frase que dice uno de los audios descargados, para poder comparar # el clon con el original diciendo exactamente lo mismo. FRASE = "Buenas noches, señor. Soy su asistente personal." TODAS = ["jarvis_lento_b.wav", "jarvis_lento_a.wav", "jarvis_ref.wav"] LARGA = ["jarvis_lento_b.wav"] # la mas larga y limpia (25,8 s) # cada configuracion: (nombre, referencias, kwargs de latents, kwargs de sintesis) CONFIGS = [ ("A_actual", TODAS, dict(gpt_cond_len=6, gpt_cond_chunk_len=6, max_ref_length=30), dict(temperature=0.65, repetition_penalty=2.0, speed=1.0)), ("B_condicion_larga", TODAS, dict(gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30), dict(temperature=0.65, repetition_penalty=2.0, speed=1.0)), ("C_una_ref_larga", LARGA, dict(gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30), dict(temperature=0.65, repetition_penalty=2.0, speed=1.0)), ("D_una_ref_fria", LARGA, dict(gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30), dict(temperature=0.55, repetition_penalty=5.0, speed=1.0)), ("E_una_ref_normalizada", LARGA, dict(gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30, sound_norm_refs=True), dict(temperature=0.6, repetition_penalty=5.0, speed=1.0)), ] def rutas(nombres): return [os.path.join(REF_DIR, n) for n in nombres if os.path.exists(os.path.join(REF_DIR, n))] def parecido(a, b): """Coseno entre dos embeddings de hablante.""" a = a.squeeze().float() b = b.squeeze().float() return float(torch.nn.functional.cosine_similarity(a, b, dim=0)) def main(): os.makedirs(SALIDA, exist_ok=True) from TTS.api import TTS print(f"cargando XTTS en {DISPOSITIVO}...", flush=True) t0 = time.time() api = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(DISPOSITIVO) modelo = api.synthesizer.tts_model print(f"cargado en {time.time()-t0:.1f} s\n", flush=True) # patron a batir: el embedding de la referencia mas larga _, patron = modelo.get_conditioning_latents( audio_path=rutas(LARGA), gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30) resultados = [] for nombre, refs, cond_kw, gen_kw in CONFIGS: paths = rutas(refs) if not paths: print(f" {nombre}: sin referencias, saltada") continue t0 = time.time() gpt_latent, hablante = modelo.get_conditioning_latents( audio_path=paths, **cond_kw) salida = modelo.inference( text=FRASE, language="es", gpt_cond_latent=gpt_latent, speaker_embedding=hablante, enable_text_splitting=True, **gen_kw) tardo = time.time() - t0 destino = os.path.join(SALIDA, f"{nombre}.wav") import soundfile as sf sf.write(destino, salida["wav"], 24000) # puntuar el RESULTADO contra el patron, con el mismo codificador _, emb_salida = modelo.get_conditioning_latents( audio_path=[destino], gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30) p = parecido(patron, emb_salida) resultados.append((p, nombre, tardo)) print(f" {nombre:24s} parecido {p:.4f} ({tardo:.1f}s)", flush=True) print("\n ranking (mas alto = mas se parece al original):") for p, nombre, tardo in sorted(resultados, reverse=True): print(f" {p:.4f} {nombre}") print(f"\n wavs en {SALIDA}") if __name__ == "__main__": main()