【问题标题】:AudioKit: Problems connecting new input node to mixerAudioKit:将新输入节点连接到混音器时出现问题
【发布时间】:2020-09-12 23:39:18
【问题描述】:

我有一个功能可以在运行时更改我们应用中的仪器。其目的是允许诸如“预设”之类的东西可以改变乐器设置——有点像你在 DAW 的“歌曲”文件中看到的东西。我遇到的问题是新的混音器连接似乎没有正确连接,因此音频不会从新添加的乐器输出到输出。

我的整个信号链可以像这样描绘:

[(Instrument Node -> [Instrument Effects] -> AKBooster)] -> Instrument Mixer -> Master Effects -> Master Mixer -> Output

第一个块代表一组乐器及其效果。

管理“歌曲”切换的函数是:

public func connect(trackMap: [Int : InstrumentDefinition], isReconnect: Bool) {
        
        if SequencerController.sharedInstance.synchronizeTracksToTrackMap(trackMap) {
            
            guard let sequencer = SequencerController.sharedInstance.sequencer else { return }
            guard let sequence = sequencer.sequence else { return }
            
            var midiInstruments = SequencerController.sharedInstance.midiInstruments
            // get track count AFTER synchronizing to trackMap
            var trackCount: UInt32 = 0
            var status = MusicSequenceGetTrackCount(sequence, &trackCount)
            if status != noErr {
                print("Conductor.connect(trackMap:) - Error getting track count: \(status)")
                return
            }
            
            for trackIndex in 3 ..< Int(trackCount) {
                let instrumentDef = trackMap[trackIndex]!
                var track: MusicTrack? = nil
                status = MusicSequenceGetIndTrack(sequence, UInt32(trackIndex), &track)
                if status != noErr {
                    print("Conductor.connect(trackMap:) - Error getting sequence track: \(status)")
                    // there's no track associated with this
                }
                if trackIndex == 3 {
                    if let soundID = instrumentDef.soundID {
                        InteractionController.sharedInstance.sequencerInterface.currentSound = soundID
                    }
                }
                if let track = track {
                    var midiInst: SQMIDIInstrument?
                    var didCreateInstrument = false
                    switch instrumentDef.instrumentType {
                    case .sampler:
                        if let inst = midiInstruments[trackIndex] {
                            inst.instrumentDefinition = instrumentDef
                            // While changing presets (or sources; synth/sampler) gate incoming events.
                            inst.gateEvents = true
                            // If this track contained a synth, change it to a sampler
                            if !(inst.instrumentBlock.type == .sampler) {
                                inst.instrumentBlock.type = .sampler
                            }
                            if let soundID = instrumentDef.soundID {
                                inst.instrumentBlock.changeSoundID(soundID)
                            }
                            self.instrumentBlocksByTrack[trackIndex] = inst.instrumentBlock
                            inst.gateEvents = false
                            midiInst = inst
                        } else {
                            // create a new SQMIDISampler and add
                            midiInst = SQMIDIInstrument(withInstrumentDefinition: instrumentDef)
                            // We're replacing this track, so nullify in current midiInstruments dictionary.
                            midiInstruments[trackIndex] = nil
                            self.instrumentBlocksByTrack[trackIndex] = midiInst?.instrumentBlock
                            didCreateInstrument = true
                        }
                    case .synth:
                        // create a new AKSynthOne-based instrument and set preset
                        if let inst = midiInstruments[trackIndex] {
                            inst.instrumentDefinition = instrumentDef
                            inst.gateEvents = true
                            // If this track contained a sampler, change it to a synth
                            if !(inst.instrumentBlock.type == .synth) {
                                inst.instrumentBlock.type = .synth
                            }
                            if let soundID = instrumentDef.soundID {
                                inst.instrumentBlock.changeSoundID(soundID)
                            }
                            self.instrumentBlocksByTrack[trackIndex] = inst.instrumentBlock
                            inst.gateEvents = false
                            midiInst = inst
                        } else {
                            // create a new SQMIDISampler and add
                            midiInst = SQMIDIInstrument(withInstrumentDefinition: instrumentDef)
                            // We're replacing this track, so nullify in current midiInstruments dictionary.
                            midiInstruments[trackIndex] = nil
                            self.instrumentBlocksByTrack[trackIndex] = midiInst?.instrumentBlock
                            didCreateInstrument = true
                        }
                    case .drumSampler:
                        if let inst = midiInstruments[trackIndex] {
                            inst.instrumentDefinition = instrumentDef
                            inst.gateEvents = true
                            if !(inst.instrumentBlock.type == .drumSampler) {
                                inst.instrumentBlock.type = .drumSampler
                            }
                            if let soundID = instrumentDef.soundID {
                                inst.instrumentBlock.changeSoundID(soundID)
                            }
                            self.instrumentBlocksByTrack[trackIndex] = inst.instrumentBlock
                            inst.gateEvents = false
                            midiInst = inst
                        } else {
                            midiInst = SQMIDIInstrument(withInstrumentDefinition: instrumentDef)
                            // We're replacing this track, so nullify in current midiInstruments dictionary.
                            midiInstruments[trackIndex] = nil
                            self.instrumentBlocksByTrack[trackIndex] = midiInst?.instrumentBlock
                            didCreateInstrument = true
                        }
                    default: ()
                    }
                    
                    if let inst = midiInst {
                        SequencerController.setMIDIOutput(inst.midiIn, forTrack: track)
                        if !isReconnect || didCreateInstrument {
                            self.instrumentMixer.connect(input: inst.instrumentBlock.booster)
                        }
                        midiInstruments[trackIndex] = midiInst
                    }
                }
            }
            // update the SequencerController's set of midiInstruments
            SequencerController.sharedInstance.midiInstruments = midiInstruments
        }
    }

