⚙ Mcaster1AudioPipe
Virtual audio device driver that creates system-level audio pipe devices on macOS. Route audio between applications with zero latency — the invisible backbone of the Mcaster1 broadcast ecosystem.
Overview
Mcaster1AudioPipe is a CoreAudio HAL AudioServerPlugin that creates virtual audio
devices on macOS. It provides the critical audio routing layer in the Mcaster1 broadcast stack,
enabling multiple Mcaster1AMP player instances to output to numbered pipe devices
(Mcaster1AudioPipe 1, Mcaster1AudioPipe 2,
Mcaster1AudioPipe 3, etc.) which the Mcaster1DSPEncoder captures as input
devices for broadcast encoding.
What It Does
- Creates virtual audio devices visible in Audio MIDI Setup and all CoreAudio applications
- Provides zero-latency internal audio routing between applications via shared ring buffers
- Each pipe device pairs an output stream (AMP writes via WriteMix) with an input stream (Encoder reads via ReadInput) sharing a lock-free ring buffer
- Silent by default — audio flows internally without reaching physical speakers
- Optional monitor output forwards pipe audio to a real hardware device for live monitoring
- No kernel extension (kext) required — runs entirely in userspace as a HAL plugin
Architecture
Mcaster1AudioPipe is implemented as a CoreAudio HAL AudioServerPlugin —
a userspace driver that runs inside the coreaudiod daemon process.
Unlike legacy kernel extensions (kexts), AudioServerPlugins do not require SIP
disabling, are compatible with Apple Silicon, and survive macOS updates gracefully.
Plugin Architecture
- Bundle format:
.driverbundle (CFBundle withAudioServerPlugIntype) - Install path:
/Library/Audio/Plug-Ins/HAL/Mcaster1AudioPipe.driver - Entry point:
AudioServerPlugInDriverInterfacevtable with 26 callbacks - Process context: Loaded by
coreaudiodat daemon startup; runs in the audio server’s address space - Object hierarchy: Plugin → Device(s) → Stream(s) → Controls
Object Hierarchy
Ring Buffer Design
Each virtual device contains a lock-free single-producer single-consumer (SPSC) ring buffer that bridges the output and input streams. The output stream’s IO callback writes audio frames into the buffer, and the input stream’s IO callback reads them out. Because CoreAudio IO callbacks execute on real-time threads, the ring buffer uses atomic load/store operations with acquire/release memory ordering — no mutexes, no system calls, no priority inversion.
Virtual Clock
Each device maintains a virtual sample clock driven by mach_absolute_time().
The GetZeroTimeStamp callback returns monotonically advancing host-time /
sample-time pairs, allowing CoreAudio to synchronize the virtual device with the system
audio graph. The clock ticks at the configured sample rate regardless of whether any
client is performing IO.
Audio Pipeline
Mcaster1AudioPipe sits at the center of the Mcaster1 broadcast chain, providing the zero-latency bridge between media playback and broadcast encoding. Each AMP instance selects a numbered pipe device as its audio output; the DSP Encoder captures from the corresponding pipe device as an input source.
Full Broadcast Signal Path
Features
Multiple Virtual Devices
Creates numbered pipe devices (Mcaster1AudioPipe 1, 2, 3...) for independent audio streams. Each device is fully isolated with its own ring buffer and clock.
Zero-Latency Routing
Lock-free SPSC ring buffer transfers audio in shared memory. No kernel transitions, no copies beyond the IO cycle, no sample-rate conversion overhead.
Multi-Rate Support
Supports 44,100 Hz, 48,000 Hz, and 96,000 Hz sample rates. Stereo (2-channel) Float32 native format throughout.
Silent by Default
Audio flows internally without reaching physical speakers. No accidental audio bleed during broadcast — professional silent monitoring behavior.
Monitor Mode
Optional monitor output forwards pipe audio to a real hardware device for live headphone monitoring while maintaining the internal pipe routing.
Lock-Free SPSC Buffer
Single-producer single-consumer ring buffer using atomic operations with acquire/release memory ordering. No mutexes on real-time audio threads.
Audio MIDI Setup Visible
All pipe devices appear in macOS Audio MIDI Setup, System Preferences Sound panel, and any application’s audio device picker.
Code-Signed
Signed driver bundle passes Gatekeeper validation. No SIP disabling required — runs entirely in userspace as a HAL AudioServerPlugin.
Virtual Clock
Precise virtual sample clock via mach_absolute_time() provides stable timing for CoreAudio graph synchronization independent of hardware clocks.
Project Structure
Key Source Files
Mcaster1AudioPipe.c
The main implementation file. Contains the AudioServerPlugInDriverInterface
vtable, all 26 HAL callbacks, device/stream/control object management, the virtual
clock implementation, and IO operation handlers. Written in plain C for direct
compatibility with the CoreAudio C API.
ring_buffer.h
Header-only lock-free single-producer single-consumer ring buffer. Uses
stdatomic.h with memory_order_acquire /
memory_order_release for safe concurrent access from real-time audio
threads. Configurable capacity, Float32 sample format, power-of-two sizing for
efficient modulo via bitmask.
Info.plist
Bundle metadata identifying the plugin to CoreAudio. Contains
CFBundleIdentifier (com.mcaster1.audio.Mcaster1AudioPipe),
AudioServerPlugIn type declaration, and version information.
Build & Install
Architecture — ARM64 vs Intel
The HAL driver is loaded in-process by coreaudiod, which runs
natively — arm64 on Apple Silicon, x86_64 on Intel.
Rosetta 2 does not apply. The driver binary must contain a matching architecture slice
or coreaudiod will silently fail to load it.
# Dev build — native host arch (auto-detected via uname -m, fast)
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
# Force a specific arch
cmake -B build -DCMAKE_OSX_ARCHITECTURES="arm64" # Apple Silicon only
cmake -B build -DCMAKE_OSX_ARCHITECTURES="x86_64" # Intel only
# Universal binary — arm64 + x86_64 in one bundle (required for distribution)
cmake -B build -DMACOS_UNIVERSAL=ON
cmake --build build
# Verify what was built
lipo -archs build/Mcaster1AudioPipe.driver/Contents/MacOS/Mcaster1AudioPipe
# → arm64 (native dev build on Apple Silicon)
# → x86_64 (native dev build on Intel)
# → arm64 x86_64 (universal / distribution build)
Build the App & Driver
CMake builds both the Qt6 GUI app and the CoreAudio HAL .driver bundle
in a single step.
# Configure + build (GUI app + HAL driver)
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -- -j$(sysctl -n hw.ncpu)
# Output:
# build/Mcaster1AudioPipe.app (Qt6 GUI)
# build/Mcaster1AudioPipe.driver (CoreAudio HAL plugin)
# Contents/MacOS/Mcaster1AudioPipe (Mach-O — arm64, x86_64, or universal)
# Contents/Info.plist
Install
# Copy to HAL plugins directory and restart coreaudiod
sudo bash scripts/install.sh
# What it does:
# 1. Detects host architecture (uname -m) and validates driver binary (lipo -archs)
# 2. Errors with rebuild instructions if arch mismatch detected
# 3. Copies Mcaster1AudioPipe.driver to /Library/Audio/Plug-Ins/HAL/
# 4. Sets ownership to root:wheel
# 5. Restarts coreaudiod (sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod)
# 6. Devices appear in Audio MIDI Setup within seconds
Uninstall
# Remove the driver and restart coreaudiod
sudo bash scripts/uninstall.sh
# What it does:
# 1. Removes /Library/Audio/Plug-Ins/HAL/Mcaster1AudioPipe.driver
# 2. Restarts coreaudiod to unload the plugin
# 3. Pipe devices disappear from the system
coreaudiod is automatically
restarted by the install/uninstall scripts. Devices appear or disappear within seconds.
Currently playing audio in other applications will be briefly interrupted during the
restart. Driver install/uninstall triggers an automatic patch bay rebuild.
Audio Driver Management
The Mcaster1AudioPipe control interface provides a tabbed view for managing drivers and audio services:
Installed Drivers Tab
Lists all installed HAL audio drivers with status indicators. Supports driver install and uninstall operations, which automatically trigger a patch bay rebuild so device routing stays consistent without manual intervention.
Audio Services Tab
Displays running audio services (coreaudiod and related daemons).
Right-click context menus on audio services provide Stop,
Start, and Restart actions for quick
troubleshooting without resorting to Terminal commands.
Verify Installation
# List HAL plugins
ls /Library/Audio/Plug-Ins/HAL/
# Check that devices are registered
system_profiler SPAudioDataType | grep -i "Mcaster1"
# Or open Audio MIDI Setup.app and look for Mcaster1AudioPipe devices
codesign --force --deep --sign "Developer ID Application: ..." build/Mcaster1AudioPipe.driver.
For local development, ad-hoc signing works: codesign --force --deep --sign - build/Mcaster1AudioPipe.driver.
Platform Support
Mcaster1AudioPipe is primarily a macOS project targeting the CoreAudio HAL AudioServerPlugin API. Future cross-platform support is planned for Windows and Linux.
| Platform | Technology | Driver Type | Status |
|---|---|---|---|
| macOS | CoreAudio HAL AudioServerPlugin | Userspace .driver bundle loaded by coreaudiod |
Active |
| Windows | WDM Virtual Audio Driver | Kernel-mode WDM driver (KMDF) or Audio Processing Object (APO) | Future |
| Linux | PulseAudio / PipeWire Null Sink | Virtual sink via pactl load-module module-null-sink or PipeWire loopback — trivial, built-in OS capability |
Trivial |
pactl load-module module-null-sink sink_name=mcaster1_pipe_1
creates the equivalent virtual device. No custom driver is needed. The Mcaster1DSPEncoder
on Linux can capture directly from these null sinks via PortAudio.
Mcaster1 Ecosystem
Mcaster1AudioPipe is one component in the Mcaster1 broadcast stack. Each component is a standalone project that integrates with the others to form a complete internet radio and broadcast automation platform.
| Component | Role | Description | Platforms |
|---|---|---|---|
| Mcaster1AMP | Media Player | Broadcaster-grade media player with A/B deck playback, DSP processing, ICY 2.2 stream support, playlist automation, and plugin system. Outputs to AudioPipe devices. | macOS, Windows, Linux |
| Mcaster1AudioPipe | Virtual Audio Routing | Creates system-level virtual audio pipe devices for zero-latency internal audio routing between applications. The invisible bridge between player and encoder. | macOS (primary) |
| Mcaster1DSPEncoder | Broadcast Encoder | Multi-format audio encoder with 10-band EQ, AGC/limiter, crossfader DSP chain. Captures from AudioPipe input devices and streams to DNAS/Icecast/Shoutcast servers. | macOS, Windows, Linux |
| Mcaster1DNAS | Streaming Server | Streaming media distribution server (DNAS = Distributed Network Audio Server). Receives encoded audio from the DSP Encoder and distributes to listeners over HTTP. | Linux (primary) |
Integration Flow
Technical Details
AudioServerPlugInDriverInterface Callbacks
The plugin implements the full AudioServerPlugInDriverInterface vtable.
These are the 26 callbacks that CoreAudio invokes on the driver, grouped by function.
| Category | Callback | Description |
|---|---|---|
| Lifecycle | Initialize |
Called once when coreaudiod loads the plugin. Allocates devices, streams, ring buffers. |
Teardown |
Called when the plugin is unloaded. Frees all resources. | |
| Reference Counting | AddRef |
Increments the plugin’s reference count. |
Release |
Decrements the reference count; calls Teardown when it reaches zero. | |
| Property Access | HasProperty |
Returns whether a given object (plugin/device/stream/control) supports a property. |
IsPropertySettable |
Returns whether a property can be modified by the client. | |
GetPropertyDataSize |
Returns the byte size of a property’s value (for buffer allocation). | |
GetPropertyData |
Reads the current value of a property (sample rate, format, volume, device name, etc.). | |
SetPropertyData |
Writes a new value for a settable property (sample rate change, volume level, mute state). | |
| IO Control | StartIO |
Called when a client begins IO on the device. Starts the virtual clock, resets the ring buffer. |
StopIO |
Called when the last client stops IO. Halts the virtual clock. | |
| IO Operations | GetZeroTimeStamp |
Returns the current host-time / sample-time pair for clock synchronization. Driven by mach_absolute_time(). |
WillDoIOOperation |
Returns whether the device supports a given IO operation (read mix, write mix, etc.). | |
BeginIOOperation |
Called before IO data transfer. Used for pre-IO bookkeeping. | |
DoIOOperation |
The core IO callback. Reads from or writes to the ring buffer. Called on a real-time thread. | |
EndIOOperation |
Called after IO data transfer. Used for post-IO bookkeeping. | |
| Object Management | CreateDevice |
Creates a new virtual audio device object (called by the plugin during initialization). |
DestroyDevice |
Destroys a virtual audio device object and frees associated resources. | |
AddDeviceClient |
Called when a client process opens the device. Tracks active client count. | |
RemoveDeviceClient |
Called when a client process closes the device. | |
| Utility | QueryInterface |
COM-style interface query (returns the driver interface pointer). |
ObjectShow |
Debug logging — prints object state to system log. | |
PerformDeviceConfigurationChange |
Executes a pending configuration change (e.g., sample rate switch) atomically. | |
AbortDeviceConfigurationChange |
Cancels a pending configuration change. |
Ring Buffer Specification
Configuration
| Parameter | Value | Notes |
|---|---|---|
| Default capacity | 8192 frames |
Power-of-two for efficient modulo via bitmask (capacity - 1) |
| Sample format | Float32 (native) |
32-bit IEEE 754 floating-point, range ±1.0 |
| Channel count | 2 (stereo) | Interleaved L/R samples: buffer stores capacity * channels floats |
| Synchronization | Atomic load/store | memory_order_acquire (consumer) / memory_order_release (producer) |
| Overflow behavior | Overwrite oldest | Producer advances write head unconditionally; consumer sees latest data |
| Underflow behavior | Zero-fill | Consumer reads silence when buffer is empty (no glitch, just silence) |
Supported Audio Formats
| Sample Rate | Channels | Bit Depth | Format | Use Case |
|---|---|---|---|---|
| 44,100 Hz | 2 (stereo) | 32-bit float | kAudioFormatLinearPCM / kAudioFormatFlagIsFloat |
CD-quality broadcast (MP3, Vorbis) |
| 48,000 Hz | 2 (stereo) | 32-bit float | kAudioFormatLinearPCM / kAudioFormatFlagIsFloat |
Broadcast standard (Opus, AAC, video) |
| 96,000 Hz | 2 (stereo) | 32-bit float | kAudioFormatLinearPCM / kAudioFormatFlagIsFloat |
High-resolution archival (FLAC) |
Object ID Allocation
CoreAudio requires each object (plugin, device, stream, control) to have a unique
AudioObjectID. Mcaster1AudioPipe uses a static allocation scheme:
// Object ID layout per device (N = device index, starting at 0)
//
// Plugin object: kAudioObjectPlugInObject (always 1)
// Device N: base + (N * 4)
// Output stream N: base + (N * 4) + 1
// Input stream N: base + (N * 4) + 2
// Volume control N: base + (N * 4) + 3
//
// Example for 3 devices (base = 100):
// Device 1: 100, 101 (out), 102 (in), 103 (vol)
// Device 2: 104, 105 (out), 106 (in), 107 (vol)
// Device 3: 108, 109 (out), 110 (in), 111 (vol)
Security
Mcaster1AudioPipe has undergone a full SAST (Static Application Security Testing) audit with 38 findings identified and 28 patched. Security hardening spans the driver, IPC layer, GUI, and build system.
Driver & IPC Hardening
- IPC input validation: NUL-termination enforcement, volume value clamping, sample rate whitelist validation
- Thread safety: C11
_Atomictypes for all shared state — no data races on real-time audio threads - Compiler hardening flags: Stack protectors, format-string checks, and position-independent code enabled in the build system
- Unit testing: 26/26 offline driver unit tests passing — full coverage of ring buffer, clock, and property callbacks
GUI Security
- Symlink rejection: File operations reject symbolic links to prevent path traversal attacks
- Atomic writes: Configuration files are written atomically to prevent partial-write corruption
- Shell escape: All subprocess arguments are properly escaped to prevent shell injection
- QPointer guard: Qt object pointers use
QPointerto safely handle deleted objects and avoid use-after-free
Roadmap
Development phases for Mcaster1AudioPipe:
| Phase | Version | Description | Status |
|---|---|---|---|
| AP1 | v0.1.0 | Core Driver — single device, ring buffer loopback | COMPLETE |
| AP2 | v0.2.0 | Multiple Devices + Sample Rates | COMPLETE |
| AP3 | v0.3.0 | Monitor Output + Control App | COMPLETE |
| AP4 | v0.4.0 | Security Hardening + SAST Audit | COMPLETE |
| AP5 | v0.5.0 | Driver Management GUI + Audio Services Control | COMPLETE |
| AP6 | v1.0.0 | Packaging & Distribution (DMG, CI, macdeployqt) | COMPLETE |
License
GNU General Public License v2.0 or later
Mcaster1AudioPipe is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
Copyright (C) 2026 David St. John
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.