Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix AVAudioEngine bypassing Multi-Output Device for audio routing

AVAudioEngine's AUHAL layer connects directly to the clock-source
hardware device rather than the virtual Multi-Output (or Aggregate)
device set as the system default, so audio never reaches secondary
members such as BlackHole. Explicitly set CurrentDevice to the system
default output after every engine start and idle resume to force the
engine to honour the full virtual device and its routing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

authored by

Esteban Uribe
Claude Sonnet 4.6
and committed by
prompt.ac/@jeffrey
a49bfe57 9a2eb9ef

+30
+30
slab/menuband/Sources/MenuBand/MenuBandSynth.swift
··· 1 1 import Foundation 2 2 import AVFoundation 3 3 import AudioToolbox 4 + import CoreAudio 4 5 5 6 /// Built-in soft-synth using Apple's bundled GM DLS sound bank 6 7 /// (`gs_instruments.dls`). ··· 80 81 NSLog("MenuBand synth engine start failed: \(error)") 81 82 return 82 83 } 84 + applyOutputDeviceOverride() 83 85 loadDefaultPatches() 84 86 primeForLowLatency() 85 87 ··· 222 224 } 223 225 do { 224 226 try engine.start() 227 + applyOutputDeviceOverride() 225 228 return true 226 229 } catch { 227 230 NSLog("MenuBand synth engine resume failed: \(error)") ··· 323 326 @inline(__always) 324 327 private func sendMIDIEvent(_ au: AudioUnit, status: UInt8, data1: UInt8, data2: UInt8 = 0) { 325 328 MusicDeviceMIDIEvent(au, UInt32(status), UInt32(data1), UInt32(data2), 0) 329 + } 330 + 331 + // MARK: - Output device 332 + 333 + /// AVAudioEngine's AUHAL layer bypasses Multi-Output / Aggregate devices 334 + /// and connects directly to the underlying clock-source hardware device, 335 + /// so audio never reaches secondary members (e.g. BlackHole). Explicitly 336 + /// setting CurrentDevice to the system default after every engine start 337 + /// forces the engine to honour the full virtual device and its routing. 338 + private func applyOutputDeviceOverride() { 339 + guard let au = engine.outputNode.audioUnit else { return } 340 + var addr = AudioObjectPropertyAddress( 341 + mSelector: kAudioHardwarePropertyDefaultOutputDevice, 342 + mScope: kAudioObjectPropertyScopeGlobal, 343 + mElement: kAudioObjectPropertyElementMain) 344 + var deviceID = AudioDeviceID(0) 345 + var size = UInt32(MemoryLayout<AudioDeviceID>.size) 346 + guard AudioObjectGetPropertyData( 347 + AudioObjectID(kAudioObjectSystemObject), &addr, 0, nil, &size, &deviceID 348 + ) == noErr, deviceID != 0 else { return } 349 + let status = AudioUnitSetProperty( 350 + au, kAudioOutputUnitProperty_CurrentDevice, 351 + kAudioUnitScope_Global, 0, 352 + &deviceID, UInt32(MemoryLayout<AudioDeviceID>.size)) 353 + if status != noErr { 354 + NSLog("MenuBand: output device override failed: \(status)") 355 + } 326 356 } 327 357 328 358 // MARK: - Audio tap for visualizer