Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1dxvtsZx8NE9fwjXUNDBr
51 lines
2.2 KiB
Python
51 lines
2.2 KiB
Python
"""Sintesi delle battute con XTTS-v2 (gira nel Python di XTTS, lo lancia tools/voci.mjs):
|
|
voci_xtts.py lavori.json
|
|
lavori.json: [{"text": ..., "speaker": ..., "out": "/assoluto/x.wav"}, ...]; un lavoro già fatto si salta.
|
|
In italiano XTTS legge i punti a fine frase («Buongiorno, punto») e inciampa sui caporali: ogni battuta si
|
|
divide in frasi, senza punti finali né caporali, e le frasi si uniscono con una pausa breve.
|
|
Il modello è sotto licenza Coqui Public Model License: solo uso non commerciale."""
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
PAUSA = 0.22 # secondi fra una frase e l'altra
|
|
os.environ["COQUI_TOS_AGREED"] = "1"
|
|
|
|
|
|
def frasi(testo: str) -> list[str]:
|
|
testo = re.sub(r"[«»\"“”]", "", testo)
|
|
parti = re.split(r"(?<=[.!?…])\s+", testo.strip())
|
|
pulite = [re.sub(r"(\.\.\.|…|\.)+$", "", p).strip() for p in parti]
|
|
return [p for p in pulite if re.search(r"\w", p)]
|
|
|
|
|
|
lavori = [j for j in json.load(open(sys.argv[1])) if not os.path.exists(j["out"])]
|
|
if lavori:
|
|
import torch
|
|
import torchaudio
|
|
from TTS.api import TTS
|
|
# Anche senza GPU (collaudo locale): XTTS_DEVICE=cpu, stesso modello e stesse voci.
|
|
device = os.environ.get("XTTS_DEVICE", "cuda")
|
|
if device == "cpu":
|
|
torch.set_num_threads(2)
|
|
model_dir = os.environ.get("XTTS_MODEL_DIR")
|
|
if model_dir:
|
|
# Modello già scaricato: nessun controllo di aggiornamenti in rete.
|
|
from TTS.utils.synthesizer import Synthesizer
|
|
tts = TTS()
|
|
tts.synthesizer = Synthesizer(model_dir=model_dir, use_cuda=device == "cuda")
|
|
else:
|
|
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device)
|
|
sr = tts.synthesizer.output_sample_rate
|
|
for i, j in enumerate(lavori, 1):
|
|
pezzi = []
|
|
for f in frasi(j["text"]):
|
|
if pezzi:
|
|
pezzi.append(torch.zeros(int(sr * PAUSA)))
|
|
pezzi.append(torch.tensor(tts.tts(f, language="it", speaker=j["speaker"], split_sentences=False), dtype=torch.float32))
|
|
torchaudio.save(j["out"] + ".tmp.wav", torch.cat(pezzi).unsqueeze(0), sr, format="wav")
|
|
os.replace(j["out"] + ".tmp.wav", j["out"])
|
|
print(f"{i}/{len(lavori)}", flush=True)
|
|
print(f"{len(lavori)} battute sintetizzate")
|