---
language: [ro]
license: other
license_name: cpml
license_link: https://coqui.ai/cpml
library_name: coqui-tts
pipeline_tag: text-to-speech
tags: [tts, xtts, romanian, voice-cloning, fine-tuning]
datasets: [eduardem/romanian-speech-v1]
base_model: coqui/XTTS-v2
---
# XTTS-v2 Romanian
> **A newer version is available: [XTTS-v2 Romanian v2](https://huggingface.co/eduardem/xtts-v2-romanian-v2)** — 15 voices, ~470 speakers, ~471K training clips. Trained on a combined dataset 3x larger than v1.
Fine-tuned [XTTS-v2](https://huggingface.co/coqui/XTTS-v2) for high-quality Romanian text-to-speech with voice cloning. Achieves **6.3% WER** (measured by Whisper large-v3) across 5 distinct voices trained on ~150 hours of Romanian speech.
**[Live Demo & Audio Samples](https://eduardm.codeberg.page/romanian-tts-xtts-v2/)** | **[Training Code (Codeberg)](https://codeberg.org/eduardm/romanian-tts-xtts-v2)**
## Audio Samples
### Costel (male, literary narration) — 1.9% WER
> "Țara românească și-a păstrat tradițiile străvechi de-a lungul secolelor."
> "Ștefan cel Mare a construit mănăstiri și cetăți în întreaga Moldovă."
### Mărioara (female, expressive storytelling) — 6.1% WER
> "Țara românească și-a păstrat tradițiile străvechi de-a lungul secolelor."
### Georgel (male, solemn delivery) — 1.8% WER
> "Țara românească și-a păstrat tradițiile străvechi de-a lungul secolelor."
### Lăcrămioara (female, clear broadcast style) — 7.2% WER
> "Țara românească și-a păstrat tradițiile străvechi de-a lungul secolelor."
### Dorel (male, conversational) — 14.5% WER
> "Țara românească și-a păstrat tradițiile străvechi de-a lungul secolelor."
## Key Innovation: Unicode Diacritics Normalization
Romanian uses **comma-below** diacritics (s-comma U+0219, t-comma U+021B), but many text sources contain visually identical **cedilla** variants (s-cedilla U+015F, t-cedilla U+0163) inherited from legacy encodings. These are different Unicode codepoints that map to different token embeddings.
This model solves the problem at two levels:
1. **Smart embedding initialization** -- new Romanian token embeddings were initialized from their closest existing donors (Turkish cedilla characters) rather than random weights, giving the model a meaningful starting point.
2. **Runtime normalization** -- all input text must be normalized to comma-below form before inference (see Quick Start below).
## Quick Start
### Installation
```bash
pip install TTS==0.22.0
```
> **Note**: TTS 0.22.0 requires patches for PyTorch 2.x compatibility and Romanian tokenizer support. See [setup_runpod.sh](https://codeberg.org/eduardm/romanian-tts-xtts-v2/src/branch/main/scripts/setup_runpod.sh) for the exact patches needed.
### Download Model
```bash
# Clone the model repo
git lfs install
git clone https://huggingface.co/eduardem/xtts-v2-romanian
cd xtts-v2-romanian
```
Or download individual files:
```python
from huggingface_hub import hf_hub_download
for fname in ["config.json", "model.pth", "dvae.pth", "mel_stats.pth", "vocab.json", "speakers_xtts.pth"]:
hf_hub_download(repo_id="eduardem/xtts-v2-romanian", filename=fname, local_dir="xtts-v2-romanian")
```
### Basic Inference
```python
import torch
import torchaudio
from TTS.tts.configs.xtts_config import XttsConfig
from TTS.tts.models.xtts import Xtts
# ---------------------------------------------------------------
# REQUIRED: Normalize cedilla -> comma-below before every inference
# Without this, diacritics will be silently mispronounced or skipped.
# ---------------------------------------------------------------
CEDILLA_TO_COMMA = str.maketrans({
"\u015f": "\u0219", # ş -> ș (lowercase s)
"\u0163": "\u021b", # ţ -> ț (lowercase t)
"\u015e": "\u0218", # Ş -> Ș (uppercase S)
"\u0162": "\u021a", # Ţ -> Ț (uppercase T)
})
# Load model
config = XttsConfig()
config.load_json("xtts-v2-romanian/config.json")
model = Xtts.init_from_config(config)
model.load_checkpoint(config, checkpoint_dir="xtts-v2-romanian", use_deepspeed=False)
model.cuda()
# Prepare text -- always normalize!
text = "Bună ziua, mă numesc Alexandru și sunt din București."
text = text.translate(CEDILLA_TO_COMMA)
# Clone a voice from a ~6s reference clip
gpt_cond_latent, speaker_embedding = model.get_conditioning_latents(
audio_path=["xtts-v2-romanian/reference_voices/costel.wav"]
)
# Generate speech
out = model.inference(
text=text,
language="ro",
gpt_cond_latent=gpt_cond_latent,
speaker_embedding=speaker_embedding,
temperature=0.3,
top_p=0.7,
top_k=30,
length_penalty=0.8,
repetition_penalty=10.0,
)
torchaudio.save("output.wav", torch.tensor(out["wav"]).unsqueeze(0), 24000)
```
### Voice Cloning with Your Own Voice
You can clone any voice from a ~6-second WAV reference clip:
```python
# Use your own reference audio (WAV, ~6 seconds, clear speech)
gpt_cond_latent, speaker_embedding = model.get_conditioning_latents(
audio_path=["my_voice.wav"]
)
out = model.inference(
text="Aceasta este o propoziție de test în limba română.".translate(CEDILLA_TO_COMMA),
language="ro",
gpt_cond_latent=gpt_cond_latent,
speaker_embedding=speaker_embedding,
)
```
### Inference Parameters
| Parameter | Default | Description |
|---|---|---|
| `temperature` | 0.3 | Sampling temperature. Lower = more deterministic. |
| `top_p` | 0.7 | Nucleus sampling threshold. |
| `top_k` | 30 | Top-k sampling. |
| `length_penalty` | 0.8 | Values <1.0 discourage overly long output. |
| `repetition_penalty` | 10.0 | Penalizes repeated tokens. |
## Voices
Five voices are included with reference audio clips in the `reference_voices/` directory.
| Voice | Speaker ID | Description | WER |
|---|---|---|---|
| Costel | `speaker_male_literature` | Male, literary narration | 1.9% |
| Mărioara | `speaker_female_hp` | Female, expressive storytelling | 6.1% |
| Lăcrămioara | `speaker_female_adr` | Female, clear broadcast style | 7.2% |
| Georgel | `speaker_male_bible` | Male, solemn delivery | 1.8% |
| Dorel | `speaker_male_4` | Male, conversational | 14.5% |
WER is measured per-voice on 10 test sentences (5 diacritics-heavy + 5 common phrases) using Whisper large-v3. Lower is better.
## Training
### Dataset
[eduardem/romanian-speech-v1](https://huggingface.co/datasets/eduardem/romanian-speech-v1) -- approximately 62,000 clips totaling ~150 hours of Romanian speech from 5 speakers, sampled at 22,050 Hz.
### Infrastructure
- **GPU**: NVIDIA RTX 5000 Ada (32 GB) on RunPod
- **Training time**: ~42 hours (50 epochs, ~51 minutes per epoch)
- **Global steps**: 678,350
- **Final loss** (epoch 45): mel=2.72, text=0.025
### Two-Phase Training Strategy
**Phase 1 -- Embedding Warmup** (1 epoch)
- Freeze all GPT layers; train only `text_embedding` and `text_pos_embedding` (1.4% of parameters)
- Learning rate: 1e-4
- Purpose: bring the 6 new Romanian token embeddings (ă, â, î, ș, ț, `[ro]`) from random initialization to a meaningful representation before unfreezing the full model
**Phase 2 -- Full GPT Fine-Tune** (50 epochs)
- Unfreeze all GPT layers
- Learning rate: 5e-6
- Batch size: 4, gradient accumulation: 63 (effective batch size = 252)
- Optimizer: AdamW (betas=0.9, 0.96)
- `text_ce_weight=0.01` (auxiliary regularizer, not primary objective)
- `gpt_use_masking_gt_prompt_approach=True` (prevents reference audio parroting)
### Critical Training Parameters
These values are non-negotiable for XTTS-v2 fine-tuning:
- **Effective batch size >= 252** -- the official Coqui recipe minimum. Smaller batches cause mode collapse.
- **text_ce_weight = 0.01** -- increasing this breaks the mel/text loss balance and causes text-prediction shortcuts.
- **gpt_use_masking_gt_prompt_approach = True** -- without this, the model learns to copy the reference audio instead of conditioning on input text.
## Evaluation
WER progression during training, measured on held-out test sentences with Whisper large-v3:
| Checkpoint | Overall WER | Diacritics WER | Common WER |
|---|---|---|---|
| Phase 1 (warmup) | 144.9% | 146.3% | 143.6% |
| Epoch 5 | 18.3% | 15.9% | 20.7% |
| Epoch 10 | 7.7% | 7.9% | 7.5% |
| Epoch 15 | 6.6% | 7.0% | 6.2% |
| Epoch 20 | 4.7% | 6.0% | 3.4% |
| Epoch 25 | 9.1% | 11.0% | 7.2% |
| Epoch 30 | 7.3% | 6.9% | 7.8% |
| Epoch 35 | 11.5% | 10.7% | 12.2% |
| Epoch 40 | 7.3% | 3.8% | 10.9% |
| **Epoch 45** | **6.3%** | **5.9%** | **6.7%** |
| Epoch 50 | 7.9% | 7.6% | 8.3% |
Epoch 45 is selected as the release checkpoint. Epoch 50 shows WER regression (7.9%), suggesting early overfitting. Training loss decreases monotonically throughout, while WER fluctuates -- a common pattern in TTS where automatic metrics don't perfectly correlate with perceptual quality.
## Model Files
| File | Size | Description |
|---|---|---|
| `config.json` | 4 KB | XTTS-v2 configuration |
| `model.pth` | 2.1 GB | Fine-tuned model weights |
| `dvae.pth` | 211 MB | Discrete VAE for mel-spectrogram tokenization |
| `mel_stats.pth` | 1 KB | Mel-spectrogram normalization statistics |
| `vocab.json` | 270 KB | Extended vocabulary with Romanian diacritics + `[ro]` |
| `speakers_xtts.pth` | 8 MB | Speaker embedding defaults |
| `reference_voices/` | — | ~6s WAV clips for each of the 5 voices |
## Limitations
- **5 voices only** -- the model may not generalize well to Romanian accents or dialects not represented in the training data.
- **WER is Whisper-based** -- Whisper large-v3 is used as an automated metric; no human evaluation has been conducted. Whisper itself may have biases on Romanian text.
- **Library patches required** -- the TTS 0.22.0 library needs patches for PyTorch 2.x compatibility and Romanian tokenizer support. See `setup_runpod.sh` in the [Codeberg repo](https://codeberg.org/eduardm/romanian-tts-xtts-v2).
- **Cedilla normalization is mandatory** -- forgetting to normalize input text will silently degrade output quality for any word containing s or t with diacritics.
## License
This model is released under the [Coqui Public Model License (CPML)](https://coqui.ai/cpml), inherited from the base XTTS-v2 model.
## Attribution
- **XTTS-v2 base model**: [Coqui AI / TTS](https://github.com/coqui-ai/TTS)
- **ADR speech data**: [gigant/romanian_speech_synthesis_0_8_1](https://huggingface.co/datasets/gigant/romanian_speech_synthesis_0_8_1)
- **Whisper evaluation**: [OpenAI Whisper](https://github.com/openai/whisper)
## Citation
```bibtex
@misc{musat2026xttsromanian,
title={Fine-tuning XTTS-v2 for Romanian: Unicode Normalization and Smart Embedding Initialization},
author={Musat, Eduard},
year={2026},
eprint={TODO},
archivePrefix={arXiv},
primaryClass={eess.AS}
}
```
## Links
- [Training code & scripts](https://codeberg.org/eduardm/romanian-tts-xtts-v2) -- full training pipeline, evaluation, and setup scripts
- [Live demo](https://eduardm.codeberg.page/romanian-tts-xtts-v2/) -- audio samples with WER comparison across all voices and epochs
- [Training dataset](https://huggingface.co/datasets/eduardem/romanian-speech-v1) -- ~62k clips, ~150 hours, 5 Romanian speakers