import Tone from "../core/Tone"; import "../component/LFO"; import "../component/Filter"; import "../effect/StereoEffect"; /** * @class Tone.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/). * * @extends {Tone.StereoEffect} * @constructor * @param {Frequency|Object} [frequency] The speed of the phasing. * @param {number} [octaves] The octaves of the effect. * @param {Frequency} [baseFrequency] The base frequency of the filters. * @example * var phaser = new Tone.Phaser({ * "frequency" : 15, * "octaves" : 5, * "baseFrequency" : 1000 * }).toMaster(); * var synth = new Tone.FMSynth().connect(phaser); * synth.triggerAttackRelease("E3", "2n"); */ Tone.Phaser = function(){ //set the defaults var options = Tone.defaults(arguments, ["frequency", "octaves", "baseFrequency"], Tone.Phaser); Tone.StereoEffect.call(this, options); /** * the lfo which controls the frequency on the left side * @type {Tone.LFO} * @private */ this._lfoL = new Tone.LFO(options.frequency, 0, 1); /** * the lfo which controls the frequency on the right side * @type {Tone.LFO} * @private */ this._lfoR = new Tone.LFO(options.frequency, 0, 1); this._lfoR.phase = 180; /** * the base modulation frequency * @type {number} * @private */ this._baseFrequency = options.baseFrequency; /** * the octaves of the phasing * @type {number} * @private */ this._octaves = options.octaves; /** * The quality factor of the filters * @type {Positive} * @signal */ this.Q = new Tone.Signal(options.Q, Tone.Type.Positive); /** * the array of filters for the left side * @type {Array} * @private */ this._filtersL = this._makeFilters(options.stages, this._lfoL, this.Q); /** * the array of filters for the left side * @type {Array} * @private */ this._filtersR = this._makeFilters(options.stages, this._lfoR, this.Q); /** * the frequency of the effect * @type {Tone.Signal} */ this.frequency = this._lfoL.frequency; this.frequency.value = options.frequency; //connect them up this.effectSendL.connect(this._filtersL[0]); this.effectSendR.connect(this._filtersR[0]); Tone.connect(this._filtersL[options.stages - 1], this.effectReturnL); Tone.connect(this._filtersR[options.stages - 1], this.effectReturnR); //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(); this._readOnly(["frequency", "Q"]); }; Tone.extend(Tone.Phaser, Tone.StereoEffect); /** * defaults * @static * @type {object} */ Tone.Phaser.defaults = { "frequency" : 0.5, "octaves" : 3, "stages" : 10, "Q" : 10, "baseFrequency" : 350, }; /** * @param {number} stages * @returns {Array} the number of filters all connected together * @private */ Tone.Phaser.prototype._makeFilters = function(stages, connectToFreq, Q){ var filters = new Array(stages); //make all the filters for (var i = 0; i < stages; i++){ var filter = this.context.createBiquadFilter(); filter.type = "allpass"; Q.connect(filter.Q); connectToFreq.connect(filter.frequency); filters[i] = filter; } Tone.connectSeries.apply(Tone, filters); return filters; }; /** * The number of octaves the phase goes above * the baseFrequency * @memberOf Tone.Phaser# * @type {Positive} * @name octaves */ Object.defineProperty(Tone.Phaser.prototype, "octaves", { get : function(){ return this._octaves; }, set : function(octaves){ this._octaves = octaves; var max = this._baseFrequency * Math.pow(2, octaves); this._lfoL.max = max; this._lfoR.max = max; } }); /** * The the base frequency of the filters. * @memberOf Tone.Phaser# * @type {number} * @name baseFrequency */ Object.defineProperty(Tone.Phaser.prototype, "baseFrequency", { get : function(){ return this._baseFrequency; }, set : function(freq){ this._baseFrequency = freq; this._lfoL.min = freq; this._lfoR.min = freq; this.octaves = this._octaves; } }); /** * clean up * @returns {Tone.Phaser} this */ Tone.Phaser.prototype.dispose = function(){ Tone.StereoEffect.prototype.dispose.call(this); this._writable(["frequency", "Q"]); this.Q.dispose(); this.Q = null; this._lfoL.dispose(); this._lfoL = null; this._lfoR.dispose(); this._lfoR = null; for (var i = 0; i < this._filtersL.length; i++){ this._filtersL[i].disconnect(); this._filtersL[i] = null; } this._filtersL = null; for (var j = 0; j < this._filtersR.length; j++){ this._filtersR[j].disconnect(); this._filtersR[j] = null; } this._filtersR = null; this.frequency = null; return this; }; export default Tone.Phaser;