How to Connect Aksequencer to a Akcallbackinstrument

How to connect AKSequencer to a AKCallbackInstrument?

True, the class AKCallbackInstrument does not have a property midiIn, although the documentation does show it being used that way. Instead of using AKCallbackInstrument, use AKMIDICallbackInstrument. That class has midiIn, and seems to work fine.

AudioKit: Using the new AKSequencer with any variety of the callback instruments

To get AKCallbackInstrument working with the new AKSequencer, try connecting your callback instrument to your output, e.g.,

metroCallback >>> mixer

Not obvious, but has worked for me.

Edit: including a minimal working version of the new AKSequencer with AKCallbackInstrument

class SequencerWrapper {
var seq: AKSequencer!
var cbInst: AKCallbackInstrument!
var mixer: AKMixer!

init() {
mixer = AKMixer()
AudioKit.output = mixer
seq = AKSequencer()
cbInst = AKCallbackInstrument()

// set up a track
let track = seq.addTrack(for: cbInst)
for i in 0 ..< 4 {
track.add(noteNumber: 60, position: Double(i), duration: 0.5)
}
track.length = 4.0
track.loopEnabled = true
track >>> mixer // must send track to mixer

// set up the callback instrument
cbInst.callback = { status, note, vel in
guard let status = AKMIDIStatus(byte: status),
let type = status.type,
type == .noteOn else { return }
print("note on: \(note)")
// trigger sampler etc from here
}
cbInst >>> mixer // must send callbackInst to mixer
}

func play() {
seq.playFromStart()
}
}

How to visualize current AKSequencer position with Audiokit?

I do this with the AKCallbackInstrument. For each sequence track that I write MIDI events to, I have a sister sequencer track sending to AKCallbackInstrument. When I write a MIDI event for the audio track, I also write a GUI event to the sister callback track.

Because you can only send MIDIStatus, MIDINote, and MIDIVelocity data to the callback instrument, you have to arbitrarily encode information into these formats. For example, a MIDINote of 0 might signify one type of GUI event, MIDINote 1 something else. Creating some enums makes this easy.

Of course the callback functions are called on a background thread, so don't forget to specify that your GUI updates should happen on the main thread.

This approach has worked quite well for me.

edit: I suspect you've already seen this sample code that illustrates something very similar, but this link might be useful for anyone else coming across this question.

Reloading AKAudioFile in AKSequencer using AKCallbackInstrument.noteOff?

You're definitely on the right track - you can easily get random audio files to loop at a fixed interval with AKSequencer + AKCallbackInstrument. But I wouldn't worry about trying to reload on the NoteOff message.

I would first load each mp3 into a separate player (e.g., AKAppleSampler) in an array (e.g.,you could call it players) and create a method that will trigger one of these players at random:

func playRandom() {
let playerIndex = Int(arc4random_uniform(UInt32(players.count)))
try? players[playerIndex].play()
}

When you create your sequencer, add a track and assign it to an AKCallbackInstrument. The callback function for this AKCallbackInstrument will call playRandom when it receives a noteOn message.

seq = AKSequencer()
track = seq.newTrack()!
callbackInst = AKCallbackInstrument()
track.setMIDIOutput(callbackInst.midiIn)
callbackInst.callback = { status, note, vel in
guard status == .noteOn else { return }
self.playRandom()
}

It isn't necessary to load the sequencer with a MIDI file. You could just add the triggering MIDI event directly to the track.

track.add(noteNumber: 48, // i.e., C3
velocity: 127,
position: AKDuration(beats: 0), // noteOn message here
duration: AKDuration(beats: 8), // noteOff 8 beats later
channel: 0)

Your problem with the sine wave is probably being caused by an extra track (probably tempo track) in the MIDI file which you created which hasn't been assigned an output. You can avoid the problem altogether by adding the MIDI events directly.

