Tone.js/Tone/event/Loop.ts

230 lines
5 KiB
TypeScript
Raw Normal View History

import { ToneEvent } from "./ToneEvent.js";
import {
NormalRange,
Positive,
Seconds,
Time,
TransportTime,
} from "../core/type/Units.js";
import {
ToneWithContext,
ToneWithContextOptions,
} from "../core/context/ToneWithContext.js";
import { optionsFromArguments } from "../core/util/Defaults.js";
import { noOp } from "../core/util/Interface.js";
import { BasicPlaybackState } from "../core/util/StateTimeline.js";
2019-09-17 18:45:58 +00:00
export interface LoopOptions extends ToneWithContextOptions {
callback: (time: Seconds) => void;
interval: Time;
playbackRate: Positive;
iterations: number;
probability: NormalRange;
mute: boolean;
humanize: boolean | Time;
}
/**
* Loop creates a looped callback at the
* specified interval. The callback can be
2019-09-17 18:45:58 +00:00
* started, stopped and scheduled along
* the Transport's timeline.
2019-09-17 18:45:58 +00:00
* @example
* const loop = new Tone.Loop((time) => {
* // triggered every eighth note.
2019-09-17 18:45:58 +00:00
* console.log(time);
* }, "8n").start(0);
* Tone.Transport.start();
2019-09-17 18:45:58 +00:00
* @category Event
*/
export class Loop<
Options extends LoopOptions = LoopOptions,
> extends ToneWithContext<Options> {
2019-09-17 18:45:58 +00:00
readonly name: string = "Loop";
/**
* The event which produces the callbacks
*/
private _event: ToneEvent;
/**
* The callback to invoke with the next event in the pattern
*/
callback: (time: Seconds) => void;
2019-09-17 18:45:58 +00:00
/**
* @param callback The callback to invoke at the time.
* @param interval The time between successive callback calls.
2019-09-17 18:45:58 +00:00
*/
constructor(callback?: (time: Seconds) => void, interval?: Time);
constructor(options?: Partial<LoopOptions>);
constructor() {
super(
optionsFromArguments(Loop.getDefaults(), arguments, [
"callback",
"interval",
])
);
const options = optionsFromArguments(Loop.getDefaults(), arguments, [
"callback",
"interval",
]);
2019-09-17 18:45:58 +00:00
this._event = new ToneEvent({
context: this.context,
callback: this._tick.bind(this),
loop: true,
loopEnd: options.interval,
playbackRate: options.playbackRate,
probability: options.probability,
humanize: options.humanize,
2019-09-17 18:45:58 +00:00
});
this.callback = options.callback;
// set the iterations
this.iterations = options.iterations;
}
static getDefaults(): LoopOptions {
return Object.assign(ToneWithContext.getDefaults(), {
interval: "4n",
callback: noOp,
playbackRate: 1,
iterations: Infinity,
probability: 1,
mute: false,
humanize: false,
2019-09-17 18:45:58 +00:00
});
}
/**
2019-10-23 03:04:52 +00:00
* Start the loop at the specified time along the Transport's timeline.
* @param time When to start the Loop.
*/
2019-09-17 18:45:58 +00:00
start(time?: TransportTime): this {
this._event.start(time);
return this;
}
/**
2019-10-23 03:04:52 +00:00
* Stop the loop at the given time.
* @param time When to stop the Loop.
*/
stop(time?: TransportTime): this {
2019-09-17 18:45:58 +00:00
this._event.stop(time);
return this;
}
/**
2019-10-23 03:04:52 +00:00
* Cancel all scheduled events greater than or equal to the given time
* @param time The time after which events will be cancel.
*/
cancel(time?: TransportTime): this {
2019-09-17 18:45:58 +00:00
this._event.cancel(time);
return this;
}
/**
2019-10-23 03:04:52 +00:00
* Internal function called when the notes should be called
* @param time The time the event occurs
*/
2019-11-07 19:39:28 +00:00
protected _tick(time: Seconds): void {
2019-09-17 18:45:58 +00:00
this.callback(time);
}
/**
2019-10-23 03:04:52 +00:00
* The state of the Loop, either started or stopped.
*/
2019-09-17 18:45:58 +00:00
get state(): BasicPlaybackState {
return this._event.state;
}
/**
* The progress of the loop as a value between 0-1. 0, when the loop is stopped or done iterating.
2019-10-23 03:04:52 +00:00
*/
2019-09-17 18:45:58 +00:00
get progress(): NormalRange {
return this._event.progress;
}
/**
* The time between successive callbacks.
2019-10-23 03:04:52 +00:00
* @example
* const loop = new Tone.Loop();
2019-10-24 22:01:27 +00:00
* loop.interval = "8n"; // loop every 8n
2019-10-23 03:04:52 +00:00
*/
2019-09-17 18:45:58 +00:00
get interval(): Time {
return this._event.loopEnd;
}
set interval(interval) {
this._event.loopEnd = interval;
}
/**
* The playback rate of the loop. The normal playback rate is 1 (no change).
* A `playbackRate` of 2 would be twice as fast.
2019-10-23 03:04:52 +00:00
*/
2019-09-17 18:45:58 +00:00
get playbackRate(): Positive {
return this._event.playbackRate;
}
set playbackRate(rate) {
this._event.playbackRate = rate;
}
/**
* Random variation +/-0.01s to the scheduled time.
2019-10-23 03:04:52 +00:00
* Or give it a time value which it will randomize by.
*/
2019-09-17 18:45:58 +00:00
get humanize(): boolean | Time {
return this._event.humanize;
}
set humanize(variation) {
this._event.humanize = variation;
}
/**
2019-10-23 03:04:52 +00:00
* The probably of the callback being invoked.
*/
2019-09-17 18:45:58 +00:00
get probability(): NormalRange {
return this._event.probability;
}
set probability(prob) {
this._event.probability = prob;
}
/**
2019-10-23 03:04:52 +00:00
* Muting the Loop means that no callbacks are invoked.
*/
2019-09-17 18:45:58 +00:00
get mute(): boolean {
return this._event.mute;
}
set mute(mute) {
this._event.mute = mute;
}
/**
2019-10-23 03:04:52 +00:00
* The number of iterations of the loop. The default value is `Infinity` (loop forever).
*/
2019-09-17 18:45:58 +00:00
get iterations(): number {
if (this._event.loop === true) {
return Infinity;
} else {
return this._event.loop as number;
}
}
set iterations(iters) {
if (iters === Infinity) {
this._event.loop = true;
} else {
this._event.loop = iters;
}
}
dispose(): this {
2019-09-17 18:45:58 +00:00
super.dispose();
this._event.dispose();
return this;
}
}