UNPKG

tone

Version:

A Web Audio framework for making interactive music in the browser.

102 lines (87 loc) 2.63 kB
import { Gain } from "../../core/context/Gain.js"; import { connectSeries, ToneAudioNode, ToneAudioNodeOptions, } from "../../core/context/ToneAudioNode.js"; /** * PhaseShiftAllpass is an very efficient implementation of a Hilbert Transform * using two Allpass filter banks whose outputs have a phase difference of 90°. * Here the `offset90` phase is offset by +90° in relation to `output`. * Coefficients and structure was developed by Olli Niemitalo. * For more details see: http://yehar.com/blog/?p=368 * @category Component */ export class PhaseShiftAllpass extends ToneAudioNode<ToneAudioNodeOptions> { readonly name: string = "PhaseShiftAllpass"; readonly input = new Gain({ context: this.context }); /** * The Allpass filter in the first bank */ private _bank0: IIRFilterNode[]; /** * The Allpass filter in the seconds bank */ private _bank1: IIRFilterNode[]; /** * A IIR filter implementing a delay by one sample used by the first bank */ private _oneSampleDelay: IIRFilterNode; /** * The phase shifted output */ readonly output = new Gain({ context: this.context }); /** * The PhaseShifted allpass output */ readonly offset90 = new Gain({ context: this.context }); constructor(options?: Partial<ToneAudioNodeOptions>) { super(options); const allpassBank1Values = [ 0.6923878, 0.9360654322959, 0.988229522686, 0.9987488452737, ]; const allpassBank2Values = [ 0.4021921162426, 0.856171088242, 0.9722909545651, 0.9952884791278, ]; this._bank0 = this._createAllPassFilterBank(allpassBank1Values); this._bank1 = this._createAllPassFilterBank(allpassBank2Values); this._oneSampleDelay = this.context.createIIRFilter( [0.0, 1.0], [1.0, 0.0] ); // connect Allpass filter banks connectSeries( this.input, ...this._bank0, this._oneSampleDelay, this.output ); connectSeries(this.input, ...this._bank1, this.offset90); } /** * Create all of the IIR filters from an array of values using the coefficient calculation. */ private _createAllPassFilterBank(bankValues: number[]): IIRFilterNode[] { const nodes: IIRFilterNode[] = bankValues.map((value) => { const coefficients = [ [value * value, 0, -1], [1, 0, -(value * value)], ]; return this.context.createIIRFilter( coefficients[0], coefficients[1] ); }); return nodes; } dispose(): this { super.dispose(); this.input.dispose(); this.output.dispose(); this.offset90.dispose(); this._bank0.forEach((f) => f.disconnect()); this._bank1.forEach((f) => f.disconnect()); this._oneSampleDelay.disconnect(); return this; } }