tone 15.5.58 → 15.5.60
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.
- package/Tone/core/clock/Clock.test.ts +32 -0
- package/Tone/core/clock/Clock.ts +47 -19
- package/Tone/source/Source.test.ts +50 -0
- package/Tone/version.ts +1 -1
- package/build/Tone.js +1 -1
- package/build/Tone.js.map +1 -1
- package/build/esm/core/clock/Clock.d.ts +6 -2
- package/build/esm/core/clock/Clock.js +40 -19
- package/build/esm/core/clock/Clock.js.map +1 -1
- package/build/esm/version.js +1 -1
- package/package.json +1 -1
|
@@ -428,6 +428,38 @@ describe("Clock", () => {
|
|
|
428
428
|
["start", restartTime],
|
|
429
429
|
]);
|
|
430
430
|
});
|
|
431
|
+
|
|
432
|
+
it("invokes pending ticks and state events in time order", async () => {
|
|
433
|
+
const events: Array<[string, number]> = [];
|
|
434
|
+
let pauseTime = 0;
|
|
435
|
+
await Offline(() => {
|
|
436
|
+
const clock = new Clock((time, ticks) => {
|
|
437
|
+
events.push(["tick " + ticks, time]);
|
|
438
|
+
}, 200);
|
|
439
|
+
clock.on("pause", (time) => events.push(["pause", time]));
|
|
440
|
+
clock.on("start", (time) => events.push(["start", time]));
|
|
441
|
+
clock.start(0);
|
|
442
|
+
// ticks 0-4 (every 0.005s) and the pause are all pending in
|
|
443
|
+
// the same clock update; the ticks before the pause must be
|
|
444
|
+
// invoked before the pause event, not after it
|
|
445
|
+
return atTime(
|
|
446
|
+
0.022,
|
|
447
|
+
holdClock(clock, (time) => {
|
|
448
|
+
pauseTime = time;
|
|
449
|
+
clock.pause(time);
|
|
450
|
+
})
|
|
451
|
+
);
|
|
452
|
+
}, 0.03);
|
|
453
|
+
expect(events).to.deep.equal([
|
|
454
|
+
["start", 0],
|
|
455
|
+
["tick 0", 0],
|
|
456
|
+
["tick 1", 0.005],
|
|
457
|
+
["tick 2", 0.01],
|
|
458
|
+
["tick 3", 0.015],
|
|
459
|
+
["tick 4", 0.02],
|
|
460
|
+
["pause", pauseTime],
|
|
461
|
+
]);
|
|
462
|
+
});
|
|
431
463
|
});
|
|
432
464
|
|
|
433
465
|
context("Events", () => {
|
package/Tone/core/clock/Clock.ts
CHANGED
|
@@ -7,7 +7,12 @@ import { assertContextRunning } from "../util/Debug.js";
|
|
|
7
7
|
import { optionsFromArguments } from "../util/Defaults.js";
|
|
8
8
|
import { Emitter } from "../util/Emitter.js";
|
|
9
9
|
import { noOp, readOnly } from "../util/Interface.js";
|
|
10
|
-
import {
|
|
10
|
+
import { GT } from "../util/Math.js";
|
|
11
|
+
import {
|
|
12
|
+
PlaybackState,
|
|
13
|
+
StateTimeline,
|
|
14
|
+
StateTimelineEvent,
|
|
15
|
+
} from "../util/StateTimeline.js";
|
|
11
16
|
import { TickSignal } from "./TickSignal.js";
|
|
12
17
|
import { TickSource } from "./TickSource.js";
|
|
13
18
|
|
|
@@ -290,39 +295,62 @@ export class Clock<TypeName extends "bpm" | "hertz" = "hertz">
|
|
|
290
295
|
|
|
291
296
|
/**
|
|
292
297
|
* Invoke the state-transition events (start/stop/pause) and tick
|
|
293
|
-
* callbacks scheduled between startTime and endTime
|
|
294
|
-
* _lastUpdate to endTime.
|
|
298
|
+
* callbacks scheduled between startTime and endTime in time order,
|
|
299
|
+
* and advance _lastUpdate to endTime.
|
|
295
300
|
* @param startTime The beginning of the range to process.
|
|
296
301
|
* @param endTime The end of the range to process.
|
|
297
302
|
*/
|
|
298
303
|
private _processRange(startTime: number, endTime: number): void {
|
|
299
304
|
this._lastUpdate = endTime;
|
|
300
305
|
if (startTime !== endTime) {
|
|
301
|
-
// the state change events
|
|
306
|
+
// the state change events, interleaved with the tick callbacks
|
|
307
|
+
// so that e.g. a tick just before a pause is delivered before
|
|
308
|
+
// the pause event, not after it
|
|
309
|
+
const stateEvents: StateTimelineEvent[] = [];
|
|
302
310
|
this._state.forEachBetween(startTime, endTime, (e) => {
|
|
303
|
-
|
|
304
|
-
case "started":
|
|
305
|
-
const offset = this._tickSource.getTicksAtTime(e.time);
|
|
306
|
-
this.emit("start", e.time, offset);
|
|
307
|
-
break;
|
|
308
|
-
case "stopped":
|
|
309
|
-
if (e.time !== 0) {
|
|
310
|
-
this.emit("stop", e.time);
|
|
311
|
-
}
|
|
312
|
-
break;
|
|
313
|
-
case "paused":
|
|
314
|
-
this.emit("pause", e.time);
|
|
315
|
-
break;
|
|
316
|
-
}
|
|
311
|
+
stateEvents.push(e);
|
|
317
312
|
});
|
|
318
|
-
|
|
313
|
+
let nextStateEvent = 0;
|
|
314
|
+
const emitStateEventsUpTo = (time: Seconds) => {
|
|
315
|
+
// a callback may have disposed the clock
|
|
316
|
+
while (
|
|
317
|
+
!this.disposed &&
|
|
318
|
+
nextStateEvent < stateEvents.length &&
|
|
319
|
+
!GT(stateEvents[nextStateEvent].time, time)
|
|
320
|
+
) {
|
|
321
|
+
this._emitStateEvent(stateEvents[nextStateEvent]);
|
|
322
|
+
nextStateEvent++;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
319
325
|
this._tickSource.forEachTickBetween(
|
|
320
326
|
startTime,
|
|
321
327
|
endTime,
|
|
322
328
|
(time, ticks) => {
|
|
329
|
+
emitStateEventsUpTo(time);
|
|
323
330
|
this.callback(time, ticks);
|
|
324
331
|
}
|
|
325
332
|
);
|
|
333
|
+
emitStateEventsUpTo(endTime);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Emit the event corresponding to a state change
|
|
339
|
+
*/
|
|
340
|
+
private _emitStateEvent(event: StateTimelineEvent): void {
|
|
341
|
+
switch (event.state) {
|
|
342
|
+
case "started":
|
|
343
|
+
const offset = this._tickSource.getTicksAtTime(event.time);
|
|
344
|
+
this.emit("start", event.time, offset);
|
|
345
|
+
break;
|
|
346
|
+
case "stopped":
|
|
347
|
+
if (event.time !== 0) {
|
|
348
|
+
this.emit("stop", event.time);
|
|
349
|
+
}
|
|
350
|
+
break;
|
|
351
|
+
case "paused":
|
|
352
|
+
this.emit("pause", event.time);
|
|
353
|
+
break;
|
|
326
354
|
}
|
|
327
355
|
}
|
|
328
356
|
|
|
@@ -3,6 +3,7 @@ import { expect } from "chai";
|
|
|
3
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
|
+
import { noOp } from "../core/util/Interface.js";
|
|
6
7
|
import { Player } from "./buffer/Player.js";
|
|
7
8
|
import { Oscillator } from "./oscillator/Oscillator.js";
|
|
8
9
|
|
|
@@ -351,6 +352,55 @@ describe("Source", () => {
|
|
|
351
352
|
expect(output.getValueAtTime(0.4)).to.be.closeTo(0.7, 0.01);
|
|
352
353
|
});
|
|
353
354
|
|
|
355
|
+
it("stops a source whose start was pending in the same clock update as a pause (#1289)", async () => {
|
|
356
|
+
const output = await Offline(({ transport }) => {
|
|
357
|
+
const source = new Player(rampBuffer).toDestination();
|
|
358
|
+
source.sync().start(0.5);
|
|
359
|
+
transport.start(0);
|
|
360
|
+
// the clip's start tick at 0.5 and the pause at ~0.52 are
|
|
361
|
+
// processed in the same clock update
|
|
362
|
+
return atTime(
|
|
363
|
+
0.52,
|
|
364
|
+
holdClock(transport, (time) => {
|
|
365
|
+
transport.pause(time);
|
|
366
|
+
})
|
|
367
|
+
);
|
|
368
|
+
}, 1);
|
|
369
|
+
expect(output.getValueAtTime(0.7)).to.equal(0);
|
|
370
|
+
expect(output.getValueAtTime(0.9)).to.equal(0);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("stops a source whose start was pending in the same clock update as a stop (#1289)", async () => {
|
|
374
|
+
const output = await Offline(({ transport }) => {
|
|
375
|
+
const source = new Player(rampBuffer).toDestination();
|
|
376
|
+
source.sync().start(0.5);
|
|
377
|
+
transport.start(0);
|
|
378
|
+
return atTime(
|
|
379
|
+
0.52,
|
|
380
|
+
holdClock(transport, (time) => {
|
|
381
|
+
transport.stop(time);
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
}, 1);
|
|
385
|
+
expect(output.getValueAtTime(0.7)).to.equal(0);
|
|
386
|
+
expect(output.getValueAtTime(0.9)).to.equal(0);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it("stops a source whose start was pending in the same clock update as a stop scheduled ahead of time", async () => {
|
|
390
|
+
const output = await Offline(({ transport }) => {
|
|
391
|
+
const source = new Player(rampBuffer).toDestination();
|
|
392
|
+
source.sync().start(0.5);
|
|
393
|
+
transport.start(0);
|
|
394
|
+
// stop scheduled in advance; the start tick at 0.5 and the
|
|
395
|
+
// stop at 0.52 are both pending when the clock catches up
|
|
396
|
+
transport.stop(0.52);
|
|
397
|
+
return atTime(0.55, holdClock(transport, noOp));
|
|
398
|
+
}, 1);
|
|
399
|
+
expect(output.getValueAtTime(0.51)).to.be.closeTo(0.01, 0.005);
|
|
400
|
+
expect(output.getValueAtTime(0.7)).to.equal(0);
|
|
401
|
+
expect(output.getValueAtTime(0.9)).to.equal(0);
|
|
402
|
+
});
|
|
403
|
+
|
|
354
404
|
it("gives the correct offset on time on start/stop events when started with an offset", async () => {
|
|
355
405
|
const output = await Offline(({ transport }) => {
|
|
356
406
|
const source = new Player(rampBuffer).toDestination();
|
package/Tone/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version: string = "15.5.
|
|
1
|
+
export const version: string = "15.5.60";
|