Skip to content

Dictation

Marchyo can add voice dictation to the Hyprland desktop with voxtype (open source, 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.

It is off by default: it needs a microphone. When enabled, a recording indicator joins the top bar, with desktop notifications, sound cues and a Super+Shift+H status window.

marchyo dictation start|stop|toggle|status [--json] drives the backend; the binds call it. The daemon’s evdev hotkey holds F9.

Option Type Default Description
marchyo.dictation.enable bool false Enable dictation (hold F9 push-to-talk + a Super+Ctrl+X toggle bind)
marchyo.dictation.backend enum "voxtype" The dictation engine; "voxtype" is the only one
marchyo.dictation.pushToTalk.enable bool true Hold-to-talk on F9: the daemon’s evdev hotkey (adds 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 marchyo dictation 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 (voxtype status stream, or marchyo dictation status polled every 2 s)
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 voxtype only: 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.

With backend = "voxtype", 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 marchyo dictation 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.