File: src/BB.AFX.js
/**
* a base module for creating more complicated audio effects (as AFX addons) in the style of <a href="BB.AudioFX.html" target="_blank">BB.AudioFX</a>
* @module BB.AFX
* @extends BB.AudioBase
*/
define(['./BB', './BB.AudioBase'],
function( BB, AudioBase) {
'use strict';
/**
* a base module for creating more complicated audio effects (as AFX addons) in the style of <a href="BB.AudioFX.html" target="_blank">BB.AudioFX</a>
* @class BB.AFX
* @constructor
* @extends BB.AudioBase
*
* @param {Object} config A config object to initialize the effect
*
* @example
* here is an example of a basic addon audio effect, we'll call it <i>AFXname.js</i><br>
* ( in this case nothing really, just a simple delay ), extended from BB.AFX
* <br><br>
* <code class="code prettyprint">
* function AFXname( config ){<br>
* <br>
* BB.AFX.call(this, config);<br>
* <br>
* // this.node must be some kind of AudioNode<br>
* // likely something like ConvolverNode, DelayNode, etc. ( or combo )<br>
* this.node = this.ctx.createDelay();<br>
* <br>
* // initialze the input >> dry/wet >> output ( see BB.AFX._init() )<br>
* // must be called after this.node is declared<br>
* this._init(); <br>
* }<br>
* <br>
* AFXname.prototype = Object.create(BB.AFX.prototype);<br>
* AFXname.prototype.constructor = AFXname;
* </code>
* <br><br>
* by extending from BB.AFX, our AFXname has an input && an output, as well as the delay node ( this.node above ) automatically connected to a "wet" channel ( which is inversely proportional to the "dry" channel )<br>
* <img src="../assets/images/afxloop.png">
* <br><br>
* view the advanced <a href="../../examples/editor/?file=audio-fx-addons&type=advanced" target="_blank">BB.AFX addons</a> example
*/
BB.AFX = function( config ) {
BB.AudioBase.call(this, config);
// config obj
if(typeof config !== "undefined" && typeof config !== "object" ){
throw new Error('BB.AFX: first parameter should be a config object');
} else if( typeof config === "undefined" ) config = {};
this.node = null; // MUST BE DEFINED IN THE ADDON
};
BB.AFX.prototype = Object.create(BB.AudioBase.prototype);
BB.AFX.prototype.constructor = BB.AFX;
BB.AFX.prototype._init = function(){
// ................... FX loop ..............................
// must be executed in the addon's constructor ( after defining this.node )
// for bypassing fx // dry + wet channels
this.input = this.ctx.createGain(); // input :: receives connection
// output :: this.gain ( form AudioBase );
this._wet = new BB.AudioBase({ connect: this.gain });
this._dry = new BB.AudioBase({ connect: this.gain });
this._dry.volume = 0;
// input > dry > output
this.input.connect( this._dry.gain );
// input > fx > wet > output
this.input.connect( this.node );
this.node.connect( this._wet.gain );
};
/**
* the dry channel gain/volume
* @property dry
* @type Number
* @default 0
*/
Object.defineProperty(BB.AFX.prototype, "dry", {
get: function() {
return this._dry.volume;
},
set: function(v) {
if( typeof v !== 'number'){
throw new Error("BB.AFX.dry: expecing a number");
} else {
this._dry.setGain( v );
var diff = 1 - v;
this._wet.setGain( diff );
}
}
});
/**
* the wet channel gain/volume
* @property wet
* @type Number
* @default 1
*/
Object.defineProperty(BB.AFX.prototype, "wet", {
get: function() {
return this._wet.volume;
},
set: function(v) {
if( typeof v !== 'number'){
throw new Error("BB.AFX.wet: expecing a number");
} else {
this._wet.setGain( v );
var diff = 1 - v;
this._dry.setGain( diff );
}
}
});
/**
* set's the dry gain ( && adjust the wet gain accordingly, so that they total to 1 )
* @method setDryGain
* @param {Number} num a float value, 1 being the default volume, below 1 decreses the volume, above one pushes the gain
* @param {Number} ramp value in seconds for how quickly/slowly to ramp to the new value (num) specified
*
* @example
* <code class="code prettyprint">
* BB.Audio.init();<br>
* <br>
* var fx = new BB.AFX('filter');<br>
* var noise = new BB.AudioNoise({<br>
* connect: fx<br>
* });<br>
* <br>
* fx.setDryGain( 0.75, 2 ); // raises dry level from 0 - 0.75 over 2 seconds (wet level drops to 0.25)<br>
* // if no ramp value is needed, you could alternatively do<br>
* fx.dry.volume = 0.75; // immediately jumps to 0.75 (and wet to 0.25) <br>
* </code>
*/
BB.AFX.prototype.setDryGain = function( num, gradually ) {
if( typeof num !== "number" )
throw new Error('BB.AFX.setDryGain: first argument expecting a number');
this._dry._volume = num;
var diff = 1 - num;
this._wet._volume = diff;
if(typeof gradually !== "undefined"){
if( typeof num !== "number" )
throw new Error('BB.AFX.setDryGain: second argument expecting a number');
else {
this._dry._globalGainUpdate( gradually );
this._wet._globalGainUpdate( gradually );
}
}
else { this._dry._globalGainUpdate(0); this._wet._globalGainUpdate(0); }
};
/**
* set's the wet gain ( && adjust the dry gain accordingly, so that they total to 1 )
* @method setWetGain
* @param {Number} num a float value, 1 being the default volume, below 1 decreses the volume, above one pushes the gain
* @param {Number} ramp value in seconds for how quickly/slowly to ramp to the new value (num) specified
*
* @example
* <code class="code prettyprint">
* BB.Audio.init();<br>
* <br>
* var fx = new BB.AFX('filter');<br>
* var noise = new BB.AudioNoise({<br>
* connect: fx<br>
* });<br>
* <br>
* fx.setWetGain( 0.15, 2 ); // drops wet level from 1 - 0.15 over 2 seconds (dry level rises to 0.85)<br>
* // if no ramp value is needed, you could alternatively do<br>
* fx.wet.volume = 0.15; // immediately jumps to 0.15 (and dry to 0.85) <br>
* </code>
*/
BB.AFX.prototype.setWetGain = function( num, gradually ) {
if( typeof num !== "number" )
throw new Error('BB.AFX.setWetGain: first argument expecting a number');
this._wet._volume = num;
var diff = 1 - num;
this._dry._volume = diff;
if(typeof gradually !== "undefined"){
if( typeof num !== "number" )
throw new Error('BB.AFX.setWetGain: second argument expecting a number');
else {
this._wet._globalGainUpdate( gradually );
this._dry._globalGainUpdate( gradually );
}
}
else { this._wet._globalGainUpdate(0); this._dry._globalGainUpdate(0); }
};
return BB.AFX;
});