Rule-based Quran phonemizer for offline audio alignment and tajwid analysis.
Project description
Tilavet Phonemizer
Developed by Tarık İsmet ALKAN
Rule-based Quranic Arabic phonemizer for the Hafs an Asim recitation.
This project is a core component of the broader Tilavet AI ecosystem.
Converts fully-vowelled Uthmani-script Quran text into a deterministic phoneme sequence that respects tajwid rules. Designed as a "golden label" for training acoustic speech models, aligning audio recitations to text, and building Quran-only ASR / teleprompter applications.
What it does
بِسْمِ ٱللَّهِ ٱلرَّحْمَٰنِ ٱلرَّحِيمِ
│
▼
b i s m i l l aa h i r r a H m aa n i r r a H ii m i
Two output modes:
| Mode | Use case | Example (الرحيم) |
|---|---|---|
| Wasl (connected) | Within an ayah, normal flow | r r a H ii m i |
| Waqf (stop) | At end-of-ayah, with tajwid transformations applied | r r a H ii6 m |
Waqf mode applies: tanwin drop, taa-marbuta t → h, madd-i arıd lis-sukun (6 harakah), qalqalah kubra on final ق ط ب ج د.
Why this project
Quran applications need to align speech recordings to Quran text at the word (and even letter) level — for teleprompter highlight, hafız training, tajwid analysis. The standard approach is to train a CTC acoustic model that emits phoneme posteriors, then decode against a known Quran phoneme map.
For Quranic Arabic specifically, this phoneme map needs to encode tajwid rules (ikhfa, idgham, qalqalah, madd lengths, lafzatullah tafhim/tarqiq, …) — not just letter identity. There is no open-source tool that does this end-to-end for the full Hafs an Asim Mushaf. This project fills that gap.
Free, open source, no commercial intent. Intended as an ummah service.
Install
# From PyPI (planned)
pip install tilavet-phonemizer
# From source
git clone https://github.com/tialkan/tilavet-phonemizer
cd tilavet-phonemizer
pip install -e .
Requires Python 3.9+. No runtime dependencies.
Usage
Command line
tilavet-phonemize "بِسْمِ ٱللَّهِ ٱلرَّحْمَٰنِ ٱلرَّحِيمِ"
# → b i s m i l l aa h i r r a H m aa n i r r a H ii m i
tilavet-phonemize --json "بِسْمِ ٱللَّهِ"
# → {"symbols":["b","i",...], "text":"b i s m i l l aa h i", "words":[...]}
tilavet-phonemize --rules-only "بِسْمِ" # print only rule trace
tilavet-phonemize --waqf "ٱلرَّحِيمِ "
# → ' a r r a H ii6 m PAUSE
tilavet-phonemize --file ayahs.txt # one ayah per line
echo "هُوَ" | tilavet-phonemize - # read from stdin
tilavet-phonemize --print-inventory # canonical phoneme list
Python API
from tilavet_phonemizer import Phonemizer, PhonemizerConfig
# Wasl (default)
p = Phonemizer()
result = p.phonemize("بِسْمِ ٱللَّهِ")
print(result.text)
# b i s m i l l aa h i
# Per-word offsets (for alignment / highlighting)
for word in result.words:
syms = result.symbols[word.start:word.end]
print(f"{word.token:20} → {' '.join(syms)}")
# بِسْمِ → b i s m i
# ٱللَّهِ → l l aa h i
# Waqf (end-of-ayah)
p_waqf = Phonemizer(PhonemizerConfig(waqf_on_pause=True))
print(p_waqf.phonemize("ٱلرَّحِيمِ ").text)
# r r a H ii6 m PAUSE
# Cross-ayah connected (nun qutni)
p_cross = Phonemizer(PhonemizerConfig(cross_ayah_wasl=True))
# Batch processing (e.g. building a dataset)
results = p.phonemize_many(["بِسْمِ", "ٱللَّهِ", "هُوَ"])
# JSON-serializable raw/debug output for web backend or inspection
ayah = p.phonemize("بِسْمِ ٱللَّهِ").to_dict(include_rules=False)
# {"symbols": [...], "text": "...", "words": [{"token":..., "start":..., "end":...}, ...]}
# CTC/aligner target: elisions removed, PAUSE emitted as metadata
target = p.phonemize("رَيْبَ ۛ فِيهِ").to_alignment_dict()
# {"symbols": ["r","a","y","b","a","f","ii","h","i"], "pauses": [...], "words": [...]}
# Phoneme inventory for an acoustic model (49 entries incl. PAUSE;
# prepend the CTC blank for the persisted 50-class file)
Phonemizer.phoneme_inventory(include_metadata=True)
# ["'", "b", "t", ..., "PAUSE"]
The first downstream aligner contract is sketched in docs/aligner-mvp.md.
Inspecting tajwid rules per phoneme
result = p.phonemize("هُم مُّصِيبَةٌۭ")
for hit in result.rules:
print(f" {result.symbols[hit.symbol_index]} ← {hit.rule}")
# m_g ← idgham_shafawi
# m ← shadda
# u ← vowel
# ...
Symbol convention
Consonants
| Sym | Letter | Sym | Letter | Sym | Letter | Sym | Letter |
|---|---|---|---|---|---|---|---|
' |
ء (hamza) | b |
ب | t |
ت | th |
ث |
j |
ج | H |
ح | kh |
خ | d |
د |
dh |
ذ | r |
ر | z |
ز | s |
س |
sh |
ش | S |
ص | D |
ض | T |
ط |
Z |
ظ | 3 |
ع | gh |
غ | f |
ف |
q |
ق | k |
ك | l |
ل (tarqiq) | L |
lafzatullah tafhim |
m |
م | n |
ن | h |
ه | w |
و (cons.) |
y |
ي (cons.) |
Vowels & madd
| Symbol | Meaning |
|---|---|
a i u |
Short vowels (fatha, kasra, damma) |
aa ii uu |
Madd-i tabii (2 harakah) |
aa4 ii4 uu4 |
4-harakah madd (muttasil, munfasil, silah-kubra) |
aa6 ii6 uu6 |
6-harakah madd (lazim, arıd lis-sukun, farq) |
Tajwid variants
| Symbol | Meaning |
|---|---|
n_g |
Nun/tanwin ihfa OR idgham ma'al-ghunna |
m_g |
Mim idgham shafawi, iqlab, or ihfa shafawi |
q_qal T_qal b_qal j_qal d_qal |
Qalqalah on sakin ق ط ب ج د |
PAUSE |
Mushaf optional-stop marker (advisory, does not break wasl rules) |
Design rules
Lonly for lafzatullah tafhim. After kasra it becomes tarqiq →l. (بِسْمِ ٱللَّهِ→l l aa h i,تَٱللَّهِ→L L aa h i.)- Tanwin + lam/ra idgham bila ghunna: the
nelides (هُدًى لِّلْمُتَّقِينَ→h u d a l l ...). - Dagger alif / small alif is a single long vowel
aa(nota aa). - Hamza-i kat' is always pronounced; never dropped by wasl.
كُفُوًاis rendered with waw, not hamza:k u f u w a n.- Muqattaat letters apply inter-letter ihfa/idgham:
كهيعص→... 3 aa6 y n_g S aa6 d,الم→... l aa6 m_g m ii6 m. - Mushaf optional-pause markers (ۖ ۚ ۛ ۗ) emit
PAUSEbut do not block cross-word ikhfa/idgham/iqlab in wasl mode. - In waqf mode: tanwin drops, taa-marbuta
t→h, sakin qalqalah harf gets_qal, final tabii madd becomes 6-harakah arıd.
Full specification: docs/phoneme-spec.md.
What the symbols do not capture (V1 scope)
- Ra tafhim/tarqiq (no
R/rsplit — rule available inresult.rulesmetadata only) - Imala, ishmam, sakta (rare in Hafs; flagged via U+06EA as plain
ifor theمَجْر۪ىٰهَاcase) - Idgham naqis vs kamil (always rendered as complete assimilation)
- PAUSE typing (lazim ۚ vs jaiz ۖ vs mutekarrir ۛ — distinction in metadata, single
PAUSEsymbol in output)
These are intentional simplifications for V1 to keep the CTC class set compact (50 classes incl. blank + PAUSE). All decisions documented in docs/v1-backlog.md.
Validation
The V1 release was reviewed by 5 independent hafız reviewers (3 LLM-based, 2 human-style detailed audits) over 60+ ayet samples. All flagged bugs have been fixed; their feedback is preserved in docs/llm-review-synthesis.md.
A 35-ayah gold dataset (gold_wasl_v1 + gold_waqf_on_pause_v1) lives at data/validation/recovered_seed_with_validation.jsonl. Each ayet has multi-reviewer verdicts and aggregate consensus.
Full-Quran sanity scan (6236 ayet):
Crashes: 0
Empty output: 0
4+ consecutive consonant runs: 0
3-consonant runs: 4 (all are correct: muqattaat ikhfa + rare orthography)
Pre-computed outputs
data/phoneme_index_full.json (≈14 MB): every ayet of the Quran processed by V1, with both wasl and waqf phoneme sequences and per-word offsets. Ready to drop into a decoder.
{
"version": "v1.0",
"source": "alquran.cloud/quran-uthmani",
"ayah_count": 6236,
"ayahs": [
{
"surah": 1, "ayah": 1,
"arabic": "بِسْمِ ٱللَّهِ ٱلرَّحْمَٰنِ ٱلرَّحِيمِ",
"wasl": {
"symbols": ["b","i","s","m","i","l","l","aa","h","i", ...],
"words": [{"token":"بِسْمِ","start":0,"end":5}, ...]
},
"waqf": { ... }
}
]
}
CTC class file (data/ctc_classes.json) lists the 50 phonemes (incl. blank and PAUSE) in fixed order for acoustic-model training.
Testing
PYTHONPATH=src python3 -m pytest tests/
# 108 passed, 35 subtests passed in ~0.07s
For development:
pip install -e ".[dev]"
pytest tests/ -v --cov=src/tilavet_phonemizer
Scope and disclaimers
- Riwāyat: Hafs an Asim only (
الرواية). Other turuq (Shâtibî, Tayyibe, …) are not supported by this V1. - Religious authority: This tool is a computational representation, not a tajwid teacher. Phoneme symbols are engineering tokens, not a replacement for traditional learning under a qualified teacher. Latin transliteration is not displayed to end-users by design.
- Source text: Uthmani Mushaf (via
alquran.cloud). Variant orthography is normalized to standard Unicode forms.
Roadmap (post-V1)
- Râ tafhim/tarqiq as a metadata layer
- Variant-graph output for PAUSE branches (wasl vs waqf paths)
- Multilingual Support: Documentation and usage guides will be expanded to Arabic, English, Urdu, Indonesian, Turkish, and French across all platforms (GitHub, Hugging Face, PyPI).
- Acoustic model integration example (wav2vec2 / Whisper fine-tune)
- Extension hooks for Shâtibî / Tayyibe rules
See docs/v1-backlog.md for the complete backlog.
License
MIT. Free for commercial and personal use, attribution required.
Citation
@software{tilavet_phonemizer_2026,
title = {Tilavet Phonemizer: Open-source Quran phonemizer for Hafs an Asim},
year = {2026},
url = {https://github.com/tialkan/tilavet-phonemizer}
}
Acknowledgments
Built with the guidance of multiple hafız reviewers who took the time to read every output sample and challenge every architectural decision. May Allah accept their effort.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tilavet_phonemizer-0.2.0.tar.gz.
File metadata
- Download URL: tilavet_phonemizer-0.2.0.tar.gz
- Upload date:
- Size: 110.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f68fc2c06def9e953467cc2da9b0af2bd10335121f601f496fdebe797dfe193e
|
|
| MD5 |
46ad78ab19a8b2fa8a9a5666607a28a0
|
|
| BLAKE2b-256 |
95b0c43df0fdba4f9a711608b9135c18837426bd2d1cfbb299186f5d7b85da77
|
File details
Details for the file tilavet_phonemizer-0.2.0-py3-none-any.whl.
File metadata
- Download URL: tilavet_phonemizer-0.2.0-py3-none-any.whl
- Upload date:
- Size: 27.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ebce83eef09393f51cdb40a73978ea2b4b383894299bfede674c5e666b4e6e8
|
|
| MD5 |
325973d6e0b5d1d16a96de6334b14cf9
|
|
| BLAKE2b-256 |
d83626abe7dbdc5a14b040f3cd363e6720bbaf439bd9d9ada2cee44ce558d7c6
|