使用AudioKit v5-main时麦克风Pitch Tap触发崩溃,求解决方法
解决AudioKit v5中同时使用PitchTap和NodeRecorder导致的崩溃问题
崩溃原因分析
你遇到的这个错误:
[avae] AVAEInternal.h:76 required condition is false: [AVAEGraphNode.mm:817:CreateRecordingTap: (nullptr == Tap())]
核心原因是Core Audio的AVAudioEngine不允许在同一个音频节点实例上同时添加多个录制/监听Tap。AudioKit的PitchTap(用于音频分析)和NodeRecorder(用于录音)底层都会给绑定的节点添加Tap,当你把它们都挂在同一个boostMic节点上时,就触发了这个冲突,直接导致崩溃。
解决方案:拆分音频信号分支
我们需要把麦克风的信号拆分成两个独立的分支,让PitchTap和NodeRecorder分别处理不同的分支节点,这样就不会产生Tap冲突了。这里提供两种简洁的实现方式:
方法1:使用Splitter拆分信号
AudioKit的Splitter可以将单个输入信号拆分为多个独立的输出分支,每个分支都是独立的音频节点,完美解决Tap冲突问题:
class ViewController: UIViewController { let engine = AudioEngine() var silence: Fader! var boostMic: Fader! var tracker: PitchTap! var recorder: NodeRecorder! var player = AudioPlayer() var mixer = Mixer() // 新增Splitter用于拆分麦克风信号 var micSplitter: Splitter! override func viewDidLoad() { super.viewDidLoad() guard let mic = engine.input else { debugPrint("无法获取麦克风输入") return } boostMic = Fader(mic, gain: 5) // 将boostMic的信号拆分为两个独立分支 micSplitter = Splitter(boostMic) // PitchTap监听第一个分支 tracker = PitchTap(micSplitter[0], handler: { (pitch, amp) in DispatchQueue.main.async { debugPrint("amp: \(amp.first!.description)") } }) // NodeRecorder录制第二个分支 do { recorder = try NodeRecorder(node: micSplitter[1]) } catch { debugPrint(error.localizedDescription) } // 将其中一个分支接入静音Fader(不影响录音和分析) silence = Fader(micSplitter[0], gain: 0) mixer.addInput(silence) mixer.addInput(player) engine.output = mixer Settings.audioInputEnabled = true do { try engine.start() tracker.start() NodeRecorder.removeTempFiles() try recorder.record() } catch { debugPrint(error.localizedDescription) } } }
方法2:使用两个独立Mixer复制信号
如果你不想用Splitter,也可以创建两个独立的Mixer,分别接收原始麦克风信号,让PitchTap和NodeRecorder绑定到不同的Mixer上:
class ViewController: UIViewController { let engine = AudioEngine() var silence: Fader! var boostMic: Fader! var tracker: PitchTap! var recorder: NodeRecorder! var player = AudioPlayer() var mixer = Mixer() // 新增两个Mixer分别处理分析和录音分支 var tapMixer: Mixer! var recorderMixer: Mixer! override func viewDidLoad() { super.viewDidLoad() guard let mic = engine.input else { debugPrint("无法获取麦克风输入") return } boostMic = Fader(mic, gain: 5) // 创建两个独立的Mixer,都接入boostMic的信号 tapMixer = Mixer(boostMic) recorderMixer = Mixer(boostMic) // PitchTap绑定到tapMixer tracker = PitchTap(tapMixer, handler: { (pitch, amp) in DispatchQueue.main.async { debugPrint("amp: \(amp.first!.description)") } }) // NodeRecorder绑定到recorderMixer do { recorder = try NodeRecorder(node: recorderMixer) } catch { debugPrint(error.localizedDescription) } // 将tapMixer接入静音Fader silence = Fader(tapMixer, gain: 0) mixer.addInput(silence) mixer.addInput(player) engine.output = mixer Settings.audioInputEnabled = true do { try engine.start() tracker.start() NodeRecorder.removeTempFiles() try recorder.record() } catch { debugPrint(error.localizedDescription) } } }
为什么这样能解决问题?
两种方法的核心逻辑都是让PitchTap和NodeRecorder绑定到不同的音频节点实例,这样它们各自添加的Tap就不会冲突,完全符合Core Audio的限制要求,同时还能保证两者都能获取到完整的麦克风信号。
内容的提问来源于stack exchange,提问作者SwiftBeginner
相关产品推荐
相关产品推荐

