# How Vorssaint Tracks Audio Output Routing and Per-App Volume Settings

> Learn how Vorssaint tracks audio output routing and per-app volume settings on macOS. Discover its MixerRoutingSupport helper class and Core Audio integration for real-time volume control.

- Repository: [vorssaint/vorssaint-utils](https://github.com/vorssaint/vorssaint-utils)
- Tags: deep-dive
- Published: 2026-09-06

---

**Vorssaint utilizes a `MixerRoutingSupport` helper class that interfaces directly with macOS Core Audio to monitor default output devices, persist per-application volume fractions in `UserDefaults`, and apply real-time volume adjustments using `AudioObjectSetPropertyData`.**

Vorssaint's approach to granular audio management centers on the open-source `vorssaint/vorssaint-utils` repository and its sophisticated handling of system-level audio APIs. By tracking audio output routing and per-app volume settings through a dedicated helper class, the application enables users to maintain independent volume levels for each application while seamlessly switching between output devices.

## Core Architecture: The MixerRoutingSupport Helper

The entire audio routing system revolves around **`MixerRoutingSupport`**, a Swift helper defined in [`Sources/Vorssaint/UI/MenuPanel/MixerSection.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/MenuPanel/MixerSection.swift). This class serves as the exclusive bridge between the application's UI layer and macOS Core Audio, handling three primary responsibilities:

- **Device Monitoring**: Registers property listeners on `kAudioHardwarePropertyDefaultOutputDevice` to detect when users plug in headphones or switch to external displays.
- **Volume Persistence**: Maintains a `[String: Double]` dictionary in `UserDefaults` that maps application identifiers to linear volume fractions (0.0 to 1.0).
- **System Integration**: Translates UI percentages into Core Audio scalar values using `AudioObjectGetPropertyData` and `AudioObjectSetPropertyData` on `kAudioDevicePropertyVolumeScalar`.

The UI components, including `MixerSection` and `MixerPercentNativeTextField`, bind to this helper rather than accessing Core Audio directly, ensuring consistent behavior across the application.

## Tracking Default Output Devices with Core Audio

Vorssaint implements live device tracking through a multi-step initialization and monitoring process:

1. **Initial Detection**: On launch, `MixerRoutingSupport.systemDefaultSelectionID` queries the current default output device using `AudioObjectGetPropertyData` on the `kAudioHardwarePropertyDefaultOutputDevice` selector.
2. **Continuous Monitoring**: The helper registers an `AudioObjectPropertyListener` callback that triggers whenever the system reports a hardware change, such as headphones being connected or disconnected.
3. **Programmatic Switching**: When users activate the Output Switcher feature, `MixerRoutingSupport.nextSelectedOutputDeviceUID(from:in:)` rotates through a user-defined list of device UIDs and commits the selection via `AudioObjectSetPropertyData`.

This architecture ensures that `mixer.currentOutputDeviceUID` always reflects the actual system state, allowing the UI to update instantly when hardware changes occur.

## Persisting Per-App Volume Levels

Per-application volume storage relies on stable identity keys generated by `MixerRoutingSupport.rowIdentity(bundleIdentifier:)`:

- **Key Generation**: For applications with valid bundle identifiers (e.g., `com.apple.Music`), the helper uses the bundle ID as the dictionary key. For processes lacking bundle information, it falls back to the display name.
- **Storage Format**: Volume levels are stored as raw `Double` fractions in `UserDefaults`, accessible via `MixerRoutingSupport.savedVolumeFraction(for:)`.
- **Retrieval**: When constructing the mixer interface, `MixerRoutingSupport.rowMayBeTapped(savedVolume:)` retrieves the persisted fraction or returns a system default if no prior setting exists.

The persistence layer guarantees that volume preferences survive application restarts and system reboots without requiring background processes.

## Applying Volume Changes to the System

When users adjust a volume slider, the UI invokes `MixerRoutingSupport.setVolume(forApp:toFraction:)`, which executes a three-phase update:

1. **Translation**: Converts the linear fraction (0.0–1.0) to the device-specific scalar volume scale required by Core Audio.
2. **System Commit**: Calls `AudioObjectSetPropertyData` targeting the specific output device's `kAudioDevicePropertyVolumeScalar` address.
3. **Persistence**: Writes the new fraction back to `UserDefaults` under the application's identity key.

This synchronous write-through strategy ensures that volume changes take effect immediately at the hardware level while maintaining durable state for future sessions.

## Handling Edge Cases and Error States

The helper includes defensive logic for common hardware scenarios found in [`Tests/MetricsTests.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Tests/MetricsTests.swift):

- **Headphone Disconnect Protection**: If the current output device disappears (e.g., Bluetooth headphones powering down), Vorssaint automatically restores a previously saved "audible floor" volume or the last known safe level, preventing silent output. This behavior is verified by the `headphone disconnect protection starts at an audible volume` test case.
- **Read-Only Volumes**: When encountering read-only audio destinations (such as Disk Images mounted as audio devices), the helper sets a `volumeIsReadOnly` flag that disables sliders and prevents write attempts to `kAudioDevicePropertyVolumeScalar`.
- **Input Validation**: malformed percentage strings (e.g., `"loud"` or `"nan"`) are rejected by `volumeFraction(fromPercentageText:)`, preserving the existing volume rather than applying undefined behavior.

## Code Example: Reading and Writing Audio Routes

The following Swift patterns demonstrate direct interaction with the routing system:

```swift
import Vorssaint

// Retrieve the current system output device UID
let currentUID = MixerRoutingSupport.systemDefaultSelectionID

// Rotate to the next device in a predefined list
if let nextUID = MixerRoutingSupport.nextSelectedOutputDeviceUID(
        from: currentUID,
        in: ["Built-in Output", "AirPods Pro", "Studio Display"])
{
    try MixerRoutingSupport.setCurrentOutputDeviceUID(nextUID)
}

// Read the saved volume for a specific application
let bundleID = "com.spotify.client"
let savedLevel = MixerRoutingSupport.savedVolumeFraction(for: bundleID)

// Apply a new 75% volume level and persist it
try MixerRoutingSupport.setVolume(
        forApp: bundleID,
        toFraction: 0.75)

```

All operations route through the same Core Audio pathways used by the system Sound preferences panel, ensuring compatibility with hardware volume controls and system notifications.

## Summary

- **Centralized Control**: `MixerRoutingSupport` in [`Sources/Vorssaint/UI/MenuPanel/MixerSection.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/MenuPanel/MixerSection.swift) manages all interactions with macOS Core Audio for device routing and volume control.
- **Live Monitoring**: Property listeners on `kAudioHardwarePropertyDefaultOutputDevice` enable real-time updates when hardware changes occur.
- **Durable Storage**: Per-app volumes persist as `[String: Double]` maps in `UserDefaults`, keyed by bundle identifier or display name.
- **Immediate Application**: Volume changes apply instantly via `AudioObjectSetPropertyData` on `kAudioDevicePropertyVolumeScalar`, with fallbacks for disconnected hardware and read-only devices.

## Frequently Asked Questions

### How does Vorssaint identify individual applications for volume tracking?

Vorssaint generates stable identity keys using `MixerRoutingSupport.rowIdentity(bundleIdentifier:)`. For standard applications with bundle identifiers, it uses the reverse-DNS string (e.g., `com.apple.Music`). For background processes or helpers without bundle IDs, it falls back to the process display name, ensuring every audio-producing entity receives a unique persistence key.

### What happens to per-app volume settings when I switch audio devices?

Per-app volume fractions remain stored in `UserDefaults` keyed by application identity, not by output device. When switching devices via `nextSelectedOutputDeviceUID(from:in:)`, Vorssaint applies the stored volume levels to the new hardware immediately. The system does not maintain separate volume profiles per device; instead, it applies the last known user preference regardless of which speakers or headphones are active.

### Does Vorssaint handle unexpected hardware disconnections?

Yes. The helper includes headphone disconnect protection logic verified in [`Tests/MetricsTests.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Tests/MetricsTests.swift). If the current output device disappears (such as Bluetooth headphones losing connection), Vorssaint detects the property change and restores volume to an audible floor level or the previously saved safe value, preventing scenarios where the system becomes unexpectedly silent.

### Where are the per-app volume levels physically stored?

Volume data persists in the application's `UserDefaults` domain as a `[String: Double]` dictionary. The `Preferences` struct wraps this storage, and `MixerRoutingSupport` synchronizes the in-memory `Mixer` object with this backing store on every volume change, ensuring data survives application termination and system restarts without requiring elevated privileges or background daemons.