How it works › Hush AI Microphone
Docs›How it works

Hush AI Microphone

The Hush AI Microphone drivers for macOS and Windows, and how they were tested.

A virtual microphone. Hush writes enhanced speech into it, and any app (Google Meet, Zoom, Teams, Discord, a browser) can choose Hush AI Microphone as its microphone. No VB-CABLE or BlackHole is needed.

Text
real microphone ─▶ Hush app (model) ─▶ ring buffer ─▶ driver ─▶ "Hush AI Microphone" ─▶ meeting app

Design#

RuleWhy
The driver only copies audio. The model runs in the appA meeting app cannot stall the model, and the driver stays small
48 kHz mono float in a ring of 16384 frames (341 ms)One fixed format: no conversion in the driver
Reads are indexed by the device clockSeveral apps can record at once and get the same audio
The driver reads 10 ms behind the newest sampleMargin for the app's writer thread
More than 100 ms off: the driver jumps to the newest audioBounded delay after a pause or restart
Writer idle for 100 ms, or since the reader started: silenceOld audio never plays or repeats when the app stops, crashes or starts late
Samples are made finite and clipped to [-1, 1] in the driverA bad value cannot reach a meeting app
One writer at a time (macOS: process ID slot; Windows: exclusive device)Two Hush windows cannot mix their audio
No data: silence. No locks, no waiting, no allocation in the audio pathAudio deadlines are never missed by the driver

Both drivers use the same reader, ring.h. test_ring.c tests it without a driver on Windows and macOS: silence before any write, no stale audio, writer stop and restart, late writer, two clients, read sizes from 1 to 4096 frames with writer jitter, long writer gaps, positions above 2^40, bursts, device time jumps, NaN and infinities. Without the idle rule, the test finds 39 repeated blocks and 38,688 samples of old audio after the app stops.

The app side is hush_core::virtual_mic (VirtualMic, Feeder). The feeder thread keeps one driver read plus 10 ms buffered and follows the driver's clock through the live worker's clock correction. It runs with real-time priority: Mach time-constraint policy on macOS, MMCSS on Windows. In the app, turn noise cancellation on; the output goes to Hush AI Microphone.

macOS#

macos/ is a Core Audio server plug-in, structured like Apple's NullAudio sample: one plug-in with two devices. Hush AI Microphone has one input stream. Hush AI Speaker (below) has one output stream. The plug-in creates the POSIX shared memory /hush.mic.v1 (layout in shared.h); the app maps it.

Shell
virtual_mic/macos/build.sh                      # universal arm64 + x86_64, ad-hoc signed
clang -framework CoreAudio -framework CoreFoundation -o target/test_plugin virtual_mic/macos/test_plugin.c
target/test_plugin target/HushMic.driver/Contents/MacOS/HushMic
sudo cp -R target/HushMic.driver /Library/Audio/Plug-Ins/HAL/ && sudo killall coreaudiod
cargo run --release -p hush-core --features devices --example virtual_mic_check 30

Remove: sudo rm -rf /Library/Audio/Plug-Ins/HAL/HushMic.driver && sudo killall coreaudiod. virtual_mic_check needs microphone permission. Run it from Terminal on the Mac, not over SSH: macOS gives SSH sessions silence.

Results (M1 Mac mini, macOS 26.6): the device appears in System Settings and in system_profiler SPAudioDataType (48 kHz, 1 channel, Virtual). test_plugin passes. virtual_mic_check records a 440 Hz tone through Core Audio. macos/edge_tests.command runs these cases; all pass:

CaseResult
Baseline (20 s; earlier also 6 × 30 s)0 glitches, no silence, longest writer gap 2.1 ms
Recorder buffer 64 frames / 4096 frames0 glitches
App stops while recording0 samples after 250 ms
App starts 4 s after the recorderNo audio before; audio at once after
Old audio in the ring, no appSilence
Two recording apps at onceBoth clean; the driver reads 17.0 s in 17 s (not twice)
A second writerRefused: "in use by another Hush window"
coreaudiod restarts while the app writesNew recordings clean
8 busy processes on the 8 cores0 glitches

