https://github.com/altuzar/sonicflow
Per-app volume for macOS, built with CoreAudio Process Taps
https://github.com/altuzar/sonicflow
audio macos
Last synced: 27 days ago
JSON representation
Per-app volume for macOS, built with CoreAudio Process Taps
- Host: GitHub
- URL: https://github.com/altuzar/sonicflow
- Owner: altuzar
- License: mit
- Created: 2026-05-16T16:31:50.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-16T16:59:56.000Z (about 2 months ago)
- Last Synced: 2026-05-16T18:47:10.341Z (about 2 months ago)
- Topics: audio, macos
- Language: Swift
- Homepage: http://altuzar.github.io/sonicflow
- Size: 1.59 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Roadmap: ROADMAP.md
- Agents: AGENTS.md
Awesome Lists containing this project
README

# SonicFlow
> Per-app volume for macOS. Finally.
A free, open-source macOS menu bar app for **per-app volume control**.
Built in Swift 6 with SwiftUI + CoreAudio Process Taps.
[**Download v0.1**](https://github.com/altuzar/sonicflow/releases/latest) ยท
[**Landing**](https://altuzar.github.io/sonicflow) ยท
[**Roadmap**](ROADMAP.md) ยท
[**Marketing kit**](marketing/)
---
## What it does
- ๐ง **Per-app audio detection** โ every app currently producing audio shows
up live in the menu bar panel.
- ๐๏ธ **Per-app volume slider** โ adjust Music, Zoom, Chrome, Spotify
independently. Changes apply in ~10 ms via a realtime IOProc.
- ๐ **Click-to-mute** โ tap any app's icon to mute it instantly.
- ๐ข **Auto-ducking** โ when a communication app (Zoom, Teams, Slack)
actively outputs voice, other apps automatically lower by a configurable
amount (default 50 %).
- ๐๏ธ **Master volume** โ drives the macOS system volume; follows the
keyboard volume keys (F11/F12).
- ๐ช **Menu bar only** โ `LSUIElement = true`, no Dock icon, no window
on launch.
- โก **Low overhead** โ < 1 % CPU at idle, 1โ2 % under realtime audio
processing.
---
## Architecture
Three layered phases, each behind the `AudioEngine` protocol so the UI is
unchanged across phases:
```
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SwiftUI MenuBarExtra panel โ
โ Observable AudioState โ AppRowView ยท MasterControlView ยท โฆ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโ any AudioEngine
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Phase 2: AudioProcessDetector โ
โ โข kAudioHardwarePropertyProcessObjectList โ
โ โข kAudioProcessPropertyIsRunningOutput (piro) listener โ
โ โข 1 Hz fallback poll for HAL state-change latency โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Phase 3: AudioGainController (per-app real-time gain) โ
โ โ
โ ProcessTap (one per app, CATapMutedWhenTapped) โ
โ โ โ
โ AggregateOutputDevice (private, captures taps) โ
โ IOProc: applies per-app gain, mixes stereo โ
โ โ โ
โ FloatRingBuffer (lock-free SPSC, 8192 samples) โ
โ โ โ
โ PlaybackDevice (IOProc on user's default output) โ
โ adds to outputData โ speakers โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
```
Tapped apps' direct path to the speakers is silenced (`CATapMutedWhenTapped`)
so we don't get double audio. Non-tapped apps flow through the system mixer
unchanged.
---
## Build & run
Requires **macOS 14.2+**, **Xcode 16+**, **Swift 6+**.
```bash
make sign # build โ wrap in .app โ ad-hoc sign
open build/SonicFlow.app # launch as menu bar app
```
For development:
```bash
make debug # debug build
make run # build + sign + open
make icon # regenerate AppIcon.icns from SVG
```
### Runtime flags
| Flag | Effect |
|---|---|
| (none) | Production: Phase 3 enabled, menu bar only |
| `--no-gain` | Disable Phase 3 (detection only) |
| `--debug` | Verbose stats to stderr โ HAL snapshots, frame counters |
| `--preview` | Mock engine in a floating window (design work) |
| `--preview-live` | Live engine in a floating window (useful when Ice hides the menu bar icon) |
| `--test-gain-cycle` | Auto-cycle the first active app through 25 % / 100 % / mute / 100 % / 50 % every 3 s |
---
## Permissions
- **AppleScript Automation** โ for compatible apps (Music, Spotify, TV,
Podcasts), SonicFlow uses AppleScript to also move the app's own volume
slider. macOS will prompt "SonicFlow wants to control X" on first contact.
- **System Audio Capture (TCC)** โ process taps may require explicit grant
on macOS 14.4+ for system audio capture. The privacy string is in
`Info.plist`.
- **No microphone permission requested** โ the purple mic dot never appears.
---
## Project layout
```
sonicflow-app/
โโโ Package.swift SwiftPM, macOS 14.2+, Swift 6
โโโ Makefile build/sign/bundle helpers
โโโ README.md / CLAUDE.md / ROADMAP.md
โโโ Resources/Icon/ AppIcon.svg โ AppIcon.icns
โโโ Sources/SonicFlow/
โ โโโ App/ @main, AppDelegate, signal cleanup
โ โโโ Models/ AudioApp, AudioState (@Observable)
โ โโโ Audio/ Detector, Engine, GainController,
โ โ ProcessTap, AggregateOutputDevice,
โ โ PlaybackDevice, RingBuffer, โฆ
โ โโโ Services/PermissionsManager Accessibility hooks (future hotkeys)
โ โโโ UI/ ControlCenterView, AppRowView,
โ โ FluidSlider, SettingsView, โฆ
โ โโโ Resources/Info.plist / SonicFlow.entitlements
โโโ build/SonicFlow.app output bundle (832 KB)
```
---
## Honest status
- โ
**Phase 1 โ UI mockup** โ done
- โ
**Phase 2 โ Live detection** โ done, push + poll fallback
- โ
**Phase 3 โ Real per-app gain** โ done, two-IOProc + ring buffer
- โ ๏ธ Not yet: real RMS meters from the IOProc, headphone hot-swap,
persistence between launches, Developer ID signing.
- See [ROADMAP.md](ROADMAP.md) for the full open list.
---
## Acknowledgements
Inspired by the architecture used by SoundSource, BackgroundMusic, and
similar pro audio utilities. CoreAudio Process Tap API by Apple (macOS
14.2+).