1

I'm trying to get a callback when a midi note is triggered so I can display a graphical representation of the current beat in the bar. Nothing I do seems to callback. This is the simplest version of the code that I have tried - audio plays back fine but the callback is never triggered. I have enabled Audio in Background Modes.

Here is the code:

class ViewMain: UIViewController {

    let engine = AudioEngine()
    let kicks = Sampler()
    let claps = Sampler()
    let sequencer = Sequencer()
    let mixer = Mixer()

    let midiCallbackInstrument = MIDICallbackInstrument()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        midiCallbackInstrument.callback = myCallBack
        
        mixer.addInput(midiCallbackInstrument)
        mixer.addInput(kicks)
        mixer.addInput(claps)
        
        engine.output = mixer
        do {
            try engine.start()
        } catch {
            Log("AudioKit did not start! \(error)")
        }
        
        kicks.unloadAllSamples()
        claps.unloadAllSamples()
        
        guard let url = Bundle.main.resourceURL?.appendingPathComponent("SamplesSFZ/sfz/909A.sfz") else { return }
        kicks.loadSFZ(url: url)
        
        guard let url = Bundle.main.resourceURL?.appendingPathComponent("SamplesSFZ/sfz/909B.sfz") else { return }
        claps.loadSFZ(url: url)
        
        sequencer.addTrack(for: midiCallbackInstrument)
        
        sequencer.tracks[0].add(noteNumber: 36, velocity: 120, channel: 0, position: 0.0, duration: 1.0)
        sequencer.tracks[0].add(noteNumber: 37, velocity: 120, channel: 1, position: 0.5, duration: 1.0)
        sequencer.tracks[0].add(noteNumber: 38, velocity: 120, channel: 2, position: 1.0, duration: 1.0)
        sequencer.tracks[0].add(noteNumber: 39, velocity: 120, channel: 3, position: 0.5, duration: 1.0)
        sequencer.tracks[0].add(noteNumber: 40, velocity: 120, channel: 4, position: 2.0, duration: 1.0)
        sequencer.tracks[0].add(noteNumber: 41, velocity: 120, channel: 5, position: 0.5, duration: 1.0)
        sequencer.tracks[0].add(noteNumber: 42, velocity: 120, channel: 6, position: 3.0, duration: 1.0)
        sequencer.tracks[0].add(noteNumber: 43, velocity: 120, channel: 7, position: 0.5, duration: 1.0)
        
        sequencer.addTrack(for: kicks)
        sequencer.tracks[1].add(noteNumber: 24, velocity: 120, channel: 0, position: 0.0, duration: 2.0)

        sequencer.addTrack(for: claps)
        sequencer.tracks[2].add(noteNumber: 25, velocity: 120, channel: 0, position: 1.0, duration: 0.1)
        sequencer.tracks[2].add(noteNumber: 25, velocity: 120, channel: 0, position: 3.0, duration: 0.1)
        
        sequencer.loopEnabled = true
        sequencer.tempo = 120.0
        sequencer.length = 4.0
        
        sequencer.play()
    }

    func myCallBack(a: UInt8, b:MIDINoteNumber, c:MIDIVelocity) -> () {
        // THIS IS NEVER CALLED
        print("*** callback invoked")
    }
}

Thanks in advance - I really appreciate any help here.

domhawken
  • 11
  • 4
  • You're sending the callback instrument as well as the sampler outputs to the mixer, but not the sequencer tracks themselves. Iirc, connecting the tracks is also necessary, see https://stackoverflow.com/a/61545391/2717159 – c_booth Feb 08 '22 at 19:47
  • Many thanks for coming back! If I try: mixer.addInput( sequencer.addTrack(for: midiCallbackInstrument) ) it failesbecause a sequencer track is not a node, which is the expected type for a mixer input, which makes sense. Still no joy here. – domhawken Feb 10 '22 at 23:33

2 Answers2

1

I have had good results with the newer AudioKit CallbackInstrument, which is intended as a replacement for MIDICallbackInstrument.


To get CallbackInstrument working with your code, you only need to make two changes:

// replace let midiCallbackInstrument = MIDICallbackInstrument() with

          var midiCallbackInstrument = CallbackInstrument()

// replace midiCallbackInstrument.callback = myCallBack with

    midiCallbackInstrument = CallbackInstrument(midiCallback: { [self] (event, beat, velocity) in
        myCallBack(a: event, b: beat, c: velocity) } )

CallbackInstrument is documented here: https://github.com/AudioKit/AudioKit/wiki/CallbackInstrument

and the Shaker Metronome recipe in the AudioKit Cookbook is an example of how it is used. https://github.com/AudioKit/Cookbook

Brian H
  • 153
  • 7
0

Use sequencer.setGlobalMIDIOutput(midiCallbackInstrument.midiIn) or sequencer.tracks[0].setMIDIOutput(midiCallbackInstrument.midiIn) to connect sequencer and midiCallbackInstrument.

Stefano Sansone
  • 2,377
  • 7
  • 20
  • 39
zzxgbgba
  • 1
  • 2