如果您打算开发某种循环应用程序,我认为方法是使用音序器来协调音频文件的播放。在声音应开始播放的每个位置,您都可以向序列中添加一个事件。
定序器可以连接到回调工具,回调工具是将事件传递给用户定义函数的节点。在回调函数中,您将开始播放该事件指示的音频文件。
这是应该做什么的概述:
- 创建一个轨道来包含播放事件,使用AK序列器 https://audiokit.io/docs/Classes/AKSequencer.html's addTrack https://audiokit.io/docs/Classes/AKSequencer.html#/s:8AudioKit11AKSequencerC8addTrack3forAA0cE0CAA6AKNodeC_tF方法。将此轨道连接到AK回调工具 https://audiokit.io/docs/Classes/AKCallbackInstrument.html。请参见这个答案 https://stackoverflow.com/a/61545391/2679386了解如何将 AKCallbackInstrument 连接到 AKSequencer 轨道。设置音序器和轨道的
length
到歌曲的总持续时间(注意这些属性以节拍而不是秒表示)。设置定序器的loopEnabled
如预期的。
- 将播放事件添加到轨道中您希望每个声音播放的时间位置。由于您将使用回调函数自己解释事件,因此您使用什么类型的事件并不重要。你可以简单地使用
Note On
.
- 在回调函数中,收到该事件时开始播放相应的声音。
您需要几个类来表示有关每个样本在歌曲中出现位置的一些基本信息:
class Beat {
var sample: Sample!
var onsetTime: Double
var endTime {
get {
return onsetTime + sample.duration
}
}
}
class Sample {
var url: URL!
var duration: Double
}
class ViewController: UIViewController {
var samples: [Sample] = []
var beats: [Beat] = []
var player: AKPlayer!
var sequencer: AKSequencer!
}
在音符的音高上,您可以存储要播放的样本的索引。这就是你的回调函数的样子:
func playCallback(status:UInt8, note:MIDINoteNumber, vel:MIDIVelocity) -> () {
guard let status = AKMIDIStatus(byte: status),
let type = status.type,
type == .noteOn else { return }
DispatchQueue.main.async {
player.load(samples[note].url)
player.play()
}
}
您可以使用AKSequencer
’s seek
方法在某个任意位置开始播放歌曲。
有一种特殊情况,音序器在节拍中间开始播放。在这种情况下,您必须手动开始播放该样本。
func play(at time: Double = 0) {
sequencer.seek(time)
sequencer.play()
for beat in beats {
if beat.onsetTime < time && time < beat.endTime {
player.load(beat.sample.url)
player.play(from: time - beat.onsetTime)
}
}
}