“midiInstruments”是 AKMIDIInstrument 子类,它包含一个采样器和一个带有乐器效果和增强器的合成器(在 instrumentBlock 中),以及乐器的参数(在其 instrumentDefinition 中)。 synchronizeTracksToTrackMap() 确保序列具有正确数量的轨道。此函数归Conductor 所有,似乎失败的步骤是self.instrumentMixer.connect(input: inst.instrumentBlock.booster) 调用。具体来说,当这个函数添加一个新轨道时,我看到了问题,需要一个新的混音器连接(即connect(input:))。我可以看到inst 正在获取 MIDI 事件,但没有输出。奇怪的是,一旦创建了新的“歌曲”(添加了它的音轨和乐器),我们就可以切换到另一首歌曲,再切换回来,乐器就连接成功了。

最终,我们希望能够交换或重新配置上述信号链的第一块;即[(Instrument Node -&gt; [Instrument Effects] -&gt; AKBooster)] 部分,它代表乐器(采样器/合成器)及其效果。我找不到可靠的方法来做到这一点,并且非常感谢任何人提供的任何建议。

更新:从这里跟随 SamB 的领导:How to reconnect AKPlayer and AKMixer after AudioKit.stop(),并使用AudioKit.engine.connect(inst.instrumentBlock.booster.outputNode, to: self.instrumentMixer.avAudioUnitOrNode, format: nil) 而不仅仅是self.instrumentMixer.connect(input:),我能够更接近。仍然没有声音,但我在 instrumentBlock 中放入的 AKAmplitudeTap(以查看可能出现的问题)显示至少那里有信号......

【问题讨论】:

    标签: swift audiokit


    【解决方案1】:

    观看 WWDC15 视频“Core Audio 中的新功能”很清楚,构建任何 AVAudioFoundation 信号链的过程是先将 attach 节点 首先 然后将 connect 它们制成图表.所以我重写了所有 AudioKit 代码的逻辑,以跳转到 Apple 的方法,并明确附加(或分离)和连接节点——例如,

    AudioKit.engine.attach(akNode.avAudioNode)
    AudioKit.engine.connect(akNode.outputNode, to: mixer.avAudioUnitOrNode, format: nil)
    

    以这种方式重新考虑它,我能够让事情按预期工作。据我了解,在附加/分离时必须关闭引擎,因此我用块包围任何附加/分离操作以关闭/打开引擎。我希望能够避免停止音频,所以如果这不是真的必要,任何建议或澄清将不胜感激。

    尽管 AudioKit 很方便,但我认为有点遗憾的是,它通过使所有内容看起来都像连接,从而向用户隐藏了附加/连接的区别。如果您还不了解 Core Audio——例如,像我一样主要通过 AudioKit 来了解它——我强烈建议您考虑先连接、后连接语义……当然除非您的用例永远不会要求更改节点总数。在我们的案例中,我们需要尽可能减少附件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-09
      • 1970-01-01
      • 1970-01-01
      • 2022-11-04
      • 2016-10-05
      相关资源
      最近更新 更多