Tone.js/Tone/component/envelope/FrequencyEnvelope.ts
2024-05-06 10:55:55 -04:00

142 lines
3.4 KiB
TypeScript

import { optionsFromArguments } from "../../core/util/Defaults.js";
import { Frequency, Hertz, NormalRange, Time } from "../../core/type/Units.js";
import { Envelope, EnvelopeOptions } from "./Envelope.js";
import { Scale } from "../../signal/Scale.js";
import { Pow } from "../../signal/Pow.js";
import { assertRange } from "../../core/util/Debug.js";
export interface FrequencyEnvelopeOptions extends EnvelopeOptions {
baseFrequency: Frequency;
octaves: number;
exponent: number;
}
/**
* FrequencyEnvelope is an {@link Envelope} which ramps between {@link baseFrequency}
* and {@link octaves}. It can also have an optional {@link exponent} to adjust the curve
* which it ramps.
* @example
* const oscillator = new Tone.Oscillator().toDestination().start();
* const freqEnv = new Tone.FrequencyEnvelope({
* attack: 0.2,
* baseFrequency: "C2",
* octaves: 4
* });
* freqEnv.connect(oscillator.frequency);
* freqEnv.triggerAttack();
* @category Component
*/
export class FrequencyEnvelope extends Envelope {
readonly name: string = "FrequencyEnvelope";
/**
* Private reference to the base frequency as a number
*/
private _baseFrequency: Hertz;
/**
* The number of octaves
*/
private _octaves: number;
/**
* Internal scaler from 0-1 to the final output range
*/
private _scale: Scale;
/**
* Apply a power curve to the output
*/
private _exponent: Pow;
/**
* @param attack the attack time in seconds
* @param decay the decay time in seconds
* @param sustain a percentage (0-1) of the full amplitude
* @param release the release time in seconds
*/
constructor(
attack?: Time,
decay?: Time,
sustain?: NormalRange,
release?: Time
);
constructor(options?: Partial<FrequencyEnvelopeOptions>);
constructor() {
const options = optionsFromArguments(
FrequencyEnvelope.getDefaults(),
arguments,
["attack", "decay", "sustain", "release"]
);
super(options);
this._octaves = options.octaves;
this._baseFrequency = this.toFrequency(options.baseFrequency);
this._exponent = this.input = new Pow({
context: this.context,
value: options.exponent,
});
this._scale = this.output = new Scale({
context: this.context,
min: this._baseFrequency,
max: this._baseFrequency * Math.pow(2, this._octaves),
});
this._sig.chain(this._exponent, this._scale);
}
static getDefaults(): FrequencyEnvelopeOptions {
return Object.assign(Envelope.getDefaults(), {
baseFrequency: 200,
exponent: 1,
octaves: 4,
});
}
/**
* The envelope's minimum output value. This is the value which it
* starts at.
*/
get baseFrequency(): Frequency {
return this._baseFrequency;
}
set baseFrequency(min) {
const freq = this.toFrequency(min);
assertRange(freq, 0);
this._baseFrequency = freq;
this._scale.min = this._baseFrequency;
// update the max value when the min changes
this.octaves = this._octaves;
}
/**
* The number of octaves above the baseFrequency that the
* envelope will scale to.
*/
get octaves(): number {
return this._octaves;
}
set octaves(octaves: number) {
this._octaves = octaves;
this._scale.max = this._baseFrequency * Math.pow(2, octaves);
}
/**
* The envelope's exponent value.
*/
get exponent(): number {
return this._exponent.value;
}
set exponent(exponent) {
this._exponent.value = exponent;
}
/**
* Clean up
*/
dispose(): this {
super.dispose();
this._exponent.dispose();
this._scale.dispose();
return this;
}
}