如何在AKSequencer播放时获取Timebase?需用于功能与应用同步
解决AKSequencer播放时Timebase持续递减的问题
我来帮你分析下这个问题的根源,以及对应的解决方案:
问题出在哪?
你当前计算timeBase的方式mach_absolute_time() - AVAudioTime.hostTime(forSeconds: posSeconds)之所以会持续递减,核心原因是AKSequencer的播放时钟和系统主机时钟存在微小的频率偏差:
sequencer.currentPosition.seconds是由音频硬件时钟驱动的(专门为音频播放优化,精度高);mach_absolute_time()则是系统主机时钟的计时,和音频时钟的增速不可能完全一致。
随着播放持续,两者的时间差会不断累积,导致你计算出的timeBase出现持续变化(你看到的是递减,说明音频时钟走得比主机时钟慢一丢丢)。
正确的解决方案
方案1:播放开始时一次性记录基准偏移
不要每次播放过程中重新计算timeBase,而是在播放启动的瞬间记录好固定的基准值,后续直接复用:
- 先在你的类里加两个属性存基准值:
private var playbackStartHostOffset: UInt64 = 0 private var playbackStartPosition: Double = 0
- 监听AKSequencer的播放启动事件,记录初始基准:
// 注册播放启动通知 NotificationCenter.default.addObserver( forName: .AKSequencerPlaybackStarted, object: sequencer, queue: .main ) { [weak self] _ in guard let self = self else { return } self.playbackStartPosition = self.sequencer.currentPosition.seconds // 计算并保存初始的时间基准偏移 self.playbackStartHostOffset = mach_absolute_time() - AVAudioTime.hostTime(forSeconds: self.playbackStartPosition) }
- 修改你的
getTimebase函数,直接返回预存的基准:
func getTimebase() -> UInt64 { return sequencer.isPlaying ? playbackStartHostOffset : 0 }
用这个基准转换时间的示例:
// 从主机时间转换为对应的播放位置秒数 let currentHostTime = mach_absolute_time() let elapsedHostTime = currentHostTime - getTimebase() let currentPlaybackSeconds = AVAudioTime.seconds(forHostTime: elapsedHostTime)
方案2:直接使用音频时钟(更可靠)
AKSequencer基于AVAudioEngine运行,你可以直接用它关联的音频时钟来获取完全同步的时间基准,彻底避免时钟偏差问题:
func getAudioAlignedTimebase() -> UInt64 { guard sequencer.isPlaying, let audioClock = sequencer.engine?.mainMixerNode.outputAudioClock else { return 0 } // 用音频时钟的当前时间和主机时间计算基准 let currentAudioTime = audioClock.currentTime().seconds return audioClock.hostTime() - AVAudioTime.hostTime(forSeconds: currentAudioTime) }
这个方式的精度更高,因为它直接绑定了音频播放的硬件时钟,和AKSequencer的播放进度完全同步。
额外建议
如果你的核心需求是让其他功能和AKSequencer严格同步,其实更推荐直接使用sequencer.currentPosition或者AVAudioClock的时间,而不是绕到mach_absolute_time——毕竟音频时钟是专门为音频播放设计的,同步性和精度都更有保障。
内容的提问来源于stack exchange,提问作者Fischer
相关产品推荐
相关产品推荐

