gpt4 book ai didi

ios - 用于 MIDI 播放和录音的 AVAudioEngine 同步

转载 作者:可可西里 更新时间:2023-11-01 00:54:29 24 4
gpt4 key购买 nike

问题 1我的第一个问题涉及使用 AVAudioPlayerNodeAVAudioSequencer 进行 MIDI 时的播放同步。基本上我正在尝试通过 MIDI 播放一些东西,但它们需要完美同步。

我知道 AVAudioPlayerNode 有同步方法,但音序器似乎没有类似的方法。

目前我已经尝试在单独的线程上使用 CAMediaTime() + delayusleep,但它们似乎工作得不是很好。

问题 2 我正在使用 engine.inputNode 上的点击来获取录音,与音乐播放分开。不过,好像录音开始得更早了。当我将录制的数据与原始播放进行比较时,差异约为 300 毫秒。我可以在 300 毫秒后开始录制,但即便如此,这也不能保证精确同步,并且可能取决于机器。

所以我的问题是,有什么好的方法可以确保在播放开始的那一刻准确地开始录制?

最佳答案

对于同步音频io,通常最好创建一个引用时间,然后使用这个时间进行所有与时序相关的计算。

AVAudioPlayerNode.play(at:)是你需要的播放器。对于水龙头,您需要使用闭包中提供的时间手动过滤掉(部分)缓冲区。不幸的是,AVAudioSequencer 没有在特定时间开始的功能,但是您可以使用 hostTime(forBeats) 获得与已经播放的音序器的节拍相关的引用时间。 .如果我没记错的话,你不能将音序器设置到负位置,所以这并不理想。

这是一个应该会产生非常准确结果的 hacky 解决方法:

AVAudioSequencer 必须在获取引用时间之前启动,将所有 midi 数据偏移 1,启动音序器,然后立即获取与节拍 1 相关的引用时间,然后将播放器的启动同步到这个时间,并且还可以使用它来过滤掉点击捕获的不需要的音频。

func syncStart() throws {
//setup
sequencer.currentPositionInBeats = 0
player.scheduleFile(myFile, at: nil)
player.prepare(withFrameCount: 4096)

// Start and get reference time of beat 1
try sequencer.start()
// Wait until first render cycle completes or hostTime(forBeats) will err - AVAudioSequencer is fragile :/
while (self.sequencer.currentPositionInBeats <= 0) { usleep(UInt32(0.001 * 1000000.0)) }
var nsError: NSError?
let hostTime = sequencer.hostTime(forBeats: 1, error: &nsError)
let referenceTime = AVAudioTime(hostTime: hostTime)

// AVAudioPlayer is great for this.
player.play(at: referenceTime)

// This just rejects buffers that come too soon. To do this right you need to record partial buffers.
engine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: nil) { (buffer, audioTime) in
guard audioTime.hostTime >= referenceTime.hostTime else { return }
self.recordBuffer(buffer: buffer)
}
}

关于ios - 用于 MIDI 播放和录音的 AVAudioEngine 同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52902746/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com