Sequence and arp
Drive notes from the transport.To fire notes in time, gate a Clock off the host playhead. update(ctx.ppqPosition())
returns true on the block where a new step begins.
private clock: Clock = new Clock(0.25); // one step per sixteenth
private pattern: StaticArray<bool> = new StaticArray<bool>(32);
private len: i32 = 0;
private cur: i32 = -1;Bake a Euclidean pattern with euclid(pulses, steps, rotation, out) (only when its
settings change, never per sample), then read the current step on each tick:
process(ctx: Ctx): void {
this.len = euclid(4, 16, 0, this.pattern); // 4 hits across 16 steps
if (ctx.isPlaying() && this.clock.update(ctx.ppqPosition())) {
if (this.cur >= 0) { ctx.noteOff(0, this.cur); this.cur = -1; }
const idx = this.clock.step() % this.len;
if (this.pattern[idx]) { ctx.noteOn(0, 36, 110); this.cur = 36; }
}
}For an arpeggiator, collect held notes and step through them instead:
private arp: Arpeggiator = new Arpeggiator(16);
// in process: feed it MIDI, then step on the clock
if (ev.isNoteOn()) this.arp.noteOn(ev.note);
else if (ev.isNoteOff()) this.arp.noteOff(ev.note);
if (ctx.isPlaying() && this.clock.update(ctx.ppqPosition())) {
const n = this.arp.next(); // -1 when nothing is held
if (n >= 0) ctx.noteOn(0, n, 100);
}setPattern(ArpPattern.UpDown) and setOctaves(n) shape the run. For a mono voice,
MonoNoteStack(maxNotes) tracks held notes and reports the one to sound with
current(NotePriority.Highest). Construct Clock, Arpeggiator, and MonoNoteStack
in prepare.
See Harmony and Sync to tempo and key.