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
This commit is contained in:
yaxu
2026-10-16 09:25:24 +02:00
parent 7f02baf93b
commit 8dbf5ce74c
+25 -91
View File
@@ -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());