Aplikasi Windows berbasis Python untuk terjemahan suara dua arah secara real-time di meeting Zoom/Meet/Teams, via whisper.cpp + VB-Audio Virtual Cable:
- Inbound ("Telinga"): audio lawan bicara (Zoom/YouTube/dll) → transkrip → terjemahan Indonesia → dibacakan ke headset Anda.
- Outbound ("Pita Suara"): suara Anda dari microphone → transkrip
(Bahasa Indonesia) → terjemahan Inggris → disintesis dan dimainkan ke
CABLE-A Input, yang di Zoom/Meet Anda pilih sebagai microphone
(
CABLE-A Output) — lawan bicara mendengar suara Inggris hasil AI.
Lihat proposal BABELSYNC.pdf untuk konteks produk lengkap.
babelsync-stt/
├── src/
│ ├── __init__.py
│ ├── config.py # Semua nilai yang bisa dikonfigurasi
│ ├── exceptions.py # Exception khusus per komponen
│ ├── audio_capture.py # Capture audio dari VB-Cable via sounddevice
│ ├── vad.py # Voice Activity Detection -> segmentasi ucapan
│ ├── whisper_client.py # Kelola subprocess whisper-server + HTTP client
│ ├── transcriber.py # Orkestrator threading (capture/VAD/inferensi)
│ ├── display.py # Output status & transkrip ke terminal
│ └── main.py # Entry point + argparse + Ctrl+C handling
├── requirements.txt
├── run.bat # Launcher cepat untuk Windows
└── README.md # Dokumen ini
Catatan arsitektur: whisper.cpp tidak dipanggil ulang per segmen audio (itu akan me-reload model setiap kali → sangat lambat). Sebagai gantinya,
whisper-server(binary bawaan whisper.cpp) dijalankan sekali sebagai subprocess persisten saat aplikasi start, model ter-load sekali ke memory, lalu tiap segmen ucapan dikirim lewat HTTP lokal (/inference). Ini yang memungkinkan latency < 2 detik. Detail lebih lanjut ada di komentarwhisper_client.py.
cd babelsync-stt
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txtIsi requirements.txt:
sounddevice>=0.4.6
numpy>=1.24.0
requests>=2.31.0
scipy>=1.11.0
colorama>=0.4.6
sounddevice— capture audio dari device (via PortAudio, WASAPI di Windows)scipy— resampling berkualitas tinggi jika device tidak native 16kHzrequests— HTTP client ke whisper-server lokalcolorama— warna teks di CMD/PowerShell
-
Clone & build (butuh CMake + Visual Studio Build Tools / MSVC, atau bisa juga pakai MinGW):
git clone https://github.com/ggerganov/whisper.cpp.git cd whisper.cpp cmake -B build cmake --build build --config ReleaseJika build sukses, akan muncul beberapa executable di
build\bin\Release\, termasukwhisper-server.exe(yang dipakai aplikasi ini) danmain.exe.Untuk performa CPU lebih baik, tambahkan flag AVX2/OpenBLAS sesuai dokumentasi whisper.cpp. Untuk GPU NVIDIA, build dengan dukungan CUDA (
-DGGML_CUDA=ON) supaya inferensi jauh lebih cepat — sangat direkomendasikan untuk mengejar target latency < 2 detik dengan model yang lebih besar dari "base". -
Download model ggml (format whisper.cpp, bukan model OpenAI Whisper Python biasa):
cd models download-ggml-model.cmd baseIni akan menghasilkan
models\ggml-base.bin. Untuk bahasa Indonesia, model multilingual (bukan varian.en) wajib dipakai —base,small, ataumediumsemuanya multilingual secara default. Semakin besar model → akurasi naik, tapi latency juga naik. Rekomendasi awal:baseatausmalluntuk keseimbangan latency/akurasi di CPU. -
Sesuaikan path di
src/config.py(atau lewat argumen CLI, lihat Bagian 6) agar mengarah ke lokasi hasil clone di atas:server_executable = "whisper.cpp/build/bin/Release/whisper-server.exe" model_path = "whisper.cpp/models/ggml-base.bin"
-
Download dari situs resmi VB-Audio Virtual Cable (vb-audio.com/Cable), lalu install dan restart Windows.
-
Setelah restart, akan muncul dua device audio baru:
CABLE Input (VB-Audio Virtual Cable)— bertindak sebagai speaker/outputCABLE Output (VB-Audio Virtual Cable)— bertindak sebagai microphone/input
-
Set sebagai default output (opsional, paling praktis) atau atur per-aplikasi:
- Cara A (global): klik kanan ikon speaker di taskbar → Sound
settings → set Output device =
CABLE Input. Semua suara sistem (YouTube, Zoom, Spotify, dll) akan mengalir ke virtual cable. - Cara B (per-aplikasi, direkomendasikan): Windows 10/11 →
Settings → System → Sound → Volume mixer (atau "App volume and
device preferences") → untuk aplikasi seperti Chrome/Zoom, set
Output-nya ke
CABLE Input. Ini agar suara notifikasi Windows lain tidak ikut tertranskripsi.
- Cara A (global): klik kanan ikon speaker di taskbar → Sound
settings → set Output device =
-
Suara asli sengaja TIDAK diperdengarkan. Audio yang dialihkan ke
CABLE Inputmasuk ke virtual cable dan tidak terdengar di earphone Anda. Ini memang perilaku yang diinginkan: sesuai visi "Invisible AI", Anda cukup mendengar terjemahannya saja, tanpa dua suara bertabrakan.Jangan aktifkan "Listen to this device" di Windows — itu akan memunculkan kembali suara asli dan bertabrakan dengan terjemahan.
Kalau Anda memang ingin tetap mendengar suara aslinya (misal untuk menangkap intonasi lawan bicara), nyalakan dengan
--monitor. Saat aktif, suara asli otomatis meredup ke 15% selagi terjemahan berbunyi (auto-ducking, lihatmonitor.py), lalu kembali normal:python -m src.main --monitor --monitor-volume 0.8 --duck-volume 0.1
-
Aplikasi Python ini akan otomatis mencari device input yang namanya mengandung
"CABLE Output"(lihatconfig.py→device_name_hint), jadi tidak perlu index device manual.
Pipeline outbound butuh satu virtual cable tambahan supaya tidak bentrok dengan cable inbound:
- Download VB-CABLE A+B dari vb-audio.com/Cable (paket terpisah
dari VB-CABLE biasa, bagian "VB-CABLE A+B"), install CABLE A,
lalu restart Windows. Akan muncul device baru
CABLE-A InputdanCABLE-A Output. - Di Zoom/Meet/Teams → Settings → Audio → Microphone → pilih
CABLE-A Output. - Selesai. BabelSync akan memainkan suara Inggris hasil terjemahan ke
CABLE-A Input, dan Zoom membacanya dariCABLE-A Outputseolah-olah itu microphone Anda. - Microphone fisik Anda tetap dipakai oleh BabelSync (device input default sistem) — jangan set mic fisik sebagai input Zoom, nanti lawan bicara dengar dua suara.
Belum install VB-CABLE A+B? Jalankan dengan
--no-outbounduntuk memakai mode inbound saja seperti sebelumnya.
venv\Scripts\activate
python -m src.mainAtau langsung pakai launcher:
run.batOutput contoh:
====================================================================
BabelSync STT - Real-Time System Audio Transcription
====================================================================
Tekan Ctrl+C untuk berhenti.
Menjalankan whisper.cpp server (loading model)...
Membuka audio device...
Audio device : CABLE Output (VB-Audio Virtual Cable)
Sample rate : 16000 Hz (target whisper.cpp: 16000 Hz)
Whisper server : http://127.0.0.1:8090
Status : READY
[14:32:10] Halo semuanya selamat datang di presentasi hari ini (410 ms)
[14:32:14] terima kasih sudah bergabung (350 ms)
-- status -- whisper=ONLINE avg_latency=380ms segments=2 queue_depth=0
Tekan Ctrl+C kapan saja untuk berhenti — aplikasi akan menutup audio
stream, mem-flush sisa audio yang belum ditranskripsi, dan mematikan
proses whisper-server dengan rapi (tidak meninggalkan proses zombie).
python -m src.main --my-language id --remote-language en --threads 8| Argumen | Keterangan |
|---|---|
--device |
Substring device capture inbound (default "CABLE Output") |
--mic-device |
Substring microphone untuk outbound (default: mic default sistem) |
--outbound-device |
Device playback outbound (default "CABLE-A Input") |
--my-language |
Bahasa Anda (default id) |
--remote-language |
Bahasa lawan bicara (default en) |
--segment-ms |
Durasi maks satu segmen (default 2000) — pengungkit latensi utama |
--tts-speed |
Kecepatan bicara minimum terjemahan (default 1.0 = alami) |
--tts-max-speed |
Batas atas kecepatan adaptif (default 1.8) |
--monitor |
Ikut perdengarkan suara asli (default: hanya terjemahan) |
--monitor-volume |
Volume suara asli 0.0-1.0 (default 1.0), saat --monitor aktif |
--duck-volume |
Volume suara asli selagi terjemahan bicara (default 0.15) |
--list-devices |
Tampilkan semua audio device lalu keluar |
--test-audio |
Putar suara tes ke tiap output untuk cari device yang terdengar |
--no-inbound |
Matikan pipeline inbound |
--no-outbound |
Matikan pipeline outbound (mode lama, inbound saja) |
--model |
Path ke file .bin model ggml |
--server-exe |
Path ke whisper-server.exe |
--port |
Port lokal whisper-server (default 8090) |
--threads |
Jumlah thread CPU inferensi |
[App sumber suara] [VB-Audio Virtual Cable] [Python App]
YouTube / Zoom / Teams CABLE Input |
Spotify / VLC / dll. ----> (virtual speaker) |
| |
CABLE Output <--- sounddevice.InputStream
(virtual microphone) (audio_capture.py)
|
block audio ~30-50ms
|
v
SpeechSegmenter (vad.py)
deteksi awal/akhir ucapan
berbasis energi RMS
|
segmen ucapan lengkap
|
v
WhisperClient (whisper_client.py)
POST WAV in-memory ke
whisper-server (HTTP lokal)
|
whisper-server (subprocess
persisten, model sudah
di-load di memory)
|
hasil teks JSON
|
v
ConsoleDisplay (display.py)
cetak teks + timestamp +
waktu proses ke terminal
Threading yang berjalan paralel (lihat transcriber.py):
- Thread capture (dikelola PortAudio) — terus menangkap block audio
kecil tanpa henti, dorong ke
queue_raw_audio. - Thread segmenter — konsumsi queue tersebut, jalankan VAD, dan begitu 1 ucapan selesai (hening terdeteksi atau batas durasi maks tercapai), submit segmen ke thread pool inferensi.
- Thread pool whisper (2 worker) — kirim HTTP request ke whisper-server, cetak hasil begitu balasan diterima. Karena menggunakan pool, capture & segmentasi ucapan berikutnya tidak pernah menunggu hasil transkripsi sebelumnya selesai.
- Thread status — cetak baris status berkala (device, whisper ONLINE/DOWN, rata-rata latency, jumlah segmen, kedalaman queue).
Target: < 2 detik dari akhir ucapan sampai suara terjemahan mulai berbunyi. Terukur pada Intel Ultra 7 155H: rata-rata 1.01s (whisper 0.53s + translate 0.15s + TTS 0.22s).
Empat hal yang paling menentukan, semuanya sudah diterapkan by default:
-
audio_ctx = 768— pengungkit terbesar. Konteks encoder penuh (1500) setara 30 detik audio, padahal segmen kita maksimal 3.5 detik, jadi sisanya diproses percuma. Terukur pada 16 segmen podcast identik: penuh = 1.53s rata-rata,768= 0.79s dengan puncak jauh lebih stabil (1.33s vs 2.58s).Jangan tergoda menyetel
audio_ctxotomatis per durasi segmen. Sudah dicoba dan lebih buruk: nilai terlalu kecil membuat whisper kehilangan keyakinan lalu mengulang inferensi dengan temperature lebih tinggi, sehingga puncaknya melonjak ke 4.59s dan segmen ikut hilang. -
max_segment_ms = 3500+ pemotongan di titik hening (cut_search_ms). Waktu inferensi whisper.cpp hampir konstan berapa pun panjang audionya (0.5 detik audio = 1.57s, 8 detik audio = 0.87s), karena input selalu di-padding ke 30 detik. Jadi segmen pendek tidak menghemat apa pun — ia hanya merusak konteks kalimat. Segmen panjang justru lebih efisien; latensi tetap rendah karena potongan hampir selalu dipicu jeda alami pembicara, bukan batas waktu. -
threads = 12(dulu 4) — terukur di CPU 16-core: 4 thread = 1.03s, 8 = 0.82s, 12 = 0.63s, 16 = 0.66s (mulai jenuh). -
Session HTTP persisten untuk Google Translate & Google TTS — menghindari handshake TLS tiap segmen. Terukur translate 1.17s -> 0.09s dan TTS 1.58s -> 0.27s. Ini penghematan terbesar kedua setelah
max_segment_ms. -
Kecepatan bicara adaptif + anggaran waktu — terjemahan Indonesia terukur 1.6x lebih panjang dari ucapan Inggris aslinya, dan pada kalimat panjang bisa 2-2.5x. Tanpa dipercepat, tiap segmen menambah utang waktu sampai antrean meluap dan kalimat dibuang diam-diam — terukur 26% kalimat tidak pernah berbunyi.
Tiga setelan yang menyelesaikannya (terukur turun ke 0% dibuang):
tts_max_speed = 2.0— batas 1.8 terus mentok sehingga audio tetap lebih panjang dari sumbernya.tts_time_budget = 0.85— target durasi playback dibuat lebih pendek dari ucapan aslinya. Menargetkan pas 1:1 tidak menyisakan kelonggaran sama sekali, sehingga antrean tidak pernah sempat terkuras.tts_queue_depth = 2dantts_max_staleness_sec = 4.0— pada kedalaman 1, kalimat baru langsung menendang kalimat sebelumnya sebelum sempat berbunyi. Pipeline sendiri sudah memakan ~1.4 detik, jadi ambang basi 2.5 detik membuang kalimat yang masih relevan.
Kalau suaranya terasa terlalu cepat, turunkan
--tts-max-speed 1.6; konsekuensinya sebagian kalimat akan kembali dibuang. Kalau justru masih ada yang hilang, naikkan--tts-max-speed 2.2.Percepatan memakai WSOLA time-stretch (
tts_player._wsola), bukan resampling — nada suara tetap sama persis (diuji: sinus 220 Hz dipercepat 1.6x tetap 220 Hz). Resampling biasa akan menaikkan pitch ~8 semitone pada rasio segini dan terdengar seperti chipmunk. Biaya prosesnya hanya ~50 ms per segmen.
Kalau terjemahan terasa terpotong-potong, naikkan
--segment-ms 3000(konteks lebih utuh, latensi naik ~1 detik).
- Gunakan model kecil:
baseatausmalljauh lebih cepat darimedium/largedi CPU. Kalau akurasi bahasa Indonesia kurang memuaskan denganbase, cobasmalldulu sebelum lompat ke model besar. - Build whisper.cpp dengan akselerasi hardware: AVX2 (default di
sebagian besar CPU modern) atau, idealnya, CUDA jika ada GPU
NVIDIA (
-DGGML_CUDA=ONsaatcmake). Ini bisa memangkas waktu inferensi beberapa kali lipat. - Sesuaikan
threadsdiconfig.pymendekati jumlah core fisik CPU (bukan logical/hyperthreaded) — biasanya sweet spot di 4-8. - Tuning VAD (
vad.py/VADConfig):silence_hangover_mslebih kecil → segmen "dipotong" lebih cepat setelah orang berhenti bicara → hasil tampil lebih cepat, tapi terlalu kecil bisa memotong ucapan yang masih menggantung (koma).max_segment_msmembatasi durasi maksimum 1 segmen supaya orang yang bicara panjang tanpa jeda tetap dapat transkripsi bertahap, bukan menunggu sampai dia diam total.
- whisper-server persisten (bukan spawn ulang per segmen) adalah
optimasi terbesar — sudah diterapkan by design di
whisper_client.py. - In-memory WAV — audio dikonversi ke WAV di RAM (
io.BytesIO), bukan ditulis ke file disk dulu, untuk menghindari overhead I/O. - Auto-mute / drop block terlama saat queue penuh (di
audio_capture.py) mencegah latency menumpuk tak terbatas jika inferensi sempat lebih lambat dari real-time.
| Situasi | Perilaku aplikasi |
|---|---|
| VB-Cable belum terinstall / device tidak ditemukan | AudioDeviceError dengan pesan jelas + daftar device input yang tersedia, program berhenti sebelum mulai capture |
whisper-server.exe tidak ditemukan di path yang dikonfigurasi |
WhisperServerError dengan saran cek path/instalasi, program tidak lanjut |
File model .bin tidak ditemukan |
WhisperServerError dengan saran cara download model |
whisper-server gagal start / crash sebelum siap |
WhisperServerError, program berhenti dengan pesan detail |
whisper-server crash saat sedang berjalan |
Request HTTP berikutnya gagal → otomatis mencoba restart server (maks max_restart_attempts kali, default 3) sebelum menyerah pada segmen tersebut; aplikasi tetap berjalan untuk segmen selanjutnya |
| Satu request transkripsi gagal/timeout | Segmen tersebut dilewati (error dicetak), tapi aplikasi tidak crash — capture & segmen berikutnya tetap jalan |
| Ctrl+C ditekan | Audio stream ditutup, sisa buffer di-flush, whisper-server dimatikan dengan terminate()/kill(), ringkasan jumlah segmen dicetak |
Bahasa dikonfigurasi per arah (lihat DirectionConfig di
src/config.py):
- Inbound:
source_language="en"(bahasa lawan bicara) →target_language="id"(yang Anda dengar). - Outbound:
source_language="id"(yang Anda ucapkan) →target_language="en"(yang lawan bicara dengar).
Whisper dipaksa ke bahasa sumber per-request (lebih cepat & stabil
daripada auto-detect). Set source_language="auto" di config jika ingin
whisper mendeteksi bahasa otomatis per segmen (mis. code-switching).
Ubah cepat lewat CLI: --my-language id --remote-language en.
- Tidak ada suara yang tertranskripsi sama sekali: cek Volume Mixer
Windows — pastikan aplikasi sumber suara benar diarahkan ke
CABLE Input, dan level volume aplikasi tersebut tidak 0. - Semua block dianggap "hening" padahal ada suara: turunkan
silence_rms_thresholddiVADConfig. - Kata pertama tiap kalimat sering terpotong: naikkan
pre_speech_padding_ms. - Transkrip terpotong-potong walau orang tidak sedang jeda panjang:
naikkan
max_segment_msdan/atausilence_hangover_ms. - Server whisper.cpp lambat sekali (>3-5 detik per segmen): coba
model lebih kecil, build dengan CUDA, atau turunkan
threadsbila CPU Anda sedikit core (thread berlebih justru bisa memperlambat karena overhead context-switch).