Запись функции обратного вызова в Swift с аудиобиблиотеками Superpowered или AudioKit

Мое приложение (закодированное в Swift) выполняет обработку в реальном времени на основе аудиосигналов.

Мне нужно получить функцию с левым и правым буферами на входе (2 канала от USB-микрофона) и одну функцию с буферами на выходе (тоже 2 канала).

Раньше я использовал EZAudio, но у меня проблемы с памятью с 2-канальным форматом 96K. А так как EZAudio остановился, то хотелось бы перейти либо на Superpowered, либо на Audiokit.

Моя проблема: я не могу получить ни в одной из этих библиотек функции с буферами.

Superpowered: я добавил #import «SuperpoweredIOSAudioIO.h» в заголовок моста.

Я добавил SuperpoweredIOSAudioIODelegate в свой ViewController. Это автоматически добавило функции прерывания, разрешения и mapchannels, но не audioProcessingCallback.

Я пробовал следующие вещи:

audio = SuperpoweredIOSAudioIO(delegate: self, preferredBufferSize: 12, preferredMinimumSamplerate: 96000, audioSessionCategory: AVAudioSessionCategoryPlayAndRecord, channels: 2, audioProcessingCallback: audioProcessingCallback, clientdata: UnsafeMutablePointer)
audio.start()

а также

func audioProcessingCallback(buffers: UnsafeMutablePointer<UnsafeMutablePointer<Float>>, inputChannels: UInt32, outputChannels: UInt32, numberOfSamples: UInt32, sampleRate: UInt32, hostTime: UInt64) -> Bool {
        return true
    }

Но я получаю сообщение об ошибке:

Невозможно преобразовать значение типа '(UnsafeMutablePointer>, UInt32, UInt32, UInt32, UInt32, UInt64) -> Bool' в ожидаемый тип аргумента 'audioProcessingCallback!' (иначе 'ImplicitlyUnwrappedOptional‹@convention(c) (Необязательный, Необязательный>>>, UInt32, UInt32, UInt32, UInt32, UInt64) -> Bool>')

Я не смог найти примеров этой библиотеки со Swift...

Вот что я сделал с AudioKit:

let mic = AKMicrophone()
installTap(mic)
AudioKit.output = mic
AudioKit.start()

func installTap(_ input:AKNode) {
        input.avAudioNode.installTap(onBus: 0, bufferSize: 1024, format: AudioKit.format) { [weak self] (buffer, time) -> Void in
            self?.signalTracker(didReceivedBuffer: buffer, atTime: time)
        }
    }

func signalTracker(didReceivedBuffer buffer: AVAudioPCMBuffer, atTime time: AVAudioTime){
        let samples = UnsafeBufferPointer(start: buffer.floatChannelData?[0], count:1024)
        audioProcess.ProcessDataCaptureWithBuffer(samples, numberOfSamples: UInt32(1024))
    }

Он может получить поступающие буферы в моем алгоритме, но, похоже, не в «реальном времени», я имею в виду, очень медленно.. (извините, трудно объяснить.)

Спасибо!


person jcr    schedule 29.11.2017    source источник


Ответы (2)


Если вам нужно выполнять обработку в реальном времени, вам не следует использовать Swift (или ObjC). В настоящее время способ сделать это в AudioKit — создать подкласс AUAudioUnit и выполнить обработку внутри него. Однако, если вам просто нужно, чтобы звук поступал быстрее, то AKLazyTap — хорошее решение. Он отличается от обычного крана тем, что вы должны опрашивать его для получения данных, но этот метод позволяет повторно использовать буфер, поэтому вы можете вызывать его так быстро, как хотите.

Вот пример использования AKLazyTap для получения пика:

import UIKit
import AudioKit

class ViewController: UIViewController {

    let microphone = AKMicrophone()
    var tap: AKLazyTap?

    override func viewDidLoad() {
        super.viewDidLoad()


        AudioKit.output = microphone
        AKSettings.ioBufferDuration = 0.002 // This is to decrease latency for faster callbacks.

        tap = AKLazyTap(node: microphone.avAudioNode)

        guard tap != nil,
            let buffer = AVAudioPCMBuffer(pcmFormat: microphone.avAudioNode.outputFormat(forBus: 0), frameCapacity: 44100) else {
            fatalError()
        }

        // Your timer should fire equal to or faster than your buffer duration
        Timer.scheduledTimer(withTimeInterval: AKSettings.ioBufferDuration / 2, repeats: true) { _ in

            var timeStamp = AudioTimeStamp()
            self.tap?.fillNextBuffer(buffer, timeStamp: &timeStamp)

            if buffer.frameLength == 0 { return } // This is important, since we're polling for samples, sometimes it's empty, and sometimes it will be double what it was the last call.

            let leftMono = UnsafeBufferPointer(start: buffer.floatChannelData?[0], count:Int(buffer.frameLength))
            var peak = Float(0)
            for sample in leftMono {
                peak = max(peak, fabsf(sample))
            }
            print("number of samples \(buffer.frameLength) peak \(peak)")

        }

        AudioKit.start()
    }
}
person dave234    schedule 29.11.2017
comment
Судя по ответу ниже (Габор Санто), краны действительно немного медленные. И поскольку мне нужна идеальная временная метка, я предпочитаю переключиться на Superpowered. Спасибо! - person jcr; 03.12.2017
comment
Я отредактировал ответ, включив в него точные отметки времени. - person dave234; 03.12.2017
comment
То, как устроен этот кран, совсем не медленное. В этом примере это так же быстро, как базовые обратные вызовы C, но данные были перемещены из аудиопотока, чтобы можно было использовать Swift/ObjC. Это делает его идеальным для быстрого анализа таких вещей, как запуск пользовательского интерфейса на основе аудиопотока. Лучший способ проверить скорость — измерить. Попробуйте кран в моем примере, затем сравните buffer.frameLength с buffer.frameLength в кране AVAudioNode, вы увидите, что AKLazyTap примерно в 20 раз быстрее. - person dave234; 03.12.2017
comment
Спасибо за информацию. попробую реализовать. Вы знаете, почему максимальный пик, который я могу получить, равен 0,707 вместо 1,0? Есть ли где-то ограничение или усиление ниже 1? - person jcr; 05.12.2017
comment
.707 звучит как результат получения RMS. Попробуйте получить пик, используя метод, показанный выше. - person dave234; 06.12.2017
comment
Я сделал, я полностью скопировал / вставил ваш код. У меня все еще есть это sqrt (значение) для образцов. Странный! - person jcr; 09.12.2017

Superpowered — это C++ API, поскольку Swift не рекомендуется для обработки в реальном времени. Напишите свой код обработки звука на C++. «Нажатия» медленные, используйте SuperpoweredIOSAudioIO, чтобы получить звук в реальном времени.

person Gabor Szanto    schedule 03.12.2017
comment
Спасибо! На самом деле мой алгоритм не такой уж большой и работает как в Swift, так и в Objective-C. Однако мне нужна идеальная метка времени. Я могу полностью переработать на C++ (на всякий случай!) в другом классе. Но все же хотелось бы вызывать основную функцию алгоритма из своего ВК (в Swift), но реализовать функцию audioProcessingCallback у меня не получилось. Не могли бы вы помочь? Спасибо! - person jcr; 03.12.2017
comment
Используйте Objective-C++ (это Objective-C, но можно использовать C++, просто переименуйте файл .m в .mm). Затем подключите это к Swift. - person Gabor Szanto; 03.12.2017