TTSLibre – Rioplatense Spanish proof of concept (experiment 014)

Proof-of-concept checkpoint from TTSLibre, an open recipe for a small zero-shot TTS (Supertonic-lineage: character input, flow matching over a compressed autoencoder latent, Vocos vocoder). ~19.8M parameters, trained from scratch on 8 h of Argentinian Spanish. Same architecture and code as the English PoC ttslibre-poc-en. Not a usable product: chosen on a 5-sentence probe, whole-val WER not measured yet (see the repo's experiment 014 RESULTS).

Samples

6 dataset voices (3 female arf_*, 3 male arm_*), 8 sentences (4 hand-written, 4 held-out from the dataset). For each pair the best of 3 draws is shown, all with Whisper WER <= 0.1 (48 of 48 pairs passed, mean 0.005). Sampling: 16 flow steps, cfg 2.0.

voice text WER audio
arf_06136 Mañana vamos a la playa con los chicos. 0.00
arf_06136 El perro corre por el parque todas las tardes. 0.00
arf_06136 ¿Qué hora es en Buenos Aires? 0.00
arf_06136 La reunión de mañana se pasó para el jueves a las diez. 0.08
arf_06136 Hay zonas de los océanos donde se está acabando el oxígeno 0.00
arf_06136 ¿Cuáles son las zonas en las que hay carriles de bicicletas? 0.09
arf_06136 ¿Me puedes pasar tus datos para comprarte la computadora? 0.00
arf_06136 El viaje por tierra es más largo que el viaje en barco. 0.00
arf_08784 Mañana vamos a la playa con los chicos. 0.00
arf_08784 El perro corre por el parque todas las tardes. 0.00
arf_08784 ¿Qué hora es en Buenos Aires? 0.00
arf_08784 La reunión de mañana se pasó para el jueves a las diez. 0.00
arf_08784 Hay zonas de los océanos donde se está acabando el oxígeno 0.00
arf_08784 ¿Cuáles son las zonas en las que hay carriles de bicicletas? 0.00
arf_08784 ¿Me puedes pasar tus datos para comprarte la computadora? 0.00
arf_08784 El viaje por tierra es más largo que el viaje en barco. 0.00
arf_00295 Mañana vamos a la playa con los chicos. 0.00
arf_00295 El perro corre por el parque todas las tardes. 0.00
arf_00295 ¿Qué hora es en Buenos Aires? 0.00
arf_00295 La reunión de mañana se pasó para el jueves a las diez. 0.00
arf_00295 Hay zonas de los océanos donde se está acabando el oxígeno 0.00
arf_00295 ¿Cuáles son las zonas en las que hay carriles de bicicletas? 0.00
arf_00295 ¿Me puedes pasar tus datos para comprarte la computadora? 0.00
arf_00295 El viaje por tierra es más largo que el viaje en barco. 0.00
arm_08784 Mañana vamos a la playa con los chicos. 0.00
arm_08784 El perro corre por el parque todas las tardes. 0.00
arm_08784 ¿Qué hora es en Buenos Aires? 0.00
arm_08784 La reunión de mañana se pasó para el jueves a las diez. 0.00
arm_08784 Hay zonas de los océanos donde se está acabando el oxígeno 0.00
arm_08784 ¿Cuáles son las zonas en las que hay carriles de bicicletas? 0.00
arm_08784 ¿Me puedes pasar tus datos para comprarte la computadora? 0.00
arm_08784 El viaje por tierra es más largo que el viaje en barco. 0.00
arm_06136 Mañana vamos a la playa con los chicos. 0.00
arm_06136 El perro corre por el parque todas las tardes. 0.00
arm_06136 ¿Qué hora es en Buenos Aires? 0.00
arm_06136 La reunión de mañana se pasó para el jueves a las diez. 0.00
arm_06136 Hay zonas de los océanos donde se está acabando el oxígeno 0.00
arm_06136 ¿Cuáles son las zonas en las que hay carriles de bicicletas? 0.00
arm_06136 ¿Me puedes pasar tus datos para comprarte la computadora? 0.00
arm_06136 El viaje por tierra es más largo que el viaje en barco. 0.00
arm_00610 Mañana vamos a la playa con los chicos. 0.00
arm_00610 El perro corre por el parque todas las tardes. 0.00
arm_00610 ¿Qué hora es en Buenos Aires? 0.00
arm_00610 La reunión de mañana se pasó para el jueves a las diez. 0.08
arm_00610 Hay zonas de los océanos donde se está acabando el oxígeno 0.00
arm_00610 ¿Cuáles son las zonas en las que hay carriles de bicicletas? 0.00
arm_00610 ¿Me puedes pasar tus datos para comprarte la computadora? 0.00
arm_00610 El viaje por tierra es más largo que el viaje en barco. 0.00

Files

file what
ttl.pt TTS model, experiment 014 partial checkpoint at 5h04m / step 105000 (best probe round)
ae.pt mel autoencoder (24-d latent, 6x compression), experiment 001, shared with the English PoC
vocab.json, latent_stats.pt character vocab (English vocab + Spanish letters appended) and latent normalization (same stats as the English PoC)
config.json effective training config of the run
ref_default.wav default reference clip (OpenSLR 61, speaker arf_00295)
voices/*.pt voicepacks: AE latents of an 8-9 s reference clip of each of the 6 sample voices
samples/*.ogg the samples above

Vocoder: charactr/vocos-mel-24khz (loaded from the Hub at runtime).

Training

OpenSLR 61 (Google, "Crowdsourced high-quality Argentinian Spanish speech data set", 5739 clips, 8 h, 31 female + 31 male speaker ids), 5% held out, 1x RTX 3090, 5h13m, batch 16, lr 5e-4 halved every 60k steps, random init. Stopped by the success criterion (mean held-out probe WER of the last 3 rounds < 0.2).

Probe WER during training (1 draw per sentence per round, single reference voice):

elapsed step heldout mean (3) novel mean (2)
1h10m 24000 0.78 0.59
2h21m 48000 0.38 0.42
3h30m 72000 0.10 0.06
5h04m this file 105000 0.05 0.00
5h13m (final) 108000 0.21 0.28

Inference from a cold start

Tested with Python 3.12, torch 2.10 (CUDA 12.8), GPU or CPU.

git clone https://github.com/franciscocarloserra/ttslibre && cd ttslibre
python3 -m venv venv && . venv/bin/activate
pip install torch torchaudio vocos soundfile numpy num2words einops huggingface_hub
hf download FranciscoCarlosErra/ttslibre-poc-es-ar --local-dir hfckpt
# put the files where experiments/014-spanish-8h-openslr61/config.json expects them
E=experiments/014-spanish-8h-openslr61
mkdir -p $E/runs/es8h $E/runs/ae datasets/openslr61/prep
cp hfckpt/ttl.pt $E/runs/es8h/ttl.pt
cp hfckpt/ae.pt $E/runs/ae/ae.pt
cp hfckpt/latent_stats.pt hfckpt/vocab.json datasets/openslr61/prep/
cp hfckpt/ref_default.wav datasets/openslr61/arf_00295_00000740990.wav
cp hfckpt/voices/*.pt experiments/voices/
cd $E
python synth.py "Mañana vamos a la playa con los chicos." hola.wav --run runs/es8h/ttl.pt --ref ../voices/arm_06136.pt
# add --cpu for CPU; --ref can also be any 5-10 s wav of a new speaker

In-memory use (load once, generate many)

import sys, soundfile as sf
sys.path.insert(0, "experiments/014-spanish-8h-openslr61")
from common import load_config
from synth import Synth
c = load_config("experiments/014-spanish-8h-openslr61/config.json")
S = Synth(c, run="runs/es8h/ttl.pt")
zref, rmask = S.style("../voices/arf_06136.pt")   # voicepack, or a wav path for a new speaker
for i, t in enumerate(["Primera oración.", "Segunda oración."]):
    wav, dur = S(t, zref, rmask, steps=16, cfg=2.0, duration_scale=1.0)
    sf.write(f"out{i}.wav", wav, c["data"]["sample_rate"])

To make a voicepack from a clip: python ../voice.py make clip.wav myvoice --exp 014-spanish-8h-openslr61 --run runs/es8h/ttl.pt (from experiments/).

License

Weights, voicepacks and samples CC BY-SA 4.0, because the training data is CC BY-SA 4.0: OpenSLR 61, Google, "Crowdsourced high-quality Argentinian Spanish speech data set" (https://www.openslr.org/61/). The audit and the reasoning are in the repo's docs/LICENSES.md. Code MIT.

Downloads last month
15
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Dataset used to train FranciscoCarlosErra/ttslibre-poc-es-ar