In principle, you could use the callback to check for noteOff events and trigger code from the noteOff, but I wouldn't recommend it in your case. There is no good reason to re-use a single player for multiple audiofiles. Loading the file is where you are most likely to create an error. What happens if your file hasn't finished playing and you try to load another one? The resources needed to keep multiple players in memory is pretty trivial - if you're going to play the same file more than once, it is cleaner and safer to load it once and keep the player in memory.

Starting AKSequencer from a specific point

You can move the playback with setTime. Perhaps this could be made better as "playhead time/position" instead of just time.

http://audiokit.io/docs/Classes/AKSequencer.html#/s:8AudioKit11AKSequencerC7setTimeySdF

How to trigger events or callback at a specific point in an Audio Track?

So I figured it out. The answer was basically in the posts above just updated the code so it uses AKAppleSequencer.

let sequencer = AKAppleSequencer(filename: "SaReGaMa") // the .mid file
let callbackInstr = AKMIDICallbackInstrument()
var player: AKPlayer!

func initializeSession() {
callbackInstr.callback = myCallBack
sequencer.setGlobalMIDIOutput(callbackInstr.midiIn)
if let audioFile = try? AKAudioFile(readFileName: "SaReGaMa.wav") {
player = AKPlayer(audioFile: audioFile)
player.completionHandler = { print("Finished playing file")}
player.buffering = .always

AudioKit.output = player
do {
try AudioKit.start()

} catch {
print("Error starting audiokit, \(error)")
}
}
}

// The callback gets triggered when each midi note is played by the sequencer.
func myCallBack(a: UInt8, b:MIDINoteNumber, c:MIDIVelocity) -> () {
print(a,b,c);
}
// These functions let you control the playback. 
func play() {
player.play()
sequencer.play()
}

func pause() {
sequencer.stop()
player.pause()
}

AudioKit ios AKSamplerMetronome

You could do this easily enough with AKSequencer (I did something similar). I assigned one track of the sequencer to an AKMIDISampler, generating the metronome sound, and a second track that went to an AKCallbackInstrument.

In the track being sent to the AKCallbackInstrument, I encoded the beat information arbitrarily in the MIDI data, so for example, the MIDI data for the first beat has a MIDINote of 1, the second, MIDINote 2 (you could do this with the velocity). Then the callback function could would just look at all of the noteOn messages and get the current beat from the MIDI Note number, and respond accordingly. It's a little indirect, but it works.

// create the sequencer before hand (e.g., at init); calling play() immediately after creating it causes some odd behaviour
let sequencer = AKSequencer()

// set up the sampler and callbackInst
let sampler = AKSynthSnare()
// or for your own sample:
// let sampler = AKMIDISampler()
// sampler.loadWav("myMetronomeSound)
let callbackInst = AKCallbackInstrument()
AudioKit.output = sampler
AudioKit.start()

// create two tracks for the sequencer
let metronomeTrack = sequencer.newTrack()
metronomeTrack?.setMIDIOutput(sampler.midiIn)
let callbackTrack = sequencer.newTrack()
callbackTrack?.setMIDIOutput(callbackInst.midiIn)

// create the MIDI data
for i in 0 ..< 4 {
// this will trigger the sampler on the four down beats
metronomeTrack?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(i)), duration: AKDuration(beats: 0.5))

// set the midiNote number to the current beat number
callbackTrack?.add(noteNumber: MIDINoteNumber(i), velocity: 100, position: AKDuration(beats: Double(i)), duration: AKDuration(beats: 0.5))
}

// set the callback
callbackInst.callback = {status, noteNumber, velocity in
guard status == .noteOn else { return }
print("beat number: \(noteNumber + 1)")
// e.g., resondToBeat(beatNum: noteNumber)
}

// get the sequencer ready
sequencer.enableLooping(AKDuration(beats: 4))
sequencer.setTempo(60)
sequencer.play()


Related Topics



Leave a reply



Submit