Release builds need a Developer ID signature and notarization.

Hush AI Speaker (macOS)#

The same plug-in has a virtual output device. A meeting app (Zoom, Meet in a browser, Teams) chooses Hush AI Speaker as its speaker. Hush reads that audio, can clean and transcribe it, and plays it to the real speakers.

Text
meeting app ─▶ "Hush AI Speaker" ─▶ plug-in (mono mix) ─▶ ring buffer ─▶ Hush app ─▶ real speakers
RuleWhy
48 kHz, 2 channels float32 on the deviceApps expect stereo output
The plug-in writes the mean of the 2 channels, once per IO cycleCore Audio mixes all apps first; the app gets one mono stream
The plug-in is the writer, the app the reader. The plug-in never waitsNo reader, or a slow reader: the oldest audio is overwritten
Samples are made finite and clipped to [-1, 1]A bad value cannot reach the app
The app keeps its own read positionReading never blocks; several readers do not disturb each other
Never the default system (alert) outputSystem sounds do not go to a call

Ring /hush.speaker.v1 (HushSpeakerShared in shared.h, version 1, magic "HSPK"), 64-byte lines:

OffsetFieldWritten by
0magic, version, sample_rate (48000), capacity (16384 frames, 341 ms)plug-in, at creation
64write_pos (u64, frames, stored with release order after the samples)plug-in
72write_host_time (u64, mach_absolute_time of the last write), write_frames (u32)plug-in
128read_pos (u64, diagnostics only)app
19216384 mono float32 samples, index = position % 16384plug-in

The app side is hush_core::virtual_speaker::VirtualSpeaker: open(), read(out, max_latency), buffered(), active(), skipped(). read starts at the newest audio, skips ahead when more than max_latency frames (at most 12288, 256 ms) wait, resynchronises when the writer lapped it, and discards frames the plug-in overwrote during the copy. active() is true when an app wrote in the last 500 ms.

Shell
cargo run --release -p hush-core --features devices --example virtual_speaker_check

virtual_speaker_check plays a tone to Hush AI Speaker with CPAL and reads it back. It runs over SSH (playing needs no permission). Results (M1 Mac mini, macOS 26.6, IO buffer 512 frames):

CaseResult
997 Hz, 0.5, 6 s, read every 10 ms, bound 2400 (× 3)997.0–997.2 Hz, RMS 0.3535 (expected 0.3536), 0 breaks
440 Hz, 0.8, read every 5 ms, bound 960440.1 Hz, RMS 0.5657 (expected 0.5657), 0 breaks
3001 Hz, 0.25, read every 20 ms, bound 4800, no stall3000.9 Hz, 0 skipped, at most 1536 frames waiting
Reader stalls 300 ms / 500 ms (lapped)One skip to the bound, then clean; 0 frames waiting after each read
30 s run0 breaks other than the stall
First audio read after the first play callback2–6 ms (20 ms when reading every 20 ms)
active()true 11–30 ms after play starts; false 0.47–0.49 s after it stops
Default output after installUnchanged (Mac mini Speakers)

CPAL 0.15 does not list Hush AI Speaker in output_devices(): to list output formats it opens an AUHAL unit with input enabled, which Core Audio refuses for an output-only virtual device. Find it in devices(); playing works. CPAL 0.15 also keeps the device running after a stream is dropped: pause the stream first.

Windows with VB-CABLE (now)#

Until the Hush driver has Microsoft's signature (see below), Hush uses VB-CABLE, the free virtual cable by VB-Audio, signed by Microsoft. Hush does not bundle or download it: VB-Audio's terms do not allow bundling.

