tone
Version:
A Web Audio framework for making interactive music in the browser.
174 lines (154 loc) • 5.01 kB
text/typescript
import {
ToneAudioNode,
ToneAudioNodeOptions,
} from "../../core/context/ToneAudioNode.js";
import { Cents, Frequency, GainFactor } from "../../core/type/Units.js";
import { optionsFromArguments } from "../../core/util/Defaults.js";
import { Param } from "../../core/context/Param.js";
import { assert } from "../../core/util/Debug.js";
export interface BiquadFilterOptions extends ToneAudioNodeOptions {
frequency: Frequency;
detune: Cents;
Q: number;
type: BiquadFilterType;
gain: GainFactor;
}
/**
* Thin wrapper around the native Web Audio [BiquadFilterNode](https://webaudio.github.io/web-audio-api/#biquadfilternode).
* BiquadFilter is similar to {@link Filter} but doesn't have the option to set the "rolloff" value.
* @category Component
*/
export class BiquadFilter extends ToneAudioNode<BiquadFilterOptions> {
readonly name: string = "BiquadFilter";
readonly input: BiquadFilterNode;
readonly output: BiquadFilterNode;
/**
* The frequency of the filter
*/
readonly frequency: Param<"frequency">;
/**
* A detune value, in cents, for the frequency.
*/
readonly detune: Param<"cents">;
/**
* The Q factor of the filter.
* For lowpass and highpass filters the Q value is interpreted to be in dB.
* For these filters the nominal range is [−𝑄𝑙𝑖𝑚,𝑄𝑙𝑖𝑚] where 𝑄𝑙𝑖𝑚 is the largest value for which 10𝑄/20 does not overflow. This is approximately 770.63678.
* For the bandpass, notch, allpass, and peaking filters, this value is a linear value.
* The value is related to the bandwidth of the filter and hence should be a positive value. The nominal range is
* [0,3.4028235𝑒38], the upper limit being the most-positive-single-float.
* This is not used for the lowshelf and highshelf filters.
*/
readonly Q: Param<"number">;
/**
* The gain of the filter. Its value is in dB units. The gain is only used for lowshelf, highshelf, and peaking filters.
*/
readonly gain: Param<"decibels">;
private readonly _filter: BiquadFilterNode;
/**
* @param frequency The cutoff frequency of the filter.
* @param type The type of filter.
*/
constructor(frequency?: Frequency, type?: BiquadFilterType);
constructor(options?: Partial<BiquadFilterOptions>);
constructor() {
const options = optionsFromArguments(
BiquadFilter.getDefaults(),
arguments,
["frequency", "type"]
);
super(options);
this._filter = this.context.createBiquadFilter();
this.input = this.output = this._filter;
this.Q = new Param({
context: this.context,
units: "number",
value: options.Q,
param: this._filter.Q,
});
this.frequency = new Param({
context: this.context,
units: "frequency",
value: options.frequency,
param: this._filter.frequency,
});
this.detune = new Param({
context: this.context,
units: "cents",
value: options.detune,
param: this._filter.detune,
});
this.gain = new Param({
context: this.context,
units: "decibels",
convert: false,
value: options.gain,
param: this._filter.gain,
});
this.type = options.type;
}
static getDefaults(): BiquadFilterOptions {
return Object.assign(ToneAudioNode.getDefaults(), {
Q: 1,
type: "lowpass" as const,
frequency: 350,
detune: 0,
gain: 0,
});
}
/**
* The type of this BiquadFilterNode. For a complete list of types and their attributes, see the
* [Web Audio API](https://webaudio.github.io/web-audio-api/#dom-biquadfiltertype-lowpass)
*/
get type(): BiquadFilterType {
return this._filter.type;
}
set type(type) {
const types: BiquadFilterType[] = [
"lowpass",
"highpass",
"bandpass",
"lowshelf",
"highshelf",
"notch",
"allpass",
"peaking",
];
assert(types.indexOf(type) !== -1, `Invalid filter type: ${type}`);
this._filter.type = type;
}
/**
* Get the frequency response curve. This curve represents how the filter
* responses to frequencies between 20hz-20khz.
* @param len The number of values to return
* @return The frequency response curve between 20-20kHz
*/
getFrequencyResponse(len = 128): Float32Array {
// start with all 1s
const freqValues = new Float32Array(len);
for (let i = 0; i < len; i++) {
const norm = Math.pow(i / len, 2);
const freq = norm * (20000 - 20) + 20;
freqValues[i] = freq;
}
const magValues = new Float32Array(len);
const phaseValues = new Float32Array(len);
// clone the filter to remove any connections which may be changing the value
const filterClone = this.context.createBiquadFilter();
filterClone.type = this.type;
filterClone.Q.value = this.Q.value;
filterClone.frequency.value = this.frequency.value as number;
filterClone.gain.value = this.gain.value as number;
filterClone.getFrequencyResponse(freqValues, magValues, phaseValues);
return magValues;
}
dispose(): this {
super.dispose();
this._filter.disconnect();
this.Q.dispose();
this.frequency.dispose();
this.gain.dispose();
this.detune.dispose();
return this;
}
}