An open API service indexing awesome lists of open source software.

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

Awesome Lists containing this project

README

          

![SonicFlow โ€” per-app volume for macOS](marketing/brand/lockup-banner.png)

# 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+).