Skip to content

Dictation

Marchyo can add push-to-talk voice dictation to the Hyprland desktop using voxtype with OpenAI Whisper. Hold F9 to dictate (push-to-talk), or press Super+Ctrl+X to toggle recording on and off; the transcribed text is typed at the cursor (with a clipboard fallback).

It is off by default — it needs a microphone and downloads a Whisper model. When enabled, the full desktop UI comes with it: a recording indicator on the top bar, desktop notifications, start/stop sound cues, and a Super+Shift+H floating status window. Each surface is on by default and individually opt-out.

Option Type Default Description
marchyo.dictation.enable bool false Enable voxtype dictation (hold F9 push-to-talk + a Super+Ctrl+X toggle bind)
marchyo.dictation.pushToTalk.enable bool true Daemon evdev hold hotkey; adds dictation users to the input group
marchyo.dictation.pushToTalk.key str "F9" evdev key held to record (e.g. "SCROLLLOCK", "PAUSE", "F13")
marchyo.dictation.pushToTalk.mode str "push_to_talk" "push_to_talk" (hold) or "toggle" (press to start/stop)
marchyo.dictation.toggleKey str "SUPER CTRL, X" Hyprland bind for voxtype record toggle; null to unbind
marchyo.dictation.gpu bool true Use the Vulkan GPU build (voxtype-vulkan); false for the CPU-only build
marchyo.dictation.model str "large-v3-turbo" Whisper model voxtype loads
marchyo.dictation.language str "auto" Spoken language ("auto" detects per utterance; pin with an ISO code like "en")
marchyo.dictation.preloadModel bool false Pre-download the model at activation instead of on first recording
marchyo.dictation.onDemandModel bool false Load the model on record start and unload after transcription (frees VRAM)
marchyo.dictation.gpuIsolation bool false GPU memory isolation so a discrete GPU can sleep between recordings
marchyo.dictation.spokenPunctuation bool false Convert spoken punctuation words to symbols (say “period” → .)
marchyo.dictation.replacements attrs {} Case-insensitive transcript substitutions (spoken form → typed text)
marchyo.dictation.silenceGate.enable bool true Drop recordings with no detected speech before transcription (voxtype [vad])
marchyo.dictation.silenceGate.backend str "energy" "energy" (RMS, no model) or "whisper" (Silero model, needs voxtype setup vad)
marchyo.dictation.silenceGate.threshold float 0.5 Detection threshold 0.0-1.0; higher needs louder speech
marchyo.dictation.silenceGate.minSpeechMs int 100 Minimum detected speech (ms) for a recording to be transcribed
marchyo.dictation.indicator bool true Show a recording-state segment on the top bar (driven by voxtype status --follow)
marchyo.dictation.notify bool true Desktop notifications on record start/stop and transcription
marchyo.dictation.audioFeedback bool true Start/stop sound cues
marchyo.dictation.statusWindow bool true Bind Super+Shift+H to a floating status window
marchyo.dictation = {
enable = true;
# model = "large-v3-turbo"; # multilingual + fast (default)
# language = "auto"; # detect per utterance
# preloadModel = true; # fetch the model at activation time
# indicator = false; # drop the top-bar segment
# notify = false; # silence desktop notifications
# audioFeedback = false; # silence start/stop cues
# statusWindow = false; # drop the Super+Shift+H window
# onDemandModel = true; # unload the model between dictations (frees VRAM)
# gpuIsolation = true; # let a discrete GPU sleep between recordings
# spokenPunctuation = true; # say "period" to get "."
# replacements = { "vox type" = "voxtype"; }; # fix domain jargon
# silenceGate.threshold = 0.3; # keep quieter speech the gate drops
};

Whisper invents text when it is fed silence (a stray “Thank you.” or “you”). The silence gate runs voxtype’s voice activity detection on every recording and discards one with no speech before it reaches Whisper: nothing is typed and the cancel cue plays. It is on by default with the energy backend, which needs no model download.

If quiet speech is being dropped, lower silenceGate.threshold (or minSpeechMs); if background noise still produces text, raise it or switch to backend = "whisper", the Silero model. That backend needs ggml-silero-vad.bin in voxtype’s models directory, fetched with voxtype setup vad. Without it the daemon logs a warning and transcribes every recording ungated. Set silenceGate.enable = false to send every recording to Whisper.

Enabling the option wires the upstream Home Manager services.voxtype module:

  • Writes ~/.config/voxtype/config.toml and runs the voxtype user service. wayland.display is set so the daemon gets WAYLAND_DISPLAY plus wtype/wl-clipboard on its PATH for typing into Wayland windows.
  • Recording is driven two ways: the daemon’s evdev push-to-talk hotkey (hold F9 by default) and a Hyprland bind (marchyo.dictation.toggleKey, default Super+Ctrl+X) that runs voxtype record toggle. The evdev hotkey needs /dev/input access, so dictation users are added to the input group while pushToTalk.enable is set (a real privilege — set it false to rely only on the compositor toggle).
  • Output mode is type (types at the cursor) with clipboard fallback.
  • The top-bar indicator streams voxtype status --follow (JSON) and recolors by state; notifications and sound cues use voxtype’s built-in [output.notification] / [audio.feedback]; Super+Shift+H opens a floating terminal running voxtype status --follow.

Check the daemon with systemctl --user status voxtype.