Cairo-TTS · كايرو
Fast Egyptian Arabic text-to-speech with English code-switching, live streaming and zero-shot voice cloning.
It runs on CPU or GPU, starts speaking in under 100 ms on a GPU, and includes 20 preset voices and a web studio.
Install · Python · Web studio · Colab · Space · Pronunciation · Preprocessing · Parameters
from cairo_tts import CairoTTS
tts = CairoTTS.from_pretrained() # downloads mohammedaly22/Cairo-TTS, runs on CPU
tts.save("ال meeting بكره الصبح بدري, ف يا ريت متتأخرش.", "out.wav", voice="malak")
✨ Features
| 🗣️ Egyptian Arabic | Trained on ~747 h of Egyptian podcasts collected with the EgySpeech pipeline. It reads everyday عامية naturally. |
| 🔀 Code-switching | English words inside Arabic sentences: «ال deadline بتاع ال project». |
| 🎙️ Zero-shot cloning | Clone any voice from a ~10 s recording, or use one of the 20 preset voices (10 male, 10 female). |
| ⚡ Streaming | Audio comes out in 80 ms frames while it is generated: ~80 ms to first audio on a GPU. |
| 💻 CPU or GPU | ~100M parameters, faster than real time on a laptop CPU, with optional int8. |
| 🔢 Egyptian text frontend | Numbers, dates, times, money, %, phone numbers, emails, URLs, hashtags and acronyms are read the Egyptian way. |
| ✍️ Pronunciation control | Diacritics override the model's reading; ةْ gives a pronounced ta marbuta, and ~ the Egyptian reading of a letter. |
| 🎛️ Web studio | An RTL Arabic UI with generate and stream modes, voices, cloning, parameters, live waveform and metrics, and light/dark themes. |
📦 Install
pip install cairo-tts # Python API + CLI
pip install "cairo-tts[ui]" # + web studio (FastAPI)
From source:
git clone https://github.com/MohammedAly22/Cairo-TTS && cd Cairo-TTS
pip install -e ".[ui]"
- GPU: install the CUDA build of PyTorch first (pytorch.org). Cairo-TTS runs on the CPU by default; pass
device="cuda"(or--device cuda) to use the GPU, ordevice="auto"to pick it when available. - CPU only:
pip install torch --index-url https://download.pytorch.org/whl/cpukeeps the install small.
🐍 Python
from cairo_tts import CairoTTS
tts = CairoTTS.from_pretrained(
"mohammedaly22/Cairo-TTS", # or a local folder
device="cpu", # "cpu" (default) | "cuda" | "cuda:1" | "auto"
max_prompt_sec=10, # seconds of reference audio used for cloning
quantize=False, # int8 weights on CPU
num_threads=None, # CPU threads
)
# 1) generate: a float32 numpy array at 24 kHz
audio = tts.generate("النهارده الجو حلو اوي, ايه رأيك ننزل نتمشى شوية؟")
# 2) save straight to a file, with a preset voice
tts.save("عارف ايه اكتر حاجة بتخوفني.", "ahmed.wav", voice="ahmed")
# 3) clone any voice (zero-shot) from a short, clean recording
tts.save("ده صوتي انا, بس الموديل هو اللي بيتكلم.", "me.wav", voice="my_voice.wav")
tts.export_voice("my_voice.wav", "my_voice.safetensors") # instant to reload: voice="my_voice.safetensors"
# 4) stream: chunks arrive while audio is generated
for chunk in tts.stream("حاسس اني واقف مكاني. كل يوم نفس التاسكات, نفس ال meetings.", voice="sara"):
play(chunk) # your audio sink
# 5) metrics of the last call
m = tts.last_metrics
print(f"TTFA {m.ttfa*1000:.0f} ms · {m.audio:.1f}s audio in {m.total:.2f}s · {m.x_realtime:.1f}x real time · {m.device}")
# 6) see exactly what the model reads
print(tts.normalize("كلمني على 01012345678 الساعة 3:30 م"))
List the preset voices
for v in tts.voices():
print(v["id"], v["name_ar"], v["gender"], v["description_ar"])
| Male | Female | ||
|---|---|---|---|
mohamed |
محمد | sara |
سارة |
ahmed |
أحمد | mariam |
مريم |
ali |
علي | nour |
نور |
omar |
عمر | fatma |
فاطمة |
youssef |
يوسف | heba |
هبة |
mostafa |
مصطفى | yasmin |
ياسمين |
karim |
كريم | mona |
منى |
hassan |
حسن | rana |
رنا |
tarek |
طارق | dina |
دينا |
yehia |
يحيى | malak |
ملك |
Each voice has an Arabic and an English description (pitch, style, tone) in voices/voices.json. tarek is the default voice (used when voice is not given). The names are made up.
A complete script is in examples/infer.py.
Command line
cairo-tts say "ال meeting اتأجل لبكره." -o out.wav --voice ahmed
cairo-tts say "صوتي انا." -o me.wav --voice my_voice.wav --device cpu
echo "النص من ملف" | cairo-tts say - -o out.wav
cairo-tts voices # preset voices
cairo-tts normalize "الساعة 3:30 م" # → الساعة تلاتة ونص العصر
cairo-tts serve --port 8000 # web studio
🎛️ Web studio
pip install "cairo-tts[ui]"
cairo-tts serve # http://127.0.0.1:8000 (--device cpu|cuda, --host 0.0.0.0)
- Two modes: توليد كامل (generate the whole clip) and بث مباشر (live streaming, played while it is generated).
- Voices: a searchable dropdown of the 20 presets (descriptions, play/pause previews), or استنسخ صوت (clone a voice): upload a file or record from the microphone.
- Parameters: temperature, LSD steps, EOS threshold, frames after EOS, noise clamp, chunk size.
- Metrics after every run: server TTFA, browser TTFA, total time, audio length, × real time, RTF, chunks / characters, device.
- Pronunciation toolbar (فتحة، ضمة، كسرة، سكون، شدة،
ةْ،~), a random Egyptian sentence button, and a preview of the normalized text. - A minimal Arabic, RTL, responsive layout with dark and light themes (
?theme=light|dark), a waveform that fills in while streaming, and a generation history. - Streaming plays through an AudioWorklet at the model's 24 kHz rate with a short pre-buffer, so chunks join without gaps or resampling clicks.
- A JSON API at
/api/docs:POST /api/generate,POST /api/stream(NDJSON PCM chunks),GET /api/voices,POST /api/voices/upload,POST /api/normalize.
Use it from Python (e.g. in your own app):
from cairo_tts import CairoTTS
from cairo_tts.server import create_app, serve
serve(CairoTTS.from_pretrained(device="cuda"), host="0.0.0.0", port=8000)
# or: app = create_app(tts) → mount / run with any ASGI server
📓 Colab notebooks
| Notebook | ||
|---|---|---|
| Quick start: load, preset voices, cloning, streaming, preprocessing, diacritics, parameters | 01_quickstart.ipynb |
|
| Web UI: the full studio inside Colab, no local install | 02_web_ui.ipynb |
Both download the public model from Hugging Face; no token is needed.
🤗 Hugging Face Space
The Space runs the same studio (Docker SDK, port 7860). To deploy your own:
python scripts/deploy_space.py --space <user>/Cairo-TTS --token-secret # uploads space/ + the package source
--token-secret stores your token as the Space's HF_TOKEN secret (only needed if you point it at a private model). Optional Space variables: CAIRO_TTS_MODEL, CAIRO_TTS_DEVICE, CAIRO_TTS_QUANTIZE=1.
✍️ Pronunciation control
The model adds diacritics on its own. Write them yourself only where it reads a word wrong:
| Write | Meaning | Example |
|---|---|---|
ةْ (ta marbuta + sukun) |
ة is pronounced t, as in the construct state (إضافة); normalized to تْ |
عربيةْ ابويا → ʿarabiyyit abūya; plain عربية → ʿarabiyya |
~ after a letter |
the Egyptian reading of that letter, mainly ق → glottal stop (ء) | ق~لبي → ʾalbi, ق~ولتلك → ʾultilak |
a vowel (َ ُ ِ) |
chooses between readings | نِفسَها (she wants) vs نَفسَها (herself) |
shadda ّ |
doubles the consonant | بَتمَشَّى |
sukun ْ |
no vowel | اتْحَرَجْت |
| Latin letters | English words | ال meeting, ال deal |
CEO, AI |
upper-case runs are spelled as English letters | → c e o, a i (write ceo to read it as a word) |
. , ، |
sentence end (long pause) / short pause; ? ! become . |
Order of marks: letter →
~→ shadda → vowel (e.g.ق~َ). The frontend also reorders them for you.~is the marker the model learned from CATT's Egyptian «؞» sign.
ملاحظات بالمصري
- اكتب زي ما بتتكلم: «مش عارف»، «عشان كدة»، «إزيك». الفصحى بتشتغل بس العامية أحلى.
- الكلام الإنجليزي اكتبه بالحروف اللاتيني: «ال meeting»، «ال deadline».
- لو كلمة اتقرت غلط شكّلها بس هي: «نِفسَها» بدل «نفسها».
- ةْ: التاء المربوطة اللي عليها سكون بتتنطق «ت»: «عربيةْ ابويا» ← عربيِّت أبويا.
- ~: بعد الحرف معناها انطقه بالمصري، وأشهرها «ق~» تتقري همزة: «ق~لبي» ← ألبي.
- الصوت المرجعي: ٨–١٠ ثواني نضيفة، صوت واحد، من غير مزيكا.
- لو آخر الجملة بيتقطع نزّل «حد النهاية» (
eos_threshold)، ولو الصوت مش ثابت قلل الحرارة.
🔢 Text preprocessing
preprocess_egyptian() runs before the model frontend. You can use it on its own:
from cairo_tts import preprocess_egyptian, prepare_text
preprocess_egyptian("كلمني على 01012345678 او ابعت على ahmed.ali@gmail.com")
# كلمني على زيرو واحد زيرو, واحد اتنين تلاتة اربعة, خمسة ستة سبعة تمانية, او ابعت على احمد دوت علي ات gmail دوت كوم
prepare_text("الاجتماع الساعة 3:30 م و السعر 250 جنيه بخصم 15%")[0]
# الاجتماع الساعة تلاتة ونص العصر و السعر ميتين وخمسين جنية بخصم خمستاشر في المية
| Input | Spoken |
|---|---|
01012345678, +20 10 … |
phone digits grouped 3-4-4: «زيرو واحد زيرو, …», «زائد اتنين زيرو …» |
ahmed.ali@gmail.com |
«احمد دوت علي ات gmail دوت كوم» (Arabic names typed in Latin letters are written in Arabic) |
www.youtube.com, https://… |
«w w w دوت youtube دوت كوم» |
3:30 م, 9:15, 10:45 pm |
«تلاتة ونص العصر», «تسعة وربع», «حداشر الا ربع بالليل» |
15/3/1998 |
«خمستاشر مارس سنة الف وتسعمية وتمانية وتسعين» |
250 جنيه, $99, 50 EGP, 1.5 € |
Egyptian amounts with currency names |
15%, 3.5 |
«خمستاشر في المية», «تلاتة فاصلة خمسة» |
5K, 2M, 3Bn |
«خمسة الف», «اتنين مليون», «تلاتة مليار» |
120 km/h, 5 kg, 64 GB |
units in Arabic |
د/ احمد, م/, ش., ج.م.ع |
«دكتور», «مهندس», «شارع», «جمهورية مصر العربية» |
CEO, #مصر, @user, & |
«c e o», «هاشتاج مصر», «user», «و» |
Numbers are verbalized in Egyptian (1994 → «الف وتسعمية واربعة وتسعين»). Runs of more than 9 digits are read digit by digit.
⚙️ Parameters
| Parameter | Default | |
|---|---|---|
voice |
default voice | preset id, an audio file (cloning), or an exported .safetensors |
temperature |
0.3 |
lower is steadier; higher is more expressive and less stable |
lsd_steps |
1 |
flow decoding steps per 80 ms frame |
eos_threshold |
-4.0 |
end-of-speech threshold: higher stops earlier, lower if the last word gets cut |
noise_clamp |
None |
clamps the sampling noise (e.g. 2.0) |
frames_after_eos |
auto | 80 ms frames kept after the end of speech |
max_letters |
160 |
long text is split at . then , into chunks of this many letters |
stop |
None |
a threading.Event that stops streaming early |
📊 Speed
Measured with this package after warm-up, on 7–11 s sentences:
| Device | Time to first audio | Throughput |
|---|---|---|
| NVIDIA GTX 1660 Ti (6 GB) | ~80 ms | ~2.2× real time |
| Intel i7-10750H laptop CPU | ~330 ms | ~2.6× real time |
🧠 Model
The model is a Pocket TTS (Kyutai) architecture: a causal transformer over Mimi latents with a 1-step flow head and a streaming Mimi decoder (24 kHz).
- A 24-layer teacher was fine-tuned on ~747 h of Egyptian podcasts collected with the EgySpeech pipeline, then distilled into this 6-layer student.
- Details are on the model card.
🗂️ Repository
src/cairo_tts/
├── model.py CairoTTS: loading, devices, voices, generate / stream / save, metrics
├── text/ Egyptian preprocessing + the training frontend (numbers, diacritics, ta marbuta)
├── server/ FastAPI app + static web studio (HTML / CSS / JS)
└── cli.py cairo-tts say | serve | voices | normalize
notebooks/ Colab quick start + web UI
examples/infer.py minimal script
space/ Hugging Face Space (Docker)
scripts/ prepare_voices.py · upload_to_hub.py · deploy_space.py
tests/ text + inference + API tests (pytest)
⚖️ Responsible use
Cairo-TTS can imitate voices.
- Do not use it to impersonate anyone without their consent, to deceive, or to commit fraud.
- The preset voices are short clips of podcast speakers and are provided for research and demos only.
- Label synthetic audio when you publish it.
📜 License
- The code is MIT.
- The model weights are fine-tuned from Kyutai's Pocket TTS and follow its terms. See NOTICE.md and the model card.
Metadata
Release files for cairo-tts 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cairo_tts-0.1.0.tar.gz | 138.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cairo_tts-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 274.6 kB
Release files / cairo_tts-0.1.0.tar.gz
| Download URL | cairo_tts-0.1.0.tar.gz |
|---|---|
| Size | 138.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e87247a7a4abd557e6678fd2eff8e04d343e82e5175d98e992dc7b654f24a9c6
|
|
BLAKE2b-256 checksum How to use checksums |
69cf55c78db82571e4ddfa55081190aeb6a14b7db7857aaa906f3b8ae05779d8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / cairo_tts-0.1.0-py3-none-any.whl
| Download URL | cairo_tts-0.1.0-py3-none-any.whl |
|---|---|
| Size | 136.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
19754e38f7a2f48d7775acc29859cbd7bca8b7c8dda3f50102c572bf8cc31e1a
|
|
BLAKE2b-256 checksum How to use checksums |
f31d1986e4616d85cdfd9b7f23ece9e951282ca3781f6719c4943dfcbdbd5a7e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|