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

#ios #audio #volume #avaudioengine #avaudiopcmbuffer

#iOS #Аудио #громкость #avaudioengine #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
         }
      }
 }
 

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

….

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

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

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

математика!

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

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

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

Как я должен с ними работать?

Комментарии:

1. Привет, арч, я полагаю, ты нашел ответ на этот вопрос? есть ли у вас какой-нибудь код, который вы могли бы предоставить?

2. Что такое floatArray ? здесь … let volume = 20 * log10(floatArray.reduce(0){ $0 $1} / Float(arraySize)) ....

Ответ №1:

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

Чаще всего вместо усреднения используется простой iir-фильтр с разными константами для атаки и затухания, вот <a rel=»noreferrer noopener nofollow» href=»https:///madtealab.com/?V=1amp;C=5amp;F=3amp;G=1amp;O=1amp;W=667amp;GW=613amp;GX=0.34947981033652amp;GY=0.1376430424559655amp;GS=8.6382116295777amp;GSY=0.2amp;EH=205amp;a=0.1amp;aMa=0.5amp;aN=Decayamp;b=0.2amp;bN=Noiseamp;c=440amp;cMi=50amp;cMa=1000amp;cN=Freqamp;d=0.16amp;dN=envConstantAtkamp;l=0.003amp;lN=envConstantDecamp;f1=smoothstep(0.5,1,1-x)*smoothstep(0,0.05,x)amp;fe1=0amp;f1N=Decamp;f2=Dec(x) * sin(x*pi*2*Freq) * (1 — Noise)amp;f2N=testAudioamp;f3=aToF(outEnvelope,linear,0,1)(x)amp;Expr=var samples = sample(testAudio,[0,1,4100])
var envelopeState = 0
outEnvelope = []

for(var i = 0; i < samples.length; i++) {
var rectified = abs(samples[i])
if(envelopeState лабораторная работа. Обратите внимание, что эти константы зависят от частоты дискретизации, вы можете использовать эту формулу для вычисления констант:

 1 - exp(-timePerSample*2/smoothingTime)
 

Шаг 2

Когда у вас есть огибающая, вы можете сгладить ее с помощью дополнительного фильтра, а затем сравнить две огибающие, чтобы найти звук, который громче базового уровня, вот более <a rel=»noreferrer noopener nofollow» href=»https:///madtealab.com/?V=1amp;C=7amp;F=5amp;G=1amp;O=1amp;W=685amp;GW=631amp;GX=0.47154764619588563amp;GY=0.4112919503908122amp;GS=6.9548657083441885amp;GSY=0.2amp;EH=215amp;a=0.1amp;aMa=0.5amp;aN=Decayamp;b=0.2amp;bN=Noiseamp;c=440amp;cMi=50amp;cMa=1000amp;cN=Freqamp;d=0.01amp;dN=envAtkamp;l=0.09amp;lN=envDecamp;m=0.06111111111111111amp;mN=slowFilteramp;n=4900amp;nMa=44100amp;nI=1amp;nN=sampleRateamp;f1=tri(-x*10-0.2)amp;f1N=Decamp;f2=Dec(x) * sin(x*pi*2*Freq) * (1 — Noise)amp;f2N=testAudioamp;f3=aToF(outEnvelope,linear,0,1)(x)amp;f4=aToF(outEnvelopeSlow,linear,0,1)(x)amp;f5=marker(x,firstEvent)amp;Expr=var samples = sample(testAudio,[0,1,sampleRate])
var envelopeState = 0
var envelopeStateSlow = 0
var envConstantAtk = 1 — exp(-2/(envAtk*sampleRate))
var envConstantDec = 1 — exp(-2/(envDec*sampleRate))
var slowFilterConstant = 1 — exp(-2/(slowFilter*sampleRate))

outEnvelope = []
outEnvelopeSlow = []
firstEvent = -1

for(var i = 0; i < samples.length; i++) {
var rectified = abs(samples[i])
if(envelopeState < rectified) {
envelopeState += envConstantAtk * (rectified — envelopeState)
} else {
envelopeState += envConstantDec * (rectified — envelopeState)
}

envelopeStateSlow += slowFilterConstant * (envelopeState — envelopeStateSlow)

if(i != 0 && firstEvent == -1 &&
envelopeStateSlow < envelopeState &&
outEnvelope[outEnvelope.length-1] полная лабораторная работа.

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

Комментарии:

1. Спасибо за лабораторные демонстрации! Очень полезно: D

Ответ №2:

Благодаря ответу от @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
    }
}