As I'm migrating my app to Swift, I'm encountering following missing feature:
Using AKSequencer and enableLooping, with each loop the currentPosition goes further beyond length. With MusicPlayer I added a MusicSequenceSetUserCallback to the longest track and rewind / restart MusicPlayer on each callback and animate the playing position accordingly.
Suggested solution for AKSequencer.swift:
```swift
/// Add a block that is called when all tracks did end playing.
///
/// - Parameters:
/// - callback: Block to be executed at end of longest track
///
/// Can be used when looping is not sufficient. With looping enabled
/// currentPosition will go on and on and on beyond the length.
/// Setting a block will remove previously added blocks
open func setDidEndSequencing(_ callback: () -> Swift.Void) {
// enjoy adding pure old c MusicSequenceSetUserCallback and MusicTrackNewUserEvent
// typically add the callback to MusicPlayer in AKSequencer play() as the length can change when tracks are edited / added
}
```
I might be misunderstanding something, but are you sure that you can't use AKCallbackInstrument to do something like this?
What I do is add a "user event" to the MusicSequence and loop manually based on receiving that event simply because getting a "done" message was flaky at best.
"user event" is what I have now in my ObjC-Code with MusicSequenceSetUserCallback(). I'd like to migrate this to AudioKit. Would the user event be added with AKMusicTrack addController or AKMusicTrack addSysex? Or somewhere else?
When the AKCallbackInstrument is in the event flow: which AKMIDIStatus would I have to listen to the "user event"?
I've done this with user events as well. I cannot remember if those are supported in AudioKit yet.
Probably because the callbackinstrument has been working very well for me. When you write the callback, you'll get a status, note, and velocity message. You can then choose what to do for each status / note / velocity combination as you want.
If there is a slight delay using the callback instrument, I have gotten around it by wrapping everything sequence related into a callback instrument - that makes the delay uniform across sequencing events so its as if there is really no delay.
This is how I do it in regular Core MIDI
let sequencerCallback: MusicSequenceUserCallback = {
(clientData: UnsafeMutableRawPointer?,
sequence: MusicSequence,
track: MusicTrack,
eventTime: MusicTimeStamp,
eventData: UnsafePointer<MusicEventUserData>,
startSliceBeat: MusicTimeStamp,
endSliceBeat: MusicTimeStamp)
-> Void in
let mySelf = Unmanaged<GridModel>.fromOpaque(UnsafeRawPointer(clientData)!).takeUnretainedValue()
let d = eventData.pointee
if d.data == 0xAA {
//print("got the marker")
DispatchQueue.main.async {
mySelf.hitEnd()
}
}
if d.data == 0xBE {
mySelf.gridDelegate?.beatReceived(eventTime)
}
}
status = MusicSequenceSetUserCallback(musicSequence!, sequencerCallback, p)
and I add the markers like this
func addMarker(startBeat: MusicTimeStamp) {
let endBeat = startBeat + MusicTimeStamp(numBeats)
print("adding end of phrase marker at \(endBeat)")
var event = MusicEventUserData(length: 1, data: (0xAA))
let status = MusicTrackNewUserEvent(markerTrack!, endBeat, &event)
if status != noErr {
AudioUtils.checkError(status)
}
}
@genedelisa Thanks for that - I know when I was first doing the AKSequencer I wanted to use user events but got held up passing one of the data structures around. Now that I know a little bit more about this, I will probably attempt it again.
I smell a rewrite of AKSequencer coming on....
As this issue is too specific, I wanted to ask for a more general solution: Add user events to AudioKit-MIDI. See #1472
Most helpful comment
This is how I do it in regular Core MIDI
and I add the markers like this