⚠️ Warning: macOS still needs to be tested. This branch ports two features back from the Windows build — the dead-air stop prompt and the "Change recordings folder…" menu item (see "How to use" and "Config" below) — intoSources/quill/Audio/ActivityTracker.swift,MicRecorder.swift,SystemAudioRecorder.swift,RecordingSession.swift,Config.swift,MenuBarController.swift, andQuill.swift. They were written and reviewed on a Windows machine with no macOS access to build or run them —swift buildhas not been run against these changes. Build and test on a real Mac, especially the noise-vs-voice threshold inActivityTracker.swift(tuned from a Windows microphone measurement, not a Mac one), before relying on either feature.
A minimal, fully local macOS meeting recorder + transcriber. One menu-bar click records your mic and all system audio as two separate tracks; when you stop, quill transcribes both on-device and writes a speaker-tagged transcript. Nothing ever leaves the machine.
Named for the feather. Sibling of parrot, same skeleton: single Swift binary, menu-bar tray, no app bundle.
On Windows? See
windows/README.mdfor the C#/.NET port. It records mic + system audio (Phase 1, done); transcription and everything past that isn't built yet. The dead-air prompt and recordings-folder picker described below started as Windows-only additions and were ported back here — see the warning above.
cd quill
swift build -c release
sudo cp .build/release/quill /usr/local/bin/quill
quill install --launch-at-login # optional — runs in the background on loginRequires: macOS 15+ (Core Audio process taps for system audio — no virtual device, no kernel extension). Apple Silicon recommended for transcription speed.
- Run it (
quillin a terminal, or the LaunchAgent). - Click the feather in the menu bar → Start recording. First use prompts for microphone and System Audio Recording permissions. While recording, the icon turns red with a running elapsed counter, and macOS shows the purple recording indicator.
- Click → Stop recording when the meeting ends. Transcription starts automatically (the menu shows progress); a notification fires when the transcript is ready.
If neither track picks up any voice for 3 minutes, quill sends a
notification telling you to stop — unlike the Windows version's tray
balloon, this one isn't clickable (see the warning at the top of this file);
stop the recording yourself from the menu when you see it. Use the menu's
Change recordings folder… item to pick a new folder without editing
config.json by hand — it only affects recordings started after you change
it.
Each session lands in ~/Recordings/<yyyy.MM.dd-HHmm>/:
| File | Contents |
|---|---|
mic.caf |
your side (default input device, AAC) |
system.caf |
everything the Mac played — the other side of the call (AAC) |
meta.json |
start/end timestamps, duration, per-track start offsets |
transcript.json |
canonical transcript — engine provenance + timed, speaker-tagged segments |
transcript.md |
the same transcript rendered for reading |
transcribe.log |
transcription progress/errors for this session |
Two tracks on purpose: speech models do better on clean single-source audio,
and mic-vs-system is free two-party diarization — me vs them with no
speaker-identification model. CAF on purpose: unlike m4a, it needs no
finalization pass — if the process dies mid-meeting, everything already
written is still readable.
Built in, on-device, automatic. The default engine is Parakeet TDT 0.6B v2
(English) via FluidAudio's
Core ML port — roughly 20 seconds per hour of audio on Apple Silicon. Models
(~600 MB) download once on first transcription; quill doctor tells you
whether they're already cached so you're never downloading after an important
meeting.
Each track is transcribed separately, shifted by its start offset so both
share one clock, and merged by timestamp. Jobs run in a serial queue — you can
start a new recording while the last one transcribes. Unfinished jobs resume
on next launch (the filesystem is the queue: a session with meta.json but no
transcript.json is pending). Failures append to the session's
transcribe.log and never block later jobs.
The engine sits behind a small protocol; a Whisper engine (WhisperKit large-v3-turbo) is planned as the fallback / re-transcription option.
Optional, at ~/.config/quill/config.json:
{
"recordings_dir": "~/Recordings",
"transcription": { "enabled": true, "engine": "parakeet" },
"on_stop": "my-hook"
}recordings_dir— where sessions land. Resolution order:--outflag > config >~/Recordings.transcription.enabled— setfalseto just record.mic_voice_processing— Apple's echo cancellation on the mic (default off). Settruewhen recording meetings through the speakers, so playback doesn't bleed into the mic track and get transcribed twice as "me". The trade: while the voice unit is live, macOS ducks other playback slightly (.minducking is configured, but it can't be zeroed). On headphones there's no echo to cancel, so raw capture is the better default.on_stop— shell command spawned with the session directory as its argument, after the transcript is written (or right after recording if transcription is disabled). Wire it to whatever comes next: summarization, filing, indexing.
quill # run the menu-bar daemon (^C to quit)
quill run --out <dir> # custom recordings root (default ~/Recordings)
quill doctor # check permissions, recordings folder, models
quill install --launch-at-login
quill install --uninstall- Swift — single SPM executable target
- Core Audio process tap (
AudioHardwareCreateProcessTap, macOS 14.2+) — system audio capture via a private aggregate device - AVAudioEngine — mic capture
- AVAudioFile — streaming AAC encode into CAF
- FluidAudio / Parakeet — on-device Core ML transcription
- NSStatusItem — the whole UI
- A global tap records everything the Mac plays — notification dings, music, all of it. Don't play Spotify during meetings (or ask for a per-process picker if it bothers you).
- If recordings come out silent, check System Settings → Privacy & Security → Screen & System Audio Recording.
- Parakeet v2 is English-only. Other languages will come with the Whisper engine.
- The binary embeds its Info.plist (
__TEXT,__info_plist) so TCC can attribute permissions to quill itself when running as a LaunchAgent.