AudioKit同时使用MIDI乐器与麦克风输入触发_engine!=nil崩溃排查
崩溃根因
报错required condition is false: _engine != nil是AVAudioEngine的底层断言触发,核心原因有两个:
- 音频链路被错误覆盖:
viewDidLoad中你先将engine.output赋值为instrument,后续又直接覆盖为silence节点,导致MIDISampler实例根本没有接入音频引擎的渲染链路,底层不会为该节点绑定对应的引擎实例引用。 - 执行顺序错误:
start()方法中你先调用instrument.loadInstrument()加载音色,再启动引擎;此时脱离链路的采样器节点关联的引擎实例为空,加载音色时底层校验直接失败崩溃。
修复步骤
要同时实现MIDI音符输出、麦克风音高检测功能,不能用覆盖输出的方式接节点,需要通过混音器合并两路音频流,同时调整启动顺序:
- 修正音频链路搭建逻辑,用Mixer同时接入MIDI采样器和麦克风链路,作为引擎最终输出
override func viewDidLoad() { super.viewDidLoad() print("init started ") guard let input = engine.input else { fatalError() } guard let device = engine.inputDevice else { fatalError() } print("input selected") initialDevice = device mic = input // 搭建麦克风检测链路 tappableNodeA = Fader(mic) tappableNodeB = Fader(tappableNodeA) tappableNodeC = Fader(tappableNodeB) // 麦克风输出增益设为0,避免外放啸叫,不影响PitchTap取数据 silence = Fader(tappableNodeC, gain: 0) // 混音器合并MIDI乐器、麦克风两路信号,作为引擎总输出 let mainMixer = Mixer([instrument, silence]) engine.output = mainMixer print("objects init") tracker = PitchTap(mic) { pitch, amp in DispatchQueue.main.async { self.update(pitch[0], amp[0]) } } start() }
- 调整启动顺序,先启动引擎完成所有节点的引擎关联,再加载采样器音色
func start() { do { // 先启动引擎,所有接入链路的节点会自动绑定引擎实例 try engine.start() tracker.start() } catch let err { print("caught error at start") Log(err) return } // 引擎启动完成后再加载采样器音色 do { if let fileURL = Bundle.main.url(forResource: "Sounds/Sampler Instruments/sawPiano1", withExtension: "exs") { try instrument.loadInstrument(url: fileURL) } else { Log("Could not find file") } } catch { Log("Could not load instrument") } }
补充说明
- PitchTap是直接挂载在输入节点
mic上的,后续链路的增益调整不会影响音高检测的准确性,如果需要开启麦克风监听,把silence节点的gain改成1即可。 - 所有涉及音频节点的加载、属性修改操作,都要确保节点已经接入音频链路、引擎处于启动状态,避免触发底层状态校验断言。
内容的提问来源于stack exchange,提问作者N0ctrn
相关产品推荐
相关产品推荐

