Releases: nikhi1g/whisper_hotkey
Release list
v4.2.7
Version 4.2.7 fixes capture failures caused by microphone format changes and
corrects the misleading interruption shown for silent dictations.
Microphone route changes no longer terminate the app
macOS can change an input device's native sample rate while the app is idle,
especially after wake or when an audio route changes. The prior capture path
could read 48 kHz, then attempt to install that stale format after the hardware
had moved to 24 kHz. AVAudioEngine raises an uncaught native exception for that
mismatch.
Capture now installs its tap using the input node's current native format. The
private writer creates or replaces its converter from the actual incoming
buffer, so a route change remains compatible with the fixed private 16 kHz mono
WAV used for local recognition.
Correct silent-dictation result
The recognition coordinator now preserves the recognizer's explicit no-speech
result. Silence shows the bounded No Speech Detected state instead of being
collapsed into Transcription Interrupted. Genuine provider failures retain the
interruption path.
Verification
- The complete Swift suite passes: 377 tests, with three intentional opt-in
integration tests skipped. - A format-change regression feeds a 24 kHz microphone buffer after a stale
48 kHz converter and verifies valid 16 kHz output. - The installed signed bundle survived ten consecutive capture/cancel cycles
and a completed silent session without a crash or format-mismatch log. - The installed app and controller match their built SHA-256 hashes and the
bundle passes deep strict code-signature verification.
Compatibility
No preference is migrated or reset. The release remains a native arm64 macOS
14-or-newer app with local-only audio and recognition. It uses the same three
permissions: Microphone, Accessibility, and Input Monitoring.
The app is signed with the project's stable Apple Development identity and is
not notarized. A first manual installation may still require System Settings >
Privacy & Security > Open Anyway. The ZIP is the human download; the DMG
remains for the in-app updater.
whisper_hotkey 4.2.6
Version 4.2.6 hardens capture recovery after an interrupted audio engine.
Capture lifecycle
AVAudioEngine permits only one input tap on a bus. If an interrupted capture
left that tap attached, the next dictation could terminate the app inside the
native InstallTapOnNode path before Swift could report a normal capture
failure. Every new session now clears the input bus before installing its
tap, while the existing private audio cleanup and failure paths remain intact.
This is a capture-integrity patch only. Recognition output, model selection,
preferences, permissions, and the local-only processing boundary are
unchanged.
Verification
- The complete Swift suite passes: 375 tests, with three intentional opt-in
integration tests skipped. - The repaired signed bundle was installed at
/Applications/whisper_hotkey.app
and passed deep strict code-signature verification. - Two consecutive synthetic Right Option dictation sessions completed without
a process exit, and the installed controller returned the app to idle.
Compatibility
No preference is migrated or reset. The release remains a native arm64 macOS
14-or-newer app with local-only audio and recognition. It uses the same three
permissions: Microphone, Accessibility, and Input Monitoring.
The app is signed with the project's stable Apple Development identity and is
not notarized. A first manual installation may still require System Settings >
Privacy & Security > Open Anyway. The ZIP is the human download; the DMG
remains for the in-app updater.
whisper_hotkey 4.2.5
Version 4.2.5 starts microphone capture at the physical hotkey edge and keeps
recording independent from model, UI, Accessibility, and file-writing work.
Capture starts first
The selected key now sends a token-scoped prime or cancel command directly to
a dedicated capture runtime. AVAudioEngine starts before private WAV and
converter preparation. Native microphone buffers that arrive during that setup
are retained in strict order, so model loading, badge placement, and temporary
file creation cannot throw away audio Core Audio has already delivered.
The real-time tap no longer converts, meters, runs speech detection, or writes
one or two files. It performs one bounded native PCM copy and enqueue. A serial
writer queue owns conversion, VAD, metering, the continuous canonical WAV,
inference-segment WAVs, and ordered rotation/finalization barriers. Recognition
receives only closed immutable segments. If the queue ever loses continuity,
capture fails explicitly instead of silently returning truncated speech.
This path is shared by Parakeet Unified, Balanced, Fast, and Whisper Turbo, and
by Decode After Speaking, Model Ready, and Decode While Speaking. The
microphone remains off at idle.
Faster presentation and correct gesture timing
The reusable listening badge is constructed while hidden at launch and appears
immediately at its pointer fallback. Exact caret geometry resolves afterward;
Accessibility latency cannot block recording or initial presentation.
Hold dwell is measured from the physical key timestamp instead of from delayed
main-thread delivery. Caps Lock now primes through the same recorder path.
Model preparation begins on its own actor after the gesture is accepted, and
decode-while-speaking segment rotation no longer shares the badge's update
loop.
Toggle Mode also closes correctly after a successful pipeline delivery. The
final transcript advances through insertion and returns the app to idle exactly
once instead of leaving the listening/transcribing UI open.
Recognition pipeline foundation, without a new accuracy claim
Recognition providers now preserve bounded word timing and evidence through a
shared rich-result contract. Canonical audio has cancellable lease ownership,
streaming windows use bounded stable-prefix reconciliation, and only the
coordinator may deliver text. The release also adds lexical-invariant
formatting, confidence/fusion guardrails, frozen benchmark manifests, and
privacy-safe performance and verifier harnesses.
The verifier preflight did not have the required local candidates and consented
corpus, so no verifier model was promoted. The selected primary remains
authoritative. Decoder output and the published WER measurements are unchanged;
4.2.5 improves capture startup and integrity rather than claiming a lower WER.
Verification
- The complete Swift suite passes: 375 tests, with three intentional opt-in
integration tests skipped. - Capture tests cover early-buffer FIFO order, explicit overflow, first usable
buffer/first committed sample timing, private 16 kHz WAV output, and cleanup. - Hotkey tests prove the capture edge reaches the immediate sink before deferred
app delivery and that Hold uses the physical-key deadline. - The integration matrix covers all 4 recognition choices x 3 input behaviors
x 3 processing modes with exactly-once delivery.
Compatibility
No preference is migrated or reset. The release remains a native arm64 macOS
14-or-newer app with local-only audio and recognition. It uses the same three
permissions: Microphone, Accessibility, and Input Monitoring.
The app is signed with the project's stable Apple Development identity and is
not notarized. A first manual installation may still require System Settings >
Privacy & Security > Open Anyway. The ZIP is the human download; the DMG remains
for the in-app updater.
whisper_hotkey 4.2.4
Version 4.2.4 removes an accuracy subsystem that never ran.
What changed
4.2.3 shipped a coordinator layer intended to arbitrate between two recognition
passes. It never executed. Its first line returned to the plain recognizer for
any engine other than Whisper, and Parakeet has been the default since 3.7.0,
so it was skipped on every dictation a shipped install performs. Even on the
Whisper path it was a pass-through: the default Precision profile built it with
no secondary provider and took a single beam pass.
1,542 lines left the app. No behaviour changed -- this is dead code leaving
the binary, not a feature being withdrawn.
Why it was removed rather than finished
Measurement, not taste. Before deleting it, the ceiling was measured on the
project's own 100-utterance LibriSpeech set:
| Combined WER | |
|---|---|
| Parakeet Unified (shipping default) | 2.46% |
| Parakeet Balanced | 2.62% |
| Oracle: always pick the better of the two | 2.13% |
An arbiter that is right every single time is worth 0.33pp, across five of a
hundred utterances. Nothing real reaches that.
Approaching it needs a confidence signal that the Parakeet runtime does not
expose: UnifiedAsrManager.transcribe returns a bare string and discards the
per-token probability its own decoder computes. That is why the Parakeet
candidate provider reported every confidence field as nil -- there was nothing
to report.
And every arbiter that needs no confidence is worse than shipping Unified
alone: utterance-level consensus scores 2.62%, word-level ROVER 2.68%. The
three Parakeet variants are one architecture family with correlated errors, so
the weaker models outvote the strongest exactly where it was right.
The analysis is reproducible through Benchmarks/Scripts/ensemble_ceiling.py,
and the code is preserved on the accuracy/parakeet-dual-pass branch.
Smart Decode is unaffected
It never depended on the coordinator. The recognizer derives its strategy from
the stored decoding profile, and the adaptive fallback lives in the helper.
Measured on this release: 4.04% WER against Precision's 4.32%, with a 2%
fallback rate -- unchanged.
Also fixed
The LibriSpeech benchmark harness, which the v2 helper protocol had broken in
three places. One raised a KeyError and stopped the run. The other two were
silent: the launch-time strategy no longer survived into a request, so the
adaptive profile measured greedy, and the fallback flag moved into a nested
object, so it reported zero fallbacks. Together they made Smart Decode look
like it had stopped working. It had not.
Parakeet Unified also has its own row in the engine table now. It became the
default in 3.7.0, which made the one configuration a new install actually runs
the one configuration the table did not describe.
Compatibility
Identical to 4.2.3 in behaviour. No stored preference is migrated or rewritten,
and the permissions are the same three: Microphone, Accessibility and Input
Monitoring.
The app is signed with a stable Apple Development identity and is not
notarized, so the first launch still needs one approval through System
Settings > Privacy & Security > Open Anyway. Audio and transcripts remain
local, and the on-demand Whisper model is verified against a pinned SHA-256.
whisper_hotkey 4.2.3
Version 4.2.3 restores Return and Escape during dictation.
What was wrong
3.7.0 removed Input Monitoring from setup, on the reasoning that granting
Accessibility already satisfied the event tap's listen check. That reasoning
was right about the tap and wrong about what the tap receives, and the gap it
left was only visible in one feature.
The two grants do different jobs:
- Accessibility authorises creating the event tap and posting the
synthetic paste. A tap holding only this grant does receiveflagsChanged. kTCCServiceListenEvent(Input Monitoring) is what deliverskeyDown
to that tap.
The dictation key is a modifier, so it arrives as flagsChanged and kept
working. Return and Escape are keyDown, so they never arrived at all. The
reducer saw no completion key, the session was never finished from the
keyboard, and the transcript was inserted without being submitted. Everything
else reported healthy, including verify-setup, because everything else
genuinely was.
Tracing the tap confirmed it directly: flagsChanged events arriving,
keyDown entirely absent.
The permission was also unreachable. An app has no row in the Input Monitoring
list until it asks for the grant at least once, and nothing in the app asked
outside the setup window's own button -- so an install that had never pressed
that button had nothing in System Settings to switch on. The app now registers
on first reconcile, which is what makes the row exist.
Also fixed
- The hotkey monitor preflighted listen access before attempting to create
the tap, so it refused to start on a Mac that had everything the tap needs.
Creating the tap is now the precondition; the preflight only distinguishes
which failure occurred. - A stray "Open Settings" button was drawn on top of an unrelated setup row.
Collapsing a grid row hid its cells, and the row's action button was then
re-shown by code that sets visibility from readiness alone. - The setup window's name column was pinned 45pt narrower than "Selected
Parakeet model", the label the engine picker writes into it at runtime.
Compatibility
Setup asks for Microphone, Accessibility and Input Monitoring again. Existing
installations that already granted all three see no change; an installation
upgrading from 3.7.x is prompted once for Input Monitoring, which is the grant
that makes Return and Escape work.
Parakeet Unified remains the bundled default, Whisper Turbo is still fetched on
demand, and no stored preference is migrated or rewritten.
The app is signed with a stable Apple Development identity and is not
notarized, so the first launch still needs one approval through System
Settings > Privacy & Security > Open Anyway. Audio and transcripts remain
local, and the on-demand Whisper model is verified against a pinned SHA-256.
whisper_hotkey 3.7.1
Version 3.7.1 fixes the Settings window sizing short of its own content.
What was wrong
3.7.0 sized the Settings window to fit its content instead of to a fixed
height, and clamped that height against the display so the window could never
grow past the screen. The clamp was written as one expression:
min(content, (available ?? content) - 40)The 40pt screen margin belongs only to the clamped branch. Folding it into the
fallback meant that wherever no display was reported, the window was sized 40pt
shorter than its own content -- so the version line, the GitHub link and the
help button landed outside the frame.
The screen can now only ever shrink the window, and only once there is a screen
to measure against. Sizing at construction is content-driven and independent of
whatever display happens to be attached.
Compatibility
Identical to 3.7.0 in every other respect. Parakeet Unified is the bundled
default, Whisper Turbo is fetched on demand, and no stored preference is
migrated or rewritten.
The app is signed with a stable Apple Development identity and is not
notarized, so the first launch still needs one approval through System
Settings > Privacy & Security > Open Anyway. Audio and transcripts remain
local, and the on-demand Whisper model is verified against a pinned SHA-256.
whisper_hotkey 3.7.0
Version 3.7.0 makes the most accurate engine the one you already have.
Parakeet Unified is bundled, and is now the default
Unified has been the most accurate option since 3.5.0, and it was also the one
option a new user had to wait 596 MB to try. It now ships inside the app, and a
fresh install starts on it.
Measured over the same 100 LibriSpeech utterances on an Apple M5 Pro, both
warmed up:
| Model | Combined WER | test-clean | test-other | Mean | Median |
|---|---|---|---|---|---|
| Parakeet Unified | 2.46% | 1.44% | 3.63% | 50 ms | 41 ms |
| Parakeet Balanced | 2.62% | 1.54% | 3.86% | 56 ms | 53 ms |
It wins every accuracy figure and both mean and median latency. Its one
regression is the tail: audio past 15 seconds is transcribed with overlapping
windows and is slower than Balanced. A dictation phrase is seconds, not tens of
seconds, so the median is what a user feels.
Whisper Turbo swapped places with it. Shipping both would have taken the
download past 1.8 GB against GitHub's 2 GB asset ceiling. Turbo is still fully
selectable and is fetched on demand through the same progress panel and pinned
checksum that every other install uses. The download is no larger than 3.6.2's.
One less permission to grant
Setup asked for Microphone, Accessibility and Input Monitoring. Granting
Accessibility already satisfies the event tap's listen check, so Input
Monitoring was never a separate action a user had to take -- it just made a
two-permission app look like a three-permission one.
It is now hidden unless it is genuinely required: Accessibility granted and the
preflight still failing. The check itself is unchanged, so an OS that stops
letting Accessibility cover the tap still surfaces the step rather than leaving
a hotkey that silently does nothing.
Toggle is the default
Press and Hold asked a new user to hold a modifier down for the length of a
sentence before anything appeared to work. Toggle starts and stops on a tap.
Existing installations keep whatever they had. The old preference was read with
defaults.bool(forKey:), which cannot tell "absent" from "false", so a missing
key silently meant Hold; an explicitly stored choice is now honoured as one.
Settings fits its own content
The window opened at a fixed height with a band of empty space above the
controls, because the scroll view's document view was not flipped: content
shorter than the window is pinned to the bottom. It is now top-anchored, sized
to whatever rows are actually visible, and fixed at one width -- the layout is a
single column of labelled rows, and every other width either stranded the
controls or clipped them.
The dictation key menu shows the real modifier glyphs, and the model names say
what distinguishes them rather than reading as three kinds of "accurate".
Also in this release
ParakeetModelInstaller.bundledDirectory refused to look for Unified at all,
and judged completeness differently from isInstalled. Both now share one
check. Nothing depended on this before, because Unified was never bundled.
Compatibility
Every engine, model, preset, and decoding profile from 3.6.2 is still
selectable, and no stored preference is migrated or rewritten. An existing
installation keeps its engine, its model, and its activation mode.
The app is signed with a stable Apple Development identity and is not
notarized, so the first launch still needs one approval through System
Settings > Privacy & Security > Open Anyway. Audio and transcripts remain
local, and the on-demand Whisper model is verified against a pinned SHA-256.
whisper_hotkey 3.6.2
Version 3.6.2 puts the ZIP back as the download.
What went wrong in 3.6.0
3.6.0 shipped the DMG as the only download. The reasoning was a test: a disk
image stamped with a real com.apple.quarantine value mounted normally, and
the app copied out of it carried only com.apple.provenance. Every one of
those observations was true, and the conclusion drawn from them was wrong.
The test ran hdiutil from a terminal, and hdiutil does not go through
Gatekeeper's verification path. Finder does. A double-click on the exact same
file is refused with "Apple could not verify 'whisper_hotkey.dmg' is free of
malware that may harm your Mac or compromise your privacy," offering only Move
to Trash and Done.
There is no Open Anyway for a disk image. That override exists for
applications, which is why the ZIP works: quarantine lands on the extracted
app, and one approval through System Settings > Privacy & Security clears it.
What changed
- The site's download button resolves to
whisper_hotkey.zipagain, with the
DMG as the fallback so links to older releases keep working - The release workflow publishes both assets again
- The README and the site's install instructions name the ZIP
The DMG is still published and is still what the in-app updater consumes -- it
downloads the file directly rather than through a browser, so it is never
quarantined and never sees this prompt. Existing installations update
normally and are unaffected.
Also in this release
fresh_restart_application_test.sh returns a Mac to a brand-new user's state
-- no installed app, no preferences, no model caches, no permission grants --
so the download-and-first-launch path can be exercised for real instead of
reasoned about. It backs your settings up first and --restore puts them back.
This is the tooling whose absence let 3.6.0 ship.
Compatibility
Every engine, model, preset, and decoding profile behaves as it did in 3.6.0.
The app is signed with a stable Apple Development identity and is not
notarized, so the first launch still needs one approval through System
Settings > Privacy & Security > Open Anyway. Audio and transcripts remain
local, and the bundled Whisper model is verified against a pinned SHA-256.
whisper_hotkey 3.6.0
Version 3.6.0 puts progress where the work was started, makes it honest, and
takes 141 MB and one redundant asset out of the download.
Progress lives in Settings now
Installing a model opened a floating utility panel. That was a second window
to manage, it could be dragged away from the control that started it, and it
read as a system dialog rather than as part of this app.
The bar is now a row inside Settings, directly under the model picker, with
its own Cancel. It appears when work starts and disappears when it ends.
The bar is deterministic end to end
It covers every phase, not just the transfer:
| Phase | Share of the bar | Source |
|---|---|---|
| Downloading | 0-85% | Real byte counts |
| Compiling for this Mac | 85-97% | Paced against a measured typical duration |
| First load | 97-100% | Paced against a measured typical duration |
Core ML compilation reports no byte count, so that segment is paced rather
than measured. It eases toward the top of its slice without arriving, so the
bar keeps moving instead of freezing on a number, never reports backwards, and
never claims to be finished before it is.
A smaller download
Whisper Base is no longer bundled. It left the recognition list in 3.5.7
and nothing could select it afterwards, so 141 MB of every download bought
nothing. Parakeet Fast is smaller, faster, more accurate, and still bundled,
so the low-memory first-run profile points there instead.
The ZIP is gone from new releases; the DMG is the download. The ZIP
existed because an unnotarized disk image was believed not to mount. That was
tested: a quarantined DMG mounts normally, and the app copied out of it
carries only com.apple.provenance rather than the com.apple.quarantine
flag the ZIP path propagated. Shipping one asset also halves the upload.
The in-app updater already used the DMG, so updates are unaffected. Links to
the ZIP on older releases keep working.
Settings comes to the front when you ask it to
Three separate defects, all with the same symptom:
- It was gated on the dictation state, so the menu item did nothing at all
while the app was listening or transcribing. Opening a window is not a
configuration change; the controls already arrive disabled. - The window is built once and reused, so it stayed on whichever Space it was
first opened on. It now moves to the active Space. - A miniaturized window ignores
makeKeyAndOrderFront, so Settings could not
be recovered from the Dock. It is deminiaturized first.
Wording
After Recording is now Decode After Speaking. It names what you did
rather than what the app did, and reads as one scale with Decode While
Speaking. No stored preference changes.
Compatibility
Every engine, model, preset, and decoding profile behaves as it did in 3.5.9.
The app is signed with a stable Apple Development identity and is not
notarized, so the first launch may still need one approval through System
Settings > Privacy & Security > Open Anyway. Audio and transcripts remain
local, and the bundled Whisper model is verified against a pinned SHA-256.
whisper_hotkey 3.5.9
Version 3.5.9 removes the Cohere Transcribe engine.
Cohere was added in 3.5.0 as the highest-ranked permissively licensed model
with an existing Apple Silicon path. Measured on this repository's own corpus
rather than on a leaderboard, it won exactly one comparison:
| Engine | test-clean | test-other | Combined | Mean latency | Size |
|---|---|---|---|---|---|
| Cohere Transcribe | 1.13% | 4.21% | 2.57% | 629 ms | 2.4 GB |
| Parakeet Unified | 1.44% | 3.63% | 2.46% | 50 ms | 594 MB |
It is better on clean read speech and worse on everything else: noisy speech,
combined accuracy, latency by a factor of twelve, and download size by a
factor of four. One narrow win does not earn a permanent entry in a list that
every user has to read.
A saved Cohere selection migrates to Parakeet Accurate. Nothing else changes,
and the 2.4 GB checkpoint can be deleted from
~/Library/Application Support/FluidAudio/Models/cohere-transcribe.
The app is signed with a stable Apple Development identity and is not
notarized, so the first launch still needs one approval through System
Settings > Privacy & Security > Open Anyway. Audio and transcripts remain
local, and every bundled Whisper model is verified against a pinned SHA-256.