JacobLinCool/open-realtime-translate

A fully local, real-time speech translation server for MLX, CUDA, and CPU devices, wire-compatible with the OpenAI Realtime Translation API.

3

stars

4

commits

Python

primary language

May 19, 2026

updated

README

open-realtime-translate

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.

Core capabilities

CapabilityMeaning
Real-time interpretationTranslated audio and text begin streaming while the source utterance is still unfolding
OpenAI wire compatibilityWebSocket and WebRTC clients can reuse the Realtime translation protocol by changing the base URL
Cross-lingual voice cloningThe Qwen3-TTS backend can speak translated text in a reference speaker's voice
Local privacy boundarySpeech, transcripts, and model inference remain on the machine
Pluggable model stagesASR, translation, and TTS are independently selectable
Latency/quality controlIncremental mode favors low latency; whole-utterance mode favors complete-sentence accuracy

Supported languages

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.

StageDecided byCoverage
Input speechActive ASR backendQwen3-ASR on MLX/CUDA: 30 languages + 22 Chinese dialects; optional Whisper: about 99 languages
TranslationTranslateGemma55 languages (ISO 639-1)
Output speechActive TTS backendQwen3-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)

CodeLanguageCodeLanguageCodeLanguageCodeLanguage
zhChineseenEnglishyueCantonesearArabic
deGermanfrFrenchesSpanishptPortuguese
idIndonesianitItaliankoKoreanruRussian
thThaiviVietnamesejaJapanesetrTurkish
hiHindimsMalaynlDutchsvSwedish
daDanishfiFinnishplPolishcsCzech
filFilipinofaPersianelGreekhuHungarian
mkMacedonianroRomanian

Output languages (depends on the TTS backend)

TTS backend#Languages
qwen3_tts (default on MLX/CUDA, voice cloning)10English, Chinese, Japanese, Korean, French, German, Italian, Portuguese, Russian, Spanish
kokoro8English, Chinese, Japanese, Spanish, French, Hindi, Italian, Portuguese (BR)
supertonic (default on CPU)31en, 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
saymanymacOS 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.

Performance

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):

MetricValue
Per committed span, zh→en end-to-end2.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 voicecosine ≈ 0.98, including cross-lingual output
Startupuse --warmup to preload models and compile first-use kernels

TTS backends compared (cold start, resident memory, real-time factor; lower RTF is faster):

BackendCold startResidentRTFNotes
qwen3_tts (MLX/CUDA default)1.9 s (warm 3.8 s)3.5 GB0.33–0.62Voice cloning with cross-lingual timbre transfer
kokoro2.6 s2.0 GB0.09–0.11Fastest and lightest neural TTS path
supertonic0.3 s0.6 GB0.19–0.31Smallest 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.

How it works

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

RuntimeASR defaultTranslation defaultTTS defaultNotes
mlxQwen3-ASR-1.7B through mlx-audioTranslateGemma-4b-it through mlx-lmQwen3-TTS-12Hz-1.7B-Base through mlx-audioDefault when ORT_RUNTIME=auto runs on Apple Silicon
cudaQwen3-ASR-1.7B through qwen-asrTranslateGemma-4b-it through Transformers/TorchQwen3-TTS-12Hz-1.7B-Base through qwen-ttsSet ORT_RUNTIME=cuda; use ORT_DEVICE=cuda:N for a specific GPU
cpuWhisper through faster-whisper/CTranslate2 CPUTranslateGemma-4b-it through Transformers/TorchSupertonic-3 through ONNXDefault 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

TransportEndpointNotes
WebSocket/v1/realtime/translationsJSON events + base64 PCM16 audio
WebRTC…/client_secrets…/callsSDP offer/answer; bidirectional Opus audio track; events over the oai-events DataChannel

Wire-contract details are documented in PROTOCOL.md.

Voice cloning

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.

Requirements

ItemRequirement / notes
RuntimeApple Silicon MLX, NVIDIA CUDA, or CPU
Memory32 GB recommended for the verified MLX stack (≈ 7.1 GB resident); CPU/CUDA sizing depends on model and dtype
Package manageruv; project pins Python 3.12
ffmpegAudio decoding and optional static reference-voice conversion
espeak-ngOptional Kokoro multilingual phonemization support: brew install espeak-ng
macOS sayLightweight system TTS when ORT_TTS_BACKEND=say
Hugging Face accessRequired 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 outputOptional, Kokoro only: one-time uv run python -m unidic download (≈ 526 MB)
OpenAI keyNot required

Quick start

# 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.

Browser demo

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.

Configuration

