【问题标题】:How to implement band stop filter using AVAudioEngine如何使用 AVAudioEngine 实现带阻滤波器
【发布时间】:2021-06-14 23:11:44
【问题描述】:

我正在构建一个应用程序,该应用程序需要对其从麦克风接收到的音频进行实时分析。在我的应用程序中,我还需要同时播放哔声并开始录制音频,也就是说,我无法播放哔声然后开始录制。这引入了在我的录音中听到哔声的问题,(这可能是因为我正在通过扬声器播放哔声,但不幸的是我在这方面也不能妥协)。由于哔声只是大约 2350 kHz 的音调,我想知道如何在我的录音中排除该频率范围(例如从 2300 kHz 到 2400 kHz)并防止它影响我的音频样本。在做了一些谷歌搜索之后,我想出了我认为可能是解决方案,带阻滤波器。根据 Wikipedia 的说法:“带阻滤波器或带阻滤波器是一种通过大多数频率不变的滤波器,但将特定范围内的频率衰减到非常低的水平”。这似乎是我需要在我的录音中排除 2300 kHz 到 2400 kHz 的频率(或者至少在播放哔声时录音的第一秒)。我的问题是:我将如何使用 AVAudioEngine 实现这一点?有没有办法可以在录制的第一秒后关闭过滤器,当哔声播放完毕而不停止录制?

由于我是使用 AVAudioEngine 处理音频的新手(我一直坚持使用更高级别的 AVFoundation),因此我遵循this 教程来帮助我创建一个类来处理所有杂乱的东西。这是我的代码的样子:

class Recorder {
  enum RecordingState {
    case recording, paused, stopped
  }
  
  private var engine: AVAudioEngine!
  private var mixerNode: AVAudioMixerNode!
  private var state: RecordingState = .stopped
    
  private var audioPlayer = AVAudioPlayerNode()
  
  init() {
    setupSession()
    setupEngine()
  }
    
    
  fileprivate func setupSession() {
      let session = AVAudioSession.sharedInstance()
      //The original tutorial sets the category to .record
      //try? session.setCategory(.record)
      try? session.setCategory(.playAndRecord, options: [.mixWithOthers, .defaultToSpeaker])
      try? session.setActive(true, options: .notifyOthersOnDeactivation)
   }
    
    fileprivate func setupEngine() {
      engine = AVAudioEngine()
      mixerNode = AVAudioMixerNode()

      // Set volume to 0 to avoid audio feedback while recording.
      mixerNode.volume = 0

      engine.attach(mixerNode)

    //Attach the audio player node
    engine.attach(audioPlayer)
        
      makeConnections()

      // Prepare the engine in advance, in order for the system to allocate the necessary resources.
      engine.prepare()
    }

    
    fileprivate func makeConnections() {
       
      let inputNode = engine.inputNode
      let inputFormat = inputNode.outputFormat(forBus: 0)
      engine.connect(inputNode, to: mixerNode, format: inputFormat)

      let mainMixerNode = engine.mainMixerNode
      let mixerFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: inputFormat.sampleRate, channels: 1, interleaved: false)
    
      engine.connect(mixerNode, to: mainMixerNode, format: mixerFormat)
        
      //AudioPlayer Connection
      let path = Bundle.main.path(forResource: "beep.mp3", ofType:nil)!
      let url = URL(fileURLWithPath: path)
      let file = try! AVAudioFile(forReading: url)
      engine.connect(audioPlayer, to: mainMixerNode, format: nil)
      audioPlayer.scheduleFile(file, at: nil)
        
    }
    
    
    //MARK: Start Recording Function
    func startRecording() throws {
        print("Start Recording!")
      let tapNode: AVAudioNode = mixerNode
      let format = tapNode.outputFormat(forBus: 0)

      let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        
      // AVAudioFile uses the Core Audio Format (CAF) to write to disk.
      // So we're using the caf file extension.
        let file = try AVAudioFile(forWriting: documentURL.appendingPathComponent("recording.caf"), settings: format.settings)
       
      tapNode.installTap(onBus: 0, bufferSize: 4096, format: format, block: {
        (buffer, time) in
        
        try? file.write(from: buffer)
        print(buffer.description)
        print(buffer.stride)
        let floatArray = Array(UnsafeBufferPointer(start: buffer.floatChannelData![0], count:Int(buffer.frameLength)))
        
      })

      try engine.start()
      audioPlayer.play()
      state = .recording
    }
    
    
    //MARK: Other recording functions
    func resumeRecording() throws {
      try engine.start()
      state = .recording
    }

    func pauseRecording() {
      engine.pause()
      state = .paused
    }

    func stopRecording() {
      // Remove existing taps on nodes
      mixerNode.removeTap(onBus: 0)
      
      engine.stop()
      state = .stopped
    }
    
    
}

【问题讨论】:

    标签: ios swift audio avaudioplayer avaudioengine


    【解决方案1】:

    一个稍微更完整的答案,链接到 IBAction;在此示例中,我使用 .parametric 作为过滤器类型,其频带比所需的更多,以便更广泛地了解如何使用它:

    @IBAction func PlayWithEQ(_ sender: Any) {
        self.engine.stop()
        self.engine = AVAudioEngine()
        let player = AVAudioPlayerNode()
        let url = Bundle.main.url(forResource:"yoursong", withExtension: "m4a")!
        let f = try! AVAudioFile(forReading: url)
        self.engine.attach(player)
    
        // adding eq effect node
        let effect = AVAudioUnitEQ(numberOfBands: 4)
        let bands = effect.bands
        let freq = [125, 250, 2350, 8000]
        for i in 0...(bands.count - 1) {
            bands[i].frequency = Float(freq[i])
        }
        bands[0].gain = 0.0
        bands[0].filterType = .parametric
        bands[0].bandwidth = 1
        bands[1].gain = 0.0
        bands[1].filterType = .parametric
        bands[1].bandwidth = 0.5
        // filter of interest, rejecting 2350Hz (adjust bandwith as needed)
        bands[2].gain = -60.0
        bands[2].filterType = .parametric
        bands[2].bandwidth = 1
        bands[3].gain = 0.0
        bands[3].filterType = .parametric
        bands[3].bandwidth = 1
    
        self.engine.attach(effect)
        self.engine.connect(player, to: effect, format: f.processingFormat)
        let mixer = self.engine.mainMixerNode
        self.engine.connect(effect, to: mixer, format: f.processingFormat)
        player.scheduleFile(f, at: nil) {
            delay(0.05) {
                if self.engine.isRunning {
                    self.engine.stop()
                }
            }
        }
        self.engine.prepare()
        try! self.engine.start()
        player.play()        
    }
    

    【讨论】:

      【解决方案2】:

      AVAudioUnitEQ 支持带阻滤波器。

      可能是这样的:

      // Create an instance of AVAudioUnitEQ and connect it to the engine's main mixer
      let eq = AVAudioUnitEQ(numberOfBands: 1)
      engine.attach(eq)
      engine.connect(eq, to: engine.mainMixerNode, format: nil)
      engine.connect(player, to: eq, format: nil)
      eq.bands[0].frequency = 2350
      eq.bands[0].filterType = .bandStop
      eq.bands[0].bypass = false
      

      【讨论】:

        猜你喜欢
        • 2012-05-15
        • 1970-01-01
        • 1970-01-01
        • 2012-08-19
        • 2013-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多