Search code examples
iosswiftaudioaudiokit

Swift - AudioKit AKMidi to AKSequencer


I currently have an application which uses an AKKeyboard to create sounds using an Oscillator. Whenever the keyboard is played I get the MIDI data also. What I would like to do is create an AKSequence from the MIDI data I receive.

Any advice or pointers will be greatly appreciated, thank you.

Here is a partial amount of my code:

var bank = AKOscillatorBank()
var midi: AKMIDI!
let sequencer = AKSequencer()
let sequenceLength = AKDuration(beats: 8.0)

func configureBank() {
    AudioKit.output = bank

    do {
        try AudioKit.start()
    } catch {
        AKLog("AudioKit couldn't be started")
    }

    midi = AudioKit.midi
    midi.addListener(self)
    midi.openInput()
}

// AKKeyboard Protocol methods
func noteOn(note: MIDINoteNumber) {
    let event = AKMIDIEvent(noteOn: note, velocity: 80, channel: 5)
    midi.sendEvent(event)
    bank.play(noteNumber: note, velocity: 100)
}

func noteOff(note: MIDINoteNumber) {
    let event = AKMIDIEvent(noteOff: note, velocity: 0, channel: 5)
    midi.sendEvent(event)
    bank.stop(noteNumber: note)
}

// AKMIDIListener Protocol methods..
func receivedMIDINoteOff(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel) {
    print("ReceivedMIDINoteOff: \(noteNumber), velocity: \(velocity), channel: \(channel)")
}

Solution

  • You don't actually need to build the sequence directly from the AKMIDIEvents. Just query the sequence's currentPosition when you call AKKeyboardView's noteOn and noteOff methods and programmatically add events to a sequencer track based on this.

    The process is basically identical to this (minus the final step, of course): https://stackoverflow.com/a/50071028/2717159

    Edit - To get the noteOn and noteOff times, and duration:

    // store notes and times in a dictionary:
    var noteDict = [MIDINoteNumber: MIDITimeStamp]()
    
    // when you get a noteOn, note the time
    noteDict[currentMIDINote] = seq.currentPosition.beats
    
    // when you get a noteOff
    let endTime = seq.currentPosition.beats
    if let startTime = noteDict[currentMIDINote] {
        let durationInBeats = endTime - startTime
        // use the startTime, duration and currentMIDINote to add event to track
        noteDict[currentMIDINote] = nil
    }