我在 AVAudioEngine
中使用多个 AVAudioPlayerNode
混合音频文件以进行播放。一旦所有设置都完成(引擎准备好、启动、音频文件分段安排好),我会在每个播放器节点上调用play()
方法开始播放。
由于循环遍历所有播放器节点需要时间,因此我会获取第一个节点的lastRenderTime
值的快照,并用它来计算节点play(at:)
方法的开始时间,以保持节点之间的播放同步:
let delay = 0.0
let startSampleTime = time.sampleTime // time is the snapshot value
let sampleRate = player.outputFormat(forBus: 0).sampleRate
let startTime = AVAudioTime(
sampleTime: startSampleTime + AVAudioFramePosition(delay * sampleRate),
atRate: sampleRate)
player.play(at: startTime)
问题出在当前播放时间上。
我使用以下计算方法来获取该值,其中seekTime
是我跟踪的值,以防我们寻找播放器。在开始时它的值为0.0
:
private var _currentTime: TimeInterval {
guard player.engine != nil,
let lastRenderTime = player.lastRenderTime,
lastRenderTime.isSampleTimeValid,
lastRenderTime.isHostTimeValid else {
return seekTime
}
let sampleRate = player.outputFormat(forBus: 0).sampleRate
let sampleTime = player.playerTime(forNodeTime: lastRenderTime)?.sampleTime ?? 0
if sampleTime > 0 && sampleRate != 0 {
return seekTime + (Double(sampleTime) / sampleRate)
}
return seekTime
}
虽然这产生了一个相对正确的值,但我能听到我演奏和我听到第一个声音之间的延迟。因为一旦我调用play(at:)
,lastRenderTime
立即开始前进,并且必须有某种处理/缓冲时间偏移。
明显的延迟约为100毫秒,非常大,我需要精确的当前时间值来进行可视化渲染。
可能没有关系,但每个音频文件都是AAC音频,我在播放器节点中安排它们的段落,我不直接使用缓冲区。
段长度可能会有所不同。一旦我安排好音频数据,我还会在每个播放器节点上调用prepare(withFrameCount:)
。
所以我的问题是,我观察到的延迟是否是缓冲问题?(我的意思是,例如,我应该安排较短的片段),是否有一种方法可以精确计算此值,以便我可以调整当前播放时间计算?
当我在一个AVAudioPlayerNode
上安装一个tap块时,该块被调用并带有一个长度为4410
的缓冲区,采样率为44100 Hz
,这意味着0.1秒的音频数据。我应该依赖此来计算延迟吗?
我想知道是否可以信任我在tap块中获得的缓冲区长度。另外,我试图计算我的音频图的总延迟。有人可以提供关于如何精确确定此值的见解吗?