CoreAudio: вычисление общей задержки между входом и выходом с помощью kAudioUnitSubType_VoiceProcessingIO - PullRequest
2 голосов
/ 15 января 2020

Я работаю над приложением, использующим CoreAudio на iPhone / iPad. Приложение воспроизводит звук через динамики (выход), а также записывает звук с микрофона (вход) одновременно. Для целей этого приложения чрезвычайно важно, чтобы я мог сравнивать вход и выход, особенно то, насколько хорошо они «выстраиваются» во временной области. Из-за этого критически важно правильно рассчитать общую задержку между входным и выходным каналами.

Я тестирую на 3 разных устройствах. iPhone, iPad и симулятор. Мне удалось эмпирически определить, что задержка для iPhone составляет где-то около 4050 сэмплов, iPad ближе к 4125 сэмплам, а симулятор - примерно 2500 сэмплов. Я нашел небольшое обсуждение в Интернете о вычислении задержки в CoreAudio, но они обычно касаются использования CoreAudio в OSX, а не iOS. Из-за этого они ссылаются на различные функции, которых нет в iOS. Однако, похоже, что для iOS правильное решение будет использовать AVAudioSession и некоторую комбинацию inputLatency, outputLatency и IOBufferDuration. Тем не менее, никакие комбинации этих значений, по-видимому, не составляют эмпирически определенных значений выше. Кроме того, я получаю совершенно разные значения для каждого параметра, когда проверяю их до и после вызова AudioUnitInitialize. Еще более запутанным является то, что значения намного ближе к ожидаемой задержке до вызова AudioUnitInitialize, что противоположно тому, что я ожидал.

Вот значения, которые я имею Наблюдение 1102

iPhone (до): в 0,065125, из 0,004500, буф 0,021333, всего выборок 4011 iPhone (после): 0,000354, из 0,000292, буф 0,021333, всего образцов 969

Симулятор всегда возвращает 0,01 для входных и выходных данных, но я подозреваю, что это не фактические / правильные значения и что симулятор просто не поддерживает эту функцию.

Еще один потенциально интересным примечанием является то, что я использую kAudioUnitSubType_VoiceProcessingIO, а не kAudioUnitSubType_RemoteIO, что я ожидаю добавить дополнительную задержку. Я предполагаю, что это будет включено в значение inputLatency, но, возможно, есть другое значение, которое мне нужно запросить, чтобы включить это?

Как правильно определить общую задержку между вводом и выводом в iOS

Ответы [ 2 ]

1 голос
/ 23 января 2020

Каждое устройство имеет свои собственные индикаторы задержки. Даже если та же модель и версия ОС. Оценивать время на тренажерах не имеет смысла. Фактическая задержка устройств не будет отображаться.

Задержка не может быть рассчитана с высокой точностью. Вы не учитываете время, в течение которого ваш сигнал достигает микрофона. Также при каждом запуске по-прежнему накладывается задержка работы с потоками.

Также влияет микрофон, выбранный для записи. начиная с iPhone 6 их как минимум три. По умолчанию ниже.

Я занимаюсь такими вопросами уже два года. Наиболее эффективным способом является калибровка (балансировка) устройства. При запуске аудиоустройства вам необходимо отправить случайный высокочастотный сигнал. Получив его на входе, оцените разницу и начните с нее.

Я настраиваю сами потоки с помощью буферов, чтобы всегда обрабатывать соответствующие выборки.

Лучше делать при каждом запуске. Это занимает долю секунды, но ваши потоки ввода / вывода всегда синхронизируются c.

РЕДАКТИРОВАТЬ 1

Если вы будете выполнять калибратор:

  1. Имейте в виду, что в процессе голосовой обработки высокочастотные звуки ухудшаются.
  2. Слышимость частот выше 18 кГц значительно падает.
  3. При одновременной записи и воспроизведении верхняя часть Динамик используется по умолчанию (вы, скорее всего, уже знаете это).
  4. При генерации сигнала используйте только несколько частот (я не знаю, как в Engli sh). Частоты должны быть кратны sampleRate / frameSize .

Например, с частотой дискретизации 44100 и размером выборки 512 , вы можете использовать частоты, кратные отношению 44100/512 = 86,13 .

Частоты: 86,13 Гц , 172,27 Гц , 258,40 Гц , 344,53 Гц , 430,66 Гц , 516.80 Гц , 602,93 Гц, 689,06 Гц , 775,20 Гц , 861,33 Гц , 947,46 Гц , 1033,59 Гц , 1119,73 Гц , 1205,86 Гц и др. c.

