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;
}
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位")
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,看看是否可以获取更高电平的数据。