ios获取麦克风音频输入数据(PCM)计算DBSPL后与真实值差距很大

iOS开发者请教,使用OC框架AVFAudio,获取设备32位麦克风的PCM数据,获取的结果均为-1~1之间(已经使用了固定噪声源真实值已经突破100分贝),根据换算的结果无法突破100分贝(20 * log10(PCM/ 0.00002)),请问各位知道是什么原因或问题吗?

import Foundation
import AVFAudio
import Accelerate
final class DecibelAudioManager: NSObject {
  private override init() {
  }
  @objc public static func shared() -> DecibelAudioManager {
    struct SingleStruct {
      static var single = DecibelAudioManager()
    }
    return SingleStruct.single
  }
  private var sampleRate: Double = 44100
  lazy var audioEngine = {
    let engine = AVAudioEngine()
    let inputNode = engine.inputNode
    let bus = 0
    let inputFormat = inputNode.inputFormat(forBus: bus)
    var setting = inputFormat.settings
    print("默认采用PCM格式\(inputFormat.settings)")
    inputNode.installTap(onBus: bus, bufferSize: AVAudioFrameCount(0.1 * sampleRate), format: inputFormat) { [weak self] (buffer, time) -> Void in
      guard let this = self else {
       return
      }
      let channels = UnsafeBufferPointer(start: buffer.floatChannelData, count: Int(buffer.format.channelCount))
      let floats = Array(UnsafeBufferPointer(start: channels[0], count: Int(buffer.frameLength)))
      for i in 0..<50 {
        let rms = floats[i]
        print("pcm:\(rms) ---- 32位")
      }
    }
    engine.prepare()
    return engine
  }()
  @objc public func startRecord() -> Void {
    let audiosession = AVAudioSession.sharedInstance()
    do{
      try audiosession.setCategory(.playAndRecord)
      try audiosession.setPreferredSampleRate(44100)
      try audiosession.setPreferredIOBufferDuration(0.1)
      try audiosession.setActive(true, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation)
    }catch{
      print(error)
    }
    try! self.audioEngine.start()
  }
  @objc public func stopRecord() -> Void {
    audioEngine.stop()
  }
}
结果输出
pcm:-0.82569194 ---- 32位
pcm:-0.82774025 ---- 32位
pcm:-0.83398014 ---- 32位
pcm:-0.87197787 ---- 32位
pcm:-0.90468484 ---- 32位
pcm:-0.9037836 ---- 32位
pcm:-0.9085202 ---- 32位
pcm:-0.913189 ---- 32位
pcm:-0.89893526 ---- 32位
pcm:-0.8763739 ---- 32位
pcm:-0.84504193 ---- 32位
pcm:-0.8274844 ---- 32位
pcm:-0.8118507 ---- 32位
pcm:-0.77658844 ---- 32位
pcm:-0.7597292 ---- 32位
pcm:-0.7476263 ---- 32位
pcm:-0.7350073 ---- 32位
pcm:-0.7227265 ---- 32位
pcm:-0.69862187 ---- 32位
pcm:-0.6939462 ---- 32位
pcm:-0.6953905 ---- 32位
pcm:-0.697331 ---- 32位
pcm:-0.71514064 ---- 32位
pcm:-0.7449271 ---- 32位
pcm:-0.765127 ---- 32位
pcm:-0.7787468 ---- 32位
pcm:-0.8179313 ---- 32位
pcm:-0.8538925 ---- 32位
pcm:-0.8675249 ---- 32位
pcm:-0.8712871 ---- 32位
pcm:-0.88458264 ---- 32位
pcm:-0.90770584 ---- 32位
pcm:-0.91529685 ---- 32位
pcm:-0.026977815 ---- 32位
pcm:0.024105616 ---- 32位
pcm:0.0715868 ---- 32位
pcm:0.13334471 ---- 32位
pcm:0.2160877 ---- 32位
pcm:0.3117581 ---- 32位
pcm:0.37369388 ---- 32位

输出分贝试试看

if let channelData = buffer.int32ChannelData {
  let channelCount = Int(buffer.format.channelCount)
  
  for channelIndex in 0..<channelCount {
    let channel = channelData[channelIndex]
    let channelPointer = channel.bindMemory(to: Int32.self, capacity: Int(buffer.frameLength))
    let pcmData = Array(UnsafeBufferPointer(start: channelPointer, count: Int(buffer.frameLength)))
    
    for i in 0..<pcmData.count {
      let pcm = pcmData[i]
      let decibel = 20 * log10(abs(Float(pcm)) / 0.00002)
      print("pcm:\(pcm) ---- 分贝值:\(decibel)")
    }
  }
}

采样率设置太低了,PCM 数据结构限制,20 * log10(PCM/ 0.00002) 这个公式只适用于 16 位 PCM 数据
升高采样率到 96KHz 或更高,使用不同的 PCM 转分贝公式,扩大 PCM 值的范围

可以用ios通过PCM计算声音分贝大小

