Tone.js/Tone/core/clock/TransportRepeatEvent.ts

136 lines
3.8 KiB
TypeScript
Raw Normal View History

import { BaseContext } from "../context/BaseContext";
import { TicksClass } from "../type/Ticks";
import { Seconds, Ticks, Time } from "../type/Units";
2019-05-22 03:37:03 +00:00
import { TransportEvent, TransportEventOptions } from "./TransportEvent";
2019-07-11 13:57:06 +00:00
type Transport = import("../clock/Transport").Transport;
2019-05-22 03:37:03 +00:00
interface TransportRepeatEventOptions extends TransportEventOptions {
interval: Ticks;
duration: Ticks;
}
/**
* TransportRepeatEvent is an internal class used by Tone.Transport
* to schedule repeat events. This class should not be instantiated directly.
*/
export class TransportRepeatEvent extends TransportEvent {
/**
* When the event should stop repeating
*/
private duration: Ticks;
/**
* The interval of the repeated event
*/
private _interval: Ticks;
/**
* The ID of the current timeline event
*/
2019-11-17 18:09:19 +00:00
private _currentId = -1;
2019-05-22 03:37:03 +00:00
/**
* The ID of the next timeline event
*/
2019-11-17 18:09:19 +00:00
private _nextId = -1;
2019-05-22 03:37:03 +00:00
/**
* The time of the next event
*/
private _nextTick = this.time;
/**
* a reference to the bound start method
*/
private _boundRestart = this._restart.bind(this);
/**
* The audio context belonging to this event
*/
protected context: BaseContext;
2019-05-22 03:37:03 +00:00
2019-08-21 20:59:01 +00:00
/**
2019-10-23 03:04:52 +00:00
* @param transport The transport object which the event belongs to
2019-08-21 20:59:01 +00:00
*/
2019-05-22 03:37:03 +00:00
constructor(transport: Transport, opts: Partial<TransportRepeatEventOptions>) {
super(transport, opts);
const options = Object.assign(TransportRepeatEvent.getDefaults(), opts);
2019-09-15 18:39:55 +00:00
this.duration = new TicksClass(transport.context, options.duration).valueOf();
this._interval = new TicksClass(transport.context, options.interval).valueOf();
2019-05-22 03:37:03 +00:00
this._nextTick = options.time;
this.transport.on("start", this._boundRestart);
this.transport.on("loopStart", this._boundRestart);
this.context = this.transport.context;
this._restart();
}
static getDefaults(): TransportRepeatEventOptions {
return Object.assign({}, TransportEvent.getDefaults(), {
duration: Infinity,
interval: 1,
once: false,
});
}
/**
* Invoke the callback. Returns the tick time which
* the next event should be scheduled at.
* @param time The AudioContext time in seconds of the event
*/
invoke(time: Seconds): void {
// create more events if necessary
this._createEvents(time);
// call the super class
super.invoke(time);
}
/**
* Push more events onto the timeline to keep up with the position of the timeline
*/
private _createEvents(time: Seconds): void {
// schedule the next event
const ticks = this.transport.getTicksAtTime(time);
if (ticks >= this.time && ticks >= this._nextTick && this._nextTick + this._interval < this.time + this.duration) {
this._nextTick += this._interval;
this._currentId = this._nextId;
this._nextId = this.transport.scheduleOnce(this.invoke.bind(this),
new TicksClass(this.context, this._nextTick).toSeconds());
}
}
/**
* Push more events onto the timeline to keep up with the position of the timeline
*/
private _restart(time?: Time): void {
this.transport.clear(this._currentId);
this.transport.clear(this._nextId);
this._nextTick = this.time;
const ticks = this.transport.getTicksAtTime(time);
if (ticks > this.time) {
this._nextTick = this.time + Math.ceil((ticks - this.time) / this._interval) * this._interval;
}
this._currentId = this.transport.scheduleOnce(this.invoke.bind(this),
new TicksClass(this.context, this._nextTick).toSeconds());
this._nextTick += this._interval;
this._nextId = this.transport.scheduleOnce(this.invoke.bind(this),
new TicksClass(this.context, this._nextTick).toSeconds());
}
/**
* Clean up
*/
dispose(): this {
super.dispose();
this.transport.clear(this._currentId);
this.transport.clear(this._nextId);
this.transport.off("start", this._boundRestart);
this.transport.off("loopStart", this._boundRestart);
return this;
}
}