removed unwanted bar now with waveforms, major updates and enhancements

This commit is contained in:
Mark Gregory
2026-11-06 14:17:31 +00:00
parent a33ebdc1d3
commit f9655503df
3 changed files with 69 additions and 30 deletions
+20 -27
View File
@@ -57,14 +57,17 @@ import CoreAudio
let range = pending.map{sourceTime >= $0.at ? $0.range : scheduledLoop} ?? scheduledLoop
let raw = pending.map{sourceTime >= $0.at ? $0.raw : scheduledRawLoop} ?? scheduledRawLoop
guard let range,let raw,let source = raw.floatChannelData,!raw.format.isInterleaved else { return false }
let position = displayTime,rate = file.processingFormat.sampleRate
let rate = file.processingFormat.sampleRate,now = mach_absolute_time()
let elapsed = render.isHostTimeValid && now >= render.hostTime ? min(0.1,AVAudioTime.seconds(forHostTime:now-render.hostTime))*tempo : 0
let sample = clock.sampleTime+AVAudioFramePosition(elapsed*rate)+512
let position = loopPosition(anchor+Double(sample)/rate)
let when = AVAudioTime(sampleTime:sample,atRate:rate)
let offset = max(0,min(Int(raw.frameLength)-1,Int((position-range.lowerBound)*rate)))
let count = min(2048,Int(raw.frameLength)-offset)
guard count > 0,let bridge = AVAudioPCMBuffer(pcmFormat:raw.format,frameCapacity:AVAudioFrameCount(count)),let output = bridge.floatChannelData else { return false }
bridge.frameLength = AVAudioFrameCount(count)
for channel in 0..<Int(raw.format.channelCount) { memcpy(output[channel],source[channel].advanced(by:offset),count*MemoryLayout<Float>.size) }
let now = mach_absolute_time(),elapsed = render.isHostTimeValid && now >= render.hostTime ? min(0.1,AVAudioTime.seconds(forHostTime:now-render.hostTime))*tempo : 0
anchor = position-Double(clock.sampleTime)/rate-elapsed;time = position
anchor = position-Double(sample)/rate;time = position
generation = UUID();let token = generation
nativeLoopActive = false;pendingLoopSwitch = nil;scheduledLoop = nil;scheduledRawLoop = nil
// Interrupt only the scheduled loop, leaving the running node and its clock intact.
@@ -77,33 +80,23 @@ import CoreAudio
}
}
if next < file.length {
node.scheduleBuffer(bridge,at:nil,options:.interrupts,completionHandler:nil)
node.scheduleBuffer(bridge,at:when,options:.interrupts,completionHandler:nil)
node.scheduleSegment(file,startingFrame:next,frameCount:AVAudioFrameCount(min(file.length-next,Int64(UInt32.max))),at:nil,completionCallbackType:.dataPlayedBack,completionHandler:finished)
} else {
node.scheduleBuffer(bridge,at:nil,options:.interrupts,completionCallbackType:.dataPlayedBack,completionHandler:finished)
node.scheduleBuffer(bridge,at:when,options:.interrupts,completionCallbackType:.dataPlayedBack,completionHandler:finished)
}
startTimer();self.render();return true
}
private func activateNativeLoopWithoutRestart() -> Bool {
guard playing,let file,let range = preparedLoop,let raw = preparedRawLoop,let buffer = loopBuffer,
let renderTime = node.lastRenderTime,let clock = node.playerTime(forNodeTime:renderTime),
let source = buffer.floatChannelData,!raw.format.isInterleaved else { return false }
let current = displayTime
guard current >= range.lowerBound else { return false } // Keep playing toward a future loop.
let position = current < range.upperBound ? current : range.lowerBound,rate = file.processingFormat.sampleRate
let offset = max(0,min(Int(raw.frameLength)-1,Int((position-range.lowerBound)*rate)))
let count = Int(raw.frameLength)-offset
guard count > 0,let bridge = AVAudioPCMBuffer(pcmFormat:raw.format,frameCapacity:AVAudioFrameCount(count)),let output = bridge.floatChannelData else { return false }
bridge.frameLength = AVAudioFrameCount(count)
for channel in 0..<Int(raw.format.channelCount) { memcpy(output[channel],source[channel].advanced(by:offset),count*MemoryLayout<Float>.size) }
let now = mach_absolute_time(),elapsed = renderTime.isHostTimeValid && now >= renderTime.hostTime ? min(0.1,AVAudioTime.seconds(forHostTime:now-renderTime.hostTime))*tempo : 0
anchor = position-Double(clock.sampleTime)/rate-elapsed;time = position
generation = UUID();pendingLoopSwitch = nil;scheduledLoop = range;scheduledRawLoop = raw;nativeLoopActive = true
// Replace the remaining file command, not the running player. The bridge
// continues the current pass; the prepared buffer then repeats in audio time.
node.scheduleBuffer(bridge,at:nil,options:.interrupts,completionHandler:nil)
node.scheduleBuffer(buffer,at:nil,options:.loops,completionHandler:nil)
render();return true
private func activatePreparedLoop() -> Bool {
guard playing,let range = preparedLoop,preparedRawLoop != nil,loopBuffer != nil else { return false }
let position = displayTime
guard position >= range.lowerBound else { return false }
let handoff = position < range.upperBound ? position : range.lowerBound+(position-range.upperBound).truncatingRemainder(dividingBy:range.upperBound-range.lowerBound)
// File commands cannot be interrupted by scheduling a PCM buffer. Replace
// the node's commands once, with all loop audio already prepared. Keep the
// engine, effects and output route running throughout the handoff.
schedule(at:handoff,play:true,notifyPlayback:false)
return nativeLoopActive
}
private func prepareNativeLoop() {
loopBufferTask?.cancel()
@@ -152,7 +145,7 @@ import CoreAudio
self.node.scheduleBuffer(buffer,at:nil,options:[.loops,.interruptsAtLoop],completionHandler:nil)
self.pendingLoopSwitch = (raw+remaining,start...end,self.preparedRawLoop!)
} else if resume {
_ = self.activateNativeLoopWithoutRestart()
_ = self.activatePreparedLoop()
} else {
self.loopAudioRestartCount += 1
self.schedule(at:position < end ? position : start,play:false,notifyPlayback:false)
@@ -600,7 +593,7 @@ import CoreAudio
if playing, let file, let renderTime = node.lastRenderTime, let playerTime = node.playerTime(forNodeTime:renderTime) {
let next = min(duration,max(0,loopPosition(anchor+Double(playerTime.sampleTime)/file.processingFormat.sampleRate)))
if abs(time-next) > 0.0001 { time = next }
if !nativeLoopActive,loopEnabled,loopBuffer != nil { _ = activateNativeLoopWithoutRestart() }
if !nativeLoopActive,loopEnabled,loopBuffer != nil { _ = activatePreparedLoop() }
if !nativeLoopActive,loopEnabled,let loopIn,let loopOut,time >= loopOut { schedule(at:loopIn,play:true);return }
}
if !fading { setFade(autoFade && fadeSeconds > 0 && duration > 0 ? min(1,max(0,(duration-time)/max(0.5,tempo)/fadeSeconds)) : 1) }