VariableDefaultValues / notes
ORT_RUNTIMEautoauto / mlx / cuda / cpu; auto selects MLX on Apple Silicon and CPU elsewhere
ORT_DEVICEautomlx, cpu, cuda, or cuda:N; explicit CUDA devices imply ORT_RUNTIME=cuda when runtime is auto
ORT_ASR_BACKENDautoqwen3 on MLX/CUDA; whisper on CPU; may be set to qwen3 / whisper / mock
ORT_QWEN_ASR_MODELQwen/Qwen3-ASR-1.7B0.6b / 1.7b / full HF repo id
ORT_QWEN_ASR_DEVICEautoNon-MLX Qwen3-ASR device override; defaults to ORT_DEVICE
ORT_QWEN_ASR_MAX_NEW_TOKENS256Qwen3-ASR generation cap for committed spans
ORT_TRANSLATOR_BACKENDautotranslategemma / mock; MLX uses ORT_TRANSLATEGEMMA_MODEL, CUDA/CPU use ORT_TRANSLATEGEMMA_TORCH_MODEL
ORT_TRANSLATEGEMMA_MODELmlx-community/translategemma-4b-it-4bitMLX TranslateGemma repo id
ORT_TRANSLATEGEMMA_TORCH_MODELgoogle/translategemma-4b-itTransformers/Torch TranslateGemma repo id; requires Gemma terms access
ORT_TTS_BACKENDautoqwen3_tts on MLX/CUDA; supertonic on CPU; may be set to qwen3_tts / kokoro / supertonic / say / mock
ORT_KOKORO_VOICEaf_heartKokoro voice, applied when ORT_TTS_BACKEND=kokoro
ORT_QWEN3_TTS_MODEL1.7b0.6b / 1.7b / full HF repo id
ORT_QWEN3_TTS_REF_AUDIOemptyOptional static reference WAV for direct Qwen3-TTS calls or warmup; sessions normally use rolling ASR audio
ORT_QWEN3_TTS_REF_TEXTemptyExact transcript of the optional static reference clip
ORT_SUPERTONIC_VOICEF1Supertonic voice M1M5 or F1F5
ORT_WHISPER_MODELsmallfaster-whisper model size or HF repo id
ORT_WHISPER_DEVICEautocuda under CUDA runtime, otherwise cpu
ORT_WHISPER_DEVICE_INDEXautoCTranslate2 CUDA device index; inferred from ORT_DEVICE=cuda:N
ORT_WHISPER_COMPUTEautofloat16 on CUDA, int8 on CPU unless overridden
ORT_TORCH_DTYPEautobfloat16 on CUDA, float32 on CPU; also accepts Torch dtype names such as float16
ORT_TORCH_ATTNautosdpa on CUDA, eager on CPU; set none to omit the argument
ORT_TORCH_MAX_NEW_TOKENS256TranslateGemma generation cap
ORT_INCREMENTAL10 = whole-utterance mode
ORT_COMMIT_SILENCE_MS300Incremental mode: clause-pause commit threshold
ORT_MAX_COMMIT_MS3500Incremental 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

Testing

CommandScope
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 e2eEnd-to-end zh→en on the verified MLX profile with real model downloads
uv run --extra dev pytestComplete 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.

Model licenses

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 / backendUsed forRuntime sourceLicense / terms
Qwen3-ASR-1.7B / 0.6BMLX/CUDA ASR default and optional smaller ASRQwen/Qwen3-ASR-1.7B, loaded directly through qwen-asr or through MLX community mirrorsApache-2.0
TranslateGemma-4b-itTranslation default across profilesgoogle/translategemma-4b-it, loaded directly on Torch or as mlx-community/translategemma-4b-it-4bitGoogle Gemma terms (gemma); see Gemma Terms of Use
Qwen3-TTS-12Hz-0.6B / 1.7B BaseMLX/CUDA voice-cloning TTS defaultQwen/Qwen3-TTS-12Hz-1.7B-Base, loaded directly through qwen-tts or through MLX community mirrorsApache-2.0
Kokoro-82MOptional fast TTShexgrad/Kokoro-82M, via the kokoro packageApache-2.0
Supertonic-3CPU TTS default and optional multilingual TTSSupertone/supertonic-3, via the supertonic packageBigScience OpenRAIL-M (openrail)
Whisper / faster-whisperCPU ASR default and optional ASROpenAI Whisper weights, commonly loaded as Systran/faster-whisper-smallMIT
macOS say voicesOptional system TTSApple system voices installed with macOSGoverned by the macOS software license; not redistributed by this project
mock backendsTests and protocol developmentNo model weightsNot 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.

Contributors

JacobLinCool

4 commits

