Context:
I am making an attempt to play audio information over an AppleSequencer
.
The audio file playbacks ought to be triggered in correct sync with a predefined sequence of AppleSequencer
beats: file 1 playback begins at beat 4, file 2 begins at beat 8, file N begins at beat 4N (there is no overlap: every audio file lasts lower than 4 beats).
Query:
Is there an answer to this drawback which permits looking for to any offset of the sequencer? As an example, after we search to beat 4.123, then file 1 playback ought to begin on the offset which corresponds to “beat 0.123” (by this I imply that the playback ought to be in sync with the sequencer, the identical sync it might have if the sequencer have been began at beat 0).
Present answer:
My present sync answer is just not appropriate with looking for at any offset (once I search at an offset in the midst of an audio file, that file is not performed in any respect, solely subsequent information are). This answer makes use of this SO reply, which solutions the case N = 1: begin AppleSequencer
occasion, fetch its hostTime
comparable to beat 4, after which begin AudioPlayer
occasion at the moment.
For basic N >= 1, I take advantage of a MIDICallbackInstrument
which can run the above technique a bit earlier than every beat that may be a a number of of 4 (NB: I play the identical AudioPlayer
occasion within the snippet beneath for simplicity):
// Add MIDI occasions, which can be picked by a callback, simply earlier than multiples of 4
let monitor = appleSequencer.tracks[0]
for i in 0..<N {
monitor.add(noteNumber: 66, velocity: 127, place: Length(beats: 3.5 + 4.0 * Double(i)), length: Length(beats: 1))
}
// Set a callback to play the AudioPlayer occasion when it encounters these occasions
midiCallbackInstrument.callback = { _, word, _ in
if word == 66 {
let nextAudioStartInBeats = ceil(self.appleSequencer.currentPosition.beats / 4.0) * 4.0
do {
let nextAudioStartInHostTime = strive AVAudioTime(hostTime: self.appleSequencer.hostTime(forBeats: nextAudioStartInBeats))
audioplayer.play(at: nextAudioStartInHostTime)
} catch { /* ... */ }
}
}