A fully local, real-time speech interpretation server for MLX, CUDA, and CPU devices, wire-compatible with the OpenAI Realtime Translation API (/v1/realtime/translations).
open-realtime-translate turns live speech into translated speech and transcripts on the user's machine. A Realtime client can point to the local server in place of a hosted API endpoint: audio is received, transcribed, translated, and synthesized on-device, while API keys and hosted audio services stay outside the runtime path. Apple Silicon uses the MLX profile; NVIDIA systems can use CUDA; CPU mode provides a smaller, portable baseline.
| Capability | Meaning |
|---|---|
| Real-time interpretation | Translated audio and text begin streaming while the source utterance is still unfolding |
| OpenAI wire compatibility | WebSocket and WebRTC clients can reuse the Realtime translation protocol by changing the base URL |
| Cross-lingual voice cloning | The Qwen3-TTS backend can speak translated text in a reference speaker's voice |
| Local privacy boundary | Speech, transcripts, and model inference remain on the machine |
| Pluggable model stages | ASR, translation, and TTS are independently selectable |
| Latency/quality control | Incremental mode favors low latency; whole-utterance mode favors complete-sentence accuracy |
The system composes language coverage across three stages. Source speech is recognized by the active ASR backend, translated by the active MT backend, and rendered by the active TTS backend. Source language is detected per audio span, with an optional session-level ASR hint at session.audio.input.transcription.language; target language defaults to en and is changed per session with session.audio.output.language within the active backend's range.
| Stage | Decided by | Coverage |
|---|---|---|
| Input speech | Active ASR backend | Qwen3-ASR on MLX/CUDA: 30 languages + 22 Chinese dialects; optional Whisper: about 99 languages |
| Translation | TranslateGemma | 55 languages (ISO 639-1) |
| Output speech | Active TTS backend | Qwen3-TTS 10 · Kokoro 8 · Supertonic-3 31 · say system voices |
Usable speech-to-speech pairs are the intersection of these stage-level constraints; the exact tables live in ort/languages.py. Multilingual translation is handled by the default translategemma backend.
Input languages (Qwen3-ASR, auto-detected)
| Code | Language | Code | Language | Code | Language | Code | Language |
|---|---|---|---|---|---|---|---|
| zh | Chinese | en | English | yue | Cantonese | ar | Arabic |
| de | German | fr | French | es | Spanish | pt | Portuguese |
| id | Indonesian | it | Italian | ko | Korean | ru | Russian |
| th | Thai | vi | Vietnamese | ja | Japanese | tr | Turkish |
| hi | Hindi | ms | Malay | nl | Dutch | sv | Swedish |
| da | Danish | fi | Finnish | pl | Polish | cs | Czech |
| fil | Filipino | fa | Persian | el | Greek | hu | Hungarian |
| mk | Macedonian | ro | Romanian |
Output languages (depends on the TTS backend)
| TTS backend | # | Languages |
|---|---|---|
qwen3_tts (default on MLX/CUDA, voice cloning) | 10 | English, Chinese, Japanese, Korean, French, German, Italian, Portuguese, Russian, Spanish |
kokoro | 8 | English, Chinese, Japanese, Spanish, French, Hindi, Italian, Portuguese (BR) |
supertonic (default on CPU) | 31 | en, ko, ja, ar, bg, cs, da, de, el, es, et, fi, fr, hi, hr, hu, id, it, lt, lv, nl, pl, pt, ro, ru, sk, sl, sv, tr, uk, vi |
say | many | macOS system voices; quality and language coverage depend on the installed voices |
End-to-end checks have covered the default Qwen3-TTS path on zh→en, en→es, and zh→ja. The optional Kokoro path has also been checked across zh→ja, en→es, es→fr, en→zh, zh→hi, and ja→en.
The following measurements were taken on an Apple M5 Pro with model files already cached. They describe a verified MLX reference profile and are intended as local reference points, not service-level guarantees. CUDA and CPU throughput depends strongly on the selected ASR model, GPU memory, Torch dtype, and TTS backend.
MLX reference measurement (Qwen3-ASR-0.6B → TranslateGemma-4b-it → Qwen3-TTS-12Hz-1.7B-Base):
| Metric | Value |
|---|---|
Per committed span, zh→en end-to-end | ≈ 2.8 s |
| Stage latency (ASR / MT / TTS) | ≈ 0.16 s / 0.53 s / 2.09 s |
| Resident memory (full stack) | ≈ 7.1 GB |
| Speaker similarity to reference voice | cosine ≈ 0.98, including cross-lingual output |
| Startup | use --warmup to preload models and compile first-use kernels |
TTS backends compared (cold start, resident memory, real-time factor; lower RTF is faster):
| Backend | Cold start | Resident | RTF | Notes |
|---|---|---|---|---|
qwen3_tts (MLX/CUDA default) | 1.9 s (warm 3.8 s) | 3.5 GB | 0.33–0.62 | Voice cloning with cross-lingual timbre transfer |
kokoro | 2.6 s | 2.0 GB | 0.09–0.11 | Fastest and lightest neural TTS path |
supertonic | 0.3 s | 0.6 GB | 0.19–0.31 | Smallest footprint and broadest TTS language set |
Kokoro remains available when the priority is minimum latency or a smaller resident footprint. Qwen3-TTS is the default because it gives the strongest voice identity behavior and carries the translation across languages in the reference speaker's timbre.
Accuracy is strongest for conversational speech whose meaning unfolds in sequence. Very long single sentences with cross-clause reordering can fragment in incremental mode. For maximum accuracy, use whole-utterance mode (ORT_INCREMENTAL=0), which exchanges latency for complete-sentence context.
flowchart LR
client["Realtime client"]
mic["Live speech"]
ws["WebSocket<br/>base64 PCM16"]
rtc["WebRTC<br/>Opus audio track"]
decode["Decode + resample<br/>24 kHz wire -> 16 kHz internal"]
vad["VAD + silence tracking"]
commit{"Commit span?"}
queue["Ordered span queue"]
runtime{"Runtime profile<br/>auto / mlx / cuda / cpu"}
mlx["MLX profile<br/>Qwen3-ASR + TranslateGemma MLX + Qwen3-TTS MLX"]
cuda["CUDA profile<br/>Qwen3-ASR Torch + TranslateGemma Torch + Qwen3-TTS Torch"]
cpu["CPU profile<br/>Whisper CPU + TranslateGemma Torch + Supertonic-3"]
asr["ASR<br/>source speech -> source text"]
context["Committed context<br/>source -> target history"]
mt["Translate<br/>source text -> target text"]
ref["Rolling voice reference<br/>latest ASR audio + transcript<br/>0-10 seconds"]
pad["TTS handoff padding<br/>pad copy to 3 seconds if needed"]
tts["TTS / voice clone<br/>target text -> target speech"]
audio["Translated speech<br/>24 kHz PCM16 / Opus"]
text["Transcript events<br/>source + target deltas"]
mic --> ws
mic --> rtc
ws --> decode
rtc --> decode
decode --> vad
vad --> commit
commit -- "short pause or max span" --> queue
commit -- "keep listening" --> vad
runtime -- "Apple Silicon / ORT_RUNTIME=mlx" --> mlx
runtime -- "ORT_RUNTIME=cuda" --> cuda
runtime -- "ORT_RUNTIME=cpu" --> cpu
mlx -.-> asr
mlx -.-> mt
mlx -.-> tts
cuda -.-> asr
cuda -.-> mt
cuda -.-> tts
cpu -.-> asr
cpu -.-> mt
cpu -.-> tts
queue --> asr
asr --> ref
asr --> context
context --> mt
asr --> text
mt --> text
mt --> tts
ref --> pad
pad --> tts
tts --> audio
audio --> client
text --> client
The processing path is deliberately staged. Audio capture and network I/O can continue while inference runs. MLX model initialization and inference are serialized on a dedicated Apple Silicon thread; CUDA and CPU backends use their native PyTorch, CTranslate2, or ONNX execution paths. The incremental committer finalizes short spans at clause-boundary pauses, which lets the server speak before a long source sentence has fully ended.
Model stack
| Runtime | ASR default | Translation default | TTS default | Notes |
|---|---|---|---|---|
mlx | Qwen3-ASR-1.7B through mlx-audio | TranslateGemma-4b-it through mlx-lm | Qwen3-TTS-12Hz-1.7B-Base through mlx-audio | Default when ORT_RUNTIME=auto runs on Apple Silicon |
cuda | Qwen3-ASR-1.7B through qwen-asr | TranslateGemma-4b-it through Transformers/Torch | Qwen3-TTS-12Hz-1.7B-Base through qwen-tts | Set ORT_RUNTIME=cuda; use ORT_DEVICE=cuda:N for a specific GPU |
cpu | Whisper through faster-whisper/CTranslate2 CPU | TranslateGemma-4b-it through Transformers/Torch | Supertonic-3 through ONNX | Default when ORT_RUNTIME=auto runs outside Apple Silicon |
All profiles keep the same VAD, segmentation, OpenAI-compatible events, and context-aware translation loop. Individual stages remain replaceable with ORT_ASR_BACKEND, ORT_TRANSLATOR_BACKEND, and ORT_TTS_BACKEND; mock backends are reserved for protocol tests.
Transport
| Transport | Endpoint | Notes |
|---|---|---|
| WebSocket | /v1/realtime/translations | JSON events + base64 PCM16 audio |
| WebRTC | …/client_secrets → …/calls | SDP offer/answer; bidirectional Opus audio track; events over the oai-events DataChannel |
Wire-contract details are documented in PROTOCOL.md.
The qwen3_tts backend uses Qwen3-TTS-12Hz-1.7B-Base to synthesize each translation in the current source speaker's voice. Each session starts with an empty reference buffer. After ASR succeeds for a committed span, the server appends that span's source audio and ASR transcript to a rolling voice reference. The buffer itself stores only real ASR audio and is capped at the newest 10 s. On each TTS call, the backend makes a TTS-ready copy of the latest reference; only that copy is right-padded to 3 s when the real reference is shorter. The output language may differ from the reference language. This backend is the default on MLX and CUDA; CPU mode defaults to Supertonic-3 because it is smaller and does not require voice cloning.
# Optional: provide a static reference for direct backend calls or warmup
ORT_TTS_BACKEND=qwen3_tts \
ORT_QWEN3_TTS_REF_AUDIO=/path/to/voice.wav \
ORT_QWEN3_TTS_REF_TEXT="exact transcript of the reference clip" \
uv run --extra mlx python -m ort.app --warmup
Use --extra nonmlx for the CUDA implementation. In normal translation sessions, explicit reference files are no longer required because the source speaker reference comes from the ASR input itself. A clean microphone signal and accurate ASR transcript improve the clone quality.
| Item | Requirement / notes |
|---|---|
| Runtime | Apple Silicon MLX, NVIDIA CUDA, or CPU |
| Memory | 32 GB recommended for the verified MLX stack (≈ 7.1 GB resident); CPU/CUDA sizing depends on model and dtype |
| Package manager | uv; project pins Python 3.12 |
ffmpeg | Audio decoding and optional static reference-voice conversion |
espeak-ng | Optional Kokoro multilingual phonemization support: brew install espeak-ng |
macOS say | Lightweight system TTS when ORT_TTS_BACKEND=say |
| Hugging Face access | Required for gated Gemma/TranslateGemma weights; accept the model terms before the first Torch run |
| Model download | ≈ 5 GB on first default run; offline afterward |
| Kokoro Japanese output | Optional, Kokoro only: one-time uv run python -m unidic download (≈ 526 MB) |
| OpenAI key | Not required |
# 1. Install dependencies (creates .venv, Python 3.12)
# Apple Silicon / MLX:
uv sync --extra dev --extra mlx
# 2. Start the server (--warmup preloads models before the first connection)
uv run --extra mlx python -m ort.app --host 127.0.0.1 --port 8000 --warmup
# 3. Translate a Chinese sentence into English speech in out.wav
uv run --extra mlx python examples/client.py --text "今天天氣很好,謝謝你。" --out out.wav
Point an OpenAI Realtime client at:
ws://127.0.0.1:8000/v1/realtime/translations
For CUDA or CPU, install the non-MLX profile instead:
# CUDA: Qwen3-ASR Torch + TranslateGemma Torch + Qwen3-TTS Torch
uv sync --extra dev --extra nonmlx
ORT_RUNTIME=cuda uv run --extra nonmlx python -m ort.app --host 127.0.0.1 --port 8000 --warmup
# CPU: Whisper CPU + TranslateGemma Torch + Supertonic-3
uv sync --extra dev --extra nonmlx
ORT_RUNTIME=cpu uv run --extra nonmlx python -m ort.app --host 127.0.0.1 --port 8000 --warmup
The mlx and nonmlx extras are intentionally mutually exclusive. The MLX stack and the non-MLX Qwen packages currently resolve to different Transformers / Hugging Face dependency ranges, so each runtime profile should have its own environment.
A Gradio UI for recording from the microphone or uploading a clip, viewing the source and translated transcripts, and playing back the translated speech:
uv sync --extra demo --extra mlx # one-time: install Gradio for the MLX profile
uv run --extra mlx python -m ort.app --warmup & # start the server
uv run --extra demo python examples/gradio_demo.py # open the printed local URL
Use --extra nonmlx in place of --extra mlx when the demo should run against CUDA or CPU.
| Variable | Default | Values / notes |
|---|---|---|
ORT_RUNTIME | auto | auto / mlx / cuda / cpu; auto selects MLX on Apple Silicon and CPU elsewhere |
ORT_DEVICE | auto | mlx, cpu, cuda, or cuda:N; explicit CUDA devices imply ORT_RUNTIME=cuda when runtime is auto |
ORT_ASR_BACKEND | auto | qwen3 on MLX/CUDA; whisper on CPU; may be set to qwen3 / whisper / mock |
ORT_QWEN_ASR_MODEL | Qwen/Qwen3-ASR-1.7B | 0.6b / 1.7b / full HF repo id |
ORT_QWEN_ASR_DEVICE | auto | Non-MLX Qwen3-ASR device override; defaults to ORT_DEVICE |
ORT_QWEN_ASR_MAX_NEW_TOKENS | 256 | Qwen3-ASR generation cap for committed spans |
ORT_TRANSLATOR_BACKEND | auto | translategemma / mock; MLX uses ORT_TRANSLATEGEMMA_MODEL, CUDA/CPU use ORT_TRANSLATEGEMMA_TORCH_MODEL |
ORT_TRANSLATEGEMMA_MODEL | mlx-community/translategemma-4b-it-4bit | MLX TranslateGemma repo id |
ORT_TRANSLATEGEMMA_TORCH_MODEL | google/translategemma-4b-it | Transformers/Torch TranslateGemma repo id; requires Gemma terms access |
ORT_TTS_BACKEND | auto | qwen3_tts on MLX/CUDA; supertonic on CPU; may be set to qwen3_tts / kokoro / supertonic / say / mock |
ORT_KOKORO_VOICE | af_heart | Kokoro voice, applied when ORT_TTS_BACKEND=kokoro |
ORT_QWEN3_TTS_MODEL | 1.7b | 0.6b / 1.7b / full HF repo id |
ORT_QWEN3_TTS_REF_AUDIO | empty | Optional static reference WAV for direct Qwen3-TTS calls or warmup; sessions normally use rolling ASR audio |
ORT_QWEN3_TTS_REF_TEXT | empty | Exact transcript of the optional static reference clip |
ORT_SUPERTONIC_VOICE | F1 | Supertonic voice M1–M5 or F1–F5 |
ORT_WHISPER_MODEL | small | faster-whisper model size or HF repo id |
ORT_WHISPER_DEVICE | auto | cuda under CUDA runtime, otherwise cpu |
ORT_WHISPER_DEVICE_INDEX | auto | CTranslate2 CUDA device index; inferred from ORT_DEVICE=cuda:N |
ORT_WHISPER_COMPUTE | auto | float16 on CUDA, int8 on CPU unless overridden |
ORT_TORCH_DTYPE | auto | bfloat16 on CUDA, float32 on CPU; also accepts Torch dtype names such as float16 |
ORT_TORCH_ATTN | auto | sdpa on CUDA, eager on CPU; set none to omit the argument |
ORT_TORCH_MAX_NEW_TOKENS | 256 | TranslateGemma generation cap |
ORT_INCREMENTAL | 1 | 0 = whole-utterance mode |
ORT_COMMIT_SILENCE_MS | 300 | Incremental mode: clause-pause commit threshold |
ORT_MAX_COMMIT_MS | 3500 | Incremental mode: forced commit cap when no pause is detected |
Use lighter ASR/TTS around the default translator:
ORT_ASR_BACKEND=whisper ORT_TTS_BACKEND=say \
uv run --extra mlx python -m ort.app --warmup
| Command | Scope |
|---|---|
uv run --extra dev pytest -m "not e2e" | Protocol, runtime-resolution, and processing-stage tests with mock backends; no model downloads |
uv run --extra dev --extra mlx pytest -m e2e | End-to-end zh→en on the verified MLX profile with real model downloads |
uv run --extra dev pytest | Complete non-e2e suite in the active environment |
Backend benchmarks live in scripts/, including bench_comprehensive.py for the full-stack and TTS comparison, and verify_qwen3_tts.py for voice-clone fidelity.
Third-party model weights remain governed by their publishers' terms. Review those terms before redistribution, hosted use, or commercial deployment. License metadata below was checked against the linked public model pages on 2026-05-19.
| Model / backend | Used for | Runtime source | License / terms |
|---|---|---|---|
| Qwen3-ASR-1.7B / 0.6B | MLX/CUDA ASR default and optional smaller ASR | Qwen/Qwen3-ASR-1.7B, loaded directly through qwen-asr or through MLX community mirrors | Apache-2.0 |
| TranslateGemma-4b-it | Translation default across profiles | google/translategemma-4b-it, loaded directly on Torch or as mlx-community/translategemma-4b-it-4bit | Google Gemma terms (gemma); see Gemma Terms of Use |
| Qwen3-TTS-12Hz-0.6B / 1.7B Base | MLX/CUDA voice-cloning TTS default | Qwen/Qwen3-TTS-12Hz-1.7B-Base, loaded directly through qwen-tts or through MLX community mirrors | Apache-2.0 |
| Kokoro-82M | Optional fast TTS | hexgrad/Kokoro-82M, via the kokoro package | Apache-2.0 |
| Supertonic-3 | CPU TTS default and optional multilingual TTS | Supertone/supertonic-3, via the supertonic package | BigScience OpenRAIL-M (openrail) |
| Whisper / faster-whisper | CPU ASR default and optional ASR | OpenAI Whisper weights, commonly loaded as Systran/faster-whisper-small | MIT |
macOS say voices | Optional system TTS | Apple system voices installed with macOS | Governed by the macOS software license; not redistributed by this project |
mock backends | Tests and protocol development | No model weights | Not applicable |
Permissive licenses such as Apache-2.0 and MIT still require preserving notices. Gemma and OpenRAIL-M terms include additional use and redistribution conditions.
4 commits
Python
100.0%
A fully local, real-time speech interpretation server for MLX, CUDA, and CPU devices, wire-compatible with the OpenAI Realtime Translation API (/v1/realtime/translations).
open-realtime-translate turns live speech into translated speech and transcripts on the user's machine. A Realtime client can point to the local server in place of a hosted API endpoint: audio is received, transcribed, translated, and synthesized on-device, while API keys and hosted audio services stay outside the runtime path. Apple Silicon uses the MLX profile; NVIDIA systems can use CUDA; CPU mode provides a smaller, portable baseline.
| Capability | Meaning |
|---|---|
| Real-time interpretation | Translated audio and text begin streaming while the source utterance is still unfolding |
| OpenAI wire compatibility | WebSocket and WebRTC clients can reuse the Realtime translation protocol by changing the base URL |
| Cross-lingual voice cloning | The Qwen3-TTS backend can speak translated text in a reference speaker's voice |
| Local privacy boundary | Speech, transcripts, and model inference remain on the machine |
| Pluggable model stages | ASR, translation, and TTS are independently selectable |
| Latency/quality control | Incremental mode favors low latency; whole-utterance mode favors complete-sentence accuracy |
The system composes language coverage across three stages. Source speech is recognized by the active ASR backend, translated by the active MT backend, and rendered by the active TTS backend. Source language is detected per audio span, with an optional session-level ASR hint at session.audio.input.transcription.language; target language defaults to en and is changed per session with session.audio.output.language within the active backend's range.
| Stage | Decided by | Coverage |
|---|---|---|
| Input speech | Active ASR backend | Qwen3-ASR on MLX/CUDA: 30 languages + 22 Chinese dialects; optional Whisper: about 99 languages |
| Translation | TranslateGemma | 55 languages (ISO 639-1) |
| Output speech | Active TTS backend | Qwen3-TTS 10 · Kokoro 8 · Supertonic-3 31 · say system voices |
Usable speech-to-speech pairs are the intersection of these stage-level constraints; the exact tables live in ort/languages.py. Multilingual translation is handled by the default translategemma backend.
Input languages (Qwen3-ASR, auto-detected)
| Code | Language | Code | Language | Code | Language | Code | Language |
|---|---|---|---|---|---|---|---|
| zh | Chinese | en | English | yue | Cantonese | ar | Arabic |
| de | German | fr | French | es | Spanish | pt | Portuguese |
| id | Indonesian | it | Italian | ko | Korean | ru | Russian |
| th | Thai | vi | Vietnamese | ja | Japanese | tr | Turkish |
| hi | Hindi | ms | Malay | nl | Dutch | sv | Swedish |
| da | Danish | fi | Finnish | pl | Polish | cs | Czech |
| fil | Filipino | fa | Persian | el | Greek | hu | Hungarian |
| mk | Macedonian | ro | Romanian |
Output languages (depends on the TTS backend)
| TTS backend | # | Languages |
|---|---|---|
qwen3_tts (default on MLX/CUDA, voice cloning) | 10 | English, Chinese, Japanese, Korean, French, German, Italian, Portuguese, Russian, Spanish |
kokoro | 8 | English, Chinese, Japanese, Spanish, French, Hindi, Italian, Portuguese (BR) |
supertonic (default on CPU) | 31 | en, ko, ja, ar, bg, cs, da, de, el, es, et, fi, fr, hi, hr, hu, id, it, lt, lv, nl, pl, pt, ro, ru, sk, sl, sv, tr, uk, vi |
say | many | macOS system voices; quality and language coverage depend on the installed voices |
End-to-end checks have covered the default Qwen3-TTS path on zh→en, en→es, and zh→ja. The optional Kokoro path has also been checked across zh→ja, en→es, es→fr, en→zh, zh→hi, and ja→en.
The following measurements were taken on an Apple M5 Pro with model files already cached. They describe a verified MLX reference profile and are intended as local reference points, not service-level guarantees. CUDA and CPU throughput depends strongly on the selected ASR model, GPU memory, Torch dtype, and TTS backend.
MLX reference measurement (Qwen3-ASR-0.6B → TranslateGemma-4b-it → Qwen3-TTS-12Hz-1.7B-Base):
| Metric | Value |
|---|---|
Per committed span, zh→en end-to-end | ≈ 2.8 s |
| Stage latency (ASR / MT / TTS) | ≈ 0.16 s / 0.53 s / 2.09 s |
| Resident memory (full stack) | ≈ 7.1 GB |
| Speaker similarity to reference voice | cosine ≈ 0.98, including cross-lingual output |
| Startup | use --warmup to preload models and compile first-use kernels |
TTS backends compared (cold start, resident memory, real-time factor; lower RTF is faster):
| Backend | Cold start | Resident | RTF | Notes |
|---|---|---|---|---|
qwen3_tts (MLX/CUDA default) | 1.9 s (warm 3.8 s) | 3.5 GB | 0.33–0.62 | Voice cloning with cross-lingual timbre transfer |
kokoro | 2.6 s | 2.0 GB | 0.09–0.11 | Fastest and lightest neural TTS path |
supertonic | 0.3 s | 0.6 GB | 0.19–0.31 | Smallest footprint and broadest TTS language set |
Kokoro remains available when the priority is minimum latency or a smaller resident footprint. Qwen3-TTS is the default because it gives the strongest voice identity behavior and carries the translation across languages in the reference speaker's timbre.
Accuracy is strongest for conversational speech whose meaning unfolds in sequence. Very long single sentences with cross-clause reordering can fragment in incremental mode. For maximum accuracy, use whole-utterance mode (ORT_INCREMENTAL=0), which exchanges latency for complete-sentence context.
flowchart LR
client["Realtime client"]
mic["Live speech"]
ws["WebSocket<br/>base64 PCM16"]
rtc["WebRTC<br/>Opus audio track"]
decode["Decode + resample<br/>24 kHz wire -> 16 kHz internal"]
vad["VAD + silence tracking"]
commit{"Commit span?"}
queue["Ordered span queue"]
runtime{"Runtime profile<br/>auto / mlx / cuda / cpu"}
mlx["MLX profile<br/>Qwen3-ASR + TranslateGemma MLX + Qwen3-TTS MLX"]
cuda["CUDA profile<br/>Qwen3-ASR Torch + TranslateGemma Torch + Qwen3-TTS Torch"]
cpu["CPU profile<br/>Whisper CPU + TranslateGemma Torch + Supertonic-3"]
asr["ASR<br/>source speech -> source text"]
context["Committed context<br/>source -> target history"]
mt["Translate<br/>source text -> target text"]
ref["Rolling voice reference<br/>latest ASR audio + transcript<br/>0-10 seconds"]
pad["TTS handoff padding<br/>pad copy to 3 seconds if needed"]
tts["TTS / voice clone<br/>target text -> target speech"]
audio["Translated speech<br/>24 kHz PCM16 / Opus"]
text["Transcript events<br/>source + target deltas"]
mic --> ws
mic --> rtc
ws --> decode
rtc --> decode
decode --> vad
vad --> commit
commit -- "short pause or max span" --> queue
commit -- "keep listening" --> vad
runtime -- "Apple Silicon / ORT_RUNTIME=mlx" --> mlx
runtime -- "ORT_RUNTIME=cuda" --> cuda
runtime -- "ORT_RUNTIME=cpu" --> cpu
mlx -.-> asr
mlx -.-> mt
mlx -.-> tts
cuda -.-> asr
cuda -.-> mt
cuda -.-> tts
cpu -.-> asr
cpu -.-> mt
cpu -.-> tts
queue --> asr
asr --> ref
asr --> context
context --> mt
asr --> text
mt --> text
mt --> tts
ref --> pad
pad --> tts
tts --> audio
audio --> client
text --> client
The processing path is deliberately staged. Audio capture and network I/O can continue while inference runs. MLX model initialization and inference are serialized on a dedicated Apple Silicon thread; CUDA and CPU backends use their native PyTorch, CTranslate2, or ONNX execution paths. The incremental committer finalizes short spans at clause-boundary pauses, which lets the server speak before a long source sentence has fully ended.
Model stack
| Runtime | ASR default | Translation default | TTS default | Notes |
|---|---|---|---|---|
mlx | Qwen3-ASR-1.7B through mlx-audio | TranslateGemma-4b-it through mlx-lm | Qwen3-TTS-12Hz-1.7B-Base through mlx-audio | Default when ORT_RUNTIME=auto runs on Apple Silicon |
cuda | Qwen3-ASR-1.7B through qwen-asr | TranslateGemma-4b-it through Transformers/Torch | Qwen3-TTS-12Hz-1.7B-Base through qwen-tts | Set ORT_RUNTIME=cuda; use ORT_DEVICE=cuda:N for a specific GPU |
cpu | Whisper through faster-whisper/CTranslate2 CPU | TranslateGemma-4b-it through Transformers/Torch | Supertonic-3 through ONNX | Default when ORT_RUNTIME=auto runs outside Apple Silicon |
All profiles keep the same VAD, segmentation, OpenAI-compatible events, and context-aware translation loop. Individual stages remain replaceable with ORT_ASR_BACKEND, ORT_TRANSLATOR_BACKEND, and ORT_TTS_BACKEND; mock backends are reserved for protocol tests.
Transport
| Transport | Endpoint | Notes |
|---|---|---|
| WebSocket | /v1/realtime/translations | JSON events + base64 PCM16 audio |
| WebRTC | …/client_secrets → …/calls | SDP offer/answer; bidirectional Opus audio track; events over the oai-events DataChannel |
Wire-contract details are documented in PROTOCOL.md.
The qwen3_tts backend uses Qwen3-TTS-12Hz-1.7B-Base to synthesize each translation in the current source speaker's voice. Each session starts with an empty reference buffer. After ASR succeeds for a committed span, the server appends that span's source audio and ASR transcript to a rolling voice reference. The buffer itself stores only real ASR audio and is capped at the newest 10 s. On each TTS call, the backend makes a TTS-ready copy of the latest reference; only that copy is right-padded to 3 s when the real reference is shorter. The output language may differ from the reference language. This backend is the default on MLX and CUDA; CPU mode defaults to Supertonic-3 because it is smaller and does not require voice cloning.
# Optional: provide a static reference for direct backend calls or warmup
ORT_TTS_BACKEND=qwen3_tts \
ORT_QWEN3_TTS_REF_AUDIO=/path/to/voice.wav \
ORT_QWEN3_TTS_REF_TEXT="exact transcript of the reference clip" \
uv run --extra mlx python -m ort.app --warmup
Use --extra nonmlx for the CUDA implementation. In normal translation sessions, explicit reference files are no longer required because the source speaker reference comes from the ASR input itself. A clean microphone signal and accurate ASR transcript improve the clone quality.
| Item | Requirement / notes |
|---|---|
| Runtime | Apple Silicon MLX, NVIDIA CUDA, or CPU |
| Memory | 32 GB recommended for the verified MLX stack (≈ 7.1 GB resident); CPU/CUDA sizing depends on model and dtype |
| Package manager | uv; project pins Python 3.12 |
ffmpeg | Audio decoding and optional static reference-voice conversion |
espeak-ng | Optional Kokoro multilingual phonemization support: brew install espeak-ng |
macOS say | Lightweight system TTS when ORT_TTS_BACKEND=say |
| Hugging Face access | Required for gated Gemma/TranslateGemma weights; accept the model terms before the first Torch run |
| Model download | ≈ 5 GB on first default run; offline afterward |
| Kokoro Japanese output | Optional, Kokoro only: one-time uv run python -m unidic download (≈ 526 MB) |
| OpenAI key | Not required |
# 1. Install dependencies (creates .venv, Python 3.12)
# Apple Silicon / MLX:
uv sync --extra dev --extra mlx
# 2. Start the server (--warmup preloads models before the first connection)
uv run --extra mlx python -m ort.app --host 127.0.0.1 --port 8000 --warmup
# 3. Translate a Chinese sentence into English speech in out.wav
uv run --extra mlx python examples/client.py --text "今天天氣很好,謝謝你。" --out out.wav
Point an OpenAI Realtime client at:
ws://127.0.0.1:8000/v1/realtime/translations
For CUDA or CPU, install the non-MLX profile instead:
# CUDA: Qwen3-ASR Torch + TranslateGemma Torch + Qwen3-TTS Torch
uv sync --extra dev --extra nonmlx
ORT_RUNTIME=cuda uv run --extra nonmlx python -m ort.app --host 127.0.0.1 --port 8000 --warmup
# CPU: Whisper CPU + TranslateGemma Torch + Supertonic-3
uv sync --extra dev --extra nonmlx
ORT_RUNTIME=cpu uv run --extra nonmlx python -m ort.app --host 127.0.0.1 --port 8000 --warmup
The mlx and nonmlx extras are intentionally mutually exclusive. The MLX stack and the non-MLX Qwen packages currently resolve to different Transformers / Hugging Face dependency ranges, so each runtime profile should have its own environment.
A Gradio UI for recording from the microphone or uploading a clip, viewing the source and translated transcripts, and playing back the translated speech:
uv sync --extra demo --extra mlx # one-time: install Gradio for the MLX profile
uv run --extra mlx python -m ort.app --warmup & # start the server
uv run --extra demo python examples/gradio_demo.py # open the printed local URL
Use --extra nonmlx in place of --extra mlx when the demo should run against CUDA or CPU.
| Variable | Default | Values / notes |
|---|---|---|
ORT_RUNTIME | auto | auto / mlx / cuda / cpu; auto selects MLX on Apple Silicon and CPU elsewhere |
ORT_DEVICE | auto | mlx, cpu, cuda, or cuda:N; explicit CUDA devices imply ORT_RUNTIME=cuda when runtime is auto |
ORT_ASR_BACKEND | auto | qwen3 on MLX/CUDA; whisper on CPU; may be set to qwen3 / whisper / mock |
ORT_QWEN_ASR_MODEL | Qwen/Qwen3-ASR-1.7B | 0.6b / 1.7b / full HF repo id |
ORT_QWEN_ASR_DEVICE | auto | Non-MLX Qwen3-ASR device override; defaults to ORT_DEVICE |
ORT_QWEN_ASR_MAX_NEW_TOKENS | 256 | Qwen3-ASR generation cap for committed spans |
ORT_TRANSLATOR_BACKEND | auto | translategemma / mock; MLX uses ORT_TRANSLATEGEMMA_MODEL, CUDA/CPU use ORT_TRANSLATEGEMMA_TORCH_MODEL |
ORT_TRANSLATEGEMMA_MODEL | mlx-community/translategemma-4b-it-4bit | MLX TranslateGemma repo id |
ORT_TRANSLATEGEMMA_TORCH_MODEL | google/translategemma-4b-it | Transformers/Torch TranslateGemma repo id; requires Gemma terms access |
ORT_TTS_BACKEND | auto | qwen3_tts on MLX/CUDA; supertonic on CPU; may be set to qwen3_tts / kokoro / supertonic / say / mock |
ORT_KOKORO_VOICE | af_heart | Kokoro voice, applied when ORT_TTS_BACKEND=kokoro |
ORT_QWEN3_TTS_MODEL | 1.7b | 0.6b / 1.7b / full HF repo id |
ORT_QWEN3_TTS_REF_AUDIO | empty | Optional static reference WAV for direct Qwen3-TTS calls or warmup; sessions normally use rolling ASR audio |
ORT_QWEN3_TTS_REF_TEXT | empty | Exact transcript of the optional static reference clip |
ORT_SUPERTONIC_VOICE | F1 | Supertonic voice M1–M5 or F1–F5 |
ORT_WHISPER_MODEL | small | faster-whisper model size or HF repo id |
ORT_WHISPER_DEVICE | auto | cuda under CUDA runtime, otherwise cpu |
ORT_WHISPER_DEVICE_INDEX | auto | CTranslate2 CUDA device index; inferred from ORT_DEVICE=cuda:N |
ORT_WHISPER_COMPUTE | auto | float16 on CUDA, int8 on CPU unless overridden |
ORT_TORCH_DTYPE | auto | bfloat16 on CUDA, float32 on CPU; also accepts Torch dtype names such as float16 |
ORT_TORCH_ATTN | auto | sdpa on CUDA, eager on CPU; set none to omit the argument |
ORT_TORCH_MAX_NEW_TOKENS | 256 | TranslateGemma generation cap |
ORT_INCREMENTAL | 1 | 0 = whole-utterance mode |
ORT_COMMIT_SILENCE_MS | 300 | Incremental mode: clause-pause commit threshold |
ORT_MAX_COMMIT_MS | 3500 | Incremental mode: forced commit cap when no pause is detected |
Use lighter ASR/TTS around the default translator:
ORT_ASR_BACKEND=whisper ORT_TTS_BACKEND=say \
uv run --extra mlx python -m ort.app --warmup
| Command | Scope |
|---|---|
uv run --extra dev pytest -m "not e2e" | Protocol, runtime-resolution, and processing-stage tests with mock backends; no model downloads |
uv run --extra dev --extra mlx pytest -m e2e | End-to-end zh→en on the verified MLX profile with real model downloads |
uv run --extra dev pytest | Complete non-e2e suite in the active environment |
Backend benchmarks live in scripts/, including bench_comprehensive.py for the full-stack and TTS comparison, and verify_qwen3_tts.py for voice-clone fidelity.
Third-party model weights remain governed by their publishers' terms. Review those terms before redistribution, hosted use, or commercial deployment. License metadata below was checked against the linked public model pages on 2026-05-19.
| Model / backend | Used for | Runtime source | License / terms |
|---|---|---|---|
| Qwen3-ASR-1.7B / 0.6B | MLX/CUDA ASR default and optional smaller ASR | Qwen/Qwen3-ASR-1.7B, loaded directly through qwen-asr or through MLX community mirrors | Apache-2.0 |
| TranslateGemma-4b-it | Translation default across profiles | google/translategemma-4b-it, loaded directly on Torch or as mlx-community/translategemma-4b-it-4bit | Google Gemma terms (gemma); see Gemma Terms of Use |
| Qwen3-TTS-12Hz-0.6B / 1.7B Base | MLX/CUDA voice-cloning TTS default | Qwen/Qwen3-TTS-12Hz-1.7B-Base, loaded directly through qwen-tts or through MLX community mirrors | Apache-2.0 |
| Kokoro-82M | Optional fast TTS | hexgrad/Kokoro-82M, via the kokoro package | Apache-2.0 |
| Supertonic-3 | CPU TTS default and optional multilingual TTS | Supertone/supertonic-3, via the supertonic package | BigScience OpenRAIL-M (openrail) |
| Whisper / faster-whisper | CPU ASR default and optional ASR | OpenAI Whisper weights, commonly loaded as Systran/faster-whisper-small | MIT |
macOS say voices | Optional system TTS | Apple system voices installed with macOS | Governed by the macOS software license; not redistributed by this project |
mock backends | Tests and protocol development | No model weights | Not applicable |
Permissive licenses such as Apache-2.0 and MIT still require preserving notices. Gemma and OpenRAIL-M terms include additional use and redistribution conditions.
4 commits
Python
100.0%