tone 15.5.53 → 15.5.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,12 @@
1
1
  import { expect } from "chai";
2
2
 
3
3
  import { BasicTests } from "../../../test/helper/Basic.js";
4
- import { atTime, Offline, whenBetween } from "../../../test/helper/Offline.js";
4
+ import {
5
+ atTime,
6
+ holdClock,
7
+ Offline,
8
+ whenBetween,
9
+ } from "../../../test/helper/Offline.js";
5
10
  import { noOp } from "../util/Interface.js";
6
11
  import { Clock } from "./Clock.js";
7
12
 
@@ -361,23 +366,14 @@ describe("Clock", () => {
361
366
  const clock = new Clock((_t, ticks) => {
362
367
  observedTicks.push(ticks);
363
368
  }, 200);
364
- const boundLoop = (
365
- clock as unknown as { _boundLoop: () => void }
366
- )._boundLoop;
367
369
  clock.start(0);
368
- // Detach the clock's own loop from the context's "tick" event
369
- // so _lastUpdate genuinely never catches up, simulating the
370
- // tick loop's cadence (default updateInterval=0.05s) falling
371
- // behind a rapid stop/start cycle, as described in #1419.
372
- clock.context.off("tick", boundLoop);
373
- return atTime(0.02, (time) => {
374
- clock.stop(time);
375
- clock.start(time, 40);
376
- // Reattach the loop: without a fix, it would replay ticks
377
- // from [_lastUpdate, time) using the pre-restart TickSource
378
- // state instead of the new offset.
379
- clock.context.on("tick", boundLoop);
380
- });
370
+ return atTime(
371
+ 0.02,
372
+ holdClock(clock, (time) => {
373
+ clock.stop(time);
374
+ clock.start(time, 40);
375
+ })
376
+ );
381
377
  }, 0.03);
382
378
  expect(observedTicks).to.deep.equal([0, 1, 2, 3, 4, 40, 41]);
383
379
  });
@@ -389,22 +385,17 @@ describe("Clock", () => {
389
385
  const clock = new Clock((_t, ticks) => {
390
386
  observedTicks.push(ticks);
391
387
  }, 200);
392
- const boundLoop = (
393
- clock as unknown as { _boundLoop: () => void }
394
- )._boundLoop;
395
388
  clock.on("stop", (time) => {
396
389
  stopEvents.push(time);
397
390
  });
398
391
  clock.start(0);
399
- // Same staleness simulation as above, but this time stop() and
400
- // the following start() are scheduled at two distinct times
401
- // within the stale window, rather than the same instant.
402
- clock.context.off("tick", boundLoop);
403
- return atTime(0.025, () => {
404
- clock.stop(0.015);
405
- clock.start(0.02, 40);
406
- clock.context.on("tick", boundLoop);
407
- });
392
+ return atTime(
393
+ 0.025,
394
+ holdClock(clock, () => {
395
+ clock.stop(0.015);
396
+ clock.start(0.02, 40);
397
+ })
398
+ );
408
399
  }, 0.03);
409
400
  // the "stop" event scheduled at 0.015 must still fire
410
401
  expect(stopEvents).to.deep.equal([0.015]);
