From 8dbf5ce74c0cd7c119c9b759add0f35ee4d1e993 Mon Sep 17 00:00:00 2001 From: yaxu Date: Fri, 16 Oct 2026 09:25:24 +0200 Subject: [PATCH] revert 3583091e43f618309fa496f329267177dc7ade58 revert Merge pull request 'Fix Cyclist skipping haps during triggered increases in cps' (#2103) from tzwaan/fix-cyclist-cps into main Reviewed-on: https://codeberg.org/uzu/strudel/pulls/2103 --- packages/core/cyclist.mjs | 116 ++++++++------------------------------ 1 file changed, 25 insertions(+), 91 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 163ca3232..cc68bb102 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -28,34 +28,31 @@ export class Cyclist { this.started = false; this.beforeStart = beforeStart; this.cps = 0.5; - this.checkpoint = { - // new checkpoints are created when there's a change in cps - cycles: 0, // number of cycles when cps was changed - seconds: 0, // clock phase when cps was changed - }; - this.num_ticks_since_checkpoint = 0; + this.num_ticks_since_cps_change = 0; this.lastTick = 0; // absolute time when last tick (clock callback) happened this.lastBegin = 0; // query begin of last tick this.lastEnd = 0; // query end of last tick this.getTime = getTime; // get absolute time + this.num_cycles_at_cps_change = 0; + this.seconds_at_cps_change; // clock phase when cps was changed this.onToggle = onToggle; this.latency = latency; // fixed trigger time offset this.clock = createClock( getTime, // called slightly before each cycle (phase, duration, _, t) => { - if (this.num_ticks_since_checkpoint === 0) { - this.checkpoint = { - cycles: this.lastEnd, - seconds: phase, - }; + if (this.num_ticks_since_cps_change === 0) { + this.num_cycles_at_cps_change = this.lastEnd; + this.seconds_at_cps_change = phase; } - this.num_ticks_since_checkpoint++; + this.num_ticks_since_cps_change++; + const seconds_since_cps_change = this.num_ticks_since_cps_change * duration; + const num_cycles_since_cps_change = seconds_since_cps_change * this.cps; try { - var begin = this.lastEnd; + const begin = this.lastEnd; this.lastBegin = begin; - var end = this.checkpoint.cycles + this.num_ticks_since_checkpoint * duration * this.cps; + const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; this.lastEnd = end; this.lastTick = phase; @@ -65,80 +62,20 @@ export class Cyclist { return; } - const tickEndTime = phase + duration; + // query the pattern for events + const haps = this.pattern.queryArc(begin, end, { _cps: this.cps, cyclist: 'cyclist' }); - var cpsChanged = false; - do { - cpsChanged = false; - - // query the pattern for events - const haps = this.pattern - .queryArc(begin, end, { _cps: this.cps, cyclist: 'cyclist' }) - // Sort the haps based on their onset, so cps changes within the - // tick duration happen in the correct order. - .sort((a, b) => { - const aBegin = a.whole.begin.valueOf(); - const bBegin = b.whole.begin.valueOf(); - // If haps happen at the same time, trigger the haps that cause - // cps changes first, so the durations of the simultaneous haps - // are adjusted accordingly. - if (aBegin === bBegin) { - if (a.value.cps !== undefined) { - return b.value.cps !== undefined ? 0 : -1; - } - if (b.value.cps !== undefined) { - return 1; - } - return 0; - } - return aBegin - bBegin; - }); - - haps.forEach((hap) => { - if (hap.hasOnset()) { - const targetTime = (hap.whole.begin - this.checkpoint.cycles) / this.cps + this.checkpoint.seconds; - if (tickEndTime < targetTime) { - // If the targetTime exceeds the tickEndTime, this means - // the cps has slowed down, and the latter haps from the query - // no longer fall within the window of the current tick. - // They'll be scheduled during the next tick. - return; - } - onSchedule?.(hap, targetTime, hap.value.cps ?? this.cps); - if (hap.value.cps !== undefined && this.cps != hap.value.cps) { - cpsChanged = true; - this.cps = hap.value.cps; - this.num_ticks_since_checkpoint = 0; - this.checkpoint = { - cycles: hap.whole.begin, - seconds: targetTime, - }; - } - } - }); - - // If there are any haps in the query that trigger changes to the - // cps that result in the pattern speeding up, the current cycle end - // may not capture all the haps that need to be scheduled. - // If this is the case, we repeat the query + scheduling with a - // newly calculated end time. - // This process can repeat multiple times until no additional - // changes to the cps are triggered. - // In practice, this doesn't happen more than once during a tick. - if (cpsChanged) { - begin = end; - const remainingSecondsSinceCheckpoint = tickEndTime - this.checkpoint.seconds; - const remainingCycles = remainingSecondsSinceCheckpoint * this.cps; - end = this.checkpoint.cycles + remainingCycles; - this.lastEnd = end; - if (end < begin) { - // If the new end is before the old end this means the track - // slowed down, so we don't need to schedule additional haps. - // Otherwise, schedule the remaining events. - break; + haps.forEach((hap) => { + if (hap.hasOnset()) { + const targetTime = + (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; + onSchedule?.(hap, targetTime, this.cps); + if (hap.value.cps !== undefined && this.cps != hap.value.cps) { + this.cps = hap.value.cps; + this.num_ticks_since_cps_change = 0; } } - } while (cpsChanged); + }); } catch (e) { errorLogger(e); onError?.(e); @@ -164,11 +101,8 @@ export class Cyclist { } async start() { await this.beforeStart?.(); - this.checkpoint = { - cycles: 0, - seconds: 0, - }; - this.num_ticks_since_checkpoint = 0; + this.num_ticks_since_cps_change = 0; + this.num_cycles_at_cps_change = 0; if (!this.pattern) { throw new Error('Scheduler: no pattern set! call .setPattern first.'); } @@ -198,7 +132,7 @@ export class Cyclist { return; } this.cps = cps; - this.num_ticks_since_checkpoint = 0; + this.num_ticks_since_cps_change = 0; } log(begin, end, haps) { const onsets = haps.filter((h) => h.hasOnset());