Instructions to use atasoglu/seda-tts-v0.1 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Piper
How to use atasoglu/seda-tts-v0.1 with Piper:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
seda-tts-v0.1: lightweight Turkish text-to-speech
seda-tts-v0.1 is a small, low-latency Turkish text-to-speech model. It runs on a single CPU thread and starts speaking about 0.1 s after it gets the text. It is a VITS model (the Piper "medium" size) with a frozen Turkish BERT text encoder, trained from scratch on 80 hours of expressive synthetic speech and exported to ONNX. sedalib runs it with onnxruntime, without PyTorch.
This voice is synthetic. It was designed from a text description with VoxCPM2; it is not a real person's voice or recording, and all training speech was AI-generated. Label the audio you make with it as AI-generated (EU AI Act, Article 50).
- Real time on a CPU. One thread, first audio in 80–150 ms, about 4.5× faster than real time.
- Emotion from the text. It hears the emotion in the sentence and reads it that way. You can also set one of 8 emotions.
- Whisper. A separate whispering style, with its own settings.
- Small. 19.1M parameters in the acoustic model and vocoder, plus a 29.3M frozen BERT. 194 MB of fp32 ONNX files.
Samples
Each clip is one render with the default settings and seed=0, not picked from several. The code below makes them again.
| Style | Emotion | Text | Audio |
|---|---|---|---|
| normal | happy | Sınavı geçtim! İnanamıyorum, bütün o geceler sonunda işe yaradı. Hadi bu akşam kutlayalım! | |
| normal | sad | Bütün yaz bu geziyi bekledim ama son anda iptal edildi. Şimdi hiçbir şey yapmak istemiyorum. | |
| normal | angry | Yeter artık! Kaç kere söyleyeceğim sana? Arabamı bir daha sormadan alırsan çok fena olur! | |
| normal | fearful | Biri içeride! Kapının arkasında bir gölge var, çabuk polisi ara! Lütfen acele et! | |
| normal | surprised | Ne? Gerçekten mi? Piyangodan büyük ikramiye bize mi çıktı? Bu imkânsız, inanamıyorum! | |
| normal | neutral | Toplantı 12 Mayıs'ta saat 14:30'da başlayacak; katılım ücreti 250 TL. | |
| whisper | calm | Uyu artık, her şey yolunda. Ben hemen yanındayım. | |
| whisper | fearful | Sokak çok karanlık. Biri bizi takip ediyor, korkuyorum. | |
| whisper | sad | Konser biletleri tükenmiş, bu akşam gidemiyoruz. Çok üzüldüm ama ona belli etme. | |
| whisper | neutral | Anahtar masanın sağ çekmecesinde, mavi defterin arasında. Kimseye söyleme. |
Usage
pip install "sedalib[tts]" # sedalib 1.4.0 or newer
from sedalib import Synthesizer, write_wav
tts = Synthesizer.from_pretrained() # atasoglu/seda-tts-v0.1
wav = tts.synthesize("Merhaba, ben Seda. Bugün hava 24 derece, şöyle bir yürüyüşe çıkalım mı?")
write_wav("merhaba.wav", wav, tts.sample_rate) # mono, 22050 Hz
synthesize returns float32 samples in [-1, 1].
VITS is not a streaming model: it needs a whole piece of text (up to 120 characters, see Long text) before it makes any audio. What can start early is the decoding. stream decodes each piece in short windows and yields them as they are ready, so playback starts before the piece is finished. The first chunk is 0.28 s of audio, the next ones 0.56 s:
for chunk in tts.stream("Toplantı 12 Mayıs'ta saat 14:30'da başlayacak."):
player.write(chunk) # e.g. a sounddevice.OutputStream at tts.sample_rate
Pick an emotion and a style:
tts.synthesize("Yeter artık! Kaç kere söyleyeceğim sana?", emotion="angry")
tts.synthesize("Sokak çok karanlık. Biri bizi takip ediyor, korkuyorum.", style="whisper", emotion="fearful")
tts.styles # ['normal', 'whisper']
tts.emotions_for("whisper") # ['neutral', 'calm', 'sad', 'fearful']
tts.defaults_for("whisper") # {'noise_scale': 0.5, 'noise_scale_w': 0.6, 'length_scale': 1.0}
from_pretrained downloads only the model files (not the samples) and caches them. Synthesizer("path/to/dir") loads a local copy.
For a browser demo with the samples above as examples:
pip install "sedalib[demo,tts]"
python -m sedalib.demo.tts
Reproduce the samples
from sedalib import Synthesizer, write_wav
tts = Synthesizer.from_pretrained("atasoglu/seda-tts-v0.1")
wav = tts.synthesize("Uyu artık, her şey yolunda. Ben hemen yanındayım.", style="whisper", emotion="calm", seed=0)
write_wav("07-whisper-calm.wav", wav, tts.sample_rate)
The same settings and seed give the same audio, up to floating-point rounding on other CPUs.
Voice, styles and emotions
| Voice | v03: female, middle-aged, mature and slightly husky. Designed with VoxCPM2 from a text description. |
| Styles | normal, whisper |
Emotions, normal |
neutral, calm, happy, sad, angry, fearful, disgusted, surprised |
Emotions, whisper |
neutral, calm, sad, fearful (the emotions the whispers were recorded in) |
No emotion (emotion=None, default) |
the model infers it from the text |
Emotion follows the text. In the training data, every sentence was written for its emotion and checked by an independent text label. So the model reads the emotion it hears in the words, and a set emotion works when it agrees with them. Asking for one the words contradict (a sad sentence read angrily) is not something it was trained for, and the result is unpredictable.
Settings
| Argument | normal |
whisper |
What it does |
|---|---|---|---|
noise_scale |
0.2 | 0.5 | How much the voice varies. A whisper needs more to sound breathy; at 0.2 it buzzes. |
noise_scale_w |
0.2 | 0.6 | How much the rhythm and pauses vary. |
length_scale |
1.0 | 1.0 | Speaking rate; above 1 is slower. At 1.0 it speaks 7–11% faster than the training speech. |
The defaults were chosen in blind listening tests and are stored in config.json (inference_defaults and release.style_defaults). None (the default) takes the style's own value.
Long text
Text is split into pieces of up to 120 characters, the longest the model was trained on. Short sentences are kept together, so the model pauses between them as a speaker would. A longer sentence is split between words. A 0.12 s pause is added where a piece ends a sentence. With stream, each piece starts playing while the next is decoded.
Text normalization
The model reads Turkish letters and . , ! ? ; : ' -. Before that, normalizer-tr (Apache-2.0) writes out numbers, dates, times, currency, units and common abbreviations: "12 Mayıs'ta saat 14:30'da … 250 TL" → "on iki Mayıs'ta saat on dört otuzda … iki yüz elli Türk lirası". It needs Python 3.11 or newer. On 3.10, pass expand=False and spell numbers out yourself. q, w, x and â, î, û are kept; other characters, such as emoji or other accented letters, become spaces.
Speed and size
One thread of an Intel Core 5 210H laptop CPU, onnxruntime, fp32. The CPU frequency was not pinned, so these numbers are on the slow side.
| Text | Audio | First audio | RTF (decoding in windows) |
|---|---|---|---|
| 18 characters | 1.4 s | 81 ms | 0.23 |
| 42 characters | 2.5 s | 96 ms | 0.22 |
| 108 characters | 6.9 s | 150 ms | 0.22 |
- First audio is the BERT pass, the encoder and the first decoder window.
- RTF (synthesis time / audio length) is 0.22 when
streamorsynthesizedecodes in the default 48-frame windows and 24 frames of context on each side. Decoding a whole piece at once is 0.12, but then nothing plays until it is done. Less than 24 frames of context leaves audible steps at the window borders. - Parts of one 2.4 s sentence: BERT 8 ms, encoder 29 ms; the decoder alone runs at RTF 0.11.
- Memory: about 350 MB peak for the whole Python process, model and text normalizer included.
Files
| File | Size | Description |
|---|---|---|
encoder.onnx |
29 MB | Text encoder, stochastic duration predictor and conditioning: symbols + BERT features → prior mean, log-variance and speaker/emotion/style vector |
decoder.onnx |
48 MB | Normalizing flow (reverse) and HiFi-GAN vocoder: latent frames → 22.05 kHz audio. Runs in short windows, so playback can start early. |
bert.onnx |
117 MB | ytu-ce-cosmos/turkish-small-bert-uncased, frozen, exported as is |
tokenizer.json |
0.8 MB | Its WordPiece tokenizer (lower-casing is done before, with Turkish rules) |
config.json |
2 KB | Symbols, sample rate, speaker/emotion/style ids, default settings and the released voice and styles |
samples/ |
2.3 MB | The audio of the table above |
All graphs use opset 17 and have no random nodes. The noise is drawn by the caller, so the output is reproducible. Input and output names are documented in sedalib's tts.py.
Model details
| Architecture | VITS (Kim et al., 2021) in the Piper "medium" size: 6-layer transformer text encoder (192 dims, 2 heads), stochastic duration predictor, 4 residual coupling layers, HiFi-GAN decoder (resblock type 2, 256 initial channels, upsampling 8·8·4) |
| Parameters | 7.06M encoder + 12.04M decoder; 29.26M frozen BERT |
| Input | Lower-case Turkish letters and punctuation (symbols in config.json), with a blank between symbols, plus 512-dim BERT token features spread over the characters of each token |
| Conditioning | Sum of speaker, emotion and style embeddings (512 dims) fed to the duration predictor, the flow and the decoder |
| Output | 22,050 Hz mono, 256-sample hop (11.6 ms frames) |
| Text encoder features | BERT small, 4 layers, 512 dims, kept frozen; features were stored in fp16 during training and are rounded the same way at inference |
| Precision | fp32 ONNX (no quantization) |
Training
- From scratch. No pretrained TTS weights. Only the BERT is pretrained, and it stays frozen.
- Data: atasoglu/turkish-emotional-speech-100h, a fully synthetic Turkish emotional speech set (5 voices, 8 emotions, 4 styles).
- Filtered to 74,784 clips (79.8 h): no code-switched sentences; in the normal style, only clips whose emotion an automatic audio judge confirmed (
judge_emotion == emotion,judge_p_target ≥ 0.5). Whispers, sighs and laughing speech were kept as they are. - 74,021 clips for training and 763 for validation, split by sentence so that no sentence is in both.
- Resampled from 48 kHz to 22.05 kHz.
- Text: the sentence as it was sent to the teacher TTS (
text_spoken), Turkish lower-cased, punctuation kept. It contains no digits, so number reading comes from normalizer-tr at inference. - The released voice,
v03, has 20.4 h in the filtered set, including 2,851 whispered clips.
- Filtered to 74,784 clips (79.8 h): no code-switched sentences; in the normal style, only clips whose emotion an automatic audio judge confirmed (
- All 5 voices and 4 styles were trained together, so they share what they learn about Turkish. Only the voice and styles listed above are released; the others are in
config.jsonbecause the model's ids follow them, but they are not supported. - Emotion dropout: each clip's emotion label was replaced by an extra
autolabel 30% of the time. This is whatemotion=Noneuses: the model learns to read the emotion from the text. - Recipe: 300,000 steps (127 epochs), batch 32, bf16 mixed precision (losses and mel in fp32), learning rate 2e-4, the standard VITS losses. About 50 hours on one RTX 5070.
- Validation: mel loss 0.47 on the 763 held-out clips.
- Export: to ONNX with the weight norm removed. On 121 checks, the ONNX encoder and decoder match PyTorch within 3e-5 (the BERT within 1e-3, the fp16 rounding of its features), and windowed decoding matches decoding at once within 2e-5 on average.
Evaluation
The model was judged by ear. There is no MOS study or automatic metric yet.
- Blind listening, one native listener, 1–5 scale. Clips were shuffled and labeled by letters only.
- At the default settings, normal speech and whispers scored 4.
- That is the same score as the teacher's own clips of the training data, which were in the same rounds.
- The listening rounds set the defaults:
- Whispers scored 2 at the normal-speech settings (0.2/0.2) and 4 at 0.5/0.6.
- Reading the sentences of a text one by one halved the pause between them: 128 ms against 244 ms in the training speech. This was the reason for the 120-character packing.
- Voice choice:
v01andv03, the two voices with whispers, scored the same and sound much alike.v03has more data.
Limitations
- One voice, two styles. Laughing speech and sighs were in the training data but are not part of this release.
- The quality ceiling is the teacher. All training speech is synthetic, from VoxCPM2 and Ahenk TTS. The model inherits their pronunciation, including some unlengthened long vowels in loanwords (e.g. sipariş).
- Whispers can have a slight metallic tint, and fearful whispers can come out louder than a whisper.
- English words are read with Turkish letter sounds, because code-switched text was left out of training.
- Text the normalizer does not cover is dropped: digits left after normalization, emoji, symbols. Uncommon abbreviations are read letter by letter or as words, not always correctly.
- Very short inputs (one or two words) are rarer in the training data and can sound less natural than full sentences.
- Not a streaming model. Each piece of text (up to 120 characters) is encoded whole before any of it is heard, so text that arrives word by word, for example from an LLM, has to be buffered to a sentence first.
- RTF 0.22 is comfortably real time for one voice; one core keeps up with about four at once.
- Turkish only.
License and attribution
- Model (
encoder.onnx,decoder.onnx,config.json): Apache-2.0. bert.onnx,tokenizer.json: converted without changes from ytu-ce-cosmos/turkish-small-bert-uncased by YTU COSMOS, MIT.- Training data: atasoglu/turkish-emotional-speech-100h, Apache-2.0. Its speech was made with:
- VoxCPM2 (Apache-2.0, OpenBMB), which also designed the voice;
- Ahenk TTS by Özgür Güler (CC BY 4.0), a Turkish LoRA on VoxCPM2, for normal speech. Attribution is given here as CC BY 4.0 requires; keep it when you redistribute the model.
- Its sentences were written by open-weight LLMs; see the dataset card for the full list and their licenses.
- Code lineage: VITS (MIT) and Piper (MIT) model code, retrained with a plain PyTorch loop. No Piper or VITS checkpoints were used.
- Runtime: sedalib, onnxruntime (MIT), normalizer-tr (Apache-2.0).
- Synthetic voice: the voice is no real person. Label generated audio as AI-generated, and do not use it to impersonate someone or to deceive.
Citation
@inproceedings{kim2021vits,
title = {Conditional Variational Autoencoder with Adversarial Learning for End-to-End Text-to-Speech},
author = {Kim, Jaehyeon and Kong, Jungil and Son, Juhee},
booktitle = {Proceedings of the 38th International Conference on Machine Learning (ICML)},
year = {2021}
}
@article{kesgin2023developing,
title = {Developing and Evaluating Tiny to Medium-Sized Turkish BERT Models},
author = {Kesgin, Himmet Toprak and Yuce, Muzaffer Kaan and Amasyali, Mehmet Fatih},
journal = {arXiv preprint arXiv:2307.14134},
year = {2023}
}
- Downloads last month
- 46