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