JacobLinCool/open-realtime-translate

A fully local, real-time speech translation server for MLX, CUDA, and CPU devices, wire-compatible with the OpenAI Realtime Translation API.

3

stars

4

commits

Python

primary language

May 19, 2026

updated

README

open-realtime-translate

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.

Core capabilities

CapabilityMeaning
Real-time interpretationTranslated audio and text begin streaming while the source utterance is still unfolding
OpenAI wire compatibilityWebSocket and WebRTC clients can reuse the Realtime translation protocol by changing the base URL
Cross-lingual voice cloningThe Qwen3-TTS backend can speak translated text in a reference speaker's voice
Local privacy boundarySpeech, transcripts, and model inference remain on the machine
Pluggable model stagesASR, translation, and TTS are independently selectable
Latency/quality controlIncremental mode favors low latency; whole-utterance mode favors complete-sentence accuracy

Supported languages

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.

StageDecided byCoverage
Input speechActive ASR backendQwen3-ASR on MLX/CUDA: 30 languages + 22 Chinese dialects; optional Whisper: about 99 languages
TranslationTranslateGemma55 languages (ISO 639-1)
Output speechActive TTS backendQwen3-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)

CodeLanguageCodeLanguageCodeLanguageCodeLanguage
zhChineseenEnglishyueCantonesearArabic
deGermanfrFrenchesSpanishptPortuguese
idIndonesianitItaliankoKoreanruRussian
thThaiviVietnamesejaJapanesetrTurkish
hiHindimsMalaynlDutchsvSwedish
daDanishfiFinnishplPolishcsCzech
filFilipinofaPersianelGreekhuHungarian
mkMacedonianroRomanian

Output languages (depends on the TTS backend)

TTS backend#Languages
qwen3_tts (default on MLX/CUDA, voice cloning)10English, Chinese, Japanese, Korean, French, German, Italian, Portuguese, Russian, Spanish
kokoro8English, Chinese, Japanese, Spanish, French, Hindi, Italian, Portuguese (BR)
supertonic (default on CPU)31en, 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
saymanymacOS 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.

Performance

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):

MetricValue
Per committed span, zh→en end-to-end2.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 voicecosine ≈ 0.98, including cross-lingual output
Startupuse --warmup to preload models and compile first-use kernels

TTS backends compared (cold start, resident memory, real-time factor; lower RTF is faster):

BackendCold startResidentRTFNotes
qwen3_tts (MLX/CUDA default)1.9 s (warm 3.8 s)3.5 GB0.33–0.62Voice cloning with cross-lingual timbre transfer
kokoro2.6 s2.0 GB0.09–0.11Fastest and lightest neural TTS path
supertonic0.3 s0.6 GB0.19–0.31Smallest 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.

How it works

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

RuntimeASR defaultTranslation defaultTTS defaultNotes
mlxQwen3-ASR-1.7B through mlx-audioTranslateGemma-4b-it through mlx-lmQwen3-TTS-12Hz-1.7B-Base through mlx-audioDefault when ORT_RUNTIME=auto runs on Apple Silicon
cudaQwen3-ASR-1.7B through qwen-asrTranslateGemma-4b-it through Transformers/TorchQwen3-TTS-12Hz-1.7B-Base through qwen-ttsSet ORT_RUNTIME=cuda; use ORT_DEVICE=cuda:N for a specific GPU
cpuWhisper through faster-whisper/CTranslate2 CPUTranslateGemma-4b-it through Transformers/TorchSupertonic-3 through ONNXDefault 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

TransportEndpointNotes
WebSocket/v1/realtime/translationsJSON events + base64 PCM16 audio
WebRTC…/client_secrets…/callsSDP offer/answer; bidirectional Opus audio track; events over the oai-events DataChannel

Wire-contract details are documented in PROTOCOL.md.

Voice cloning

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.

Requirements

ItemRequirement / notes
RuntimeApple Silicon MLX, NVIDIA CUDA, or CPU
Memory32 GB recommended for the verified MLX stack (≈ 7.1 GB resident); CPU/CUDA sizing depends on model and dtype
Package manageruv; project pins Python 3.12
ffmpegAudio decoding and optional static reference-voice conversion
espeak-ngOptional Kokoro multilingual phonemization support: brew install espeak-ng
macOS sayLightweight system TTS when ORT_TTS_BACKEND=say
Hugging Face accessRequired 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 outputOptional, Kokoro only: one-time uv run python -m unidic download (≈ 526 MB)
OpenAI keyNot required

Quick start

# 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.

Browser demo

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.

Configuration

