Каждое устройство имеет свои собственные индикаторы задержки. Даже если та же модель и версия ОС. Оценивать время на тренажерах не имеет смысла. Фактическая задержка устройств не будет отображаться.
Задержка не может быть рассчитана с высокой точностью. Вы не учитываете время, в течение которого ваш сигнал достигает микрофона. Также при каждом запуске по-прежнему накладывается задержка работы с потоками.
Также влияет микрофон, выбранный для записи. начиная с iPhone 6 их как минимум три. По умолчанию ниже.
Я занимаюсь такими вопросами уже два года. Наиболее эффективным способом является калибровка (балансировка) устройства. При запуске аудиоустройства вам необходимо отправить случайный высокочастотный сигнал. Получив его на входе, оцените разницу и начните с нее.
Я настраиваю сами потоки с помощью буферов, чтобы всегда обрабатывать соответствующие выборки.
Лучше делать при каждом запуске. Это занимает долю секунды, но ваши потоки ввода / вывода всегда синхронизируются c.
РЕДАКТИРОВАТЬ 1
Если вы будете выполнять калибратор:
- Имейте в виду, что в процессе голосовой обработки высокочастотные звуки ухудшаются.
- Слышимость частот выше 18 кГц значительно падает.
- При одновременной записи и воспроизведении верхняя часть Динамик используется по умолчанию (вы, скорее всего, уже знаете это).
- При генерации сигнала используйте только несколько частот (я не знаю, как в 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
Как работает калибровка в простом:
- Отправка сигнала.
- Прослушивание входного потока и ожидание сигнала.
- Когда вы находите выборку с сигналом выше порога, используйте предыдущий ток и следующую выборку для двоичного поиска.