A macOS menu bar app that captures microphone and system audio, mixes them together, and exposes the mix as a virtual input device. Any app that accepts audio input (QuickTime, Zoom, OBS, Discord, etc.) can select "MacAudio Virtual Device" to receive the combined stream.
Requires macOS 14.2+ (Sonoma) — uses the Core Audio Taps API for system audio capture.
Mic (CoreAudio IOProc) ──┐
├─→ AudioMixer ──→ SharedRingBuffer (shm) ──→ HAL Driver ──→ Virtual Input Device
System (CATapDescription) ┘
The app and driver run in separate processes. The Swift app captures and mixes audio, then writes interleaved Float32 samples to a POSIX shared memory ring buffer. The C driver (an AudioServerPlugin) reads from the same ring buffer and presents the data as a standard macOS input device.
Audio from each source is automatically resampled to 48 kHz before mixing, so devices running at different native sample rates (e.g. a 44.1 kHz USB mic) are handled transparently with no pitch or speed artifacts.
Download the latest signed .pkg or .dmg from the Releases page. The installer places MacAudio.app in /Applications and the HAL driver in /Library/Audio/Plug-Ins/HAL/. After install, launch MacAudio from Applications — it will appear as a menu bar icon.
To verify a download against the published checksums:
shasum -a 256 -c MacAudio-<version>-SHA256SUMS.txt- macOS 14.2 or later
- Xcode 16.0+
- xcodegen (
brew install xcodegen) - Developer ID certificate for code signing (required on macOS 15+ for HAL driver loading)
The app requests the following permissions on first launch:
- Microphone — to capture mic input
- Screen & System Audio Recording — to capture system audio via
AudioHardwareCreateProcessTap
Grant these in System Settings > Privacy & Security.
xcodegen generateNote: After every
xcodegen generate, you must manually restore:
MacAudio/MacAudio.entitlements— xcodegen resets it. Restore thecom.apple.security.device.audio-inputentitlement.MacAudioDriver/Info.plist— xcodegen stripsCFBundleExecutable. Re-add it.
xcodebuild -project MacAudio.xcodeproj -target MacAudio -configuration Debug build \
CODE_SIGN_IDENTITY="Developer ID Application" DEVELOPMENT_TEAM=RB4QV9W52C CODE_SIGN_STYLE=Manualxcodebuild -project MacAudio.xcodeproj -target MacAudioDriver -configuration Debug clean build \
CODE_SIGN_IDENTITY="Developer ID Application" DEVELOPMENT_TEAM=RB4QV9W52C CODE_SIGN_STYLE=Manual./Installer/install.shsudo rm -rf /Library/Audio/Plug-Ins/HAL/MacAudioDriver.driver
sudo cp -R build/Debug/MacAudioDriver.driver /Library/Audio/Plug-Ins/HAL/
sudo xattr -rc /Library/Audio/Plug-Ins/HAL/MacAudioDriver.driver
sudo killall coreaudiodVerify the driver is loaded:
system_profiler SPAudioDataType | grep -A5 "MacAudio"./Installer/uninstall.sh- Install the driver (see above) or use the signed installer from Releases
- Launch MacAudio — it appears as a menu bar icon
- Click the icon and press Start
- In any recording app, select MacAudio Virtual Device as the input device
- Use the sliders to adjust mic and system audio levels independently
The menu bar icon's Settings submenu has two opt-in toggles, both off by default:
- Launch at Login — registers the app via
SMAppServiceso it starts automatically when you log in. - Auto-Start Capturing — when launched (at login or otherwise), the app starts capturing immediately if the driver is installed and microphone permission is already granted. Enabling this also enables Launch at Login, since auto-start is only useful when the app is running. Turning Launch at Login off clears Auto-Start Capturing too.
If preconditions aren't met at login (driver missing, mic permission not yet granted), the app retries for ~10 seconds to ride out transient TCC delays before showing an error icon in the menu bar.
MacAudio/
├── MacAudio/ # Swift menu bar app
│ ├── MacAudioApp.swift # App entry point
│ ├── AudioEngine/
│ │ ├── AudioConstants.swift # Shared constants (sample rate, buffer size, etc.)
│ │ ├── AudioMixer.swift # Orchestrates capture and writes to ring buffer
│ │ ├── MicCapture.swift # Mic input via CoreAudio IOProc
│ │ ├── SystemAudioCapture.swift # System audio via CATapDescription + aggregate device
│ │ ├── SampleRateConverter.swift # AVAudioConverter wrapper for real-time resampling
│ │ └── SharedRingBufferWriter.swift # Swift wrapper for the C ring buffer API
│ ├── Models/
│ │ ├── AppState.swift # Observable state for the UI
│ │ └── AudioDeviceManager.swift # Enumerates audio devices
│ ├── Views/
│ │ ├── MenuBarView.swift # NSMenu-based menu bar UI
│ │ ├── VolumeSlider.swift # Gain sliders
│ │ └── DevicePickerView.swift # Mic device selector
│ └── Utilities/
│ ├── Permissions.swift # Permission checks
│ └── DriverInstaller.swift # In-app driver installation
├── MacAudioDriver/ # C AudioServerPlugin HAL driver
│ ├── MacAudioDriver.c # Full AudioServerPlugInDriverInterface implementation
│ ├── SharedRingBuffer.c # Lock-free SPSC ring buffer (POSIX shm)
│ └── SharedRingBuffer.h # Public header (shared with Swift via bridging)
├── Installer/
│ ├── install.sh
│ └── uninstall.sh
└── project.yml # xcodegen project definition
| Constant | Value |
|---|---|
| Sample rate | 48000 Hz (sources at other rates are resampled automatically) |
| Channels | 2 (stereo interleaved Float32) |
| Ring buffer | 16384 frames |
| Shared memory | /macaudio_ringbuffer |
| Virtual device UID | MacAudioDevice_UID |
# Driver logs
log show --last 10s --predicate 'subsystem == "com.macaudio.driver"' --info --debug
# App logs
log show --last 10s --predicate 'subsystem == "com.macaudio.app"' --info --debug
# coreaudiod logs mentioning MacAudio
log show --last 10s --predicate 'process == "coreaudiod" AND message CONTAINS "MacAudio"'
# Check driver process
ps aux | grep MacAudioDriverOn macOS Tahoe, HAL plugins load out-of-process via Core-Audio-Driver-Service.helper. This has two implications:
- Driver signing is mandatory. Ad-hoc signed drivers are silently skipped. You must sign with a Developer ID certificate and remove the
com.apple.provenanceextended attribute after copying to/Library/Audio/Plug-Ins/HAL/. - New QueryInterface UUID. Tahoe sends UUID
EEA5773D-CC43-49F1-8E00-8F96E7D23B17in addition to the standardEAAF5B97-B965-4F68-9815-E718330862D5. The driver handles both.
All rights reserved.