OpenFader is a native macOS app for per-app audio control: music, browsers, games, calls, streams, and anything else producing output.
It is built around public Core Audio process taps, a SwiftUI/AppKit desktop UI, and a local-first settings model. The goal is simple: give macOS the source mixer it should already have, without requiring a cloud account, browser extension, or proprietary audio stack.
macOS gives you one system volume slider. Real work rarely sounds like one thing.
OpenFader lets you tune each active audio source independently:
- lower a browser while music stays full
- push call audio forward without crushing everything else
- tame loud games, streams, alerts, or video playback
- save per-app profiles so your mix follows the source
- build reusable templates for night listening, voice clarity, music, cinema, gaming, and custom workflows
OpenFader is also a serious Core Audio project. It does not pretend macOS has a magic public API for another app's volume. It discovers active process audio, creates process taps, routes tapped audio through a guarded render engine, and applies gain, balance, EQ, limiting, solo, mute, and meters in the open.
| Mixer | Templates | Routing |
|---|---|---|
![]() |
![]() |
![]() |
- Per-source mixer: control active app audio from the menu bar or full app window.
- Live gain path: route process tap audio through a Core Audio render loop for per-source gain, balance, EQ, limiting, mute, solo, and meters.
- Templates: quick-load built-ins such as Voice, Music, Cinema, Night, Detail, Warm, and Gaming Focus, plus user-authored custom templates.
- Auto-apply rules: load the right preset when a matching source appears, using stable source keys, bundle IDs, or display-name fallbacks.
- Saved profiles: keep source-specific gain, balance, mute, EQ, limiter, loudness target, and active preset state.
- Loudness tools: processed peak/RMS meters and explicit one-click RMS target matching.
- Routing diagnostics: copy live route state, render health, buffer layout, route phase, tap IDs, aggregate IDs, and source settings for debugging.
- Demo mode: launch a deterministic in-memory environment for screenshots, demos, and UI review without touching your real settings file.
OpenFader is in active development and now ships through signed release artifacts. It builds, tests, packages locally, publishes a notarized GitHub DMG, and uploads a sandboxed Mac App Store package through CI. The Core Audio route path is implemented and continues to be hardened across more devices and stream layouts.
| Area | Status |
|---|---|
| Source discovery | Working through Core Audio HAL process objects |
| Menu bar UI | Working |
| Full app window | Working |
| Saved profiles and templates | Working |
| Auto-apply rules | Working |
| Process tap routing | Implemented, still being hardened |
| Gain, balance, EQ, limiter, meters | Implemented in the render graph |
Packaged .app for local testing |
Working through scripts/package-macos-app.sh |
| GitHub release DMG | Automated through release-dmg.yml |
| App Store upload | Automated through release-dmg.yml; manual upload workflow also available |
| Broad device/layout validation | Roadmap |
- macOS 14.2 or newer for Core Audio process taps
- Xcode 16 or newer recommended
- Swift 6 toolchain recommended
- System Audio Capture permission for live gain/EQ rendering
The project was initially validated with Xcode 26.2 and Swift 6.2.3.
swift build
swift testswift run OpenFaderThe SwiftPM executable is useful for quick UI and model work. For live audio permission stability, use the packaged app bundle below.
System Audio Capture permission is tied to app identity, so live per-source gain/EQ testing should run from a signed app bundle:
./scripts/package-macos-app.sh debug
open .build/openfader-app/debug/OpenFader.appThe packaging script builds OpenFader, creates a minimal .app, copies the
Info.plist, and signs it for local testing. It uses
OPENFADER_CODESIGN_IDENTITY when set, otherwise the first available Apple
Development or Developer ID identity, and falls back to ad-hoc signing when no
local identity is available.
If macOS keeps an old System Audio Capture grant around after a rebuild, reset the grant and reopen the signed app:
tccutil reset AudioCapture app.openfader.OpenFader
open .build/openfader-app/debug/OpenFader.appDemo mode launches OpenFader with staged sources, output devices, templates, profiles, auto-apply rules, and live route state. It is designed for README screenshots, product demos, and UI review.
It uses an in-memory settings repository and does not read or write:
~/Library/Application Support/OpenFader/SourceSettings.json
Launch specific sections:
OPENFADER_DEMO_MODE=1 OPENFADER_DEMO_SECTION=mixer .build/openfader-app/debug/OpenFader.app/Contents/MacOS/OpenFader
OPENFADER_DEMO_MODE=1 OPENFADER_DEMO_SECTION=templates .build/openfader-app/debug/OpenFader.app/Contents/MacOS/OpenFader
OPENFADER_DEMO_MODE=1 OPENFADER_DEMO_SECTION=routing .build/openfader-app/debug/OpenFader.app/Contents/MacOS/OpenFaderSupported sections:
setup
mixer
templates
rules
routing
profiles
settings
macOS does not expose a simple public API for setting another app's output volume. OpenFader uses the public route that does exist:
- Discover the process object for an active audio source.
- Create a private Core Audio process tap for that source.
- Mute the original source output while the tap is active.
- Read audio from the tap.
- Apply gain, balance, EQ, limiting, mute, solo, and meters.
- Write processed audio to the selected output device.
flowchart LR
A["Core Audio process objects"] --> B["OpenFader source model"]
B --> C["Saved profiles and templates"]
C --> D["Process tap routing engine"]
D --> E["Source render graph"]
E --> F["Rendered output device"]
B --> G["Menu bar and app UI"]
G --> C
The code is split into a small app target and a core audio/model library:
| Path | Purpose |
|---|---|
Sources/OpenFader |
SwiftUI/AppKit app, menu bar panel, main window, demo mode |
Sources/OpenFaderCore |
Core Audio discovery, routing engine, render graph, settings model |
Sources/OpenFaderRouteSmoke |
Runtime route validation utility |
Tests/OpenFaderCoreTests |
Profile, template, DSP, routing, and render-plan tests |
docs/ENGINE.md |
Engine contract, current risks, and work queue |
- active output source discovery through Core Audio HAL process objects
- stable source identity through bundle IDs, normalized names, and PID fallback
- per-source mute, solo, reset, compare, and diagnostics
- per-source processed peak/RMS meters
- 1 dB gain nudges plus continuous gain sliders
- balance controls for stereo positioning
- gain from 0% to 200%
- dB readouts and gain conversion helpers
- five-band EQ: Sub, Bass, Low Mid, Presence, Air
- peaking, low-shelf, and high-shelf EQ filters
- per-band gain, frequency, Q, filter type, and bypass
- full processing bypass for auditioning original audio
- limiter ceiling and gain-staging warnings
- explicit RMS target matching from live meters
- durable per-source profiles in Application Support
- built-in presets for common listening contexts
- custom templates saved from source settings
- template rename, update, duplicate, delete, export, and import
- favorite and recent template promotion
- source-aware preset recommendations
- auto-apply rules by stable source key, bundle ID, or display-name match
- stale reference cleanup for profiles, templates, rules, favorites, and recents
- output device discovery and persisted routing selection
- virtual output warnings for devices such as BlackHole, Loopback, and Soundflower
- selected-output recovery when a saved output disappears
- private aggregate route planning for tapped sources
- render-loop health checks and fail-closed behavior
- buffer layout diagnostics for interleaved stereo, planar stereo, and unsupported layouts
- copyable route diagnostics for each source
OpenFaderRouteSmoke is a runtime validation utility for engine work. It can
attach to active output sources, spawn a temporary spoken test source, sweep
available output devices, apply gain or presets, and require proof that
processing and meters were observed.
Build it:
swift build --product OpenFaderRouteSmokePackage it for permission-stable validation:
./scripts/package-route-smoke-app.sh debugRun a strong local proof:
.build/openfader-app/debug/OpenFaderRouteSmoke.app/Contents/MacOS/OpenFaderRouteSmoke \
--spawn-say \
--template-pack Tests/Fixtures/route-smoke-template-pack.json \
--auto-apply-preset custom:route-smoke-detail \
--seconds 3 \
--sweep-outputs \
--require-processing \
--require-meterMore route detail lives in docs/ENGINE.md.
OpenFader's primary direct-install path is a signed and notarized GitHub
Release DMG. Push a version tag such as v1 and the release workflow builds
the DMG, notarizes it when Apple credentials are configured, and publishes it
as a GitHub Release asset.
The same tag workflow uploads a sandboxed macOS .pkg to App Store Connect
when the App Store secrets are configured and the repository variables confirm
sandbox readiness. The App Store path does not use the GitHub DMG artifact.
See docs/DISTRIBUTION.md for required secrets,
local dry runs, notarization, and App Store upload notes. See
docs/APP_STORE_CONNECT_SETUP.md for the
Apple Developer/App Store Connect account checklist and listing metadata.
- Broader audible validation across physical and virtual output devices
- App Store review follow-through and release polish
- More guided onboarding for System Audio Capture permission
- Stronger render failure recovery for device changes and unusual stream layouts
- Reproducible release builds
Contributions are welcome, especially around Core Audio validation, UI polishing, route diagnostics, packaging, and tests.
Before opening a pull request, run:
swift test
swift build --product OpenFader
swift build --product OpenFaderRouteSmokeSee CONTRIBUTING.md for local validation notes and runtime
proof commands.
OpenFader is released under the MIT License.


