2019-06-23 19:02:38 +00:00
|
|
|
import { getContext } from "../Global";
|
2019-05-23 18:00:49 +00:00
|
|
|
import { Tone } from "../Tone";
|
|
|
|
import { FrequencyClass } from "../type/Frequency";
|
|
|
|
import { TimeClass } from "../type/Time";
|
|
|
|
import { TransportTimeClass } from "../type/TransportTime";
|
2019-04-12 14:37:47 +00:00
|
|
|
import "../type/Units";
|
2019-05-23 18:00:49 +00:00
|
|
|
import { getDefaultsFromInstance, optionsFromArguments } from "../util/Defaults";
|
|
|
|
import { isDefined } from "../util/TypeCheck";
|
|
|
|
import { Context } from "./Context";
|
2019-04-12 14:37:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A unit which process audio
|
|
|
|
*/
|
2019-05-23 18:00:49 +00:00
|
|
|
export interface ToneWithContextOptions {
|
|
|
|
context: Context;
|
2019-04-12 14:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-04-12 15:04:43 +00:00
|
|
|
* The Base class for all nodes that have an AudioContext.
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
2019-05-23 18:00:49 +00:00
|
|
|
export abstract class ToneWithContext<Options extends ToneWithContextOptions> extends Tone {
|
2019-04-12 14:37:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The context belonging to the node.
|
|
|
|
*/
|
2019-05-23 18:00:49 +00:00
|
|
|
readonly context: Context;
|
2019-04-12 14:37:47 +00:00
|
|
|
|
2019-04-12 15:04:43 +00:00
|
|
|
/**
|
|
|
|
* The default context to use if no AudioContext is passed in to the constructor
|
|
|
|
*/
|
2019-05-23 18:00:49 +00:00
|
|
|
readonly defaultContext?: Context;
|
2019-04-12 14:37:47 +00:00
|
|
|
|
2019-05-23 18:00:49 +00:00
|
|
|
constructor(context?: Context | Partial<ToneWithContextOptions>) {
|
|
|
|
const options = optionsFromArguments(ToneWithContext.getDefaults(), arguments, ["context"]);
|
2019-04-12 14:37:47 +00:00
|
|
|
super();
|
|
|
|
if (this.defaultContext) {
|
|
|
|
this.context = this.defaultContext;
|
|
|
|
} else {
|
|
|
|
this.context = options.context;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 18:00:49 +00:00
|
|
|
static getDefaults(): ToneWithContextOptions {
|
2019-04-12 14:37:47 +00:00
|
|
|
return {
|
2019-06-23 19:02:38 +00:00
|
|
|
context: getContext(),
|
2019-04-12 14:37:47 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-05-23 18:00:49 +00:00
|
|
|
* Return the current time of the Context clock plus the lookAhead.
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
|
|
|
now(): Seconds {
|
2019-05-23 18:00:49 +00:00
|
|
|
return this.context.currentTime + this.context.lookAhead;
|
2019-04-12 14:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-05-23 18:00:49 +00:00
|
|
|
* Return the current time of the Context clock without any lookAhead.
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
|
|
|
immediate(): Seconds {
|
|
|
|
return this.context.currentTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The duration in seconds of one sample.
|
|
|
|
*/
|
|
|
|
get sampleTime(): Seconds {
|
|
|
|
return 1 / this.context.sampleRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of seconds of 1 processing block (128 samples)
|
|
|
|
*/
|
|
|
|
get blockTime(): Seconds {
|
|
|
|
return 128 / this.context.sampleRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert the incoming time to seconds
|
|
|
|
*/
|
|
|
|
toSeconds(time: Time): Seconds {
|
2019-05-23 18:00:49 +00:00
|
|
|
return new TimeClass(this.context, time).toSeconds();
|
2019-04-12 14:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert the input to a frequency number
|
|
|
|
*/
|
2019-05-23 18:00:49 +00:00
|
|
|
toFrequency(freq: Frequency): Hertz {
|
|
|
|
return new FrequencyClass(this.context, freq).toFrequency();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert the input time into ticks
|
|
|
|
*/
|
|
|
|
toTicks(time: Time): Ticks {
|
|
|
|
return new TransportTimeClass(this.context, time).toTicks();
|
2019-04-12 14:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GET/SET
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the object's attributes. Given no arguments get
|
|
|
|
* will return all available object properties and their corresponding
|
|
|
|
* values. Pass in a single attribute to retrieve or an array
|
|
|
|
* of attributes. The attribute strings can also include a "."
|
|
|
|
* to access deeper properties.
|
|
|
|
* @param params the parameters to get, otherwise will return all available.
|
|
|
|
* @example
|
|
|
|
* osc.get();
|
|
|
|
* //returns {"type" : "sine", "frequency" : 440, ...etc}
|
|
|
|
* @example
|
|
|
|
* osc.get("type");
|
|
|
|
* //returns { "type" : "sine"}
|
|
|
|
* @example
|
|
|
|
* //use dot notation to access deep properties
|
|
|
|
* synth.get(["envelope.attack", "envelope.release"]);
|
|
|
|
* //returns {"envelope" : {"attack" : 0.2, "release" : 0.4}}
|
|
|
|
*/
|
|
|
|
get(): Options {
|
|
|
|
const defaults = getDefaultsFromInstance(this) as Options;
|
|
|
|
Object.keys(defaults).forEach(attribute => {
|
|
|
|
if (Reflect.has(this, attribute)) {
|
|
|
|
const member = this[attribute];
|
|
|
|
if (isDefined(member) && isDefined(member.value)) {
|
|
|
|
defaults[attribute] = member.value;
|
2019-05-23 18:00:49 +00:00
|
|
|
} else if (member instanceof ToneWithContext) {
|
2019-04-12 14:37:47 +00:00
|
|
|
defaults[attribute] = member.get();
|
|
|
|
} else {
|
|
|
|
defaults[attribute] = member;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
return defaults;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the parameters at once. Either pass in an
|
|
|
|
* object mapping parameters to values, or to set a
|
|
|
|
* single parameter, by passing in a string and value.
|
|
|
|
* The last argument is an optional ramp time which
|
|
|
|
* will ramp any signal values to their destination value
|
|
|
|
* over the duration of the rampTime.
|
|
|
|
* @param params
|
|
|
|
* @example
|
|
|
|
* //set values using an object
|
|
|
|
* filter.set({
|
|
|
|
* "frequency" : 300,
|
|
|
|
* "type" : "highpass"
|
|
|
|
* });
|
|
|
|
*/
|
2019-06-23 19:02:38 +00:00
|
|
|
set(props: Partial<Options>): this {
|
2019-04-12 14:37:47 +00:00
|
|
|
Object.keys(props).forEach(attribute => {
|
|
|
|
if (Reflect.has(this, attribute)) {
|
|
|
|
if (isDefined(this[attribute]) && isDefined(this[attribute].value)) {
|
|
|
|
this[attribute].value = props[attribute];
|
2019-05-23 18:00:49 +00:00
|
|
|
} else if (this[attribute] instanceof ToneWithContext) {
|
2019-04-12 14:37:47 +00:00
|
|
|
this[attribute].set(props[attribute]);
|
|
|
|
} else {
|
|
|
|
this[attribute] = props[attribute];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|