broken changes

This commit is contained in:
Mark Gregory
2026-11-05 21:10:04 +00:00
parent 83a97ff3fd
commit 78c4411fa6
6 changed files with 218 additions and 21 deletions
+31 -9
View File
@@ -50,7 +50,7 @@ import CoreAudio
guard let loopIn,time-loopIn >= 0.1 else { return };loopOut = time;loopEnabled = true
}
func defineLoop(start: Double,end: Double) {
guard canJog,start.isFinite,end.isFinite,start >= 0,end <= duration,end-start >= 0.1 else { return }
guard canJog,start.isFinite,end.isFinite,start >= 0,end <= duration,end-start >= 0.1-1e-9 else { return }
loopIn = start;loopOut = end;loopEnabled = true
}
func clearLoop() { loopEnabled = false;loopIn = nil;loopOut = nil }
@@ -94,13 +94,35 @@ import CoreAudio
private var persistsMix = true
func routeOutput(_ device: AudioDeviceID,firstChannel: Int = 0) throws {
guard let unit = engine.outputNode.audioUnit else { throw LibraryError.message("Audio output unavailable") }
let running = engine.isRunning;engine.stop();var value = device
let result = AudioUnitSetProperty(unit,kAudioOutputUnitProperty_CurrentDevice,kAudioUnitScope_Global,0,&value,UInt32(MemoryLayout.size(ofValue:value)))
guard result == noErr else { if running { try? engine.start() };throw LibraryError.message("Audio routing failed (\(result))") }
var map = try DJAudioRouting.channelMap(channels:DJAudioDevice.channelCount(device),first:firstChannel)
let mapResult = map.withUnsafeMutableBytes { AudioUnitSetProperty(unit,kAudioOutputUnitProperty_ChannelMap,kAudioUnitScope_Input,0,$0.baseAddress!,UInt32($0.count)) }
guard mapResult == noErr else { if running { try? engine.start() };throw LibraryError.message("Audio channel routing failed (\(mapResult))") }
if running { try engine.start() }
var previousDevice = AudioDeviceID(0),bytes = UInt32(MemoryLayout<AudioDeviceID>.size)
_ = AudioUnitGetProperty(unit,kAudioOutputUnitProperty_CurrentDevice,kAudioUnitScope_Global,0,&previousDevice,&bytes)
func setMap(_ values: inout [Int32]) -> OSStatus {
values.withUnsafeMutableBytes { AudioUnitSetProperty(unit,kAudioOutputUnitProperty_ChannelMap,kAudioUnitScope_Input,0,$0.baseAddress!,UInt32($0.count)) }
}
// Channel-only changes can be applied by HAL without interrupting the player.
if previousDevice == device,setMap(&map) == noErr { return }
tick()
let running = engine.isRunning,resume = playing,position = time,wasWaiting = waitingForBackground
var previousMap = outputChannelMap()
cancelPendingPlayback();generation = UUID();node.stop();engine.stop()
do {
var value = device
let result = AudioUnitSetProperty(unit,kAudioOutputUnitProperty_CurrentDevice,kAudioUnitScope_Global,0,&value,UInt32(MemoryLayout.size(ofValue:value)))
guard result == noErr else { throw LibraryError.message("Audio routing failed (\(result))") }
let mapResult = setMap(&map)
guard mapResult == noErr else { throw LibraryError.message("Audio channel routing failed (\(mapResult))") }
if running || resume { try engine.start() }
if file != nil { schedule(at:position,play:resume,notifyPlayback:wasWaiting) }
} catch {
if previousDevice != 0 {
_ = AudioUnitSetProperty(unit,kAudioOutputUnitProperty_CurrentDevice,kAudioUnitScope_Global,0,&previousDevice,UInt32(MemoryLayout<AudioDeviceID>.size))
if !previousMap.isEmpty { _ = setMap(&previousMap) }
}
if running || resume { try? engine.start() }
if file != nil { schedule(at:position,play:resume && engine.isRunning,notifyPlayback:wasWaiting) }
throw error
}
}
func outputChannelMap() -> [Int32] {
guard let unit = engine.outputNode.audioUnit else { return [] }
@@ -248,7 +270,7 @@ import CoreAudio
self.onPlaybackReady?();self.node.play()
}
}
private func schedule(at seconds: Double, play: Bool) {
private func schedule(at seconds: Double, play: Bool,notifyPlayback: Bool = true) {
guard let file else { return }
cancelPendingPlayback();generation = UUID(); let token = generation
node.stop();if !fading { setFade(1) }
@@ -268,7 +290,7 @@ import CoreAudio
}
}
}
if play { startNodePlayback() }; playing = play; render()
if play { if notifyPlayback { startNodePlayback() } else { node.play() } }; playing = play; render()
}
private func startTimer() {
timer?.invalidate()