An end-to-end Speech-to-Speech Translation (S2ST) system for Balti (بلتی), a low-resource Tibetic language spoken by ~400,000 people across Gilgit-Baltistan and Baltistan, written in Perso-Arabic (Nastaliq) script.
Balti remains severely under-resourced in NLP and speech technology, with limited publicly available datasets, models, and language-specific tooling. Balti Tarjuman bridges this gap by deploying an integrated pipeline that translates spoken Balti into synthesized English speech — available both as a batch pipeline and as a real-time streaming pipeline (~2–3s round-trip latency from live microphone input).
Upload a Balti audio file and hear the English translation:
Live, unscripted — speaking Balti into the mic:
🎬 Watch the full demo with audio (1:04)
The pipeline processes continuous audio through four sequential stages, with an automatic ASR fallback for robustness:
flowchart LR
A[🎙️ Silero VAD<br/>Voice Activity Detection] --> B{ASR}
B -- Primary<br/>17.40% WER --> C[📝 Fine-tuned Whisper-small]
B -- On failure, falls back to<br/>22.11% WER --> D[⚡ Fine-tuned Wav2Vec2 XLS-R]
C --> E[🌐 Fine-tuned NLLB-200<br/>Translation Engine]
D --> E
E --> F[🔊 Kokoro-82M<br/>TTS Engine]
-
Voice Activity Detection (VAD): Silero VAD isolates valid speech frames and trims silent segments.
-
ASR (Whisper primary, Wav2Vec2 fallback):
- Primary: Fine-tuned
openai/whisper-small(17.40% WER) transcribes Balti speech into Perso-Arabic (Nastaliq) text. - Fallback: The pipeline falls back to a fine-tuned
wav2vec2-xls-r-300mCTC model (22.11% WER) in two cases: if Whisper raises an exception, or if Whisper's transcription exceeds a configurable latency bound (default 3s) — the latter runs Whisper in a worker thread and abandons waiting for it once the bound is hit, so a single slow inference can't stall the live pipeline. Both fallback paths are logged separately (wav2vec2_fallback_errorvswav2vec2_fallback_timeout) for debugging which failure mode is actually occurring in practice.
- Primary: Fine-tuned
-
Machine Translation (MT): Fine-tuned
facebook/nllb-200-distilled-600Mtranslates Perso-Arabic Balti text into English. -
Text-to-Speech (TTS): Kokoro-82M synthesizes English audio from the translated text.
The pipeline runs in two modes sharing a single set of loaded models:
- Batch Mode (
pipeline.py) — processes a complete.wavfile viaBaltiTarjumanPipeline.run(). - Streaming Mode (
streaming_pipeline.py) — continuous VAD-based segmentation of a live audio stream, built on Silero'sVADIteratorplus a threaded worker/queue design so capture, inference, and playback don't block one another. Reuses the batch pipeline's already-loaded models rather than duplicating them.
The streaming pipeline achieves approximately 2–3 seconds of round-trip latency from live microphone input, measured on a Kaggle T4 GPU via the Gradio interface. This is based on informal repeated testing across multiple utterances, not a controlled multi-run benchmark.
All ASR experiments in this repository use the project's own held-out validation set and the same WER evaluation pipeline unless otherwise specified.
The BaltiVoice comparison below has not been independently verified as an identical evaluation setup (dataset split and methodology compatibility are unconfirmed) — it's included as an external reference point, not a directly controlled comparison.
For machine translation, BLEU is reported on the available Balti-English evaluation data.
Our fine-tuned Whisper-small model achieves the best WER observed in this project, improving substantially over the published BaltiVoice Whisper-small baseline of 26.74%.
| Rank | Model Architecture | Strategy | Steps | Validation WER | Link |
|---|---|---|---|---|---|
| 🥇 1 | Whisper-small (Champion) | Cold-Start + SpecAugment | 2500 | 17.40% | HF Model |
| 🥈 2 | Wav2Vec2 XLS-R 300M | SpecAugment + Tuned LR | 7560 | 22.11% | HF Model |
| 🥉 3 | BaltiVoice Paper Baseline | Published Literature | — | 26.74% | — |
| 4 | Wav2Vec2 XLS-R 300M | Cold-Start CTC (no augmentation) | 7400 | 22.82% | HF Model |
| 5 | Whisper-small (Warm-Start R2) | Standard Fine-Tuning (1e-5 LR) |
1000 | 36.38% | — |
| 6 | Whisper-small (Zero-Shot) | Base Out-of-the-Box | 0 | 63.42% | — |
The translation stage uses a fine-tuned NLLB-200 distilled 600M model adapted for Balti.
- Base:
facebook/nllb-200-distilled-600M - Balti configuration:
bft_Arab - Training data: 504 Balti-English parallel pairs from the available public Balti-English data used in this project
- Evaluation metric: BLEU
- BLEU: 4.88
NLLB-200 did not natively support Balti, so the translation stage required model adaptation before fine-tuning: the project introduced the bft_Arab language token and initialized its embedding from a related existing language representation rather than random initialization.
| Component | Repository Link | Description |
|---|---|---|
| 🏆 ASR Champion | YuvrajGujari/whisper-small-balti |
Fine-tuned Whisper-small (17.40% WER) |
| ⚡ ASR Fallback | YuvrajGujari/wav2vec2-balti-specaugment |
Fine-tuned Wav2Vec2 XLS-R 300M, SpecAugment + tuned LR (22.11% WER) |
| 🌐 MT Model | YuvrajGujari/nllb-balti-mt |
Fine-tuned NLLB-200 Distilled (4.88 BLEU) |
| 📂 ASR Dataset | YuvrajGujari/balti-tarjuman-data |
Cleaned Balti audio with Perso-Arabic text |
git clone https://github.com/YuvrajGujari/balti-tarjuman.git
cd balti-tarjuman
pip install -r requirements.txtfrom pipeline import BaltiTarjumanPipeline
pipeline = BaltiTarjumanPipeline() # auto-selects CUDA if available
result = pipeline.run("path/to/balti_audio.wav")
print("Balti transcript:", result["balti_text"])
print("English translation:", result["english_text"])
print("Output audio written to:", result["audio_path"])from pipeline import BaltiTarjumanPipeline
from streaming_pipeline import StreamingBaltiTarjumanPipeline
pipeline = BaltiTarjumanPipeline()
streaming = StreamingBaltiTarjumanPipeline(pipeline) # reuses pipeline's loaded models
streaming.start()
# Feed a complete wav file (or live mic frames via streaming.feed_audio_frame())
streaming.feed_wav_file("path/to/balti_audio.wav")
result = streaming.get_next_output(timeout=5)
if result:
print("English translation:", result["english_text"])python gradio_streaming_demo.pyLaunches a two-tab interface — live microphone streaming, and file
upload — both running through the real StreamingBaltiTarjumanPipeline.
python scripts/test_asr_fallback.pyPulls held-out clips from YuvrajGujari/balti-tarjuman-data, measures
Whisper's real latency distribution, and deliberately exercises both
the exception-based and timeout-based fallback paths to confirm they
actually route to and return valid output from the wav2vec2 backup —
not just that the code runs without error.
For a detailed account of the challenges solved along the way — dataset acquisition, adapting NLLB for an unsupported language, ASR fine-tuning strategy, and building the real-time streaming layer — see docs/Engineering Case Study.md.
Apache 2.0 — see LICENSE for details.