В противном случае при преобразовании сигнала в спектр вы получите размытие.

РЕДАКТИРОВАТЬ 2

Создать образец и получить пример кода спектра.

import Foundation
import Accelerate
import AudioUnit
import AVFoundation

public class StackExample {


    //
    // createSample(512, [1, 3, 5])
    // Was create sample with length 512 reports for frequencies: 86.13 Hz (1), 258.40 Hz (3), 430.66 Hz (5).
    // Number of frequency is number of multiplicity 44100/512
    // You can use frequencies from 1 to half of frameSize
    //
    public func createSample(frameSize: Int, frequencies: [Int]) -> [Float] {
        // result sample
        var sample = [Float]()
        // prepare diferent report in sample
        for index in 0..<frameSize {
            var report: Float = 0.0
            for frequencyNumber in frequencies {
                report += sinf(2.0 * Float.pi * Float(index) * Float(frequencyNumber) / Float(frameSize))
            }
            // report value mast been in range between -1.0 and 1.0
            // if we send more one frequencies we must divide each report by the number of frequencies
            if frequencies.count > 1 { report = report / Float(frequencies.count) }

            // with this configuration, the signal will immediately play at maximum volume. It must be smoothed in sinusoin over the entire segment.
            report *= sinf(Float.pi * Float(index) / Float(frameSize - 1))

            sample.append(report)
        }

        return sample
    }

    // spectrum was half of count of reports in sample
    // for sample with length 512 get spectrum with 256 frequencies. Frequency numbers are also multiple like in code of generation of sample.
    public func getSpectrum(frameSize: Int, sample: [Float]) -> [Float] {
        // create fft setup
        let frameLog2Size = UInt(log2(Double(frameSize)))
        let fftSetup = vDSP_create_fftsetup(frameLog2Size, FFTRadix(FFT_RADIX2))!
        let spectrumSize = frameSize / 2

        var reals = [Float]()
        var imags = [Float]()

        for (idx, element) in sample.enumerated() {
            if idx % 2 == 0 {
                reals.append(element)
            } else {
                imags.append(element)
            }
        }

        var complexBuffer = DSPSplitComplex(realp: UnsafeMutablePointer(mutating: reals), imagp: UnsafeMutablePointer(mutating: imags))
        // direct fft transform
        vDSP_fft_zrip(fftSetup, &complexBuffer, 1, UInt(frameLog2Size), Int32(FFT_FORWARD))
        var magnitudes = [Float](repeating: 0.0, count: spectrumSize)
        // calculation of magnitudes
        vDSP_zvmags(&complexBuffer, 1, &magnitudes, 1, UInt(spectrumSize))
        return magnitudes
    }
}

РЕДАКТ. 3

Как работает калибровка в простом:

  1. Отправка сигнала.
  2. Прослушивание входного потока и ожидание сигнала.
  3. Когда вы находите выборку с сигналом выше порога, используйте предыдущий ток и следующую выборку для двоичного поиска.
0 голосов
/ 21 января 2020

Частичное несоответствие задержки аудио, которое вы видите, вероятно, связано с попыткой настроить обработку звука в вашем приложении для 44100 выборок в секунду.

Собственная аппаратная частота дискретизации на любом новом iOS устройстве составляет 48 кбит / с (или, возможно, целое число, кратное ему), поэтому при инициализации аудиоустройства для 44,1 Кбайт ввода-вывода возможно добавление (скрытого программного обеспечения) преобразования частоты дискретизации Процесс или два к вашему аудио графику. Возможно, вы сможете устранить это несоответствие задержек, запустив внутренний путь сигнала вашего приложения со скоростью 48 кбит / с (или, возможно, даже 96 кбит / с или 192 кбит / с). Если вам нужно использовать файлы 44.1 .wav, то обрабатывайте все необходимые преобразования скорости вне графика аудиоустройства и в пределах собственного кода обработки до и после обработки в реальном времени вашего приложения (например, конвертируйте и перезаписывайте файлы, если это необходимо).

Вы также можете уменьшить реальную физическую задержку ввода-вывода, используя аудиосеанс для запроса гораздо более короткой длительности аудиобуфера (на более новых устройствах iOS возможно менее 5 миллисекунд) через setPreferredIOBufferDuration () ,

Не уверен, что вышеперечисленное совместимо с подтипом обработки голоса.

С другой стороны, симулятор iOS может работать на Ma c, который поддерживает собственный 44.1k частота дискретизации в аппаратном обеспечении. Таким образом, возможная причина разницы в измеренных iOS задержках устройства и симулятора.

...