Hej。 Swiftの新しいAVAudioEngine
でリアルタイムオーディオアプリケーションを実装したい。新しいフレームワークを誰かが経験しましたか?リアルタイムアプリケーションはどのように機能しますか?
私の最初のアイデアは、(処理された)入力データを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を好むなら、私はすべてのメモ、コメント、コメント、解決策などに感謝します。
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が適切でない場合があります。この質問に対する私の回答を参照してください: installTapOnBus:bufferSize:format:block :)を1秒あたり20回呼び出します
私はこれがあなたが望むことをすると思います: https://github.com/arielelkin/SwiftyAudio
歪みをコメントアウトすると、クリーンなループになります。
AppleはSwiftのリアルタイムコーディングに関して公式の立場をとっています。 2017年のCore AudioのWWDCセッション で、Appleエンジニアは、Swiftまたは実際のObjective Cメソッドのいずれも使用しないと述べました-timeオーディオコールバックコンテキスト(暗黙的にCのみ、またはおそらくC++とASMを使用することを意味します)。
これは、SwiftおよびObjective Cメソッドを使用すると、制限のないレイテンシのない内部メモリ管理操作が含まれる可能性があるためです。
これは、AVAudioEngineコントローラークラスにObjective Cを使用するのが適切なスキームである可能性があるが、tapOnBusブロック内の(Objective Cの)Cサブセットのみを意味します。
Apple at WWDC2017 in What's New in Core Audio Video によると、エンジニアはSwiftリアルタイムのコンテキストから「安全ではない」。
ここで、エンジンのレンダリングはリアルタイムのコンテキストから行われるため、オフラインでのObjective-Cのレンダリングや、デモで見たSwiftメタ)を使用できなくなります。
そして、Objective-CまたはSwiftリアルタイムコンテキストからのランタイムを使用するのは安全ではないためです。そのため、代わりに、エンジン自体が、検索とキャッシュが可能なレンダーブロックを提供し、後でこのレンダーブロックを使用して、リアルタイムコンテキストからエンジンをレンダリングします。次のことは、実行することです。入力ノードをセットアップして、入力データをエンジンに提供できるようにします。ここでは、提供する入力の形式を指定します。これは出力とは異なる形式にすることができます。また、エンジンは、入力データが必要になるたびに呼び出し、このブロックが呼び出されると、エンジンは実際に必要な入力フレームの数を通知します。