首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从AVAudioPCMBuffer中提取声压级

从AVAudioPCMBuffer中提取声压级
EN

Stack Overflow用户
提问于 2016-10-13 22:00:31
回答 2查看 2.6K关注 0票数 8

我对信号处理几乎一无所知,目前我正试图在Swift中实现一个函数,在声压级增加时触发事件(例如,当人尖叫时)。

我使用这样的回调进入AVAudioEngine的输入节点:

代码语言:javascript
复制
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只是坐在一个相当大的房间:

代码语言:javascript
复制
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

如果我在麦克风附近拍手,这个值确实会显著增加。

因此,我可以在准备阶段首先计算这些卷的平均值,并比较事件触发阶段的差异是否显著增加:

代码语言:javascript
复制
 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中的浮动值代表什么。苹果页只说

缓冲区的音频样本作为浮点值。

我该怎么和他们合作?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-10-14 05:34:09

我认为第一步是获得声音的封套。您可以使用简单的平均值来计算一个信封,但是您需要添加一个校正步骤(通常意味着使用abs()或square()来使所有样本都为正)。

更常见的是使用一个简单的iir滤波器,而不是平均,具有不同的攻击和衰减常数,这里是一个实验室。请注意,这些常数取决于采样频率,可以使用此公式计算常量:

代码语言:javascript
复制
1 - exp(-timePerSample*2/smoothingTime)

第二步

当你有了信封,你可以用一个额外的过滤器来平滑它,然后比较这两个信封,找出比基准面更响亮的声音,这是一个更多的完整实验室

请注意,检测音频“事件”可能是相当棘手的,而且很难预测,请确保您有大量的窃听辅助!

票数 6
EN

Stack Overflow用户

发布于 2018-05-08 15:49:42

感谢@teadrinker的回复,我终于找到了解决这个问题的方法。我共享我的Swift代码,它输出AVAudioPCMBuffer输入的卷:

代码语言:javascript
复制
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
    }
}
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40031738

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档