gpt4 book ai didi

ios - CoreAudio : Calculate total latency between input and output with kAudioUnitSubType_VoiceProcessingIO

转载 作者:行者123 更新时间:2023-12-01 16:13:10 24 4
gpt4 key购买 nike

我正在开发一个在 iPhone/iPad 上使用 CoreAudio 的应用程序。该应用程序既通过扬声器(输出)播放音频,又同时从麦克风(输入)录制音频。出于此应用程序的目的,我能够比较输入和输出,特别是它们在时域中“排列”的程度,这一点非常重要。因此,正确计算输入和输出 channel 之间的总延迟时间至关重要。

我正在 3 种不同的设备上进行测试。 iPhone、iPad 和模拟器。我已经能够根据经验确定 iPhone 的延迟大约为 4050 个样本,iPad 接近 4125 个样本,而模拟器大约为 2500 个样本。

经过大量研究(又名谷歌搜索),我在网上发现了一些关于计算 CoreAudio 延迟的讨论,但它们通常与在 OSX 而不是 iOS 上使用 CoreAudio 有关。因此,它们指的是 iOS 上不存在的各种功能。然而,对于 iOS 来说,正确的解决方案似乎是使用 AVAudioSession 以及 inputLatencyoutputLatencyIOBufferDuration 的一些组合。然而,这些值的组合似乎没有加起来等于上面根据经验确定的值。此外,在调用 AudioUnitInitialize 之前和之后检查每个参数时,我会得到截然不同的值。更令人困惑的是,这些值更接近调用 AudioUnitInitialize 之前的预期延迟,这与我的预期相反。

这是我看到的值。

  • iPad(之前):输入 0.032375,输出 0.013651,buf 0.023220,样本总数 3054
  • iPad(之后):输入 0.000136,输出 0.001633,buf 0.023220,样本总数 1102
  • iPhone(之前):in 0.065125,out 0.004500,buf 0.021333,样本总数 4011
  • iPhone(之后):0.000354,out 0.000292,buf 0.021333,总样本 969

模拟器总是为输入和输出返回 0.01,但我怀疑这些不是实际/正确的值,而且模拟器不支持此功能。

另一个可能有趣的注意事项是,我使用的是 kAudioUnitSubType_VoiceProcessingIO 而不是 kAudioUnitSubType_RemoteIO,我确实希望这会增加一些额外的延迟。我的假设是这将包含在 inputLatency 值中,但也许我需要查询另一个值来包含它?

确定 iOS 中输入和输出之间总延迟的正确方法是什么?

最佳答案

每个设备都有自己的延迟指标。即使相同的型号和操作系统版本。在模拟器上估计时间没有意义。它不会显示设备的实际延迟时间。

延迟无法高精度计算。因为您没有考虑信号到达麦克风的时间。此外,在每次开始使用流时,仍然存在延迟。

选择用于录音的麦克风也会受到影响。从 iPhone 6 开始,至少有三个。默认值较低。

两年来我一直在处理此类问题。最有效的方法是校准(平衡)设备。启动音频单元时,您需要发送随机高频信号。在入口处获取它,评估差异并从它开始。

我在缓冲区的帮助下自行调整流,以始终处理相应的样本。

最好在每次开始时都这样做。这需要一瞬间,但您的 I/O 流始终保持同步。

编辑 1

如果你要做一个校准器:

  1. 请记住,voiprocessing 处理高频声音效果更差。
  2. 18 kHz 以上频率的可听度显着下降。
  3. 同时录制和播放时,默认使用顶部扬声器(您很可能已经知道这一点)。
  4. 生成信号时,只使用多个频率(我不知道英文怎么说)。频率必须是 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 赫兹

否则,将信号转换为频谱时,会变得模糊。

编辑 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. 当您找到信号高于阈值的样本时,使用上一个当前样本和下一个样本进行二进制搜索。

关于ios - CoreAudio : Calculate total latency between input and output with kAudioUnitSubType_VoiceProcessingIO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59754071/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com