Reputation: 33
I have a loop-enabled aksequencer, and I want to change the length during playback. I've tried this with setLength() and setLoopInfo(), but neither one seems to work correctly.
Expected behavior: If I have a 16 beat sequence, and I call changeSequenceToLength(12), I expect the loop to play from beats 1-12, and repeat. (of course, I should handle the case where I change the length after beat 12, but you get the idea).
Actual behavior: a mixed bag. When I use setLength(endbeat), it simply deletes the midi events after the endbeat, but the loop is still 16 beats. When I call setLoopInfo(duration, numloops), it shortens the loop, but from the wrong side. So instead of playing 1-12, it plays 4-16. It also does some weird stuttering when I first change the loop duration.
Any suggestions on how to approach this would be much appreciated! I've looked at the docs for the backing apple MusicSequence too, but it's pretty sparse.
My code looks roughly like this:
import AudioKit
import AudioKitUI
final class Sequencer: AKSequencer {
static let shared = Sequencer() //Singleton
var numBeats = 16 // Number of spaces in the grid
var beatLength = 1/4.0
var sequenceDuration: AKDuration {
get {
return AKDuration(beats: (numBeats * beatLength))
}
}
private override init() {
super.init()
setTempo(120.0)
for _ in 0..<8 {
let track = newTrack()
}
let callback = AKCallbackInstrument() { status, note, velocity in
//send midi messages...
}
let midiNode = AKMIDINode(node: callback)
setLength(sequenceDuration)
AudioKit.output = midiNode
AudioKit.start()
midiNode.enableMIDI(Midi.shared.client, name: "midiNode midi in")
enableLooping()
}
func changeSequenceToLength(length: Int) {
numBeats = length
setLength(sequenceDuration)
// setLoopInfo(sequenceDuration, numberOfLoops: 50)
}
}
edit: So I've found AKMusicTrack.setLengthSoft(), so my midi sequence doesn't get deleted when I shorten the sequence. I also read the code for setLoopInfo, and it indeed is supposed to create a loop from the end backwards (shouldn't it take a startTime and offset??). But I am still having trouble with playback stuttering when I change the sequence length.
Upvotes: 3
Views: 201
Reputation: 2225
After you call setLength()
you need to re-assert the looping behaviour by calling enableLooping()
(or you can use setloopInfo
which will do both in one step). But basically changing loop length while the sequencer is running will be a problem.
It's important to understand thatAKSequencer
is essentially a Swift wrapper around CoreMIDI's MusicSequence
, so it basically inherits most of MusicSequence
's quirks and limitations. One of these limitations is that although you can set the 'left side' of the looping range, you cannot set the 'right side' - it will always loop back to MusicTimeStamp 0. Calling setLoopinfo
(or alternatively calling setLength()
followed by enableLooping()
) doesn't actually shorten the loop from the wrong side as you say (although I also thought it was doing so, at first). It is setting the loop length correctly, but also teleporting the playhead to a position which is 'new loop length' beats away from the 'old loop length'. So if you change a 16 beat loop to a 12 beat loop, the MusicSequence
will loop from 0 to 12, but it will move the playhead to beat 4 (16 - 12 = 4). It will not play from 4 to 16, but from 4 to 12, them loop from 0 to 12. Sometimes you can work around this you can make this work while you are playing, but mostly it's kind of a pain. best solution is to stop playback when you change loop length. CoreMIDI'a MusicSequence
is not very good at handling change dynamically.
Upvotes: 2