@@ -419,22 +410,17 @@ describe("Clock", () => {
419
410
  let restartTime = 0;
420
411
  await Offline(() => {
421
412
  const clock = new Clock(noOp, 200);
422
- const boundLoop = (
423
- clock as unknown as { _boundLoop: () => void }
424
- )._boundLoop;
425
413
  clock.on("stop", (time) => events.push(["stop", time]));
426
414
  clock.on("start", (time) => events.push(["start", time]));
427
415
  clock.start(0);
428
- // Same staleness simulation as above. stop() and start() are
429
- // scheduled at the same instant, as happens when both default
430
- // to now() within one task: `transport.stop(); transport.start()`
431
- clock.context.off("tick", boundLoop);
432
- return atTime(0.02, (time) => {
433
- restartTime = time;
434
- clock.stop(time);
435
- clock.start(time, 40);
436
- clock.context.on("tick", boundLoop);
437
- });
416
+ return atTime(
417
+ 0.02,
418
+ holdClock(clock, (time) => {
419
+ restartTime = time;
420
+ clock.stop(time);
421
+ clock.start(time, 40);
422
+ })
423
+ );
438
424
  }, 0.03);
439
425
  expect(events).to.deep.equal([
440
426
  ["start", 0],
@@ -167,6 +167,12 @@ export class Clock<TypeName extends "bpm" | "hertz" = "hertz">
167
167
  stop(time?: Time): this {
168
168
  const computedTime = this.toSeconds(time);
169
169
  this.log("stop", computedTime);
170
+ if (this._lastUpdate < computedTime && computedTime <= this.now()) {
171
+ // Deliver the ticks still pending before the stop against the
172
+ // events scheduled so far, so they aren't replayed against events
173
+ // scheduled for the next run between stop() and start().
174
+ this._processRange(this._lastUpdate, computedTime);
175
+ }
170
176
  this._state.cancel(computedTime);
171
177
  this._state.setStateAtTime("stopped", computedTime);
172
178
  this._tickSource.stop(computedTime);
@@ -4,7 +4,12 @@ import "../context/Destination.js";
4
4
  import { expect } from "chai";
5
5
 
6
6
  import { warns } from "../../../test/helper/Basic.js";
7
- import { atTime, Offline, whenBetween } from "../../../test/helper/Offline.js";
7
+ import {
8
+ atTime,
9
+ holdClock,
10
+ Offline,
11
+ whenBetween,
12
+ } from "../../../test/helper/Offline.js";
8
13
  import { Synth } from "../../instrument/Synth.js";
9
14
  import { Signal } from "../../signal/Signal.js";
10
15
  import { Time } from "../type/Time.js";
@@ -1008,31 +1013,40 @@ describe("Transport", () => {
1008
1013
  transport.on("stop", (time) => {
1009
1014
  stopEvents.push(time);
1010
1015
  });
1011
- const clock = (
1012
- transport as unknown as {
1013
- _clock: {
1014
- context: typeof context;
1015
- _boundLoop: () => void;
1016
- };
1017
- }
1018
- )._clock;
1019
1016
  transport.start(0);
1020
- // Detach the clock's own loop from the context's "tick"
1021
- // event so its _lastUpdate genuinely falls behind,
1022
- // simulating the tick loop's cadence (default
1023
- // updateInterval=0.05s) outrunning a rapid
1024
- // Transport.stop()/Transport.start() cycle, as described
1025
- // in #1419.
1026
- clock.context.off("tick", clock._boundLoop);
1027
- return atTime(0.02, () => {
1028
- transport.stop(0.015);
1029
- transport.start(0.018);
1030
- clock.context.on("tick", clock._boundLoop);
1031
- });
1017
+ return atTime(
1018
+ 0.02,
1019
+ holdClock(transport, () => {
1020
+ transport.stop(0.015);
1021
+ transport.start(0.018);
1022
+ })
1023
+ );
1032
1024
  }, 0.03);
1033
1025
  expect(stopEvents).to.deep.equal([0.015]);
1034
1026
  });
1035
1027
 
1028
+ it("does not fire an event scheduled after stop() from the previous run (#1502)", async () => {
1029
+ const invocations: number[] = [];
1030
+ let restartTime = 0;
1031
+ await Offline((context) => {
1032
+ const transport = new TransportInstance({ context });
1033
+ transport.start(0);
1034
+ return atTime(
1035
+ 0.02,
1036
+ holdClock(transport, (time) => {
1037
+ transport.stop(time);
1038
+ // For the next run, at a position the previous run passed
1039
+ // between its last processed tick and the stop.
1040
+ transport.schedule((t) => invocations.push(t), "5i");
1041
+ restartTime = time + 0.005;
1042
+ transport.start(restartTime);
1043
+ })
1044
+ );
1045
+ }, 0.05);
1046
+ expect(invocations).to.have.length(1);
1047
+ expect(invocations[0]).to.be.at.least(restartTime);
1048
+ });
1049
+
1036
1050
  it("invokes start event with correct offset", async () => {
1037
1051
  let wasCalled = false;
1038
1052
  await Offline((context) => {
@@ -1,7 +1,12 @@
1
1
  import { expect } from "chai";
2
2
 
3
3
  import { BasicTests } from "../../test/helper/Basic.js";
4
- import { Offline, whenBetween } from "../../test/helper/Offline.js";
4
+ import {
5
+ atTime,
6
+ holdClock,
7
+ Offline,
8
+ whenBetween,
9
+ } from "../../test/helper/Offline.js";
5
10
  import { Time } from "../core/type/Time.js";
6
11
  import { noOp } from "../core/util/Interface.js";
7
12
  import { Loop } from "./Loop.js";
@@ -217,6 +222,29 @@ describe("Loop", () => {
217
222
  });
218
223
 
219
224
  context("Looping", () => {
225
+ it("starts a Loop created between stop() and start() with the new run (#1502)", async () => {
226
+ const beats: number[] = [];
227
+ let restartTime = 0;
228
+ await Offline((context) => {
229
+ const transport = context.transport;
230
+ transport.start(0);
231
+ return atTime(
232
+ 0.02,
233
+ holdClock(transport, (time) => {
234
+ transport.stop(time);
235
+ new Loop({
236
+ context,
237
+ callback: (t) => beats.push(t),
238
+ interval: "4i",
239
+ }).start(0);
240
+ restartTime = time + 0.005;
241
+ transport.start(restartTime);
242
+ })
243
+ );
244
+ }, 0.05);
245
+ expect(beats[0]).to.be.at.least(restartTime);
246
+ });
247
+
220
248
  it("loops", async () => {
221
249
  let callCount = 0;
222
250
  await Offline(({ transport }) => {
@@ -1,6 +1,6 @@
1
1
  import { expect } from "chai";
2
2
 
3
- import { atTime, Offline } from "../../test/helper/Offline.js";
3
+ import { atTime, holdClock, Offline } from "../../test/helper/Offline.js";
4
4
  import { ToneAudioBuffer } from "../core/context/ToneAudioBuffer.js";
5
5
  import { getContext } from "../core/Global.js";
6
6
  import { Player } from "./buffer/Player.js";
@@ -334,24 +334,14 @@ describe("Source", () => {
334
334
  const output = await Offline(({ transport }) => {
335
335
  const source = new Player(rampBuffer).toDestination();
336
336
  source.sync().start(0);
337
- const clock = (
338
- transport as unknown as {
339
- _clock: {
340
- context: typeof transport.context;
341
- _boundLoop: () => void;
342
- };
343
- }
344
- )._clock;
345
337
  transport.start(0);
346
- // hold the clock loop back so that stop/start land ahead of
347
- // _lastUpdate, as they do in a realtime context where both
348
- // default to now() = currentTime + lookAhead
349
- clock.context.off("tick", clock._boundLoop);
350
- return atTime(0.2, (time) => {
351
- transport.stop(time);
352
- transport.start(time, 0.5);
353
- clock.context.on("tick", clock._boundLoop);
354
- });
338
+ return atTime(
339
+ 0.2,
340
+ holdClock(transport, (time) => {
341
+ transport.stop(time);
342
+ transport.start(time, 0.5);
343
+ })
344
+ );
355
345
  }, 0.5);
356
346
  // before the restart the ramp plays from 0
357
347
  expect(output.getValueAtTime(0.1)).to.be.closeTo(0.1, 0.01);
package/Tone/version.ts CHANGED
@@ -1 +1 @@
1
- export const version: string = "15.5.53";
1
+ export const version: string = "15.5.55";