Keep the scheduler check in e2e 34 from flaking on a clock tick (#366)
The check "the scheduler plans a looped bar" read the position and the audio clock one after the other and compared them to 1 tick. In Chrome the audio clock moves on by a render block even inside one task, so now and then the two reads lay 2.9 ms (3.5 ticks) apart. It was red in CI task 580 and in 2 of 5 runs during #351. - The clock is now read before and after the position. The position has to lie between the two, with 1 tick to spare, also across the seam of the loop. - The scheduler itself is unchanged, it was never wrong. Tests: - e2e 34 (melektaus-atx): ALL PASSED, 93 checks, 3 of 3 runs. - Counter-probe with the position 10 ticks off: this check red. - Only the e2e file changed. node --check, Prettier and ESLint are clean on it, the gate did not run again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f840ec3c44
commit
87ec62aace
1 file changed
+14
-4
+14
-4
@@ -1714,6 +1714,8 @@ export async function sound(t) {
|
||||
// quarters at 150 BPM in a loop: every note is planned before its second comes, each 0.4 s
|
||||
// after the one before, also across the seam into the next round. Where the music stands
|
||||
// is the audio clock less the output latency, counted from the first note.
|
||||
// #366: the audio clock moves on by a render block even between two reads in one expression, so
|
||||
// it is read before and after the position, and the position lies between the two.
|
||||
const bar = [60, 62, 64, 65].map((pitch, i) => ({
|
||||
tick: i * 480,
|
||||
length: 240,
|
||||
@@ -1730,13 +1732,21 @@ export async function sound(t) {
|
||||
40,
|
||||
);
|
||||
const where = await json(
|
||||
'({ position: __gm.music.position(), clock: __gm.sound.clock(), latency: __gm.music.latency() })',
|
||||
`({
|
||||
before: __gm.sound.clock(),
|
||||
position: __gm.music.position(),
|
||||
after: __gm.sound.clock(),
|
||||
latency: __gm.music.latency(),
|
||||
})`,
|
||||
);
|
||||
await evaluate('__gm.music.stop()');
|
||||
const musicOff = await waitFor(stats, (s) => s.playing === 0);
|
||||
const apart = planned.slice(1, 9).map((p, i) => +(p.at - planned[i].at).toFixed(6));
|
||||
const ahead = planned.map((p) => +(p.at - p.planned).toFixed(3));
|
||||
const heardTicks = (((where.clock - where.latency - planned[0].at) * 150 * 480) / 60) % 1920;
|
||||
const ticksHeardAt = (clock) => ((clock - where.latency - planned[0].at) * 150 * 480) / 60;
|
||||
const heardTicks = [ticksHeardAt(where.before), ticksHeardAt(where.after)];
|
||||
// How far the position lies past the first read, around the loop of 1920 ticks.
|
||||
const pastBefore = (((where.position.ticks - heardTicks[0]) % 1920) + 1920) % 1920;
|
||||
check(
|
||||
'the scheduler plans a looped bar on the audio clock: every note ahead of its second, 0.4 s apart across the seam',
|
||||
planned.length >= 9 &&
|
||||
@@ -1747,11 +1757,11 @@ export async function sound(t) {
|
||||
planned.every((p) => p.voice > 0 && Math.abs(p.duration - 0.2) < 1e-9 && p.tempo === 150) &&
|
||||
apart.every((gap) => Math.abs(gap - 0.4) < 1e-6) &&
|
||||
ahead.every((s) => s > 0 && s <= 0.16) &&
|
||||
Math.abs(where.position.ticks - heardTicks) < 1 &&
|
||||
(pastBefore <= heardTicks[1] - heardTicks[0] + 1 || pastBefore > 1919) &&
|
||||
where.position.bar === 1 &&
|
||||
musicOff.playing === 0 &&
|
||||
(await json('__gm.music.playing()')) === false,
|
||||
JSON.stringify({ apart, ahead, where, heardTicks, playing: musicOff.playing }),
|
||||
JSON.stringify({ apart, ahead, where, heardTicks, pastBefore, playing: musicOff.playing }),
|
||||
);
|
||||
|
||||
// A note planned ahead through the pitch worklet starts on its sample, also inside a render block:
|
||||
|
||||
Reference in new issue
Block a user