Аудио в реальном времени с AVAudioEngine
Хей. Я хочу реализовать аудио-приложение в реальном времени с новым AVAudioEngine
в Swift. Кто-нибудь сталкивается с новой структурой? Как работают приложения реального времени?
Моя первая идея состояла в том, чтобы сохранить (обработанные) входные данные в объект 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, я благодарен за все замечания, замечания, комментарии, решения и т.д.
Ответы
Ответ 1
Я экспериментировал с AVAudioEngine как в Objective-C, так и в Swift. В версии моего моделярия Objective-C вся обработка звука выполняется исключительно на C (путем кэширования указателей образцов исходного кода, доступных через AVAudioPCMBuffer, и для работы с данными только с кодом C). Эффективность впечатляет. Из любопытства я поместил этот двигатель в Свифт. С такими задачами, как воспроизведение аудиофайла линейно или генерирование тонов с помощью синтеза FM, производительность неплохая, но как только задействованы массивы (например, с гранулированным синтезом, где разделы аудио воспроизводятся и управляются нелинейным способом), есть значительный успех. Даже при максимальной оптимизации использование ЦП на 30-40% больше, чем при использовании версии Objective-C/C. Я новичок в Swift, поэтому, возможно, есть и другие оптимизации, о которых я не осведомлен, но, насколько я могу судить, C/С++ по-прежнему является лучшим выбором для аудио в реальном времени. Также посмотрите на The Amazing Audio Engine. Я рассматриваю это, а также прямое использование более старого API C.
Если вам нужно обработать живое аудио, тогда AVAudioEngine может не быть для вас. См. Мой ответ на этот вопрос: Я хочу вызывать 20 раз в секунду installTapOnBus: bufferSize: format: block:
Ответ 2
Я думаю, что это делает то, что вы хотите: https://github.com/arielelkin/SwiftyAudio
Прокомментируйте искажения, и у вас есть чистый цикл.
Ответ 3
Apple заняла официальную позицию по кодированию в реальном времени в Swift. На сессии WWDC 2017 года по Core Audio инженер Apple сказал, что не следует использовать методы Swift или Objective C в контексте обратного вызова аудио в реальном времени (подразумевается использование только C, или, возможно, C++ и ASM).
Это связано с тем, что использование методов Swift и Objective C может включать операции управления внутренней памятью, которые не имеют ограниченной задержки.
Это подразумевает, что правильной схемой может быть использование Objective C для вашего класса контроллера AVAudioEngine, но только подмножество C (Objective C) внутри блока tapOnBus.
Ответ 4
Согласно Apple на WWDC2017 в Что нового в Core Audio Video примерно в 19:20, инженер говорит, что использование Swift "небезопасно" в контексте реального времени. Опубликовано из стенограммы.
Теперь, потому что рендеринг движка происходит в режиме реального времени контекст, вы не сможете использовать рендеринг в автономном режиме Objective-C или Быстрая мета, которую мы видели в демоверсии.
И это потому, что небезопасно использовать Objective-C или Swift время выполнения из контекста реального времени. Итак, вместо этого, сам движок предоставляет вам блок рендеринга, который вы можете искать и кэшировать, а затем позже используйте этот блок рендеринга для рендеринга движка в реальном времени контекст. Следующее, что нужно сделать, это настроить ваш входной узел так, что вы можете предоставить свои входные данные в Engine. И вот, ты укажите формат ввода, который вы предоставите, и это может быть другой формат, чем вывод. И вы также предоставляете блок, который Двигатель будет вызывать всякий раз, когда ему нужны входные данные. И когда это блок будет вызван, двигатель даст вам знать, сколько входные кадры это действительно нужно.