mirror of
https://github.com/Tonejs/Tone.js
synced 2024-12-30 13:33:10 +00:00
172 lines
5.8 KiB
TypeScript
172 lines
5.8 KiB
TypeScript
import { Signal, SignalOptions } from "./Signal";
|
|
import { NormalRange, Seconds, Time, TransportTime, UnitMap, UnitName } from "../core/type/Units";
|
|
import { optionsFromArguments } from "../core/util/Defaults";
|
|
import { TransportTimeClass } from "../core/type/TransportTime";
|
|
import { ToneConstantSource } from "./ToneConstantSource";
|
|
import { OutputNode } from "../core/context/ToneAudioNode";
|
|
|
|
/**
|
|
* Adds the ability to synchronize the signal to the [[Transport]]
|
|
*/
|
|
export class SyncedSignal<TypeName extends UnitName = "number"> extends Signal<TypeName> {
|
|
|
|
readonly name: string = "SyncedSignal";
|
|
|
|
/**
|
|
* Don't override when something is connected to the input
|
|
*/
|
|
readonly override = false;
|
|
|
|
readonly output: OutputNode;
|
|
|
|
/**
|
|
* Keep track of the last value as an optimization.
|
|
*/
|
|
private _lastVal: UnitMap[TypeName];
|
|
|
|
/**
|
|
* The ID returned from scheduleRepeat
|
|
*/
|
|
private _synced: number;
|
|
|
|
/**
|
|
* Remember the callback value
|
|
*/
|
|
private _syncedCallback: () => void;
|
|
|
|
/**
|
|
* @param value Initial value of the signal
|
|
* @param units The unit name, e.g. "frequency"
|
|
*/
|
|
constructor(value?: UnitMap[TypeName], units?: TypeName);
|
|
constructor(options?: Partial<SignalOptions<TypeName>>);
|
|
constructor() {
|
|
|
|
super(optionsFromArguments(Signal.getDefaults(), arguments, ["value", "units"]));
|
|
const options = optionsFromArguments(Signal.getDefaults(), arguments, ["value", "units"]) as SignalOptions<TypeName>;
|
|
|
|
this._lastVal = options.value;
|
|
this._synced = this.context.transport.scheduleRepeat(this._onTick.bind(this), "1i");
|
|
|
|
this._syncedCallback = this._anchorValue.bind(this);
|
|
this.context.transport.on("start", this._syncedCallback);
|
|
this.context.transport.on("pause", this._syncedCallback);
|
|
this.context.transport.on("stop", this._syncedCallback);
|
|
|
|
// disconnect the constant source from the output and replace it with another one
|
|
this._constantSource.disconnect();
|
|
this._constantSource.stop(0);
|
|
|
|
// create a new one
|
|
this._constantSource = this.output = new ToneConstantSource<TypeName>({
|
|
context: this.context,
|
|
offset: options.value,
|
|
units: options.units,
|
|
}).start(0);
|
|
this.setValueAtTime(options.value, 0);
|
|
}
|
|
|
|
/**
|
|
* Callback which is invoked every tick.
|
|
*/
|
|
private _onTick(time: Seconds): void {
|
|
const val = super.getValueAtTime(this.context.transport.seconds);
|
|
// approximate ramp curves with linear ramps
|
|
if (this._lastVal !== val) {
|
|
this._lastVal = val;
|
|
this._constantSource.offset.setValueAtTime(val, time);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Anchor the value at the start and stop of the Transport
|
|
*/
|
|
private _anchorValue(time: Seconds): void {
|
|
const val = super.getValueAtTime(this.context.transport.seconds);
|
|
this._lastVal = val;
|
|
this._constantSource.offset.cancelAndHoldAtTime(time);
|
|
this._constantSource.offset.setValueAtTime(val, time);
|
|
}
|
|
|
|
getValueAtTime(time: TransportTime): UnitMap[TypeName] {
|
|
const computedTime = new TransportTimeClass(this.context, time).toSeconds();
|
|
return super.getValueAtTime(computedTime);
|
|
}
|
|
|
|
setValueAtTime(value: UnitMap[TypeName], time: TransportTime) {
|
|
const computedTime = new TransportTimeClass(this.context, time).toSeconds();
|
|
super.setValueAtTime(value, computedTime);
|
|
return this;
|
|
}
|
|
|
|
linearRampToValueAtTime(value: UnitMap[TypeName], time: TransportTime) {
|
|
const computedTime = new TransportTimeClass(this.context, time).toSeconds();
|
|
super.linearRampToValueAtTime(value, computedTime);
|
|
return this;
|
|
}
|
|
|
|
exponentialRampToValueAtTime(value: UnitMap[TypeName], time: TransportTime) {
|
|
const computedTime = new TransportTimeClass(this.context, time).toSeconds();
|
|
super.exponentialRampToValueAtTime(value, computedTime);
|
|
return this;
|
|
}
|
|
|
|
setTargetAtTime(value, startTime: TransportTime, timeConstant: number): this {
|
|
const computedTime = new TransportTimeClass(this.context, startTime).toSeconds();
|
|
super.setTargetAtTime(value, computedTime, timeConstant);
|
|
return this;
|
|
}
|
|
|
|
cancelScheduledValues(startTime: TransportTime): this {
|
|
const computedTime = new TransportTimeClass(this.context, startTime).toSeconds();
|
|
super.cancelScheduledValues(computedTime);
|
|
return this;
|
|
}
|
|
|
|
setValueCurveAtTime(values: UnitMap[TypeName][], startTime: TransportTime, duration: Time, scaling: NormalRange): this {
|
|
const computedTime = new TransportTimeClass(this.context, startTime).toSeconds();
|
|
duration = this.toSeconds(duration);
|
|
super.setValueCurveAtTime(values, computedTime, duration, scaling);
|
|
return this;
|
|
}
|
|
|
|
cancelAndHoldAtTime(time: TransportTime): this {
|
|
const computedTime = new TransportTimeClass(this.context, time).toSeconds();
|
|
super.cancelAndHoldAtTime(computedTime);
|
|
return this;
|
|
}
|
|
|
|
setRampPoint(time: TransportTime): this {
|
|
const computedTime = new TransportTimeClass(this.context, time).toSeconds();
|
|
super.setRampPoint(computedTime);
|
|
return this;
|
|
}
|
|
|
|
exponentialRampTo(value: UnitMap[TypeName], rampTime: Time, startTime?: TransportTime): this {
|
|
const computedTime = new TransportTimeClass(this.context, startTime).toSeconds();
|
|
super.exponentialRampTo(value, rampTime, computedTime);
|
|
return this;
|
|
}
|
|
|
|
linearRampTo(value: UnitMap[TypeName], rampTime: Time, startTime?: TransportTime): this {
|
|
const computedTime = new TransportTimeClass(this.context, startTime).toSeconds();
|
|
super.linearRampTo(value, rampTime, computedTime);
|
|
return this;
|
|
}
|
|
|
|
targetRampTo(value: UnitMap[TypeName], rampTime: Time, startTime?: TransportTime): this {
|
|
const computedTime = new TransportTimeClass(this.context, startTime).toSeconds();
|
|
super.targetRampTo(value, rampTime, computedTime);
|
|
return this;
|
|
}
|
|
|
|
dispose(): this {
|
|
super.dispose();
|
|
this.context.transport.clear(this._synced);
|
|
this.context.transport.off("start", this._syncedCallback);
|
|
this.context.transport.off("pause", this._syncedCallback);
|
|
this.context.transport.off("stop", this._syncedCallback);
|
|
this._constantSource.dispose();
|
|
return this;
|
|
}
|
|
}
|