/*jshint -W056 */
var Convolver = require('./convolver'),
Delay = require('./delay'),
Gain = require('./gain'),
Oscillator = require('./oscillator'),
SimplexNoise = require('quietriot'),
Utils = require('drawing-utils-lib');
// TODO: create play/pause methods
/**
* Creates a Player.
* @constructor
*/
function Player() {
this.name = 'Player';
this.gain = null;
this.oscA = null;
this.oscB = null;
this.convolver = null;
this.delay = null;
this.clock = 0;
}
Player.audio_context = null;
/**
* Configures an audio context.
* @param {Object} [opt_options=] A map of initial properties.
* @param {boolean} [opt_options.perlin = true] When set to true, the oscillators' frequencies cycle via Perlin noise.
* @param {number} [opt_options.reverb = 4] Reverb level.
* @param {number} [opt_options.delayTime = 0] Delay time.
* @param {number} [opt_options.oscAFreq = 150] Oscillator A's initial frequency.
* @param {number} [opt_options.oscBFreq = 200] Oscillator B's initial frequency.
* @param {number} [opt_options.oscARate = 0.001] Oscillator A's cycle rate through its frequency min/max.
* @param {number} [opt_options.oscBRate = -0.001] Oscillator B's cycle rate through its frequency min/max.
* @param {number} [opt_options.freqMin = 150] The oscillators' minimum frequency.
* @param {number} [opt_options.freqMax = 200] The oscillators' maximum frequency.
* @param {number} [opt_options.volume = 0.25] The player's initial volume. Valid values between 0 and 1.
* @param {number} [opt_options.volumeMin = 0.1] The player's minimum volume. Valid values between 0 and 1.
* @param {number} [opt_options.volumeMax= 0.25] The player's maximum volume. Valid values between 0 and 1.
* @param {Function} [opt_options.beforeStep = function() {}] A function called at the beginning of each animation frame.
*/
Player.prototype.init = function(opt_options) {
var options = opt_options || {};
var audio_context = Player.audio_context;
this.perlin = typeof options.perlin !== 'undefined' ? options.perlin : true;
this.reverb = typeof options.reverb !== 'undefined' ? options.reverb : 4;
this.delayTime = options.delayTime || 0;
this.oscAFreq = typeof options.oscAFreq !== 'undefined' ? options.oscAFreq : 150;
this.oscBFreq = typeof options.oscBFreq !== 'undefined' ? options.oscBFreq : 200;
this.oscARate = typeof options.oscARate !== 'undefined' ? options.oscARate : 0.001;
this.oscBRate = typeof options.oscBRate !== 'undefined' ? options.oscBRate : -0.001;
this.freqMin = typeof options.freqMin !== 'undefined' ? options.freqMin : 150;
this.freqMax = typeof options.freqMax !== 'undefined' ? options.freqMax : 200;
this.volume = typeof options.volume !== 'undefined' ? options.volume : 0.25;
this.volumeMin = typeof options.volumeMin !== 'undefined' ? options.volumeMin : 0.5;
this.volumeMax = typeof options.volumeMax !== 'undefined' ? options.volumeMax : 0.75;
this.beforeStep = options.beforeStep || function() {};
this.gain = new Gain(audio_context);
this.oscA = new Oscillator(audio_context);
this.oscB = new Oscillator(audio_context);
this.convolver = new Convolver(audio_context);
this.delay = new Delay(audio_context);
this.oscA.toggle();
this.oscB.toggle();
this.convolver.setEffect(this.reverb);
this.delay.setDelay(this.delayTime);
this.gain.changeGain(this.volume);
this.oscA.changeFrequency(this.oscAFreq);
this.oscB.changeFrequency(this.oscBFreq);
this.configure(audio_context);
if (this.perlin) {
this._loop();
}
};
/**
* Sets audio node configuration.
* @param {Object} context A Web Audio object.
*/
Player.prototype.configure = function(context) {
this._connect(this.gain.node, context.destination);
this._connect(this.delay.node, this.gain.node);
this._connect(this.convolver.node, this.delay.node);
this._connect(this.oscA.node, this.convolver.node);
this._connect(this.oscB.node, this.convolver.node);
};
/**
* Connects audio nodes.
* @param {Object} nodeA A Web Audio node.
* @param {Object} nodeB A Web Audio node.
* @private
*/
Player.prototype._connect = function(nodeA, nodeB) {
nodeA.connect(nodeB);
};
/**
* Updates audio node properties.
* @private
*/
Player.prototype._loop = function() {
this.beforeStep.call(this);
var valA = Utils.map(SimplexNoise.noise(this.clock * this.oscARate, 0),
-1, 1, this.freqMin, this.freqMax);
this.oscA.changeFrequency(valA);
var valB = Utils.map(SimplexNoise.noise(this.clock * this.oscBRate, 0),
-1, 1, this.freqMin, this.freqMax);
var volume = Utils.map(SimplexNoise.noise(this.clock * this.oscARate, 0),
-1, 1, this.volumeMin, this.volumeMax);
this.oscB.changeFrequency(valB);
this.gain.changeGain(volume);
this.clock++;
if (typeof window.requestAnimationFrame !== 'undefined') {
window.requestAnimationFrame(this._loop.bind(this));
}
};
(function init(g){
try {
Player.audio_context = new (g.AudioContext || g.webkitAudioContext);
} catch (e) {
throw new Error('No web audio support in this browser');
}
}(window));
module.exports = Player;