Your voice, your machine. An open-source alternative to Wispr Flow: hold a key, speak, release, and clean text lands wherever your cursor is. Speech-to-text runs locally with faster-whisper; the optional cleanup pass is bring-your-own-key or fully local via Ollama. No subscription, no audio leaving your box unless you choose it.
Push-to-talk dictation, frameless pill UI, per-app context-aware cleanup, searchable history, and text-to-speech read-aloud of any selected text.
Windows-first. macOS / Linux deferred to later phases.
- GUI: PySide6 (LGPL, commercial-friendly)
- STT:
faster-whisperSmall.en int8, loaded once and kept resident in-process - VAD: faster-whisper's built-in silero filter (
vad_filter=True) - Audio:
sounddeviceringbuffer - Hotkey:
keyboardlibrary (push-to-hold) - Paste: clipboard set +
Ctrl+Vinjection with previous-clipboard restore - Cleanup: pluggable provider,
disabled/anthropic(Claude Haiku 4.5, BYOK) /ollama(local) - Context: Win32
GetForegroundWindow→ process+title → category injected into cleanup prompt
Dictation latency is the time from letting go of the key to text appearing. MURMUR attacks it three ways:
-
Parakeet by default. NVIDIA's Parakeet TDT 0.6B v3, run locally in int8 through
onnx-asr, decodes far faster than Whisper on a CPU and scores a lower word error rate than Whisper large-v3 on the Open ASR leaderboard. Measured on a Ryzen 3700X:Clip faster-whisper small.en int8 Parakeet TDT 0.6B v3 int8 5 s dictation 2975 ms 381 ms 17 s call, WER 4.3 % 2.1 % 78 s phone call 16.5 s 7.7 s The ~600 MB model downloads from Hugging Face on first launch. If that fails (offline), MURMUR falls back to faster-whisper for the session. Switch engines under Settings > STT.
-
Transcribe while you talk. With streaming on (default), finished phrases are committed to the engine while the key is still held: a Silero VAD looks for a pause after at least two seconds of speech, that phrase is decoded in the background, and at key-up only the short tail remains. A 30-second ramble waits about as long as a 3-second one.
-
The mic is always open. The input stream stays open while MURMUR is resident with a short ring buffer, so a key press costs nothing and the 300 ms of pre-roll before the press keeps the first syllable. The model is warmed with a silent decode after loading so the first utterance is not slow.
Every transcript records e2e_ms, key-up to pasted, in History, so you can see what you actually get. Whisper's cpu_threads is now set to the physical core count.
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -m murmurOn first run, Parakeet (~600 MB) downloads to the Hugging Face cache. Whisper models (~244 MB for small.en) go to %USERPROFILE%\.murmur\models\ when that engine is selected.
- Wait for the pill at the bottom-center to switch to dim-purple
READY(model loaded). - Click into any text field anywhere on the system.
- Press and hold Right Alt, speak, release.
- The transcript pastes at the cursor.
- Hold Shift while releasing Right Alt to skip cleanup for that utterance (raw Whisper output, use for code dictation, name spellings, exact phrasing).
- Select any text and tap F9 to hear it read aloud. Tap F9 again to stop. Uses the fully offline Windows SAPI voice by default; set
tts_voicein config to a Microsoft Edge neural voice (seeedge-tts --list-voices) for much better quality, note that this sends the selected text to Microsoft's Edge TTS service.
MURMUR's on-screen presence is a small dark capsule: a lamp, a voice line, and a readout. Nothing else.
- The voice line is your voice. While you hold the key it is a live oscilloscope trace of the microphone, scrolling left; silence is flat, words are jagged. When you let go it lies flat again.
- State lives in the line, not in labels. A highlight travels along it while Whisper works. It fills purple from the left while a file transcribes. It flashes purple when your words land, with the word count in the readout. Red means an error.
- The readout only speaks when it has something to say: a running timer while recording, a percentage for a file, a word count on done, the reading position while text is read aloud. Hover and it shows your push-to-talk key.
- Drag it anywhere. The spot is saved per monitor and restored on launch; if that monitor is unplugged it falls back to bottom-center of the primary display.
- Click it for the menu: copy the last transcript, History, Read aloud, Transcribe file, Settings, Quit. Click again, press Esc, or click elsewhere to close. Right-click gives the same menu.
- Size and opacity live in Settings under WIDGET, with a Reset Position button.
It never takes keyboard focus, so the caret in whatever you were typing into stays put. It renders crisply at 100 %, 125 %, 150 % and 200 % display scaling: every hairline is laid out in device pixels and the capsule is cached at the real scale factor. Type is Geist Sans and Geist Mono (Vercel, SIL Open Font License, bundled in murmur/assets/fonts/).
Select text anywhere and tap F9. While it is reading, F9 pauses and resumes. Click the widget and choose Read aloud… for the reader window: paste anything, press Play, jump back and forward a sentence at a time with Previous / Next or Ctrl+Left / Ctrl+Right, click into a sentence and press Play to start there, Ctrl+Space to pause. The sentence being read is highlighted as it goes. Pick a voice in the same window: the Windows voices installed on your machine work offline; the Microsoft Edge neural voices (English by default, tick All languages for the rest) are fetched once and cached, and send the text to Microsoft when used. Your choice is saved to tts_voice in config. Pausing is instant on both voices: the offline Windows voice pauses mid-sentence natively and the Edge neural voice resumes from where it stopped, with the next sentence fetched ahead of time so there is no gap.
Tray menu (right-click the icon, or double-click to open History):
- Show History, searchable archive + stats
- Settings…, full settings UI (no JSON editing needed)
- Read Aloud…, the reader window
- Transcribe File…, pick a recording; progress shows on the widget, the text lands on your clipboard and in History
- Show/Hide Widget
- Quit
From the running app, click the widget and choose Transcribe File… (or use the tray menu). MURMUR can also run headless on a recording (phone call, voice memo, meeting) without starting the tray app:
.venv\Scripts\Activate.ps1
python -m murmur --file "C:\path\to\call.m4a"Anything PyAV can decode works (.m4a, .mp3, .wav, .flac, .ogg, .opus, .mp4, ...). Segments stream to the terminal as they are decoded; the finished transcript prints to stdout. A 5-minute call takes roughly a minute on CPU with small.en.
| Flag | Effect |
|---|---|
--out notes.txt |
write the transcript to a file instead of stdout |
--copy |
put the transcript on the clipboard |
--archive |
save it to MURMUR's History |
--timestamps / -t |
one segment per line, prefixed with [mm:ss] |
--model medium.en |
better accuracy on low-quality audio (downloads ~1.5 GB on first use) |
--beam-size 5 |
slower, more accurate decoding |
--no-vad |
disable the VAD filter if quiet speech is being dropped |
--cleanup |
run your configured LLM cleanup pass on the whole transcript |
--quiet / -q |
no progress output |
The tray app can keep running while you do this; file mode loads its own model copy and exits when done.
- A capsule that shows your voice: draggable, remembered position, click-to-expand menu, HiDPI-crisp, one accent colour used sparingly. Configurable size and opacity.
- Transcribe files from the widget or tray: reuses the resident model, no second load.
- Resident model, no subprocess. Parakeet by default, ~0.4 s for a 5 s utterance on a Ryzen 3700X CPU; streaming phrase commits keep long dictation just as quick.
- History window: searchable archive of every transcript with copy, delete, export to
.txt/.json. - Stats tab: total transcripts, words dictated, audio captured, today's count, average STT and cleanup latencies, top target apps, breakdown by category.
- LLM cleanup pass (optional): removes filler ("um", "uh", "like"), adds punctuation, fixes grammar without changing meaning, preserves proper nouns and code identifiers.
- Per-app context: the cleanup prompt knows whether the cursor is in a code editor, terminal, chat app, mail client, doc editor, or browser, and adapts tone accordingly (e.g. drops trailing periods in chat, preserves identifiers in code).
- Custom vocabulary: terms in the Dictionary tab are biased into Whisper's
initial_promptand the cleanup prompt so proper nouns survive transcription. - Hold-Shift-for-raw: skip cleanup for a single utterance, handy for code or exact phrasing.
- Read-aloud (F9) with a real reader: speaks the selection, pause/resume on F9, and a reader window to paste text, jump by sentence and follow along with highlighting. Offline SAPI voice by default; opt into a Microsoft Edge neural voice via
tts_voicein config (online: the text is sent to Microsoft; falls back to SAPI without network). - Single-instance guard: double-launching just points you at the tray icon instead of spawning a duplicate.
- No lone-Alt menu steal: a phantom F24 is injected during the Alt hold so Windows never yanks focus to the menu bar when you release the push-to-talk key.
- Sound cues (optional): soft blips on start / stop / done / error.
- Atomic archive writes: tmp-file +
os.replace, crash-safe history. - Widget never steals focus:
WA_ShowWithoutActivating+WindowDoesNotAcceptFocus, target caret stays put. Dragging and clicking it never activate it either; only the expanded menu takes focus, and it hands focus back on Esc.
In Settings → Cleanup:
| Provider | When to pick it | Cost |
|---|---|---|
disabled |
Privacy-only / no cleanup | Free, instant |
anthropic |
Best quality (Claude Haiku 4.5). BYOK. | ~$0.25/M input, $1.25/M output (fractions of a cent per utterance) |
ollama |
Local privacy, no API key. Needs ollama serve running locally. |
Free, ~800–1500ms TTFT on CPU |
Set ANTHROPIC_API_KEY in Settings (stored locally at ~/.murmur/config.json, plaintext, change permissions if needed).
%USERPROFILE%\.murmur\config.json, settings (now includes cleanup config + dictionary)%USERPROFILE%\.murmur\archive.json, transcript history (atomic writes)%USERPROFILE%\.murmur\models\, downloaded faster-whisper models%USERPROFILE%\.murmur\murmur.log, app log
- One resident process, one model load. Unlike VoxMemo's per-utterance subprocess, MURMUR loads
WhisperModelonce at startup in a backgroundQThreadand reuses it. This is the entire latency fix. - Push-to-talk, not VAD-driven start. End-of-utterance is the key-up event, not a VAD silence detection.
- Window flags are set in a single bitwise-OR'd call to avoid the qt.io setWindowFlags-overwriting bug.
- Foreground capture happens before the pill paints, so the cleanup prompt knows the correct target app.
- Cleanup runs in the Transcriber QThread, never on the GUI thread, paste happens regardless of LLM latency.
- Settings updates without restart: hotkey, modifier, paste, sounds, app-context apply immediately; model/compute changes trigger a background re-load with the pill briefly showing PROCESSING.
- Phase 3: Moonshine v2 streaming with partial-result preview, MyChatArchive
capture_thought"brain mode" hotkey, voice search viasearch_brain, Context Q&A with Kokoro TTS read-back, Linux X11. - macOS port: pyobjc Accessibility shim, notarized
.appbuild via $99/yr Apple Developer Program.
AGPL-3.0-or-later. Use it, fork it, ship it, but derivatives (including hosted/commercial ones) must publish their source under the same terms.