adding additional option to StateTimeline.add

enables more strict typing of returned object
This commit is contained in:
Yotam Mann 2019-07-23 13:43:11 -04:00
parent f6f2e78574
commit 9306e5188c
3 changed files with 23 additions and 29 deletions

View file

@ -1,24 +1,18 @@
// import "../type/Type";
import { Timeline, TimelineEvent } from "./Timeline";
export type PlaybackState = "started" | "stopped" | "paused";
export type BasicPlaybackState = "started" | "stopped";
export type PlaybackState = BasicPlaybackState | "paused";
export interface StateTimelineEvent extends TimelineEvent {
state: PlaybackState;
duration?: Seconds;
offset?: Seconds;
/**
* Either the buffer is explicitly scheduled to end using the stop method,
* or it's implicitly ended when the buffer is over.
*/
implicitEnd?: boolean;
}
/**
* A Timeline State. Provides the methods: `setStateAtTime("state", time)` and `getValueAtTime(time)`
* @param initial The initial state of the StateTimeline. Defaults to `undefined`
*/
export class StateTimeline extends Timeline<StateTimelineEvent> {
export class StateTimeline<AdditionalOptions extends {} = {}> extends Timeline<StateTimelineEvent & AdditionalOptions> {
/**
* The initial state
@ -47,16 +41,17 @@ export class StateTimeline extends Timeline<StateTimelineEvent> {
/**
* Add a state to the timeline.
* @param state The name of the state to set.
* @param time The time to query.
* @param state The name of the state to set.
* @param time The time to query.
* @param options Any additional options that are needed in the timeline.
*/
setStateAtTime(state: PlaybackState, time: Seconds): this {
setStateAtTime(state: PlaybackState, time: Seconds, options?: AdditionalOptions): this {
// all state changes need to be >= the previous state time
// TODO throw error if time < the previous event time
this.add({
this.add(Object.assign({}, options, {
state,
time,
});
}));
return this;
}
@ -66,7 +61,7 @@ export class StateTimeline extends Timeline<StateTimelineEvent> {
* @param time When to check before
* @return The event with the given state before the time
*/
getLastState(state: PlaybackState, time: number): StateTimelineEvent | undefined {
getLastState(state: PlaybackState, time: number): StateTimelineEvent & AdditionalOptions | undefined {
// time = this.toSeconds(time);
const index = this._search(time);
for (let i = index; i >= 0; i--) {
@ -83,7 +78,7 @@ export class StateTimeline extends Timeline<StateTimelineEvent> {
* @param time When to check from
* @return The event with the given state after the time
*/
getNextState(state: PlaybackState, time: number): StateTimelineEvent | undefined {
getNextState(state: PlaybackState, time: number): StateTimelineEvent & AdditionalOptions | undefined {
// time = this.toSeconds(time);
const index = this._search(time);
if (index !== -1) {

View file

@ -3,7 +3,7 @@ import { ToneWithContext, ToneWithContextOptions } from "../core/context/ToneWit
import { TicksClass } from "../core/type/Ticks";
import { defaultArg, optionsFromArguments } from "../core/util/Defaults";
import { noOp } from "../core/util/Interface";
import { PlaybackState, StateTimeline } from "../core/util/StateTimeline";
import { BasicPlaybackState, StateTimeline } from "../core/util/StateTimeline";
import { isBoolean, isDefined, isNumber } from "../core/util/TypeCheck";
type ToneEventCallback = (time: Seconds, value: any) => void;
@ -71,7 +71,7 @@ export class ToneEvent extends ToneWithContext<ToneEventOptions> {
/**
* Tracks the scheduled events
*/
private _state: StateTimeline = new StateTimeline("stopped");
private _state: StateTimeline<{id: number}> = new StateTimeline("stopped");
/**
* The playback speed of the note. A speed of 1
@ -143,7 +143,7 @@ export class ToneEvent extends ToneWithContext<ToneEventOptions> {
this._state.forEachFrom(after, event => {
let duration;
if (event.state === "started") {
if (isDefined(event.id)) {
if (event.id !== -1) {
this.context.transport.clear(event.id);
}
const startTick = event.time + Math.round(this.startOffset / this._playbackRate);
@ -158,7 +158,7 @@ export class ToneEvent extends ToneWithContext<ToneEventOptions> {
}
if (duration !== Infinity) {
// schedule a stop since it's finite duration
this._state.setStateAtTime("stopped", startTick + duration + 1);
this._state.setStateAtTime("stopped", startTick + duration + 1, { id : -1 });
duration = new TicksClass(this.context, duration);
}
const interval = new TicksClass(this.context, this._getLoopDuration());
@ -174,8 +174,8 @@ export class ToneEvent extends ToneWithContext<ToneEventOptions> {
/**
* Returns the playback state of the note, either "started" or "stopped".
*/
get state(): PlaybackState {
return this._state.getValueAtTime(this.context.transport.ticks);
get state(): BasicPlaybackState {
return this._state.getValueAtTime(this.context.transport.ticks) as BasicPlaybackState;
}
/**
@ -221,7 +221,7 @@ export class ToneEvent extends ToneWithContext<ToneEventOptions> {
time = this.toTicks(time);
if (this._state.getValueAtTime(time) === "stopped") {
this._state.add({
id : undefined,
id : -1,
state : "started",
time,
});
@ -238,7 +238,7 @@ export class ToneEvent extends ToneWithContext<ToneEventOptions> {
this.cancel(time);
time = this.toTicks(time);
if (this._state.getValueAtTime(time) === "started") {
this._state.setStateAtTime("stopped", time);
this._state.setStateAtTime("stopped", time, { id: -1 });
const previousEvent = this._state.getBefore(time);
let reschedulTime = time;
if (previousEvent !== null) {
@ -257,7 +257,7 @@ export class ToneEvent extends ToneWithContext<ToneEventOptions> {
time = defaultArg(time, -Infinity);
time = this.toTicks(time);
this._state.forEachFrom(time, event => {
this.context.transport.clear(event.id as number);
this.context.transport.clear(event.id);
});
this._state.cancel(time);
return this;

View file

@ -1,4 +1,3 @@
import { StateTimelineEvent } from "Tone/core/util/StateTimeline";
import { ToneAudioBuffer } from "../../core/context/ToneAudioBuffer";
import { defaultArg, optionsFromArguments } from "../../core/util/Defaults";
import { noOp } from "../../core/util/Interface";
@ -220,9 +219,9 @@ export class Player extends Source<PlayerOptions> {
// set the looping properties
if (!this._loop && !this._synced) {
// if it's not looping, set the state change at the end of the sample
this._state.setStateAtTime("stopped", startTime + computedDuration);
// mark that ending as an implicit ending
(this._state.get(startTime + computedDuration) as StateTimelineEvent).implicitEnd = true;
this._state.setStateAtTime("stopped", startTime + computedDuration, {
implicitEnd: true,
});
}
// add it to the array of active sources