Tone.js/Tone/core/context/ToneWithContext.ts

183 lines
4.8 KiB
TypeScript
Raw Normal View History

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";
import { Frequency, Hertz, Seconds, Ticks, Time } from "../type/Units";
2019-10-24 22:01:27 +00:00
import { getDefaultsFromInstance, optionsFromArguments } from "../util/Defaults";
import { RecursivePartial } from "../util/Interface";
import { isArray, isBoolean, isDefined, isNumber, isString, isUndef } from "../util/TypeCheck";
2019-05-23 18:00:49 +00:00
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
/**
2019-08-30 16:02:06 +00:00
* The default context to use if no AudioContext is passed in to the constructor.
* Probably should not be set manually. Used internally.
* @hidden
2019-04-12 15:04:43 +00:00
*/
2019-05-23 18:00:49 +00:00
readonly defaultContext?: Context;
2019-04-12 14:37:47 +00:00
/**
* Pass in a constructor as the first argument
*/
constructor(context?: Context)
constructor(options?: Partial<ToneWithContextOptions>);
constructor() {
2019-04-12 14:37:47 +00:00
super();
2019-08-30 16:02:06 +00:00
const options = optionsFromArguments(ToneWithContext.getDefaults(), arguments, ["context"]);
2019-04-12 14:37:47 +00:00
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 {
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
*/
2019-07-22 20:18:01 +00:00
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
*/
2019-07-30 14:25:17 +00:00
toTicks(time?: Time | TimeClass): Ticks {
2019-05-23 18:00:49 +00:00
return new TransportTimeClass(this.context, time).toTicks();
2019-04-12 14:37:47 +00:00
}
//-------------------------------------
2019-04-12 14:37:47 +00:00
// GET/SET
//-------------------------------------
2019-04-12 14:37:47 +00:00
/**
* Get a subset of the properties which are in the partial props
*/
protected _getPartialProperties(props: Options): Partial<Options> {
const options = this.get();
// remove attributes from the prop that are not in the partial
Object.keys(options).forEach(name => {
if (isUndef(props[name])) {
delete options[name];
}
});
return options;
}
2019-04-12 14:37:47 +00:00
/**
* Get the object's attributes.
2019-04-12 14:37:47 +00:00
* @example
2019-10-24 22:01:27 +00:00
* import { Oscillator } from "tone";
* const osc = new Oscillator();
2019-04-12 14:37:47 +00:00
* osc.get();
2019-10-24 22:01:27 +00:00
* // returns {"type" : "sine", "frequency" : 440, ...etc}
2019-04-12 14:37:47 +00:00
*/
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) && isDefined(member.setValueAtTime)) {
2019-04-12 14:37:47 +00:00
defaults[attribute] = member.value;
2019-05-23 18:00:49 +00:00
} else if (member instanceof ToneWithContext) {
defaults[attribute] = member._getPartialProperties(defaults[attribute]);
// otherwise make sure it's a serializable type
} else if (isArray(member) || isNumber(member) || isString(member) || isBoolean(member)) {
2019-04-12 14:37:47 +00:00
defaults[attribute] = member;
} else {
// remove all undefined and unserializable attributes
delete defaults[attribute];
2019-04-12 14:37:47 +00:00
}
}
});
2019-04-12 14:37:47 +00:00
return defaults;
}
/**
2019-09-29 14:19:08 +00:00
* Set multiple properties at once with an object.
2019-04-12 14:37:47 +00:00
* @example
2019-10-24 22:01:27 +00:00
* import { Filter } from "tone";
* const filter = new Filter();
* // set values using an object
2019-04-12 14:37:47 +00:00
* filter.set({
2019-10-24 22:01:27 +00:00
* frequency: 300,
* type: "highpass"
2019-04-12 14:37:47 +00:00
* });
*/
set(props: RecursivePartial<Options>): this {
2019-04-12 14:37:47 +00:00
Object.keys(props).forEach(attribute => {
if (Reflect.has(this, attribute) && isDefined(this[attribute])) {
if (this[attribute] && isDefined(this[attribute].value) && isDefined(this[attribute].setValueAtTime)) {
// small optimization
if (this[attribute].value !== props[attribute]) {
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;
}
}