-(BOOL)isQuite:(NSData *)pcmData
{
    if (pcmData == nil)
    {
        return NO;
    }
    
    long long pcmAllLenght = 0;
    
    short butterByte[pcmData.length/2];
    memcpy(butterByte, pcmData.bytes, pcmData.length);//frame_size * sizeof(short)
    
    // 将 buffer 内容取出,进行平方和运算
    for (int i = 0; i < pcmData.length/2; i++)
    {
        pcmAllLenght += butterByte[i] * butterByte[i];
    }
    // 平方和除以数据总长度,得到音量大小。
    double mean = pcmAllLenght / (double)pcmData.length;
    double volume =10*log10(mean);//volume为分贝数大小
    
    if (volume >= 45) //45分贝
    {
        //在说话
        
    }
    
    return yes;
}

  1. 将PCM数据转换为浮点数数组后,使用Accelerate框架中的vDSP函数计算RMS(均方根)值。RMS值可以用来估计音频信号的能量。
    可以使用以下代码替换原来的循环部分:
    let floats = Array(UnsafeBufferPointer(start: channels[0], count: Int(buffer.frameLength)))
    var rms: Float = 0
    vDSP_rmsqv(floats, 1, &rms, vDSP_Length(buffer.frameLength))
    print("rms: \(rms) ---- 32位")
    
  2. 如果你想要获取分贝值,可以使用以下代码将RMS值转换为分贝值:
    let decibels = 20 * log10(rms / 0.00002)
    print("decibels: \(decibels)")
    

简单的说:
一种实现方式是尝试直接读取原始的PCM数据(前提是PCM中的数据的值存在超过1的)。
计算机采集到超过 [-1, 1] 范围之外的数据会自动校正到[-1, 1] 范围之内。
如果需要得到超过 [-1, 1] 范围的数据可能需要专门的硬件。
也就是说不管固定噪声源真实值是多大,你采集的值都会被修正。
=================================
复杂的说:
模数转换器(ADC)将模拟音频信号转换为数字音频数据,ADC 是按照一定的采样率和采样位深来对模拟信号进行采样和量化的,然后将其转换为数字表示形式。通常情况下,采样位深决定了数据的精度和范围,例如 16 位的采样位深可以表示范围在 [-32768, 32767] 的有符号整数。

即:当使用计算机进行音频采集时,通常会将采集的音频数据进行归一化处理,将其范围限定在 [-1, 1] 之间。这是因为 PCM 数据使用固定点表示法,通常是以有符号的 16 位整数进行存储,范围是从 -32768 到 32767。

因此,当你调用计算机进行音频采集并获取 PCM 数据时,这些数据会经过归一化处理,其取值范围应该是 [-1, 1],不会超过这个范围。

详细的说:
根据你提供的代码来看,rms是音频数据归一化的浮点数,所以它的值永远在[-1,1]之间,而根据你提供的计算公式(20 * log10(PCM / 0.00002)),如果计算结果超过100,那么要求pcm的值大于等于2,据此,我推断你获得的rms数据被矫正了(也就是说发现读取的PCM数据越界了,修复到[-1,1]的范围之内),具体被矫正的原因:
你的例子中,获得的PCM的取值范围在[-1,1] 之间在实际应用中是没有问题的,这种是数据归一化的浮点表示法;另外一种整数表示法[-32768,32767],这两种表示法的数据可以互相转换。但浮点表示法的精度更高。
在实际应用中,超过 100 分贝的声音往往被视为噪音,所以在软件中会设置门限自动修正,例如在[-1,1]之外的数据自动校正到-1~1的范围之内。*也就是说你得到的rms是被修正过的值。

这个问题,可能是由于你的麦克风设备的限制,在精度上还达不到你要的这个效果和级别。
还有可能就是计算过程或处理过程中的数据精度的问题,你获取和存储的精度还不够,有损失。
或者含有噪声影响,或者你处理音频数据的方法存在问题。
以上这些都有可能导致你现在的这个现象,多排查下。

可能是采样率、位深度等参数设置不正确导致的。

增加采样率试试

估计是采样率、位深度设置不合适

分析原因:
1、麦克风输入的限制,不同的设备可能有不同的麦克风输入限制。
2、软件的限制,iOS系统本身可能对音频输入的电平进行限制。即使设备的硬件没有明确的限制,系统可能会对音频输入进行某种程度的限制,以确保稳定性或防止其他问题。
3、数据转换的精度,在处理PCM数据时,如果使用不适当的精度或数据类型,可能会导致数据损失或限制数据的范围。
4、噪声源的问题,你提到使用了固定噪声源,确保该噪声源的电平在可接受范围内,并且能够产生足够高的电平以进行测试。
建议:
调整音量:可以通过调整麦克风的音量来增加输入电平。在开始录制之前,可以使用AVAudioSession类的setPreferredInputGain方法来设置麦克风的增益。
更改采样率:尝试更改采样率,可能会影响输入电平的范围。尝试使用更高的采样率,例如48 kHz或96 kHz,看看是否可以获取更高电平的数据。