v1.0.0 (AP6)
GitHub Roadmap Test Harness

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.

Language: C (CoreAudio HAL API)
Type: AudioServerPlugin
Target: macOS ARM64 / Intel
License: GPL-2.0-or-later
Author: David St. John
Version
1.0.0
Platform
macOS (primary)
Driver Type
HAL Plugin
Ecosystem
Mcaster1
1

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
Why not use BlackHole or Soundflower? Mcaster1AudioPipe is purpose-built for the Mcaster1 broadcast ecosystem. It supports multiple numbered device instances, integrates tightly with Mcaster1AMP and Mcaster1DSPEncoder, and provides a controlled, code-signed driver with no third-party dependencies.
2

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: .driver bundle (CFBundle with AudioServerPlugIn type)
  • Install path: /Library/Audio/Plug-Ins/HAL/Mcaster1AudioPipe.driver
  • Entry point: AudioServerPlugInDriverInterface vtable with 26 callbacks
  • Process context: Loaded by coreaudiod at daemon startup; runs in the audio server’s address space
  • Object hierarchy: Plugin → Device(s) → Stream(s) → Controls

Object Hierarchy

AudioServerPlugin (kAudioObjectPlugInObject) | +--- AudioDevice "Mcaster1AudioPipe 1" (kAudioDeviceClassID) | | | +--- AudioStream [Output] (kAudioStreamClassID) // AMP writes here | +--- AudioStream [Input] (kAudioStreamClassID) // Encoder reads here | +--- AudioControl Volume (kAudioVolumeControlClassID) | +--- AudioControl Mute (kAudioBooleanControlClassID) | +--- AudioDevice "Mcaster1AudioPipe 2" | | | +--- AudioStream [Output] | +--- AudioStream [Input] | +--- AudioControl Volume / Mute | +--- AudioDevice "Mcaster1AudioPipe N" ...

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.

3

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

┌──────────────────────┐ ┌─────────────────────────┐ ┌──────────────────────┐ ┌────────────┐ Mcaster1AMP Mcaster1AudioPipe 1 Mcaster1DSPEncoder Mcaster1 Instance 1 │────▶│ Ring Buffer (SPSC) │────▶│ Encoder Slot 1 │────▶│ DNAS /yolo-rock playlist Output → Input EQ → AGC → MP3 /yolo-rock └──────────────────────┘ └─────────────────────────┘ └──────────────────────┘ └────────────┘ ┌──────────────────────┐ ┌─────────────────────────┐ ┌──────────────────────┐ ┌────────────┐ Mcaster1AMP Mcaster1AudioPipe 2 Mcaster1DSPEncoder Mcaster1 Instance 2 │────▶│ Ring Buffer (SPSC) │────▶│ Encoder Slot 2 │────▶│ DNAS /yolo-country list Output → Input EQ → AGC → Vorbis /yolo-ctry └──────────────────────┘ └─────────────────────────┘ └──────────────────────┘ └────────────┘ ┌──────────────────────┐ ┌─────────────────────────┐ ┌──────────────────────┐ Mcaster1AMP Mcaster1AudioPipe 3 Monitor Output Instance 3 │────▶│ Ring Buffer (SPSC) │────▶│ Real speakers (preview / cue) + Monitor tap (optional) └──────────────────────┘ └─────────────────────────┘ └──────────────────────┘
Zero-latency routing. Audio passes through a shared memory ring buffer with no kernel transitions, no sample-rate conversion, and no buffering beyond the IO cycle. The only latency is the CoreAudio IO buffer size (typically 256–512 frames at 44.1/48 kHz).
4

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.

5

Project Structure

Mcaster1AudioPipe/ | +--- src/ | +--- Mcaster1AudioPipe.c // AudioServerPlugInDriverInterface implementation | +--- ring_buffer.h // Lock-free SPSC ring buffer (header-only) | +--- Info.plist // Bundle metadata (CFBundleIdentifier, version) | +--- scripts/ | +--- install.sh // Install to /Library/Audio/Plug-Ins/HAL/ | +--- uninstall.sh // Remove driver + restart coreaudiod | +--- docs/ | +--- index.html // This documentation file | +--- PLANNING.html // Phase tracking and roadmap | +--- DEV-TEST-HARNESS.html // Windows driver test harness guide | +--- Makefile // Build .driver bundle +--- CLAUDE.md // Project memory for AI-assisted development

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.

6

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
No reboot required — auto-restarts coreaudiod. After installing or uninstalling the driver, 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
Code signing. For distribution, sign the driver bundle with a valid Developer ID certificate: codesign --force --deep --sign "Developer ID Application: ..." build/Mcaster1AudioPipe.driver. For local development, ad-hoc signing works: codesign --force --deep --sign - build/Mcaster1AudioPipe.driver.
7

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
Linux note: On Linux, virtual audio routing is a built-in capability of PulseAudio and PipeWire. A simple 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.
8

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

Mcaster1AMP ──▶ Mcaster1AudioPipe ──▶ Mcaster1DSPEncoder ──▶ Mcaster1DNAS ──▶ Listeners Plays media Routes audio Encodes + DSP Distributes Tune in via from library between apps (MP3/Vorbis/Opus) to listeners web browser or streams via ring buffers with EQ, AGC, etc. via HTTP or player
9

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)
10

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 _Atomic types 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 QPointer to safely handle deleted objects and avoid use-after-free
11

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
12

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.