VariableDefaultValues / notes
ORT_RUNTIMEautoauto / mlx / cuda / cpu; auto selects MLX on Apple Silicon and CPU elsewhere
ORT_DEVICEautomlx, cpu, cuda, or cuda:N; explicit CUDA devices imply ORT_RUNTIME=cuda when runtime is auto
ORT_ASR_BACKENDautoqwen3 on MLX/CUDA; whisper on CPU; may be set to qwen3 / whisper / mock
ORT_QWEN_ASR_MODELQwen/Qwen3-ASR-1.7B0.6b / 1.7b / full HF repo id
ORT_QWEN_ASR_DEVICEautoNon-MLX Qwen3-ASR device override; defaults to ORT_DEVICE
ORT_QWEN_ASR_MAX_NEW_TOKENS256Qwen3-ASR generation cap for committed spans
ORT_TRANSLATOR_BACKENDautotranslategemma / mock; MLX uses ORT_TRANSLATEGEMMA_MODEL, CUDA/CPU use ORT_TRANSLATEGEMMA_TORCH_MODEL
ORT_TRANSLATEGEMMA_MODELmlx-community/translategemma-4b-it-4bitMLX TranslateGemma repo id
ORT_TRANSLATEGEMMA_TORCH_MODELgoogle/translategemma-4b-itTransformers/Torch TranslateGemma repo id; requires Gemma terms access
ORT_TTS_BACKENDautoqwen3_tts on MLX/CUDA; supertonic on CPU; may be set to qwen3_tts / kokoro / supertonic / say / mock
ORT_KOKORO_VOICEaf_heartKokoro voice, applied when ORT_TTS_BACKEND=kokoro
ORT_QWEN3_TTS_MODEL1.7b0.6b / 1.7b / full HF repo id
ORT_QWEN3_TTS_REF_AUDIOemptyOptional static reference WAV for direct Qwen3-TTS calls or warmup; sessions normally use rolling ASR audio
ORT_QWEN3_TTS_REF_TEXTemptyExact transcript of the optional static reference clip
ORT_SUPERTONIC_VOICEF1Supertonic voice M1M5 or F1F5
ORT_WHISPER_MODELsmallfaster-whisper model size or HF repo id
ORT_WHISPER_DEVICEautocuda under CUDA runtime, otherwise cpu
ORT_WHISPER_DEVICE_INDEXautoCTranslate2 CUDA device index; inferred from ORT_DEVICE=cuda:N
ORT_WHISPER_COMPUTEautofloat16 on CUDA, int8 on CPU unless overridden
ORT_TORCH_DTYPEautobfloat16 on CUDA, float32 on CPU; also accepts Torch dtype names such as float16
ORT_TORCH_ATTNautosdpa on CUDA, eager on CPU; set none to omit the argument
ORT_TORCH_MAX_NEW_TOKENS256TranslateGemma generation cap
ORT_INCREMENTAL10 = whole-utterance mode
ORT_COMMIT_SILENCE_MS300Incremental mode: clause-pause commit threshold
ORT_MAX_COMMIT_MS3500Incremental 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

Testing

CommandScope
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 e2eEnd-to-end zh→en on the verified MLX profile with real model downloads
uv run --extra dev pytestComplete 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.

Model licenses

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 / backendUsed forRuntime sourceLicense / terms
Qwen3-ASR-1.7B / 0.6BMLX/CUDA ASR default and optional smaller ASRQwen/Qwen3-ASR-1.7B, loaded directly through qwen-asr or through MLX community mirrorsApache-2.0
TranslateGemma-4b-itTranslation default across profilesgoogle/translategemma-4b-it, loaded directly on Torch or as mlx-community/translategemma-4b-it-4bitGoogle Gemma terms (gemma); see Gemma Terms of Use
Qwen3-TTS-12Hz-0.6B / 1.7B BaseMLX/CUDA voice-cloning TTS defaultQwen/Qwen3-TTS-12Hz-1.7B-Base, loaded directly through qwen-tts or through MLX community mirrorsApache-2.0
Kokoro-82MOptional fast TTShexgrad/Kokoro-82M, via the kokoro packageApache-2.0
Supertonic-3CPU TTS default and optional multilingual TTSSupertone/supertonic-3, via the supertonic packageBigScience OpenRAIL-M (openrail)
Whisper / faster-whisperCPU ASR default and optional ASROpenAI Whisper weights, commonly loaded as Systran/faster-whisper-smallMIT
macOS say voicesOptional system TTSApple system voices installed with macOSGoverned by the macOS software license; not redistributed by this project
mock backendsTests and protocol developmentNo model weightsNot 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.

Contributors

JacobLinCool

4 commits

Languages

Python

100.0%