During setup (and in Set up on the home screen), Hush checks for a Hush AI Microphone:

  1. Not installed: Hush shows the steps and opens VB-Audio's page (vb-audio.com/Cable). Download the Driver Pack, extract it, run the setup program as administrator, press Install Driver, restart Windows.

  2. Installed: Hush offers to rename it. One administrator prompt starts hush-ai.exe --vb-cable setup, which renames the endpoints in the Windows property store:

    VB-CABLE endpointApps now see
    CABLE Output (VB-Audio Virtual Cable), recordingHush AI Microphone (Hush)
    CABLE Input (VB-Audio Virtual Cable), playbackHush AI Microphone Feed (Hush)

    The endpoint IDs go to %ProgramData%\Hush\vb-cable-endpoints.txt, errors to vb-cable.log in the same folder. hush-ai.exe --vb-cable restore (administrator) restores the original names of those endpoints only. Reinstalling VB-CABLE also restores them.

  3. Noise cancellation on: Hush plays its output into the feed. In Meet, Zoom or any app, choose Hush AI Microphone. If the Hush driver is installed, Hush uses it instead.

The driver stays VB-Audio's (Device Manager shows it), and VB-CABLE's unused 16-channel playback device ("CABLE In 16ch") keeps its name.

Results on the Windows laptop (VB-CABLE Driver Pack45):

TestResult
Rename (--vb-cable setup)Both endpoints renamed, IDs recorded
virtual_mic_check --cable, 20 s + 3 × 30 s2 glitches in the first run after the rename, then 0
App stops while recordingSilence afterwards
loop_latency feed → microphone83.7 ms median (82.6–84.6), on top of Hush's own delay
Live app path (headset → model → feed), 30 s, while another app recordsNo lost audio, 82% of one core

VB-CABLE adds about 84 ms. The Hush driver reads 10 ms behind the newest sample, so it will cut most of that when it is signed.

Windows driver (after signing)#

windows/ is Microsoft's SysVAD sample (audio/sysvad, commit f4c1ff8, MS-PL, see LICENSE-SysVAD.txt), reduced to one capture endpoint:

  • TabletAudioSample/minipairs.h: only the MicIn endpoint; no render endpoints.
  • TabletAudioSample/micinwavtable.h: 48 kHz 16-bit mono only.
  • hushmic.h, hushmic.cpp: the kernel ring and the control device \\.\HushAIMicrophone. The app writes with IOCTL_HUSH_MIC_WRITE; one writer at a time.
  • EndpointsCommon/minwavertstream.cpp: WriteBytes copies from the ring instead of the test tone.
  • adapter.cpp: creates the control device with the adapter, deletes it on removal; Bluetooth, USB and A2DP sideband paths are off by default.
  • TabletAudioSample/HushMic.inx: a new INF for this one device (Root\HushAIMicrophone).

Windows lists it as Hush AI Microphone (Hush).

PowerShell
virtual_mic\windows\build.ps1                    # x64 and ARM64 packages in virtual_mic\windows\out\
virtual_mic\windows\install.ps1                  # administrator; test-signing mode only
virtual_mic\windows\install.ps1 -Uninstall

build.ps1 needs Visual Studio 2022 with the Windows Driver Kit component and WDK 10.0.26100. It runs ApiValidator (both packages are "Universal") and InfVerif (valid). Code analysis (/p:RunCodeAnalysis=true) reports no issues in the Hush changes except floating point in the audio timer path (ring.h), which SysVAD's own tone generator also uses there. The control device handles every request type itself; PortCls never sees its requests, even after the device is deleted while the app still holds a handle. Build ARM64 and x64 drivers separately; never run the x64 driver on Windows ARM.

The packages are signed with the WDK test certificate. They load only in test-signing mode: bcdedit /set testsigning on, Secure Boot off, restart. Release packages need Microsoft attestation signing through the Hardware Dev Center.

Not tested yet#

  • Windows: installing and loading the driver, and audio through it. Needs test-signing mode on a physical x64 PC and on an ARM64 device. Then run windows\edge_tests.ps1 (the same cases as macOS) and a meeting app. The shared reader is already tested on Windows by test_ring.c; the kernel parts (control device, IOCTL, WaveRT copy) are not.
  • macOS: Google Meet or Zoom in a browser selecting Hush AI Microphone or Hush AI Speaker; Intel Macs.
  • Both: the full app path with a real microphone into a meeting, for an hour.

Something unclear or out of date? Tell us or edit this page.