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-07-30 19:35:27 +00:00
|
|
|
import { Frequency, Hertz, Seconds, Ticks, Time } from "../type/Units";
|
2019-10-24 22:01:27 +00:00
|
|
|
import { getDefaultsFromInstance, optionsFromArguments } from "../util/Defaults";
|
2019-07-18 18:05:42 +00:00
|
|
|
import { RecursivePartial } from "../util/Interface";
|
2019-07-23 15:28:22 +00:00
|
|
|
import { isArray, isBoolean, isDefined, isNumber, isString, isUndef } from "../util/TypeCheck";
|
2019-11-06 22:32:19 +00:00
|
|
|
import { BaseContext } from "./BaseContext";
|
2019-04-12 14:37:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A unit which process audio
|
|
|
|
*/
|
2019-05-23 18:00:49 +00:00
|
|
|
export interface ToneWithContextOptions {
|
2019-11-06 22:32:19 +00:00
|
|
|
context: BaseContext;
|
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-11-06 22:32:19 +00:00
|
|
|
readonly context: BaseContext;
|
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-11-06 22:32:19 +00:00
|
|
|
readonly defaultContext?: BaseContext;
|
2019-04-12 14:37:47 +00:00
|
|
|
|
2019-09-06 05:39:56 +00:00
|
|
|
/**
|
|
|
|
* Pass in a constructor as the first argument
|
|
|
|
*/
|
2019-11-06 22:32:19 +00:00
|
|
|
constructor(context?: BaseContext)
|
2019-09-06 05:39:56 +00:00
|
|
|
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 {
|
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.
|
2020-03-22 01:58:48 +00:00
|
|
|
* @example
|
2020-04-26 22:03:40 +00:00
|
|
|
* setInterval(() => {
|
|
|
|
* console.log(Tone.now());
|
2020-06-17 03:14:19 +00:00
|
|
|
* }, 100);
|
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.
|
2020-03-22 01:58:48 +00:00
|
|
|
* @example
|
2020-04-26 22:03:40 +00:00
|
|
|
* setInterval(() => {
|
|
|
|
* console.log(Tone.immediate());
|
2020-06-17 03:14:19 +00:00
|
|
|
* }, 100);
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
|
|
|
immediate(): Seconds {
|
|
|
|
return this.context.currentTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The duration in seconds of one sample.
|
2020-03-22 01:58:48 +00:00
|
|
|
* @example
|
2020-04-17 02:24:18 +00:00
|
|
|
* console.log(Tone.Transport.sampleTime);
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
|
|
|
get sampleTime(): Seconds {
|
|
|
|
return 1 / this.context.sampleRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of seconds of 1 processing block (128 samples)
|
2020-03-22 01:58:48 +00:00
|
|
|
* @example
|
2020-04-17 02:24:18 +00:00
|
|
|
* console.log(Tone.Destination.blockTime);
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
|
|
|
get blockTime(): Seconds {
|
|
|
|
return 128 / this.context.sampleRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-08-04 01:21:17 +00:00
|
|
|
* Convert the incoming time to seconds.
|
|
|
|
* This is calculated against the current [[Tone.Transport]] bpm
|
2020-04-26 22:03:40 +00:00
|
|
|
* @example
|
|
|
|
* const gain = new Tone.Gain();
|
2020-08-04 01:21:17 +00:00
|
|
|
* setInterval(() => console.log(gain.toSeconds("4n")), 100);
|
|
|
|
* // ramp the tempo to 60 bpm over 30 seconds
|
|
|
|
* Tone.getTransport().bpm.rampTo(60, 30);
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
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
|
2020-04-26 22:03:40 +00:00
|
|
|
* @example
|
|
|
|
* const gain = new Tone.Gain();
|
|
|
|
* console.log(gain.toFrequency("4n"));
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
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
|
2020-04-26 22:03:40 +00:00
|
|
|
* @example
|
|
|
|
* const gain = new Tone.Gain();
|
|
|
|
* console.log(gain.toTicks("4n"));
|
2019-05-23 18:00:49 +00:00
|
|
|
*/
|
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-09-14 22:06:46 +00:00
|
|
|
//-------------------------------------
|
2019-04-12 14:37:47 +00:00
|
|
|
// GET/SET
|
2019-09-14 22:06:46 +00:00
|
|
|
//-------------------------------------
|
2019-04-12 14:37:47 +00:00
|
|
|
|
2019-09-17 16:20:37 +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
|
|
|
/**
|
2019-07-19 15:09:28 +00:00
|
|
|
* Get the object's attributes.
|
2019-04-12 14:37:47 +00:00
|
|
|
* @example
|
2020-04-17 02:24:18 +00:00
|
|
|
* const osc = new Tone.Oscillator();
|
2019-11-04 03:19:42 +00:00
|
|
|
* console.log(osc.get());
|
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];
|
2019-07-18 18:05:42 +00:00
|
|
|
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) {
|
2019-09-17 16:20:37 +00:00
|
|
|
defaults[attribute] = member._getPartialProperties(defaults[attribute]);
|
2020-03-22 01:58:48 +00:00
|
|
|
// otherwise make sure it's a serializable type
|
2019-07-23 15:28:22 +00:00
|
|
|
} else if (isArray(member) || isNumber(member) || isString(member) || isBoolean(member)) {
|
2019-04-12 14:37:47 +00:00
|
|
|
defaults[attribute] = member;
|
2019-07-19 15:09:28 +00:00
|
|
|
} else {
|
|
|
|
// remove all undefined and unserializable attributes
|
|
|
|
delete defaults[attribute];
|
2019-04-12 14:37:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2019-07-19 15:09:28 +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
|
2020-08-04 01:21:17 +00:00
|
|
|
* const filter = new Tone.Filter().toDestination();
|
2019-10-24 22:01:27 +00:00
|
|
|
* // set values using an object
|
2019-04-12 14:37:47 +00:00
|
|
|
* filter.set({
|
2020-08-04 01:21:17 +00:00
|
|
|
* frequency: "C6",
|
2019-10-24 22:01:27 +00:00
|
|
|
* type: "highpass"
|
2019-04-12 14:37:47 +00:00
|
|
|
* });
|
2020-08-04 01:21:17 +00:00
|
|
|
* const player = new Tone.Player("https://tonejs.github.io/audio/berklee/Analogsynth_octaves_highmid.mp3").connect(filter);
|
|
|
|
* player.autostart = true;
|
2019-04-12 14:37:47 +00:00
|
|
|
*/
|
2019-07-18 18:05:42 +00:00
|
|
|
set(props: RecursivePartial<Options>): this {
|
2019-04-12 14:37:47 +00:00
|
|
|
Object.keys(props).forEach(attribute => {
|
2019-07-17 16:54:25 +00:00
|
|
|
if (Reflect.has(this, attribute) && isDefined(this[attribute])) {
|
2019-07-23 17:11:01 +00:00
|
|
|
if (this[attribute] && isDefined(this[attribute].value) && isDefined(this[attribute].setValueAtTime)) {
|
2019-08-08 18:21:20 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|