# How the Per-App Volume Mixer Uses CoreAudio Aggregate Devices in vorssaint-utils

> Learn how the per app volume mixer in vorssaint-utils uses CoreAudio aggregate devices to apply real-time gain adjustments for individual applications. Control your audio like never before.

- Repository: [vorssaint/vorssaint-utils](https://github.com/vorssaint/vorssaint-utils)
- Tags: internals
- Published: 2026-09-11

---

**The per-app volume mixer in vorssaint-utils creates individual CoreAudio process taps for each application and routes them through private aggregate devices to apply gain adjustments in real-time.**

The `vorssaint-utils` repository implements system-wide per-application volume control on macOS 14.4+ by leveraging low-level CoreAudio APIs that are not exposed through standard system preferences. This architecture intercepts audio at the process level, mixes it through temporary aggregate devices with custom gain staging, and outputs to any selected device without requiring application-specific support.

## Architecture Overview

The mixer operates as a middleman between audio-producing processes and physical output devices. When a user adjusts the volume for a specific app in the UI, the system evaluates whether a tap is necessary, constructs a private audio pipeline, and manages the lifecycle of CoreAudio objects to prevent resource exhaustion or HAL deadlocks.

The core components reside in [`Sources/Vorssaint/Services/Audio/AppVolumeMixer.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Services/Audio/AppVolumeMixer.swift), with routing decisions abstracted into [`MixerRoutingSupport.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/MixerRoutingSupport.swift). The implementation relies on two primary CoreAudio primitives: **process taps** (`AudioHardwareCreateProcessTap`) to capture and mute original app audio, and **aggregate devices** (`AudioHardwareCreateAggregateDevice`) to remix the signal with user-defined gain.

## Device Discovery and State Monitoring

Before any audio processing occurs, the `AppVolumeMixer` class maintains a continuous watch on the CoreAudio Hardware Abstraction Layer (HAL). It monitors three specific properties on a dedicated `halQueue`:

- `kAudioHardwarePropertyDevices` – Available output devices
- `kAudioHardwarePropertyDefaultOutputDevice` – Current system default
- `kAudioHardwarePropertyProcessObjectList` – Running audio-producing processes

When any of these change, `AppVolumeMixer.readSnapshot()` assembles a `RefreshSnapshot` containing the current state. This snapshot drives the decision engine that determines which applications require active tapping and which can pass through to the default output unmodified.

## Tap Requirement Logic

Not every application requires a dedicated audio engine. The [`MixerRoutingSupport.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/MixerRoutingSupport.swift) file contains the `requiresEngine` static function that implements a short-circuit logic to avoid unnecessary CoreAudio object creation:

```swift
// MixerRoutingSupport.swift
static func requiresEngine(
    hasAudioObjects: Bool = true,
    volume: Double,
    selectedOutputDeviceUID: String?,
    targetOutputDeviceUID: String?,
    defaultOutputDeviceUID: String?) -> Bool {
    guard hasAudioObjects else { return false }
    guard let targetOutputDeviceUID else { return false }
    if !isUnity(volume) { return true }                 // user lowered/boosted volume
    guard let selectedOutputDeviceUID else { return false }
    guard let defaultOutputDeviceUID else { return true }
    return selectedOutputDeviceUID != defaultOutputDeviceUID &&
           targetOutputDeviceUID != defaultOutputDeviceUID
}

```

If an application is at 100% volume (`isUnity`) and routed to the system default output device, the mixer returns `false` and avoids creating tap infrastructure entirely. This optimization reduces CPU usage and HAL object count for the common case where most apps use default system routing.

## Engine Construction with Aggregate Devices

When `requiresEngine` returns `true`, `AppVolumeMixer.applyRouting(for:)` spawns a `TapGainEngine` on a dedicated `buildQueue`. This engine constructs the actual audio pipeline through a specific sequence of CoreAudio calls.

First, it creates a process tap that mutes the original application output:

```swift
// AppVolumeMixer.swift – TapGainEngine init
let description = CATapDescription(stereoMixdownOfProcesses: objects)
description.muteBehavior = .mutedWhenTapped
guard AudioHardwareCreateProcessTap(description, &tapID) == noErr else { return nil }

```

Next, it builds a **private aggregate device** containing the tap as a sub-tap and the target output device as the main sub-device:

```swift
let aggregate: [String: Any] = [
    kAudioAggregateDeviceNameKey: "Vorssaint Mixer",
    kAudioAggregateDeviceUIDKey: UUID().uuidString,
    kAudioAggregateDeviceIsPrivateKey: true,
    kAudioAggregateDeviceMainSubDeviceKey: outputDeviceUID,
    kAudioAggregateDeviceSubDeviceListKey: [[kAudioSubDeviceUIDKey: outputDeviceUID]],
    kAudioAggregateDeviceTapListKey: [[
        kAudioSubTapUIDKey: description.uuid.uuidString,
        kAudioSubTapDriftCompensationKey: true,
    ]],
    kAudioAggregateDeviceTapAutoStartKey: true,
]
guard AudioHardwareCreateAggregateDevice(aggregate as CFDictionary, &aggregateID) == noErr else {
    AudioHardwareDestroyProcessTap(tapID); return nil
}

```

The `kAudioAggregateDeviceIsPrivateKey: true` flag ensures these temporary devices do not appear in system audio preference panes or other applications' device lists, keeping the UI clean while the mixer manages dozens of potential concurrent pipelines.

## Real-Time Audio Processing

Once constructed, the aggregate device's `IOProc` receives audio frames from the tapped process. Inside [`AppVolumeMixer.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/AppVolumeMixer.swift), the engine applies the user-specified gain and implements protective limiting:

```swift
// Inside the IOProc block (AppVolumeMixer.swift)
let gain = box.value
let frames = MixerRender.render(source: inputBuffers[tapIndex],
                                into: outputBuffers,
                                gain: gain)
if frames > 0 { cycles.increment() }
// Apply limiter, fallback if needed

```

A `BoostLookaheadBufferListLimiter` prevents digital clipping when users set gain above 100%. This look-ahead limiter analyzes upcoming audio frames to apply transparent gain reduction only when necessary, preserving audio quality during sudden transient spikes.

## Lifecycle Management and Cleanup

The mixer maintains an `engines: [String: any GainEngine]` dictionary keyed by process identifier to track active pipelines. When users adjust volume or change output devices, `applyRouting` either updates the existing engine's gain property or destroys and rebuilds the engine if the routing target changes.

Engine teardown is intentionally throttled using `maximumConcurrentTeardowns = 4` to prevent overwhelming the CoreAudio HAL with simultaneous destruction requests, which can cause deadlock or audio dropouts across the system. This throttling mechanism ensures that rapid UI changes—such as dragging a volume slider—do not destabilize the audio subsystem.

## Permission Handling and Edge Cases

Creating process taps requires the **Screen Recording / Audio Recording** permission on macOS 14.4 and later. If `AudioHardwareCreateProcessTap` returns a permission error, the mixer sets a `needsPermission` flag that propagates to the UI layer ([`MixerSection.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/MixerSection.swift)), directing users to System Settings to grant access.

The implementation also includes headphone-disconnect protection. If the system detects headphones unplugging, the mixer optionally lowers the system output volume and stores the previous level in `loweredOutput`, restoring it when the original device returns to prevent sudden loud playback through speakers.

## Summary

- **Process taps** mute original app audio and redirect it into the mixer's private pipeline via `AudioHardwareCreateProcessTap`.
- **Private aggregate devices** function as invisible mixers, combining the tapped stream with the target output device while applying user-defined gain.
- **Smart routing logic** in [`MixerRoutingSupport.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/MixerRoutingSupport.swift) avoids creating engines for apps at 100% volume using the default output, optimizing performance.
- **Look-ahead limiting** prevents clipping when boosting audio above unity gain.
- **Throttled teardown** and `maximumConcurrentTeardowns` protect the HAL from deadlocks during rapid configuration changes.

## Frequently Asked Questions

### What is a CoreAudio aggregate device in the context of vorssaint-utils?

In vorssaint-utils, a CoreAudio aggregate device is a temporary virtual audio device created for each tapped application. It combines the process tap (input) and the desired output device (main sub-device) into a single logical unit that the system treats as one audio device, allowing the mixer to intercept and modify the audio stream before it reaches physical hardware.

### Why does the mixer require Screen Recording permission on macOS 14.4+?

The permission requirement stems from `AudioHardwareCreateProcessTap`, which captures audio from other processes' output streams. macOS classifies this capability as audio recording (process tapping) and requires explicit user consent through the Screen Recording & Audio Recording privacy pane, even though the mixer does not record to disk or capture screen content.

### How does vorssaint-utils handle volume changes without creating audio glitches?

The mixer implements two protective mechanisms: **throttled engine teardown** (limiting concurrent destructions to 4) prevents HAL overload, and a **look-ahead limiter** (`BoostLookaheadBufferListLimiter`) prevents digital clipping when boosting volume above 100%. These ensure smooth transitions when users drag volume sliders or switch output devices.

### Can the mixer route different apps to different physical devices simultaneously?

Yes. Each `TapGainEngine` creates a private aggregate device targeting a specific output device UID. If User A routes Spotify to Headphones and Chrome to Built-in Output, the mixer maintains two separate aggregate devices with independent taps and gain settings, routing each app's audio to its designated hardware endpoint.