Подтвердить что ты не робот

Аудио в реальном времени с 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, я благодарен за все замечания, замечания, комментарии, решения и т.д.

4b9b3361

Ответ 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. И вот, ты укажите формат ввода, который вы предоставите, и это может быть другой формат, чем вывод. И вы также предоставляете блок, который Двигатель будет вызывать всякий раз, когда ему нужны входные данные. И когда это блок будет вызван, двигатель даст вам знать, сколько входные кадры это действительно нужно.