使用Swift和AVAudioEngine计算音乐应用中的歌曲位置

从根本上讲,音乐只是按时组织的声音。 因此,在制作音乐工具时,指出您所在的时间会很有帮助。

熟悉的界面(例如Logic中的位置指示器)可以在传输栏中解决此问题:

而且,如果您碰巧使用了像AVAudioSequencer这样的MIDI音序AVAudioSequencer ,则可以使用方便的属性(例如currentPositionInBeats: TimeIntervalcurrentPositionInSeconds: TimeInterval访问该值。

但是,如何使用基于香草AVAudioEngine的应用程序执行此操作? 也许您有一个只想监视录制时间或利用拍子信息增强实时演奏的应用程序?

捕获参考时间

如果您有某种参考时间,则只能确定当前位置。 捕获此值可能会与其他事件相关联,例如开始播放或首次触发乐器。

从AVAudioEngine的默认输出节点获取参考时间很方便。

 var engine = AVAudioEngine() 
var offsetTime = engine.outputNode.lastRenderTime

以秒为单位计算当前位置

计算当前位置实际上只是获取当前时间并减去参考时间的问题。 这是计算实例变量的良好候选者。

在这里,我使用音频帧中的差异,然后除以采样率以获得持续时间秒数。

 var currentPositionInSeconds: TimeInterval { 
get {
let lastRenderTime = engine.outputNode.lastRenderTime
else { return 0 }
let frames = lastRenderTime.sampleTime -
offsetTime.sampleTime
return Double(frames) / offsetTime.sampleRate
}
}

计算节拍中的当前位置

如果您有速度信息,则节拍中的当前位置可能会更有帮助。 使用节拍格式,您还可以跟踪小节和小节之类的内容。

利用我之前写的这个简单的Tempo结构,我们可以将currentPositionInSeconds除以节拍秒数。

 let myTempo = Tempo(bpm: 120.0) 
var currentPositionInBeats: TimeInterval {
get { return currentPositionInSeconds / tempo.seconds() }
}

显示节拍

就像AVAudioSequencercurrentPositionInBeats一样,这将输出AVAudioSequencer以及中间的许多小位置。

要计算小节或小节,首先需要知道小节中有多少拍。 对于4/4时间,它就是4。由于您只需要整数,因此需要四舍五入。

 let position = audioEngine.currentPositionInBeats 
let bars = Int(floor(position / 4))

要显示1.2来表示第一小节,第二节拍,您只需要查看其余部分即可。

 let bars = Int(floor(position / 4)) 
let beats = Int(floor(position.truncatingRemainder(dividingBy: 4)))
print("\(bars).\(beats)")

要获得更高的粒度,您可以仅使用值除以1/32的其他除法,显示原始余数,或显示其他刻度。