You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在iOS中使用AudioKit避免内置麦克风录音时的啸叫反馈

卡拉OK场景下AudioKit内置麦克风啸叫问题解决

问题背景

  • 卡拉OK场景:播放音频时录制自身声音,通过带混响(Reverb)、延迟(Delay)的混音器监听
  • 有线耳机/蓝牙设备使用正常,iPhone内置麦克风会出现高频啸叫反馈

需求目标

  • 使用内置麦克风时关闭监听
  • 使用外部麦克风时开启监听
  • 补充其他防啸叫方案(如切换顶部扬声器、增益调节等)

已尝试无效方案

  • 旧版AudioKit的booster.gain = 0(新版无Booster组件)
  • 绕过/更新混音器、设置混音器音量为0
  • 添加Fader静音输入(仍有啸叫,甚至无法录音)

解决方案代码实现

import AudioKit
import AVFoundation

class Conductor {
    
    static let sharedInstance = Conductor()
    
    let engine = AudioEngine()
    var mic: AudioEngine.InputNode!
    var delay: Delay!
    var reverb: Reverb!
    let mixer = Mixer()
    var recorder: NodeRecorder!
    // 用于控制监听通路的增益节点
    var monitorFader: Fader!
    
    init() {
        do {
            Settings.bufferLength = .medium
            try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(Settings.bufferLength.duration)
            // 移除.defaultToSpeaker选项,避免内置扬声器触发啸叫回路
            try AVAudioSession.sharedInstance().setCategory(.playAndRecord,
                                        options: [.mixWithOthers, .allowBluetoothA2DP])
            try AVAudioSession.sharedInstance().setActive(true)
        } catch let err {
            print(err)
        }
        
        mic = engine.input
        
        delay = Delay(mic)
        delay.time = AUValue(0.3)
        delay.feedback = AUValue(7.0)
        delay.dryWetMix = AUValue(7.0)
        
        reverb = Reverb(delay)
        reverb.loadFactoryPreset(.mediumRoom)
        reverb.dryWetMix = AUValue(0.4)
        
        // 构建监听通路:reverb → monitorFader → mixer
        monitorFader = Fader(reverb, gain: 1.0)
        mixer.addInput(monitorFader)
        
        engine.output = mixer
        
        startAudioEngine()
        // 初始化时自动适配当前麦克风类型
        updateMonitorStatus()
        // 注册路由变化监听,自动切换状态
        setupRouteChangeListener()
    }
    
    func startAudioEngine() {
        do {
            print("Audio engine was started.")
            try engine.start()
        } catch {
            Log("AudioKit did not start! \(error)")
        }
    }
    
    func prepareToRecord() {
        updateMonitorStatus()
        
        if !engine.avEngine.isRunning {
            do {
                try engine.start()
            } catch {
                Log("AudioKit did not start! \(error)")
            }
        }
        
        do {
            // 录音节点直接连接reverb,不受监听Fader的增益控制
            recorder = try NodeRecorder(node: reverb, shouldCleanupRecordings: true)
        } catch {
            print("error to start recording")
        }
    }
    
    // 根据麦克风类型切换监听状态
    func updateMonitorStatus() {
        if isUsingInternalMic() {
            // 内置麦克风时,将监听增益设为0,彻底断开监听通路
            monitorFader.gain = 0.0
            // 额外优化:强制使用顶部扬声器,减少拾音与扬声器的距离,降低啸叫概率
            try? AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
        } else {
            // 外部麦克风时恢复监听增益
            monitorFader.gain = 1.0
            // 恢复扬声器输出(如果需要)
            try? AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
        }
    }
    
    func isUsingInternalMic() -> Bool {
        let route = AVAudioSession.sharedInstance().currentRoute
        return route.inputs.contains { $0.portType == .builtInMic }
    }
    
    func startRecording() {
        do {
            try recorder.record()
        } catch {
            print("Error starting recording: \(error)")
        }
    }
    
    func stopRecording() {
        recorder.stop()
    }
    
    func stopAudioEngine() {
        engine.stop()
        print("Audio engine was stopped.")
    }
    
    // 监听音频路由变化,自动更新监听状态
    func setupRouteChangeListener() {
        NotificationCenter.default.addObserver(self, selector: #selector(updateMonitorStatus), name: AVAudioSession.routeChangeNotification, object: nil)
    }
}

关键优化说明

  • 分离监听与录音路径:录音直接从reverb节点获取,监听通路通过monitorFader独立控制,避免关闭监听时影响录音功能
  • Fader替代Booster:新版AudioKit用Fader组件控制增益,通过设置gain = 0彻底关闭监听通路,解决啸叫根源
  • AVAudioSession优化:移除.defaultToSpeaker避免内置底部扬声器近距离拾音,内置麦克风时强制切换到顶部扬声器,进一步降低反馈风险
  • 自动路由适配:注册AVAudioSession.routeChangeNotification,插拔外部麦克风时自动切换监听状态,无需手动触发

内容的提问来源于stack exchange,提问作者Nat Serrano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 16:15:59