import { optionsFromArguments } from "../../core/util/Defaults"; import { Frequency, Hertz, NormalRange, Positive, Time } from "../../core/type/Units"; import { Envelope, EnvelopeOptions } from "./Envelope"; import { Scale } from "../../signal/Scale"; import { Pow } from "../../signal/Pow"; import { assertRange } from "../../core/util/Debug"; export interface FrequencyEnvelopeOptions extends EnvelopeOptions { baseFrequency: Frequency; octaves: number; exponent: number; } /** * FrequencyEnvelope is an [[Envelope]] which ramps between [[baseFrequency]] * and [[octaves]]. It can also have an optional [[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(); */ 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: Positive; /** * 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) constructor() { super(optionsFromArguments(FrequencyEnvelope.getDefaults(), arguments, ["attack", "decay", "sustain", "release"])); const options = optionsFromArguments(FrequencyEnvelope.getDefaults(), arguments, ["attack", "decay", "sustain", "release"]); 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(): Positive { return this._octaves; } set octaves(octaves: Positive) { assertRange(octaves, 0); 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; } }