如何在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
相关产品推荐
相关产品推荐

