18

嘿。我想用AVAudioEngineSwift 中的新功能实现一个实时音频应用程序。有人对新框架有经验吗?实时应用程序如何工作?

我的第一个想法是将(处理过的)输入数据存储到一个AVAudioPCMBuffer对象中,然后让它播放,AVAudioPlayerNode正如您在我的演示类中看到的那样:

import AVFoundation

class AudioIO {
    var audioEngine: AVAudioEngine
    var audioInputNode : AVAudioInputNode
    var audioPlayerNode: AVAudioPlayerNode
    var audioMixerNode: AVAudioMixerNode
    var audioBuffer: AVAudioPCMBuffer

    init(){
        audioEngine = AVAudioEngine()
        audioPlayerNode = AVAudioPlayerNode()
        audioMixerNode = audioEngine.mainMixerNode

        let frameLength = UInt32(256)
        audioBuffer = AVAudioPCMBuffer(PCMFormat: audioPlayerNode.outputFormatForBus(0), frameCapacity: frameLength)
        audioBuffer.frameLength = frameLength

        audioInputNode = audioEngine.inputNode

        audioInputNode.installTapOnBus(0, bufferSize:frameLength, format: audioInputNode.outputFormatForBus(0), block: {(buffer, time) in
            let channels = UnsafeArray(start: buffer.floatChannelData, length: Int(buffer.format.channelCount))
            let floats = UnsafeArray(start: channels[0], length: Int(buffer.frameLength))

            for var i = 0; i < Int(self.audioBuffer.frameLength); i+=Int(self.audioMixerNode.outputFormatForBus(0).channelCount)
            {
                // doing my real time stuff
                self.audioBuffer.floatChannelData.memory[i] = floats[i];
            }
            })

        // setup audio engine
        audioEngine.attachNode(audioPlayerNode)
        audioEngine.connect(audioPlayerNode, to: audioMixerNode, format: audioPlayerNode.outputFormatForBus(0))
        audioEngine.startAndReturnError(nil)

        // play player and buffer
        audioPlayerNode.play()
        audioPlayerNode.scheduleBuffer(audioBuffer, atTime: nil, options: .Loops, completionHandler: nil)
    }
}

但这与实时相去甚远,而且效率不高。有什么想法或经验吗?没关系,如果您更喜欢 Objective-C 或 Swift,我感谢所有注释、评论、评论、解决方案等。

4

4 回答 4

12

我一直在 Objective-C 和 Swift 中尝试使用 AVAudioEngine。在我的引擎的 Objective-C 版本中,所有音频处理都完全在 C 中完成(通过缓存可通过 AVAudioPCMBuffer 获得的原始 C 样本指针,并仅使用 C 代码对数据进行操作)。表演令人印象深刻。出于好奇,我将这个引擎移植到了 Swift。对于线性播放音频文件或通过 FM 合成生成音调之类的任务,性能非常好,但只要涉及到阵列(例如,使用颗粒合成,音频部分会以非线性方式播放和操作),对性能有重大影响。即使进行了最佳优化,CPU 使用率也比 Objective-C/C 版本高 30-40%。我是 Swift 新手,所以也许还有其他我不知道的优化,但据我所知,C/C++ 仍然是实时音频的最佳选择。另请查看 The Amazing Audio Engine。我正在考虑这一点,以及直接使用旧的 C API。

如果您需要处理现场音频,那么 AVAudioEngine 可能不适合您。请参阅我对这个问题的回答:我想每秒调用 20 次 installTapOnBus:bufferSize:format:block:

于 2014-10-28T13:29:25.620 回答
6

我认为这可以满足您的要求:https ://github.com/arielelkin/SwiftyAudio

注释掉失真,你就有了一个干净的循环。

于 2015-06-15T15:12:53.327 回答
5

Apple 对 Swift 中的实时编码采取了官方立场。在2017 年关于 Core Audio 的 WWDC 会议上,一位 Apple 工程师表示不要在实时音频回调上下文中使用 Swift 或 Objective C 方法(这意味着只使用 C,或者可能是 C++ 或汇编代码)。

这是因为使用 Swift 和 Objective C 方法可能涉及没有有限延迟的内部内存管理操作。

这意味着正确的方案可能是为您的 AVAudioEngine 控制器类使用 Objective C,但仅在 tapOnBus 块内使用 C 子集(Objective C)。

于 2017-07-29T00:37:29.113 回答
2

根据 Apple 在 WWDC2017上大约 19:20 的核心音频视频中的新增功能,工程师表示从实时环境来看,使用 Swift 是“不安全的”。从成绩单发布。

现在,由于引擎的渲染发生在实时上下文中,您将无法使用我们在演示中看到的离线渲染 Objective-C 或 Swift 元数据。

这是因为,在实时上下文中使用 Objective-C 或 Swift 运行时是不安全的。因此,引擎本身为您提供了一个渲染块,您可以搜索和缓存它,然后使用此渲染块从实时上下文中渲染引擎。接下来要做的就是设置您的输入节点,以便您可以将输入数据提供给引擎。在这里,您指定您将提供的输入格式,这可以是与输出不同的格式。并且您还提供了一个块,引擎将在需要输入数据时调用它。当这个块被调用时,引擎会让你知道它实际需要多少输入帧。

于 2018-07-25T19:50:28.097 回答