---
language:
- bg
- en
license: apache-2.0
tags:
- text-to-speech
- tts
- bulgarian
- miocodec
- encoder-decoder
- voice-cloning
pipeline_tag: text-to-speech
library_name: pytorch
---
# BgTTS-38M — Bulgarian Text-to-Speech with Voice Cloning
A lightweight **38M parameter** encoder-decoder TTS model for **Bulgarian** and **English** speech synthesis with zero-shot voice cloning via [MioCodec](https://huggingface.co/Aratako/MioCodec-25Hz-24kHz).
## Audio Samples
"Това е тест на българския синтез на реч."
"This is a test of the English speech synthesis."
"Този model е trained на български and English data."
## 🎙️ Voice Cloning
This model supports **zero-shot voice cloning** — it can generate speech in any voice given just a short reference audio clip. No fine-tuning needed.
### How it Works
1. **Record or provide a reference audio** (3-10 seconds of clear speech, WAV format, ideally 24kHz)
2. **MioCodec extracts a 128-dimensional speaker embedding** (`global_embedding`) from the reference
3. **The model uses this embedding** as an additive bias in the decoder — every generated token is influenced by the speaker's voice characteristics
4. **The same embedding is used for MioCodec decoding** to reconstruct the final waveform
### Tips for Best Voice Cloning
- Use **clean audio** without background music or noise
- **3-10 seconds** is enough — longer isn't necessarily better
- The reference audio **doesn't need to be in the same language** as the generated text
- You can **save and reuse speaker embeddings** to avoid re-encoding:
```python
import torch
from codec import CodecV6
codec = CodecV6(device="cuda")
# Extract and save speaker embedding
ref = codec.encode("my_voice.wav")
torch.save(ref["global_embedding"], "my_voice_emb.pt")
# Later, load and use it
speaker_emb = torch.load("my_voice_emb.pt")
```
### CLI Voice Cloning
```bash
# Clone from a reference WAV file
python inference.py \
--checkpoint . \
--text "Здравейте, аз съм клониран глас." \
--speaker-wav my_voice.wav \
--output cloned_output.wav
# Or use a pre-saved embedding
python inference.py \
--checkpoint . \
--text "Здравейте, аз съм клониран глас." \
--speaker-emb my_voice_emb.pt \
--output cloned_output.wav
```
## ⚠️ Important: Sentence Length
> **This model works best with sentences up to ~8 seconds of audio (~200 tokens at 25fps).**
>
> For longer texts, split them into shorter sentences (1-2 sentences at a time) and concatenate the audio.
> The maximum supported length is ~19 seconds (475 tokens), but quality degrades noticeably beyond 8s.
## Model Architecture
| Component | Details |
|-----------|---------|
| **Text Encoder** | 4-layer bidirectional Transformer (d=384, 6 heads, ff=1536) |
| **Audio Decoder** | 8-layer causal Transformer (d=384, 6 heads, ff=1536) with cross-attention |
| **Speaker Injection** | Linear(128 → 384), additive bias from MioCodec global_embedding |
| **Audio Codec** | MioCodec 25Hz, 1 codebook, 12800 codes, 24kHz output (~350 bps) |
| **Total Parameters** | 38.2M (Encoder: 9.6M, Decoder: 28.6M) |
| **Activations** | SwiGLU |
| **Normalization** | RMSNorm |
| **Positional Encoding** | Learned (encoder), RoPE (decoder) |
| **Embeddings** | Tied decoder (lm_head = token_embedding) |
### Tokenizer
Character-level tokenizer supporting 146 characters:
- Bulgarian Cyrillic (А-Я, а-я)
- English Latin (A-Z, a-z)
- Digits, punctuation, whitespace
Total vocabulary: **12,955** tokens (9 special + 146 text + 12,800 audio codes)
## Training
- **Data**: 830K samples, 1,172 hours total
- Bulgarian: 292K samples (~661 hours)
- English: 538K samples (~511 hours)
- **Schedule**: 30 epochs total
- 5 epochs cosine decay from scratch
- 5 epochs warm restarts (4 cycles, decay=0.7)
- 20 epochs twophase LR (25% fast linear drop → 75% slow cosine in productive zone)
- **Best val_loss**: **5.2759** at step 63,500
- **Hardware**: NVIDIA RTX 5090 (32GB), ~600 samples/sec
## Quick Start
### Requirements
```bash
pip install torch torchaudio soundfile miocodec
```
### Inference
```python
import torch
from model import load_for_inference
from tokenizer import TTSTokenizer
from codec import CodecV6
from inference import generate
device = "cuda"
# Load model
model = load_for_inference(".", device=device)
tokenizer = TTSTokenizer()
codec = CodecV6(device=device)
# Get speaker embedding from reference audio
ref = codec.encode("reference_speaker.wav")
speaker_emb = ref["global_embedding"].to(device)
# Generate
codes = generate(
model, tokenizer,
text="Здравейте, как сте днес?",
speaker_emb=speaker_emb,
temperature=0.7,
top_k=250,
max_new_tokens=512,
device=device,
)
# Decode to audio
if codes is not None:
wav = codec.tokens_to_wav(codes, speaker_emb, "output.wav")
```
### CLI
```bash
python inference.py \
--checkpoint . \
--text "Здравейте, как сте днес?" \
--speaker-wav reference.wav \
--output output.wav \
--temperature 0.7
```
### Parameters
| Parameter | Default | Description |
|-----------|---------|-------------|
| `--temperature` | 0.7 | Sampling temperature (lower = more stable, higher = more expressive) |
| `--top-k` | 250 | Top-k filtering |
| `--top-p` | 0.95 | Nucleus sampling threshold |
| `--rep-penalty` | 1.1 | Repetition penalty on recent tokens |
| `--max-tokens` | 512 | Maximum decoder steps |
**Recommended temperature**: 0.5 for stable output, 0.7-0.8 for more natural/expressive speech.
## Files
```
checkpoint.pt # Model weights (153MB, inference-only — no optimizer state)
config.py # All model configuration constants
model.py # Model architecture (TTSEncoderDecoder)
tokenizer.py # Character-level tokenizer
codec.py # MioCodec wrapper (encode/decode)
inference.py # Inference pipeline with KV-cache
samples/ # Audio samples
```
## Limitations
- **Best with short sentences** (up to ~8 seconds / 200 tokens). Split longer texts.
- Trained primarily on Bulgarian data — Bulgarian quality is better than English.
- Zero-shot voice cloning quality depends on the reference audio clarity.
- No prosody control (pitch, speed, emotion) — these are implicitly learned.
- Character-level tokenizer may struggle with rare Unicode characters outside the supported set.
## License
Apache 2.0
## Citation
If you use this model, please cite:
```
@misc{bgtts38m,
title={BgTTS-38M: Bulgarian Text-to-Speech with MioCodec},
author={beleata74},
year={2026},
url={https://huggingface.co/beleata74/BgTTS-38M}
}
```