Об извлечении уровня звукового давления из AVAudioPCMBuffer

У меня почти ничего не известно об обработке сигналов, и в настоящее время я пытаюсь реализовать в Swift функцию, которая запускает событие при увеличении уровень звукового давления (например, когда человек кричит).

Я подключаюсь к входному узлу 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
         }
      }
 }

где средний объем вычисляется во время перехода от фазы подготовки к фазе инициирующего события: meanVolume = volumeSum / Float(volumeCount)

....

Однако, если я включаю громкую музыку помимо микрофона, значительного увеличения не будет. И в редких случаях volume больше meanVolume, даже когда окружающая среда не имеет значительного увеличения громкости (слышимого человеческими ушами).

Итак, как правильно извлечь уровень звукового давления из AVAudioPCMBuffer?

Википедия дает такую ​​формулу

математика!

где p - среднеквадратичное звуковое давление, а p0 - эталонное звуковое давление.

Но я понятия не имею, что представляют собой значения с плавающей запятой в AVAudioPCMBuffer.floatChannelData. На странице Apple только говорится

Аудиосэмплы буфера как значения с плавающей запятой.

Как мне с ними работать?


person Archy Will He 何魏奇    schedule 13.10.2016    source источник
comment
Привет, арка, я полагаю, ты догадалась ответ на этот вопрос? у вас есть какой-нибудь код, который вы могли бы предоставить?   -  person Logan    schedule 06.04.2017
comment
Что такое floatArray? вот ... let volume = 20 * log10(floatArray.reduce(0){ $0 + $1} / Float(arraySize)) ....   -  person MikeMaus    schedule 19.06.2021


Ответы (2)


Я думаю, что первый шаг - получить конверт звука. Вы можете использовать простое усреднение для расчета конверта, но вам нужно добавить шаг исправления (обычно означает использование abs () или square (), чтобы все образцы были положительными)

Чаще вместо усреднения используется простой iir-фильтр с разными константами для атаки и затухания, вот lab. Обратите внимание, что эти константы зависят от частоты дискретизации, вы можете использовать эту формулу для вычисления констант:

1 - exp(-timePerSample*2/smoothingTime)

Шаг 2

Когда у вас есть огибающая, вы можете сгладить ее с помощью дополнительного фильтра, а затем сравнить две огибающие, чтобы найти звук, который громче, чем базовый уровень, вот более выполнить лабораторную работу.

Обратите внимание, что обнаружение звуковых «событий» может быть довольно сложным и трудным для предсказания, убедитесь, что у вас есть много средств отладки!

person teadrinker    schedule 14.10.2016
comment
Спасибо за лабораторные демонстрации! Супер полезно: D - person Archy Will He 何魏奇; 15.10.2016

Благодаря ответу @teadrinker я наконец нашел решение этой проблемы. Я делюсь своим кодом Swift, который выводит объем ввода AVAudioPCMBuffer:

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
    }
}
person davebcn87    schedule 08.05.2018