关于从 AVAudioPCMBuffer 中提取声压级
Posted
技术标签:
【中文标题】关于从 AVAudioPCMBuffer 中提取声压级【英文标题】:On extracting the sound pressure level from AVAudioPCMBuffer 【发布时间】:2017-02-23 05:04:32 【问题描述】:我几乎没有信号处理方面的知识,目前我正在尝试在 Swift 中实现一个函数,当sound pressure level 增加时(例如,当一个人尖叫时)触发一个事件。
我正在使用这样的回调进入 AVAudioEngine 的输入节点:
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat)
(buffer : AVAudioPCMBuffer?, when : AVAudioTime) in
let arraySize = Int(buffer.frameLength)
let samples = Array(UnsafeBufferPointer(start: buffer.floatChannelData![0], count:arraySize))
//do something with samples
let volume = 20 * log10(floatArray.reduce(0) $0 + $1 / Float(arraySize))
if(!volume.isNaN)
print("this is the current volume: \(volume)")
将其转换为浮点数组后,我尝试通过计算平均值来粗略估计声压级。
但这给了我很多波动的价值,即使 iPad 只是坐在一个安静的房间里:
this is the current volume: -123.971
this is the current volume: -119.698
this is the current volume: -147.053
this is the current volume: -119.749
this is the current volume: -118.815
this is the current volume: -123.26
this is the current volume: -118.953
this is the current volume: -117.273
this is the current volume: -116.869
this is the current volume: -110.633
this is the current volume: -130.988
this is the current volume: -119.475
this is the current volume: -116.422
this is the current volume: -158.268
this is the current volume: -118.933
如果我在麦克风附近拍手,这个值确实会显着增加。
所以我可以先在准备阶段计算这些体积的平均值,然后比较在事件触发阶段差异是否显着增加:
if(!volume.isNaN)
if(isInThePreparingPhase)
print("this is the current volume: \(volume)")
volumeSum += volume
volumeCount += 1
else if(isInTheEventTriggeringPhase)
if(volume > meanVolume)
//triggers an event
averageVolume 是在从准备阶段到触发事件阶段的过渡期间计算的:meanVolume = volumeSum / Float(volumeCount)
....
但是,如果我在麦克风之外播放响亮的音乐,似乎没有显着增加。并且在极少数情况下,volume
大于 meanVolume
,即使环境的音量没有显着增加(人耳可以听到)。
那么从 AVAudioPCMBuffer 中提取声压级的正确方法是什么?
***给出了这样的公式
其中 p 是均方根声压,p0 是参考声压。
但我不知道AVAudioPCMBuffer.floatChannelData
中的浮点值代表什么。 The apple page 只说
缓冲区的音频样本为浮点值。
我应该如何与他们合作?
【问题讨论】:
嗨,arch,我想您已经找到了这个问题的答案?你有什么可以提供的代码吗? 什么是floatArray
?这里...let volume = 20 * log10(floatArray.reduce(0) $0 + $1 / Float(arraySize)) ....
【参考方案1】:
感谢@teadrinker 的回复,我终于找到了解决这个问题的方法。我分享了输出AVAudioPCMBuffer
输入音量的 Swift 代码:
private func getVolume(from buffer: AVAudioPCMBuffer, bufferSize: Int) -> Float
guard let channelData = buffer.floatChannelData?[0] else
return 0
let channelDataArray = Array(UnsafeBufferPointer(start:channelData, count: bufferSize))
var outEnvelope = [Float]()
var envelopeState:Float = 0
let envConstantAtk:Float = 0.16
let envConstantDec:Float = 0.003
for sample in channelDataArray
let rectified = abs(sample)
if envelopeState < rectified
envelopeState += envConstantAtk * (rectified - envelopeState)
else
envelopeState += envConstantDec * (rectified - envelopeState)
outEnvelope.append(envelopeState)
// 0.007 is the low pass filter to prevent
// getting the noise entering from the microphone
if let maxVolume = outEnvelope.max(),
maxVolume > Float(0.015)
return maxVolume
else
return 0.0
【讨论】:
【参考方案2】:我认为第一步是获取声音的envelope。您可以使用简单的平均来计算包络,但您需要添加一个校正步骤(通常意味着使用 abs() 或 square() 使所有样本为正)
更常见的是使用简单的 iir 滤波器而不是平均,具有不同的攻击和衰减常数,这里是 lab。请注意,这些常数取决于采样频率,您可以使用以下公式来计算常数:
1 - exp(-timePerSample*2/smoothingTime)
第 2 步
当你有了包络后,你可以用一个额外的滤波器来平滑它,然后比较两个包络找到比基本电平更响的声音,这里有一个更complete lab。
请注意,检测音频“事件”可能非常棘手,而且难以预测,请确保您有大量的调试工具!
【讨论】:
感谢实验室演示!超级有帮助:D以上是关于关于从 AVAudioPCMBuffer 中提取声压级的主要内容,如果未能解决你的问题,请参考以下文章
使用 Swift 中的 Accelerate 框架来自 AVAudioPCMBuffer 的频谱图
在同一个 AVAudioPCMBuffer 中改变正弦波频率
斯威夫特:AVAudioPCMBuffer vs AVAudioBuffer vs AVAudioCompressedBuffer