Tone.js/Tone/effect/Phaser.ts

188 lines
4.7 KiB
TypeScript
Raw Normal View History

2019-11-04 03:17:00 +00:00
import { StereoEffect, StereoEffectOptions } from "./StereoEffect";
import { Frequency, Hertz, Positive } from "../core/type/Units";
import { optionsFromArguments } from "../core/util/Defaults";
import { LFO } from "../source/oscillator/LFO";
import { Signal } from "../signal/Signal";
import { readOnly } from "../core/util/Interface";
export interface PhaserOptions extends StereoEffectOptions {
frequency: Frequency;
octaves: Positive;
stages: Positive;
Q: Positive;
baseFrequency: Frequency;
2019-11-04 03:17:00 +00:00
}
/**
* Phaser is a phaser effect. Phasers work by changing the phase
* of different frequency components of an incoming signal. Read more on
* [Wikipedia](https://en.wikipedia.org/wiki/Phaser_(effect)).
* Inspiration for this phaser comes from [Tuna.js](https://github.com/Dinahmoe/tuna/).
* @example
* const phaser = new Tone.Phaser({
2019-11-04 03:17:00 +00:00
* frequency: 15,
* octaves: 5,
* baseFrequency: 1000
* }).toDestination();
* const synth = new Tone.FMSynth().connect(phaser);
2019-11-04 03:17:00 +00:00
* synth.triggerAttackRelease("E3", "2n");
2019-11-05 03:29:14 +00:00
* @category Effect
2019-11-04 03:17:00 +00:00
*/
export class Phaser extends StereoEffect<PhaserOptions> {
readonly name: string = "Phaser";
/**
* the lfo which controls the frequency on the left side
*/
private _lfoL: LFO;
/**
* the lfo which controls the frequency on the right side
*/
private _lfoR: LFO;
/**
* the base modulation frequency
*/
private _baseFrequency: Hertz;
/**
* the octaves of the phasing
*/
private _octaves: Positive;
/**
* The quality factor of the filters
*/
readonly Q: Signal<"positive">;
/**
* the array of filters for the left side
*/
private _filtersL: BiquadFilterNode[];
/**
* the array of filters for the left side
*/
private _filtersR: BiquadFilterNode[];
/**
* the frequency of the effect
*/
readonly frequency: Signal<"frequency">;
/**
* @param frequency The speed of the phasing.
* @param octaves The octaves of the effect.
* @param baseFrequency The base frequency of the filters.
*/
constructor(frequency?: Frequency, octaves?: Positive, baseFrequency?: Frequency);
constructor(options?: Partial<PhaserOptions>);
constructor() {
super(optionsFromArguments(Phaser.getDefaults(), arguments, ["frequency", "octaves", "baseFrequency"]));
const options = optionsFromArguments(Phaser.getDefaults(), arguments, ["frequency", "octaves", "baseFrequency"]);
this._lfoL = new LFO({
context: this.context,
frequency: options.frequency,
2019-11-04 03:17:00 +00:00
min: 0,
max: 1
});
this._lfoR = new LFO({
context: this.context,
frequency: options.frequency,
2019-11-04 03:17:00 +00:00
min: 0,
max: 1,
phase: 180,
});
this._baseFrequency = this.toFrequency(options.baseFrequency);
this._octaves = options.octaves;
this.Q = new Signal({
context: this.context,
value: options.Q,
units: "positive",
});
this._filtersL = this._makeFilters(options.stages, this._lfoL);
this._filtersR = this._makeFilters(options.stages, this._lfoR);
this.frequency = this._lfoL.frequency;
this.frequency.value = options.frequency;
// connect them up
this.connectEffectLeft(...this._filtersL);
this.connectEffectRight(...this._filtersR);
// control the frequency with one LFO
this._lfoL.frequency.connect(this._lfoR.frequency);
// set the options
this.baseFrequency = options.baseFrequency;
this.octaves = options.octaves;
// start the lfo
this._lfoL.start();
this._lfoR.start();
readOnly(this, ["frequency", "Q"]);
}
static getDefaults(): PhaserOptions {
return Object.assign(StereoEffect.getDefaults(), {
frequency: 0.5,
octaves: 3,
stages: 10,
Q: 10,
baseFrequency: 350,
});
}
private _makeFilters(stages: number, connectToFreq: LFO): BiquadFilterNode[] {
const filters: BiquadFilterNode[] = [];
// make all the filters
for (let i = 0; i < stages; i++) {
2019-11-17 18:09:19 +00:00
const filter = this.context.createBiquadFilter();
2019-11-04 03:17:00 +00:00
filter.type = "allpass";
this.Q.connect(filter.Q);
connectToFreq.connect(filter.frequency);
filters.push(filter);
}
return filters;
}
/**
* The number of octaves the phase goes above the baseFrequency
*/
get octaves() {
return this._octaves;
}
set octaves(octaves) {
this._octaves = octaves;
2019-11-17 18:09:19 +00:00
const max = this._baseFrequency * Math.pow(2, octaves);
2019-11-04 03:17:00 +00:00
this._lfoL.max = max;
this._lfoR.max = max;
}
/**
* The the base frequency of the filters.
*/
get baseFrequency(): Frequency {
return this._baseFrequency;
}
set baseFrequency(freq) {
this._baseFrequency = this.toFrequency(freq);
this._lfoL.min = this._baseFrequency;
this._lfoR.min = this._baseFrequency;
this.octaves = this._octaves;
}
dispose(): this {
2019-11-04 03:17:00 +00:00
super.dispose();
this.Q.dispose();
this._lfoL.dispose();
this._lfoR.dispose();
this._filtersL.forEach(f => f.disconnect());
this._filtersR.forEach(f => f.disconnect());
this.frequency.dispose();
return this;
}
}