【发布时间】:2020-04-16 04:13:47
【问题描述】:
我正在尝试在 iOS 上构建一个音序器应用程序。 Apple Developer 网站上有一个示例,可以让音频单元播放重复音阶,这里:
https://developer.apple.com/documentation/audiotoolbox/incorporating_audio_effects_and_instruments
在示例代码中,有一个文件“SimplePlayEngine.swift”,其中有一个类“InstrumentPlayer”,用于处理将 MIDI 事件发送到选定的音频单元。它生成一个带有循环的线程,该循环遍历比例。它通过调用音频单元的 AUScheduleMIDIEventBlock 发送一个 MIDI Note On 消息,使线程休眠一小段时间,发送一个 Note Off,然后重复。
这是一个删减版:
DispatchQueue.global(qos: .default).async {
...
while self.isPlaying {
// cbytes is set to MIDI Note On message
...
self.audioUnit.scheduleMIDIEventBlock!(AUEventSampleTimeImmediate, 0, 3, cbytes)
usleep(useconds_t(0.2 * 1e6))
...
// cbytes is now MIDI Note Off message
self.noteBlock(AUEventSampleTimeImmediate, 0, 3, cbytes)
...
}
...
}
这对于演示来说效果很好,但它不强制执行严格的时间安排,因为只要线程唤醒,就会安排事件。
我怎样才能修改它以在特定的节奏和采样精确的时间播放音阶?
我的假设是,在每次渲染之前,我需要一种方法让合成器音频单元在我的代码中调用回调,其中包含即将渲染的帧数。然后我可以每隔“x”帧安排一个 MIDI 事件。您可以为scheduleMIDIEventBlock 的第一个参数添加一个偏移量,最大为缓冲区的大小,这样我就可以使用它在给定的渲染周期中将事件安排在正确的帧上。
我尝试使用audioUnit.token(byAddingRenderObserver: AURenderObserver),但我给它的回调从未被调用,即使应用程序正在发出声音。该方法听起来像是 AudioUnitAddRenderNotify 的 Swift 版本,从我在这里读到的内容来看,这听起来像是我需要做的 - https://stackoverflow.com/a/46869149/11924045。怎么不叫呢?甚至有可能使用 Swift 使这个“样本准确”,还是我需要为此使用 C?
我在正确的轨道上吗?感谢您的帮助!
【问题讨论】:
标签: ios avfoundation audiounit audiotoolbox auv3