swiftaudiokitaksequencer

How to output external MIDI from an AKMusicTrack in AudioKit


I'm wondering what the best way is to have a AKSequencer (actually an AKMusicTrack) output it's MIDI to an external device.

I have gotten it to work, but I feel like there is probably a more efficient way.

The way I've done it:

I've made subclass of AKPolyphonicNode ("MyPolyphonicNode")

I've used this to init a subclass of AKMIDINode ("MyMIDINode"),

class MyMIDINode:AKMIDINODE {

init(...) {
        ...
        let myPolyphonicNode = MyPolyphonicNode()
        super.init(node: myPolyphonicNode, midiOutputName: "myMIDIOutput")
        ...
        }

//etc

}

and setMIDIoutput of the output of the AKMusicTrack to the midiIn of the AKMIDINode subclass:

track.setMIDIOutput(myMIDINode.midiIn) 

Then in the MyPolyphonicNode subclass, I've overriden:

override func play(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, frequency: Double) {
    myDelegate.myDelegateFunction(noteNumber:MIDINoteNumber, velocity:MIDIVelocity, channel:myChannel)
    }

And in its delegate:

let midi:AKMIDI //set in the init

enableMIDI(midi.client, name: "midiClient") //also in the init

func myDelegateFunction(noteNumber:MIDINoteNumber, velocity:MIDIVelocity, channel:MIDIChannelNumber) {
            midi.sendEvent(AKMIDIEvent(noteOn: noteNumber, velocity: velocity, channel: channel))
        }

This works, but I am thinking there is probably a way of telling the AKMusicTracks directly to output externally without doing all this?


Solution

  • A simpler solution is to use AKMIDICallbackInstrument, although it is the same basic idea. It is easy to set up:

    callbackInst = AKMIDICallbackInstrument()
    track.setMIDIOutput(callbackInst.midiIn)
    

    You need to provide a callback function which will trigger external MIDI:

    callbackInst.callback = { statusByte, note, velocity in 
        //  NB: in the next AudioKit release, there will be an easier init for AKMIDIStatus: 
        //  let status = AKMIDIStatus(statusByte: statusByte)
        let status = AKMIDIStatus(rawValue: Int(statusByte >> 4))
        switch status {
            case .noteOn:
                midi.sendNoteOnMessage(noteNumber: note, velocity: velocity)
            case .noteOff:
                midi.sendNoteOffMessage(noteNumber: note, velocity: velocity)
            default:
                // etc.
        }
    

    This function will be called whenever events on the AKMusicTrack are played