使用 Avaudioengine iOS 的低通滤波器 + 采样率转换

Posted

技术标签:

【中文标题】使用 Avaudioengine iOS 的低通滤波器 + 采样率转换【英文标题】:Low Pass filter + sample rate conversion using Avaudioengine iOS 【发布时间】:2022-01-19 17:45:42 【问题描述】:

我们正在开展一个项目,该项目允许我们用 5k Hz 采样率和一些低通滤波器和高通滤波器记录来自麦克风的一些声音。

我们正在使用什么

我们为此目的使用 AvaudioEngine

我们正在使用 AVAudioConverter 来降低采样率。

我们将 AVAudioUnitEQ 用于低通和高通滤波器。

代码

let bus = 0
let inputNode = engine.inputNode

let equalizer = AVAudioUnitEQ(numberOfBands: 2)

equalizer.bands[0].filterType = .lowPass
equalizer.bands[0].frequency = 3000
equalizer.bands[0].bypass = false

equalizer.bands[1].filterType = .highPass
equalizer.bands[1].frequency = 1000
equalizer.bands[1].bypass = false
engine.attach(equalizer) //Attach equalizer

// Connect nodes
engine.connect(inputNode, to: equalizer, format: inputNode.inputFormat(forBus: 0))
engine.connect(equalizer, to: engine.mainMixerNode, format: inputNode.inputFormat(forBus: 0))
engine.connect(engine.mainMixerNode, to: engine.outputNode, format: inputNode.inputFormat(forBus: 0))

let outputFormat = AVAudioFormat(commonFormat: .pcmFormatInt16,
                                            sampleRate: 5000,
                                            channels: 1,
                                            interleaved: false)!

// Converter to downgrade sample rate
guard let converter: AVAudioConverter = AVAudioConverter(from: inputNode.inputFormat(forBus: 0), to: outputFormat) else 
           print("Can't convert in to this format")
           return
       

engine.mainMixerNode.installTap(onBus: bus, bufferSize: 2688, format: engine.mainMixerNode.outputFormat(forBus: 0))  (buffer, time) in
           
     var newBufferAvailable = true
           
     let inputCallback: AVAudioConverterInputBlock =  inNumPackets, outStatus in
           if newBufferAvailable 
                outStatus.pointee = .haveData
                newBufferAvailable = false
                return buffer
            else 
                outStatus.pointee = .noDataNow
                return nil
           
     
           
           
     let convertedBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate))!

           var error: NSError?
           let status = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback)
           assert(status != .error)

           
           if status == .haveData 
             // Process with converted buffer
           
            
       
       
       engine.prepare()
       
       do 
           try engine.start()
        catch 
           print("Can't start the engine: \(error)")
       

问题

低通和高通滤波器不起作用。

替代方法

为了检查代码是否正常工作,我们添加了混响效果而不是低通滤波器。 混响效果(使用 AVAudioUnitReverb)使用相同的代码。

谁能帮我在应用低通滤波器时哪里做错了?

【问题讨论】:

@sbooth 我试过了,但结果是一样的。没有影响。 【参考方案1】:

我认为这段代码的主要问题是 AVAudioConverter 是在调用 engine.prepare() 之前创建的,这可以并且将会改变 mainMixerNode 输出格式。除此之外,mainMixerNodeoutputNode 之间存在冗余连接,以及可能不正确的格式——mainMixerNode 被记录为“按需”自动创建并连接到输出节点。水龙头也不需要格式。

let bus = 0
let inputNode = engine.inputNode

let equalizer = AVAudioUnitEQ(numberOfBands: 2)

equalizer.bands[0].filterType = .lowPass
equalizer.bands[0].frequency = 3000
equalizer.bands[0].bypass = false

equalizer.bands[1].filterType = .highPass
equalizer.bands[1].frequency = 1000
equalizer.bands[1].bypass = false
engine.attach(equalizer) //Attach equalizer

// Connect nodes
engine.connect(inputNode, to: equalizer, format: inputNode.inputFormat(forBus: 0))
engine.connect(equalizer, to: engine.mainMixerNode, format: inputNode.inputFormat(forBus: 0))

// call before creating converter because this changes the mainMixer's output format
engine.prepare()

let outputFormat = AVAudioFormat(commonFormat: .pcmFormatInt16,
                                 sampleRate: 5000,
                                 channels: 1,
                                 interleaved: false)!

// Downsampling converter
guard let converter: AVAudioConverter = AVAudioConverter(from: engine.mainMixerNode.outputFormat(forBus: 0), to: outputFormat) else 
    print("Can't convert in to this format")
    return


engine.mainMixerNode.installTap(onBus: bus, bufferSize: 2688, format: nil)  (buffer, time) in
    var newBufferAvailable = true
    
    let inputCallback: AVAudioConverterInputBlock =  inNumPackets, outStatus in
        if newBufferAvailable 
            outStatus.pointee = .haveData
            newBufferAvailable = false
            return buffer
         else 
            outStatus.pointee = .noDataNow
            return nil
        
    
    
    
    let convertedBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate))!
    
    var error: NSError?
    let status = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback)
    assert(status != .error)
    
    
    if status == .haveData 
        // Process with converted buffer
    


do 
    try engine.start()
 catch 
    print("Can't start the engine: \(error)")

【讨论】:

谢谢它有效,我从没想过分配对象会导致问题。它节省了我的一天。 :) 我还有一个问题,低通滤波器的最佳最小值是多少?当我检查频率参数时,会出现类似 (Samplerate / 2) 的内容。你对此有什么想法吗? 恐怕我不知道 - 抱歉。 好的,谢谢你的帮助。

以上是关于使用 Avaudioengine iOS 的低通滤波器 + 采样率转换的主要内容,如果未能解决你的问题,请参考以下文章

使用 c 中的低通滤波器过滤 .wav 文件

我的低通滤波器有啥问题?

iOS Accelerate低通FFT滤波器镜像结果

模拟模拟伪随机 LFO 信号(Javascript 中的低通滤波)

MATLAB 中的低通/带通滤波器设计

具有 FFT 卷积的低通 FIR 滤波器 - 重叠相加,为啥以及如何