使用AVAudioEngine为低延迟节拍器安排声音

blw*_*ers 11 ios swift avaudioengine avaudioplayernode

我正在创建一个节拍器作为更大的应用程序的一部分,我有一些非常短的wav文件用作单独的声音.我想使用AVAudioEngine,因为NSTimer存在严重的延迟问题,而Core Audio在Swift中实现起来似乎相当艰巨.我正在尝试以下方法,但我目前无法实现前3个步骤,我想知道是否有更好的方法.

代码大纲:

  1. 根据节拍器的当前设置(每个节拍的节拍数和每个节拍的细分数;节拍的文件A,细分的文件B)创建文件URL数组
  2. 以编程方式根据文件的速度和长度创建具有适当静音帧数的wav文件,并将其插入每个声音之间的数组中
  3. 将这些文件读入单个AudioBuffer或AudioBufferList
  4. audioPlayer.scheduleBuffer(buffer, atTime:nil, options:.Loops, completionHandler:nil)

到目前为止,我已经能够播放单个声音文件的循环缓冲区(步骤4),但我无法从文件数组构建缓冲区或以编程方式创建静默,也没有在StackOverflow上找到任何答案解决这个问题.所以我猜这不是最好的方法.

我的问题是:是否可以使用AVAudioEngine安排一系列低延迟的声音,然后循环该序列?如果没有,哪个框架/方法最适合在Swift编码时调度声音?

5hr*_*hrp 6

我能够制作一个包含文件中的声音和所需长度的静音的缓冲区。希望这会有所帮助:

\n\n
// audioFile here \xe2\x80\x93 an instance of AVAudioFile initialized with wav-file\nfunc tickBuffer(forBpm bpm: Int) -> AVAudioPCMBuffer {\n    audioFile.framePosition = 0 // position in file from where to read, required if you're read several times from one AVAudioFile\n    let periodLength = AVAudioFrameCount(audioFile.processingFormat.sampleRate * 60 / Double(bpm)) // tick's length for given bpm (sound length + silence length)\n    let buffer = AVAudioPCMBuffer(PCMFormat: audioFile.processingFormat, frameCapacity: periodLength)\n    try! audioFile.readIntoBuffer(buffer) // sorry for forcing try\n    buffer.frameLength = periodLength // key to success. This will append silcence to sound\n    return buffer\n}\n\n// player \xe2\x80\x93 instance of AVAudioPlayerNode within your AVAudioEngine\nfunc startLoop() {\n    player.stop()\n    let buffer = tickBuffer(forBpm: bpm)\n    player.scheduleBuffer(buffer, atTime: nil, options: .Loops, completionHandler: nil)\n    player.play()\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Joh*_*cid 5

我认为以尽可能低的时间错误播放声音的可能方法之一是直接通过回调提供音频样本。在 iOS 中,您可以使用AudioUnit.

在此回调中,您可以跟踪样本计数并了解您现在的样本数。从样本计数器,您可以转到时间值(使用采样率)并将其用于节拍器等高级任务。如果您看到是时候播放节拍器声音了,那么您就开始将音频样本从该声音复制到缓冲区。

这是一个没有任何代码的理论部分,但是您可以找到许多AudioUnit回调技术的示例。