Your voice,rebuilt locally.

An experimental Linux voice changer. It waits for your pause, then speaks the phrase in another voice.

View source on GitHub
dex:signal / phrase stateschematic

State

Listening

input envelopeMIC OPEN

Dex waits for 800 ms of silence, or 10 seconds without a break.

Interactive diagram. No audio is captured or played on this page.

Linux-first

End-to-end on one development laptop

Local only

No cloud, account, or telemetry

Half-duplex

The mic pauses while Dex speaks

No public build

Run from a local checkout

The signal never leaves the machine.

Four stages between your mouth and the call. A physical microphone, one recognizer, one synthesizer, and a virtual source. No hosted audio anywhere in the path.

Stage 01 / 04

A physical microphone

Dex opens the selected physical microphone through cpal on PipeWire's PulseAudio host. It never changes your system's default speaker or microphone. Capture and playback were verified with a live fixture on the development laptop.

  • Selected explicitly in Settings
  • 20 ms playback buffer requested
  • System defaults untouched

The signal starts here, not in a browser tab.

capture / cpalphysical input

No system defaults are changed. Your selected device is opened directly.

Diagrams describe the path. They are not app screenshots.

Three voices. One local model.

Voice selection is independent of the recognizer. All three embeddings run through the same Phonon checkpoint on the CPU.

  • Default, Zoey, and Juniper
  • Installed by the Linux model script
  • No voice sample is embedded on this page

Changing the voice does not make latency faster. It is a different output embedding, not a shortcut through the queue.

Voice 01

Default

installed

Included with the Linux model script

The selected Phonon checkpoint's default English voice. It is the baseline installed voice, not a separate model.

voice / defaultillustrative, not a sample

Voice 02

Zoey

installed

Additional English voice embedding

An English voice embedding installed from the pinned catalog. Voice choice is separate from Parakeet and does not alter recognition.

voice / zoeyillustrative, not a sample

Voice 03

Juniper

installed

Additional English voice embedding

A second English embedding from the same pinned catalog. It runs through the same CPU synthesis path as the other voices.

voice / juniperillustrative, not a sample

It waits for the pause.

The phrase is the unit. Not the word, and not the millisecond. That choice defines the behavior, and the latency.

Dex keeps the microphone open until it hears 800 ms of silence, or 10 seconds without a break. Then it stops listening, synthesizes the phrase, and plays it. Anything you say while Dex is speaking is not captured.

Mic openPhrase boundaryMic muted

The operating envelope.

These are behavior settings, not benchmark claims. They explain why Dex sounds deliberate in a call, and why it is not a real-time changer.

800 ms

01

quiet before Dex speaks

A phrase boundary, not a word boundary. Short clause pauses are held together to reduce clipped words; this does not guarantee that every final word is preserved.

10 s

02

maximum uninterrupted phrase

If you keep talking without a pause, Dex closes the phrase at the cap instead of waiting forever.

30 s

03

bounded output buffer

Complete speech wins over low latency in this build. Long speech can accumulate audible delay in the queue.

Re-synthesis, not a filter. The generated voice carries the recognized text. The original emotion, timing, and delivery are not preserved.

Half-duplex, visualised

Overlap has nowhere to go.

diagram
You
phrase
800 ms
quiet
speech lost
Dex
listening
endpoint
speakingmic muted
Phrase boundaryNo echo cancellation
Dex waits for the pause, then speaks. While it is speaking it is not listening, so anything you say over it is discarded. Use headphones if another app plays DexMicrophone on your speakers.

Target not met

Under 500 ms is still out of reach.

This is not a real-time voice changer today. The phrase wait and the synthesis queue put actual use well beyond the latency target. Optimization is deferred, not quietly redefined.

The app reports a group-commit metric, not acoustic end-to-end latency. Treat any number on screen as a diagnostic, not a promise.

Where it stands.

Linux is the only tested path. Several pieces work end to end. The important ones still do not. This is the honest split.

Working on this laptop

  • The Linux route

    A device-free self-test passes, and a fixture run carried synthesized speech through to DexMicrophone on the development laptop.

  • Audio devices

    Live capture and playback were verified with a physical microphone. Virtual sinks and remapped sources enumerate as normal PipeWire devices.

  • Three voices

    Default, Zoey, and Juniper install from the pinned catalog and pass the device-free fixture test.

  • The Rune shell

    Device selection and settings are built in native Rune. The window exposes Start, Stop, a phrase state, and a Clear action.

Not ready or unverified

  • Under 500 ms

    The strict end-to-end target is not met. Latency tuning is deferred; the phrase wait and synthesis backlog dominate.

  • A Windows Rust build

    There is no Windows Rust executable or tested Windows audio route. This is a Linux-only experimental build.

  • Public distribution

    There is no public download, Linux package, installer, or hosted service. No account or backend exists.

  • Shutdown cleanup

    A crash has been reported when deliberately closing the app. It does not establish an ordinary-operation crash or a proven driver defect; teardown cleanup still needs verification.

Status recorded on the development laptop. This is not a support policy.

Linux only / no public build

Set it up on Linux.

This page is a showcase, not a download page. There is no public build or installer. Start from the public repository on GitHub and follow the route below.

  1. 01

    Enter the Rust workspace

    Start from a public checkout of the repository on GitHub. There is still no release package or installer.

    Open the repository on GitHub
    shell
    cd rust
  2. 02

    Install the pinned models

    Downloads about 1.3 GB by default, verifies SHA-256, and installs the F16 Parakeet model plus Default, Zoey, and Juniper. Use DEX_MODELS to choose another directory.

    shell
    ./scripts/install-models.sh
  3. 03

    Build the desktop app

    The first build takes several minutes. Vulkan headers, libcblas, libpulse, CMake, Ninja, glslc, and a C++17 compiler must be available first.

    shell
    cargo build --release -p dex-voice
  4. 04

    Create the virtual microphone

    Idempotent. Creates the dex_voice sink and the remapped DexMicrophone source. It does not change your normal speaker or microphone defaults.

    shell
    ./scripts/setup-virtual-mic.sh
  5. 05

    Run Dex Voice

    Open Settings, choose your real mic and DexVoice as the virtual microphone output, then pick a voice. Save and press Start, then wait for Listening before speaking.

    shell
    ./target/release/dex-voice
  6. 06

    Select DexMicrophone in the call

    Choose DexMicrophone in the calling app if it lists normal PipeWire or PulseAudio capture sources. Verify the app's microphone permissions and device list. If it was already open, reopen or reload its microphone list and reselect the source.

    Select DexMicrophone

If the route misbehaves

List audio device IDs and names

cargo run --release -p dex-audio --bin devices

Find the Dex routing modules

pactl list short modules | grep dex

Remove a module when you are done

pactl unload-module <module-ID>

Replace <module-ID> with the ID printed by the command above.

The setup script does not change your normal speaker or microphone. To undo it, list the Dex modules and unload each module ID. If a calling app does not list DexMicrophone, reopen or reload its microphone device list, then reselect the source.

The voice is local. The caveats are too.

Read the setup or the limits. Both are part of the same machine, and neither one is hidden behind a download button.