mirror of
https://github.com/Tonejs/Tone.js
synced 2024-12-28 12:33:12 +00:00
148 lines
3 KiB
TypeScript
148 lines
3 KiB
TypeScript
import { Seconds } from "../type/Units";
|
|
|
|
export type TickerClockSource = "worker" | "timeout" | "offline";
|
|
|
|
/**
|
|
* A class which provides a reliable callback using either
|
|
* a Web Worker, or if that isn't supported, falls back to setTimeout.
|
|
*/
|
|
export class Ticker {
|
|
|
|
/**
|
|
* Either "worker" or "timeout" or "offline"
|
|
*/
|
|
private _type: TickerClockSource;
|
|
|
|
/**
|
|
* The update interval of the worker
|
|
*/
|
|
private _updateInterval: Seconds;
|
|
|
|
/**
|
|
* The callback to invoke at regular intervals
|
|
*/
|
|
private _callback: () => void;
|
|
|
|
/**
|
|
* track the callback interval
|
|
*/
|
|
private _timeout!: ReturnType<typeof setTimeout>;
|
|
|
|
/**
|
|
* private reference to the worker
|
|
*/
|
|
private _worker!: Worker;
|
|
|
|
constructor(callback: () => void, type: TickerClockSource, updateInterval: Seconds) {
|
|
|
|
this._callback = callback;
|
|
this._type = type;
|
|
this._updateInterval = updateInterval;
|
|
|
|
// create the clock source for the first time
|
|
this._createClock();
|
|
}
|
|
|
|
/**
|
|
* Generate a web worker
|
|
*/
|
|
private _createWorker(): void {
|
|
|
|
const blob = new Blob([
|
|
/* javascript */`
|
|
// the initial timeout time
|
|
let timeoutTime = ${(this._updateInterval * 1000).toFixed(1)};
|
|
// onmessage callback
|
|
self.onmessage = function(msg){
|
|
timeoutTime = parseInt(msg.data);
|
|
};
|
|
// the tick function which posts a message
|
|
// and schedules a new tick
|
|
function tick(){
|
|
setTimeout(tick, timeoutTime);
|
|
self.postMessage('tick');
|
|
}
|
|
// call tick initially
|
|
tick();
|
|
`
|
|
], { type: "text/javascript" });
|
|
const blobUrl = URL.createObjectURL(blob);
|
|
const worker = new Worker(blobUrl);
|
|
|
|
worker.onmessage = this._callback.bind(this);
|
|
|
|
this._worker = worker;
|
|
}
|
|
|
|
/**
|
|
* Create a timeout loop
|
|
*/
|
|
private _createTimeout(): void {
|
|
this._timeout = setTimeout(() => {
|
|
this._createTimeout();
|
|
this._callback();
|
|
}, this._updateInterval * 1000);
|
|
}
|
|
|
|
/**
|
|
* Create the clock source.
|
|
*/
|
|
private _createClock(): void {
|
|
if (this._type === "worker") {
|
|
try {
|
|
this._createWorker();
|
|
} catch (e) {
|
|
// workers not supported, fallback to timeout
|
|
this._type = "timeout";
|
|
this._createClock();
|
|
}
|
|
} else if (this._type === "timeout") {
|
|
this._createTimeout();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clean up the current clock source
|
|
*/
|
|
private _disposeClock(): void {
|
|
if (this._timeout) {
|
|
clearTimeout(this._timeout);
|
|
}
|
|
if (this._worker) {
|
|
this._worker.terminate();
|
|
this._worker.onmessage = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The rate in seconds the ticker will update
|
|
*/
|
|
get updateInterval(): Seconds {
|
|
return this._updateInterval;
|
|
}
|
|
set updateInterval(interval: Seconds) {
|
|
this._updateInterval = Math.max(interval, 128 / 44100);
|
|
if (this._type === "worker") {
|
|
this._worker.postMessage(Math.max(interval * 1000, 1));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The type of the ticker, either a worker or a timeout
|
|
*/
|
|
get type(): TickerClockSource {
|
|
return this._type;
|
|
}
|
|
set type(type: TickerClockSource) {
|
|
this._disposeClock();
|
|
this._type = type;
|
|
this._createClock();
|
|
}
|
|
|
|
/**
|
|
* Clean up
|
|
*/
|
|
dispose(): void {
|
|
this._disposeClock();
|
|
}
|
|
}
|