mirror of
https://github.com/Tonejs/Tone.js
synced 2024-12-29 13:03:06 +00:00
2ca9d89424
no more overwritten connect/disconnect closes #421
204 lines
4.9 KiB
JavaScript
204 lines
4.9 KiB
JavaScript
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;
|
|
|