Comparing version 0.3.2 to 0.4.0
@@ -58,6 +58,3 @@ (function(root) { | ||
isBool: function(arg) { | ||
if (typeof(arg) !== "boolean") | ||
return false; | ||
return true; | ||
return typeof(arg) === "boolean"; | ||
}, | ||
@@ -77,4 +74,3 @@ | ||
// Takes a number from 0 to 1 and normalizes it | ||
// to fit within range floor to ceiling | ||
// Takes a number from 0 to 1 and normalizes it to fit within range floor to ceiling | ||
normalize: function(num, floor, ceil) { | ||
@@ -107,2 +103,13 @@ if (!Pz.Util.isNumber(num) || !Pz.Util.isNumber(floor) || !Pz.Util.isNumber(ceil)) | ||
}; | ||
/* In order to allow an AudioNode to connect to a Pizzicato | ||
Effect object, we must shim its connect method */ | ||
var gainNode = Pizzicato.context.createGain(); | ||
var audioNode = Object.getPrototypeOf(Object.getPrototypeOf(gainNode)); | ||
var connect = audioNode.connect; | ||
audioNode.connect = function(node) { | ||
var endpoint = Pz.Util.isEffect(node) ? node.inputNode : node; | ||
connect.call(this, endpoint); | ||
return node; | ||
}; | ||
@@ -199,3 +206,3 @@ Object.defineProperty(Pizzicato, 'volume', { | ||
var defaultAttack = 0.04; | ||
var defaultSustain = 0.04; | ||
var defaultRelease = 0.04; | ||
@@ -208,5 +215,6 @@ if (descriptionError) { | ||
this.masterVolume = Pizzicato.context.createGain(); | ||
this.masterVolume.connect(Pizzicato.masterGainNode); | ||
this.fadeNode = Pizzicato.context.createGain(); | ||
if (!hasOptions || !description.options.detached) | ||
this.masterVolume.connect(Pizzicato.masterGainNode); | ||
@@ -218,5 +226,13 @@ this.lastTimePlayed = 0; | ||
this.attack = hasOptions && util.isNumber(description.options.attack) ? description.options.attack : defaultAttack; | ||
this.sustain = hasOptions && util.isNumber(description.options.sustain) ? description.options.sustain : defaultSustain; | ||
this.volume = hasOptions && util.isNumber(description.options.volume) ? description.options.volume : 1; | ||
if (hasOptions && util.isNumber(description.options.release)) { | ||
this.release = description.options.release; | ||
} else if (hasOptions && util.isNumber(description.options.sustain)) { | ||
console.warn('\'sustain\' is deprecated. Use \'release\' instead.'); | ||
this.release = description.options.sustain; | ||
} else { | ||
this.release = defaultRelease; | ||
} | ||
if (!description) | ||
@@ -423,3 +439,3 @@ (initializeWithWave.bind(this))({}, callback); | ||
this.paused = this.playing = false; | ||
this.stopWithSustain(); | ||
this.stopWithRelease(); | ||
@@ -442,3 +458,3 @@ this.offsetTime = 0; | ||
this.stopWithSustain(); | ||
this.stopWithRelease(); | ||
@@ -468,3 +484,3 @@ var elapsedTime = Pz.context.currentTime - this.lastTimePlayed; | ||
attack: this.attack, | ||
sustain: this.sustain, | ||
release: this.release, | ||
volume: this.volume, | ||
@@ -545,2 +561,20 @@ sound: this | ||
connect: { | ||
enumerable: true, | ||
value: function(audioNode) { | ||
this.masterVolume.connect(audioNode); | ||
} | ||
}, | ||
disconnect: { | ||
enumerable: true, | ||
value: function(audioNode) { | ||
this.masterVolume.disconnect(audioNode); | ||
} | ||
}, | ||
connectEffects: { | ||
@@ -595,3 +629,22 @@ enumerable: true, | ||
/** | ||
* @deprecated - Use "release" | ||
*/ | ||
sustain: { | ||
enumerable: true, | ||
get: function() { | ||
console.warn('\'sustain\' is deprecated. Use \'release\' instead.'); | ||
return this.release; | ||
}, | ||
set: function(sustain){ | ||
console.warn('\'sustain\' is deprecated. Use \'release\' instead.'); | ||
if (Pz.Util.isInRange(sustain, 0, 10)) | ||
this.release = sustain; | ||
} | ||
}, | ||
/** | ||
@@ -635,2 +688,4 @@ * Returns the node that produces the sound. For example, an oscillator | ||
/** | ||
* @deprecated - Use "connect" | ||
* | ||
* Returns an analyser node located right after the master volume. | ||
@@ -644,2 +699,4 @@ * This node is created lazily. | ||
console.warn('This method is deprecated. You should manually create an AnalyserNode and use connect() on the Pizzicato Sound.'); | ||
if (this.analyser) | ||
@@ -675,6 +732,6 @@ return this.analyser; | ||
* Will take the current source node and work down the volume | ||
* gradually in as much time as specified in the sustain property | ||
* gradually in as much time as specified in the release property | ||
* of the sound before stopping the source node. | ||
*/ | ||
stopWithSustain: { | ||
stopWithRelease: { | ||
enumerable: false, | ||
@@ -689,10 +746,10 @@ | ||
if (!this.sustain) | ||
if (!this.release) | ||
stopSound(); | ||
this.fadeNode.gain.setValueAtTime(this.fadeNode.gain.value, Pizzicato.context.currentTime); | ||
this.fadeNode.gain.linearRampToValueAtTime(0.00001, Pizzicato.context.currentTime + this.sustain); | ||
this.fadeNode.gain.linearRampToValueAtTime(0.00001, Pizzicato.context.currentTime + this.release); | ||
window.setTimeout(function() { | ||
stopSound(); | ||
}, this.sustain * 1000); | ||
}, this.release * 1000); | ||
} | ||
@@ -702,2 +759,21 @@ } | ||
Pizzicato.Effects = {}; | ||
var baseEffect = Object.create(null, { | ||
connect: { | ||
enumerable: true, | ||
value: function(audioNode) { | ||
this.outputNode.connect(audioNode); | ||
} | ||
}, | ||
disconnect: { | ||
enumerable: true, | ||
value: function(audioNode) { | ||
this.outputNode.disconnect(audioNode); | ||
} | ||
} | ||
}); | ||
Pizzicato.Effects.Delay = function(options) { | ||
@@ -744,3 +820,3 @@ | ||
Pizzicato.Effects.Delay.prototype = Object.create(null, { | ||
Pizzicato.Effects.Delay.prototype = Object.create(baseEffect, { | ||
@@ -830,3 +906,3 @@ /** | ||
Pizzicato.Effects.Compressor.prototype = Object.create(null, { | ||
Pizzicato.Effects.Compressor.prototype = Object.create(baseEffect, { | ||
@@ -978,3 +1054,3 @@ /** | ||
var filterPrototype = Object.create(null, { | ||
var filterPrototype = Object.create(baseEffect, { | ||
@@ -1038,3 +1114,3 @@ /** | ||
Pizzicato.Effects.Distortion.prototype = Object.create(null, { | ||
Pizzicato.Effects.Distortion.prototype = Object.create(baseEffect, { | ||
@@ -1134,3 +1210,3 @@ /** | ||
Pizzicato.Effects.Flanger.prototype = Object.create(null, { | ||
Pizzicato.Effects.Flanger.prototype = Object.create(baseEffect, { | ||
@@ -1252,3 +1328,3 @@ time: { | ||
Pizzicato.Effects.StereoPanner.prototype = Object.create(null, { | ||
Pizzicato.Effects.StereoPanner.prototype = Object.create(baseEffect, { | ||
@@ -1345,3 +1421,3 @@ /** | ||
Pizzicato.Effects.Convolver.prototype = Object.create(null, { | ||
Pizzicato.Effects.Convolver.prototype = Object.create(baseEffect, { | ||
@@ -1414,3 +1490,3 @@ mix: { | ||
Pizzicato.Effects.PingPongDelay.prototype = Object.create(null, { | ||
Pizzicato.Effects.PingPongDelay.prototype = Object.create(baseEffect, { | ||
@@ -1514,3 +1590,3 @@ /** | ||
Pizzicato.Effects.Reverb.prototype = Object.create(null, { | ||
Pizzicato.Effects.Reverb.prototype = Object.create(baseEffect, { | ||
@@ -1601,2 +1677,110 @@ mix: { | ||
} | ||
Pizzicato.Effects.Tremolo = function(options) { | ||
// adapted from | ||
// https://github.com/mmckegg/web-audio-school/blob/master/lessons/3.%20Effects/13.%20Tremolo/answer.js | ||
this.options = {}; | ||
options = options || this.options; | ||
var defaults = { | ||
speed: 4, | ||
depth: 1, | ||
mix: 0.8 | ||
}; | ||
// create nodes | ||
this.inputNode = Pizzicato.context.createGain(); | ||
this.outputNode = Pizzicato.context.createGain(); | ||
this.dryGainNode = Pizzicato.context.createGain(); | ||
this.wetGainNode = Pizzicato.context.createGain(); | ||
this.tremoloGainNode = Pizzicato.context.createGain(); | ||
this.tremoloGainNode.gain.value = 0; | ||
this.lfoNode = Pizzicato.context.createOscillator(); | ||
this.shaperNode = Pizzicato.context.createWaveShaper(); | ||
this.shaperNode.curve = new Float32Array([0, 1]); | ||
this.shaperNode.connect(this.tremoloGainNode.gain); | ||
// dry mix | ||
this.inputNode.connect(this.dryGainNode); | ||
this.dryGainNode.connect(this.outputNode); | ||
// wet mix | ||
this.lfoNode.connect(this.shaperNode); | ||
this.lfoNode.type = 'sine'; | ||
this.lfoNode.start(0); | ||
this.inputNode.connect(this.tremoloGainNode); | ||
this.tremoloGainNode.connect(this.wetGainNode); | ||
this.wetGainNode.connect(this.outputNode); | ||
for (var key in defaults) { | ||
this[key] = options[key]; | ||
this[key] = (this[key] === undefined || this[key] === null) ? defaults[key] : this[key]; | ||
} | ||
}; | ||
Pizzicato.Effects.Tremolo.prototype = Object.create(baseEffect, { | ||
/** | ||
* Gets and sets the dry/wet mix. | ||
*/ | ||
mix: { | ||
enumerable: true, | ||
get: function() { | ||
return this.options.mix ; | ||
}, | ||
set: function(mix) { | ||
if (!Pz.Util.isInRange(mix, 0, 1)) | ||
return; | ||
this.options.mix = mix; | ||
this.dryGainNode.gain.value = Pizzicato.Util.getDryLevel(this.mix); | ||
this.wetGainNode.gain.value = Pizzicato.Util.getWetLevel(this.mix); | ||
} | ||
}, | ||
/** | ||
* Speed of the tremolo | ||
*/ | ||
speed: { | ||
enumerable: true, | ||
get: function() { | ||
return this.options.speed; | ||
}, | ||
set: function(speed) { | ||
if (!Pz.Util.isInRange(speed, 0, 20)) | ||
return; | ||
this.options.speed = speed; | ||
this.lfoNode.frequency.value = speed; | ||
} | ||
}, | ||
/** | ||
* Depth of the tremolo | ||
*/ | ||
depth: { | ||
enumerable: true, | ||
get: function() { | ||
return this.options.depth; | ||
}, | ||
set: function(depth) { | ||
if (!Pz.Util.isInRange(depth, 0, 1)) | ||
return; | ||
this.options.depth = depth; | ||
this.shaperNode.curve = new Float32Array([1-depth, 1]); | ||
} | ||
} | ||
}); | ||
Pizzicato.Effects.DubDelay = function(options) { | ||
@@ -1644,3 +1828,3 @@ | ||
Pizzicato.Effects.DubDelay.prototype = Object.create(null, { | ||
Pizzicato.Effects.DubDelay.prototype = Object.create(baseEffect, { | ||
@@ -1856,3 +2040,3 @@ /** | ||
Pizzicato.Effects.RingModulator.prototype = Object.create(null, { | ||
Pizzicato.Effects.RingModulator.prototype = Object.create(baseEffect, { | ||
@@ -1923,130 +2107,4 @@ /** | ||
}); | ||
Pizzicato.Effects.PitchShifter = function(options) { | ||
this.options = {}; | ||
options = options || this.options; | ||
var defaults = { | ||
pitch: 0.5, | ||
grainSize: 256, | ||
mix: 1.0 | ||
}; | ||
this.inputNode = Pizzicato.context.createGain(); | ||
this.outputNode = Pizzicato.context.createGain(); | ||
this.wetGainNode = Pizzicato.context.createGain(); | ||
this.dryGainNode = Pizzicato.context.createGain(); | ||
this.inputNode.connect(this.dryGainNode); | ||
this.dryGainNode.connect(this.outputNode); | ||
for (var key in defaults) { | ||
this[key] = options[key]; | ||
this[key] = (this[key] === undefined || this[key] === null) ? defaults[key] : this[key]; | ||
} | ||
}; | ||
function processAudioEvent(event) { | ||
var inputBuffer = event.inputBuffer.getChannelData(0); | ||
var overlapRatio = 0.1; | ||
var i, j; | ||
if (this.pitch === 1.0) { | ||
for (i = 0; i < this.grainSize; i++) | ||
(event.outputBuffer.getChannelData(0))[i] = inputBuffer[i]; | ||
return; | ||
} | ||
for (i = 0; i < inputBuffer.length; i++) { | ||
inputBuffer[i] = inputBuffer[i] * this.hannWindow[i]; | ||
// Shift half of the buffer | ||
this.buffer[i] = this.buffer[i + inputBuffer.length]; | ||
// Empty the buffer tail | ||
this.buffer[i + inputBuffer.length] = 0.0; | ||
} | ||
// Calculate the pitch shifted grain re-sampling and looping the input | ||
var grainData = new Float32Array(this.grainSize); | ||
for (i = 0, j = 0.0; i < this.grainSize; i++, j += this.pitch) { | ||
var index = Math.floor(j) % this.grainSize; | ||
var a = inputBuffer[index]; | ||
var b = inputBuffer[(index + 1) % this.grainSize]; | ||
grainData[i] += (a + (b - a)) * this.hannWindow[i]; | ||
} | ||
// Copy the grain multiple times overlapping it | ||
for (i = 0; i < this.grainSize; i += Math.round(this.grainSize * overlapRatio)) // is overlap ratio | ||
for (j = 0; j < this.grainSize; j++) | ||
this.buffer[i + j] += grainData[j]; | ||
// Output the first half of the buffer | ||
for (i = 0; i < this.grainSize; i++) | ||
(event.outputBuffer.getChannelData(0))[i] = this.buffer[i]; | ||
} | ||
Pizzicato.Effects.PitchShifter.prototype = Object.create(null, { | ||
grainSize: { | ||
enumberable: true, | ||
get: function() { | ||
return this.options.grainSize; | ||
}, | ||
set: function(grainSize) { | ||
// $$$ validate grainSize | ||
// if (!Pz.Util.isInRange(grainSize, 0, 1)) | ||
// return; | ||
this.options.grainSize = grainSize; | ||
this.hannWindow = new Float32Array(grainSize); | ||
// Changing the hann window to match the new grain size | ||
for (var i = 0; i < this.hannWindow.length; i++) | ||
this.hannWindow[i] = 0.5 * (1 - Math.cos(2 * Math.PI * i / (grainSize - 1))); | ||
// Changing the buffer | ||
this.buffer = new Float32Array(this.grainSize * 2); | ||
// Changing the script processor buffer to match the grain size | ||
if (this.processorNode) | ||
this.processorNode.disconnect(); | ||
this.processorNode = Pz.context.createScriptProcessor(this.grainSize, 1, 1); | ||
this.processorNode.onaudioprocess = processAudioEvent.bind(this); | ||
this.wetGainNode.disconnect(); | ||
this.inputNode.connect(this.processorNode); | ||
this.processorNode.connect(this.wetGainNode); | ||
this.wetGainNode.connect(this.outputNode); | ||
} | ||
}, | ||
mix: { | ||
enumberable: true, | ||
get: function() { | ||
return this.options.mix; | ||
}, | ||
set: function(mix) { | ||
if (!Pz.Util.isInRange(mix, 0, 1)) | ||
return; | ||
this.options.mix = mix; | ||
this.dryGainNode.gain.value = Pizzicato.Util.getDryLevel(this.mix); | ||
this.wetGainNode.gain.value = Pizzicato.Util.getWetLevel(this.mix); | ||
} | ||
} | ||
}); | ||
return Pizzicato; | ||
})(typeof window !== "undefined" ? window : global); |
@@ -1,1 +0,1 @@ | ||
!function(t){"use strict";function e(t,e){this.options={},t=t||this.options;var i={frequency:350,peak:1};this.inputNode=this.filterNode=s.context.createBiquadFilter(),this.filterNode.type=e,this.outputNode=o.context.createGain(),this.filterNode.connect(this.outputNode);for(var n in i)this[n]=t[n],this[n]=void 0===this[n]||null===this[n]?i[n]:this[n]}function i(){var t,e,i=s.context.sampleRate*this.time,n=o.context.createBuffer(2,i,s.context.sampleRate),a=n.getChannelData(0),r=n.getChannelData(1);for(e=0;i>e;e++)t=this.reverse?i-e:e,a[e]=(2*Math.random()-1)*Math.pow(1-t/i,this.decay),r[e]=(2*Math.random()-1)*Math.pow(1-t/i,this.decay);this.reverbNode.buffer=n}function n(t){var e,i,n=t.inputBuffer.getChannelData(0),o=.1;if(1!==this.pitch){for(e=0;e<n.length;e++)n[e]=n[e]*this.hannWindow[e],this.buffer[e]=this.buffer[e+n.length],this.buffer[e+n.length]=0;var s=new Float32Array(this.grainSize);for(e=0,i=0;e<this.grainSize;e++,i+=this.pitch){var a=Math.floor(i)%this.grainSize,r=n[a],c=n[(a+1)%this.grainSize];s[e]+=(r+(c-r))*this.hannWindow[e]}for(e=0;e<this.grainSize;e+=Math.round(this.grainSize*o))for(i=0;i<this.grainSize;i++)this.buffer[e+i]+=s[i];for(e=0;e<this.grainSize;e++)t.outputBuffer.getChannelData(0)[e]=this.buffer[e]}else for(e=0;e<this.grainSize;e++)t.outputBuffer.getChannelData(0)[e]=n[e]}var o={},s=o,a="object"==typeof module&&module.exports,r="function"==typeof define&&define.amd;a?module.exports=o:r?define([],o):t.Pizzicato=t.Pz=o;var c=t.AudioContext||t.webkitAudioContext;if(!c)return void console.error("No AudioContext found in this environment. Please ensure your window or global object contains a working AudioContext constructor function.");o.context=new c;var h=o.context.createGain();h.connect(o.context.destination),o.Util={isString:function(t){return"[object String]"===toString.call(t)},isObject:function(t){return"[object Object]"===toString.call(t)},isFunction:function(t){return"[object Function]"===toString.call(t)},isNumber:function(t){return"[object Number]"===toString.call(t)&&t===+t},isArray:function(t){return"[object Array]"===toString.call(t)},isInRange:function(t,e,i){return s.Util.isNumber(t)&&s.Util.isNumber(e)&&s.Util.isNumber(i)?t>=e&&i>=t:!1},isBool:function(t){return"boolean"==typeof t},isOscillator:function(t){return t&&"[object OscillatorNode]"===t.toString()},isEffect:function(t){for(var e in o.Effects)if(t instanceof o.Effects[e])return!0;return!1},normalize:function(t,e,i){return s.Util.isNumber(t)&&s.Util.isNumber(e)&&s.Util.isNumber(i)?(i-e)*t/1+e:void 0},getDryLevel:function(t){return!s.Util.isNumber(t)||t>1||0>t?0:.5>=t?1:1-2*(t-.5)},getWetLevel:function(t){return!s.Util.isNumber(t)||t>1||0>t?0:t>=.5?1:1-2*(.5-t)}},Object.defineProperty(o,"volume",{enumerable:!0,get:function(){return h.gain.value},set:function(t){s.Util.isInRange(t,0,1)&&h&&(h.gain.value=t)}}),Object.defineProperty(o,"masterGainNode",{enumerable:!1,get:function(){return h},set:function(t){console.error("Can't set the master gain node")}}),o.Events={on:function(t,e,i){if(t&&e){this._events=this._events||{};var n=this._events[t]||(this._events[t]=[]);n.push({callback:e,context:i||this,handler:this})}},trigger:function(t){if(t){var e,i,n,o;if(this._events=this._events||{},e=this._events[t]||(this._events[t]=[])){for(i=Math.max(0,arguments.length-1),n=[],o=0;i>o;o++)n[o]=arguments[o+1];for(o=0;o<e.length;o++)e[o].callback.apply(e[o].context,n)}}},off:function(t){t?this._events[t]=void 0:this._events={}}},o.Sound=function(t,e){function i(t){var e=["wave","file","input","script","sound"];if(t&&!d.isFunction(t)&&!d.isString(t)&&!d.isObject(t))return"Description type not supported. Initialize a sound using an object, a function or a string.";if(d.isObject(t)){if(!d.isString(t.source)||-1===e.indexOf(t.source))return"Specified source not supported. Sources can be wave, file, input or script";if(!("file"!==t.source||t.options&&t.options.path))return"A path is needed for sounds with a file source";if(!("script"!==t.source||t.options&&t.options.audioFunction))return"An audio function is needed for sounds with a script source"}}function n(t,e){t=t||{},this.getRawSourceNode=function(){var e=this.sourceNode?this.sourceNode.frequency.value:t.frequency,i=o.context.createOscillator();return i.type=t.type||"sine",i.frequency.value=e||440,i},this.sourceNode=this.getRawSourceNode(),d.isFunction(e)&&e()}function a(t,e){t=d.isArray(t)?t:[t];var i=new XMLHttpRequest;i.open("GET",t[0],!0),i.responseType="arraybuffer",i.onload=function(i){o.context.decodeAudioData(i.target.response,function(t){u.getRawSourceNode=function(){var e=o.context.createBufferSource();return e.loop=this.loop,e.buffer=t,e},d.isFunction(e)&&e()}.bind(u),function(i){return console.error("Error decoding audio file "+t[0]),t.length>1?(t.shift(),void a(t,e)):(i=i||new Error("Error decoding audio file "+t[0]),void(d.isFunction(e)&&e(i)))}.bind(u))},i.onreadystatechange=function(e){4===i.readyState&&200!==i.status&&console.error("Error while fetching "+t[0]+". "+i.statusText)},i.send()}function r(t,e){navigator.getUserMedia=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia,navigator.getUserMedia&&navigator.getUserMedia({audio:!0},function(t){u.getRawSourceNode=function(){return o.context.createMediaStreamSource(t)},d.isFunction(e)&&e()}.bind(u),function(t){d.isFunction(e)&&e(t)})}function c(t,e){var i=d.isFunction(t)?t:t.audioFunction,n=d.isObject(t)&&t.bufferSize?t.bufferSize:null;if(!n)try{o.context.createScriptProcessor()}catch(s){n=2048}this.getRawSourceNode=function(){var t=o.context.createScriptProcessor(n,1,1);return t.onaudioprocess=i,t}}function h(t,e){this.getRawSourceNode=t.sound.getRawSourceNode,t.sound.sourceNode&&s.Util.isOscillator(t.sound.sourceNode)&&(this.sourceNode=this.getRawSourceNode(),this.frequency=t.sound.frequency)}var u=this,d=o.Util,l=i(t),f=d.isObject(t)&&d.isObject(t.options),p=.04,v=.04;if(l)throw console.error(l),new Error("Error initializing Pizzicato Sound: "+l);this.masterVolume=o.context.createGain(),this.masterVolume.connect(o.masterGainNode),this.fadeNode=o.context.createGain(),this.lastTimePlayed=0,this.effects=[],this.playing=this.paused=!1,this.loop=f&&t.options.loop,this.attack=f&&d.isNumber(t.options.attack)?t.options.attack:p,this.sustain=f&&d.isNumber(t.options.sustain)?t.options.sustain:v,this.volume=f&&d.isNumber(t.options.volume)?t.options.volume:1,t?d.isString(t)?a.bind(this)(t,e):d.isFunction(t)?c.bind(this)(t,e):"file"===t.source?a.bind(this)(t.options.path,e):"wave"===t.source?n.bind(this)(t.options,e):"input"===t.source?r.bind(this)(t,e):"script"===t.source?c.bind(this)(t.options,e):"sound"===t.source&&h.bind(this)(t.options,e):n.bind(this)({},e)},o.Sound.prototype=Object.create(o.Events,{play:{enumerable:!0,value:function(t,e){this.playing||(s.Util.isNumber(e)||(e=this.offsetTime||0),s.Util.isNumber(t)||(t=0),this.playing=!0,this.paused=!1,this.sourceNode=this.getSourceNode(),this.applyAttack(),s.Util.isFunction(this.sourceNode.start)&&(this.lastTimePlayed=o.context.currentTime-e,this.sourceNode.start(s.context.currentTime+t,e)),this.trigger("play"))}},stop:{enumerable:!0,value:function(){(this.paused||this.playing)&&(this.paused=this.playing=!1,this.stopWithSustain(),this.offsetTime=0,this.trigger("stop"))}},pause:{enumerable:!0,value:function(){if(!this.paused&&this.playing){this.paused=!0,this.playing=!1,this.stopWithSustain();var t=s.context.currentTime-this.lastTimePlayed;this.sourceNode.buffer?this.offsetTime=t%(this.sourceNode.buffer.length/s.context.sampleRate):this.offsetTime=t,this.trigger("pause")}}},clone:{enumerable:!0,value:function(){for(var t=new o.Sound({source:"sound",options:{loop:this.loop,attack:this.attack,sustain:this.sustain,volume:this.volume,sound:this}}),e=0;e<this.effects.length;e++)t.addEffect(this.effects[e]);return t}},onEnded:{enumerable:!0,value:function(){this.playing&&this.stop(),this.paused||this.trigger("end")}},addEffect:{enumerable:!0,value:function(t){return t&&s.Util.isEffect(t)?(this.effects.push(t),this.connectEffects(),void(this.sourceNode&&(this.fadeNode.disconnect(),this.fadeNode.connect(this.getInputNode())))):void console.warn("Invalid effect.")}},removeEffect:{enumerable:!0,value:function(t){var e=this.effects.indexOf(t);if(-1===e)return void console.warn("Cannot remove effect that is not applied to this sound.");var i=this.playing;i&&this.pause(),this.fadeNode.disconnect();for(var n=0;n<this.effects.length;n++)this.effects[n].outputNode.disconnect();this.effects.splice(e,1),this.connectEffects(),i&&this.play()}},connectEffects:{enumerable:!0,value:function(){for(var t=0;t<this.effects.length;t++){var e=t===this.effects.length-1,i=e?this.masterVolume:this.effects[t+1].inputNode;this.effects[t].outputNode.disconnect(),this.effects[t].outputNode.connect(i)}}},volume:{enumerable:!0,get:function(){return this.masterVolume?this.masterVolume.gain.value:void 0},set:function(t){s.Util.isInRange(t,0,1)&&this.masterVolume&&(this.masterVolume.gain.value=t)}},frequency:{enumerable:!0,get:function(){return this.sourceNode&&s.Util.isOscillator(this.sourceNode)?this.sourceNode.frequency.value:null},set:function(t){this.sourceNode&&s.Util.isOscillator(this.sourceNode)&&(this.sourceNode.frequency.value=t)}},getSourceNode:{enumerable:!0,value:function(){this.sourceNode&&this.sourceNode.disconnect();var t=this.getRawSourceNode();return t.connect(this.fadeNode),t.onended=this.onEnded.bind(this),this.fadeNode.connect(this.getInputNode()),t}},getInputNode:{enumerable:!0,value:function(){return this.effects.length>0?this.effects[0].inputNode:this.masterVolume}},getAnalyser:{enumerable:!0,value:function(){return this.analyser?this.analyser:(this.analyser=o.context.createAnalyser(),this.masterVolume.disconnect(),this.masterVolume.connect(this.analyser),this.analyser.connect(o.masterGainNode),this.analyser)}},applyAttack:{enumerable:!1,value:function(){this.attack&&(this.fadeNode.gain.setValueAtTime(1e-5,o.context.currentTime),this.fadeNode.gain.linearRampToValueAtTime(1,o.context.currentTime+this.attack))}},stopWithSustain:{enumerable:!1,value:function(t){var e=this.sourceNode,i=function(){return s.Util.isFunction(e.stop)?e.stop(0):e.disconnect()};this.sustain||i(),this.fadeNode.gain.setValueAtTime(this.fadeNode.gain.value,o.context.currentTime),this.fadeNode.gain.linearRampToValueAtTime(1e-5,o.context.currentTime+this.sustain),window.setTimeout(function(){i()},1e3*this.sustain)}}}),o.Effects={},o.Effects.Delay=function(t){this.options={},t=t||this.options;var e={feedback:.5,time:.3,mix:.5};this.inputNode=o.context.createGain(),this.outputNode=o.context.createGain(),this.dryGainNode=o.context.createGain(),this.wetGainNode=o.context.createGain(),this.feedbackGainNode=o.context.createGain(),this.delayNode=o.context.createDelay(),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.delayNode.connect(this.feedbackGainNode),this.feedbackGainNode.connect(this.delayNode),this.inputNode.connect(this.delayNode),this.delayNode.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]},o.Effects.Delay.prototype=Object.create(null,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.mix=t,this.dryGainNode.gain.value=o.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=o.Util.getWetLevel(this.mix))}},time:{enumerable:!0,get:function(){return this.options.time},set:function(t){s.Util.isInRange(t,0,180)&&(this.options.time=t,this.delayNode.delayTime.value=t)}},feedback:{enumerable:!0,get:function(){return this.options.feedback},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.feedback=parseFloat(t,10),this.feedbackGainNode.gain.value=this.feedback)}}}),o.Effects.Compressor=function(t){this.options={},t=t||this.options;var e={threshold:-24,knee:30,attack:.003,release:.25,ratio:12};this.inputNode=this.compressorNode=o.context.createDynamicsCompressor(),this.outputNode=o.context.createGain(),this.compressorNode.connect(this.outputNode);for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]},o.Effects.Compressor.prototype=Object.create(null,{threshold:{enumerable:!0,get:function(){return this.compressorNode.threshold.value},set:function(t){o.Util.isInRange(t,-100,0)&&(this.compressorNode.threshold.value=t)}},knee:{enumerable:!0,get:function(){return this.compressorNode.knee.value},set:function(t){o.Util.isInRange(t,0,40)&&(this.compressorNode.knee.value=t)}},attack:{enumerable:!0,get:function(){return this.compressorNode.attack.value},set:function(t){o.Util.isInRange(t,0,1)&&(this.compressorNode.attack.value=t)}},release:{enumerable:!0,get:function(){return this.compressorNode.release.value},set:function(t){o.Util.isInRange(t,0,1)&&(this.compressorNode.release.value=t)}},ratio:{enumerable:!0,get:function(){return this.compressorNode.ratio.value},set:function(t){o.Util.isInRange(t,1,20)&&(this.compressorNode.ratio.value=t)}},getCurrentGainReduction:function(){return this.compressorNode.reduction}}),o.Effects.LowPassFilter=function(t){e.call(this,t,"lowpass")},o.Effects.HighPassFilter=function(t){e.call(this,t,"highpass")};var u=Object.create(null,{frequency:{enumerable:!0,get:function(){return this.filterNode.frequency.value},set:function(t){o.Util.isInRange(t,10,22050)&&(this.filterNode.frequency.value=t)}},peak:{enumerable:!0,get:function(){return this.filterNode.Q.value},set:function(t){o.Util.isInRange(t,1e-4,1e3)&&(this.filterNode.Q.value=t)}}});o.Effects.LowPassFilter.prototype=u,o.Effects.HighPassFilter.prototype=u,o.Effects.Distortion=function(t){this.options={},t=t||this.options;var e={gain:.5};this.waveShaperNode=o.context.createWaveShaper(),this.inputNode=this.outputNode=this.waveShaperNode;for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]},o.Effects.Distortion.prototype=Object.create(null,{gain:{enumerable:!0,get:function(){return this.options.gain},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.gain=t,this.adjustGain())}},adjustGain:{writable:!1,configurable:!1,enumerable:!1,value:function(){for(var t,e=s.Util.isNumber(this.options.gain)?parseInt(100*this.options.gain,10):50,i=44100,n=new Float32Array(i),o=Math.PI/180,a=0;i>a;++a)t=2*a/i-1,n[a]=(3+e)*t*20*o/(Math.PI+e*Math.abs(t));this.waveShaperNode.curve=n}}}),o.Effects.Flanger=function(t){this.options={},t=t||this.options;var e={time:.45,speed:.2,depth:.1,feedback:.1,mix:.5};this.inputNode=o.context.createGain(),this.outputNode=o.context.createGain(),this.inputFeedbackNode=o.context.createGain(),this.wetGainNode=o.context.createGain(),this.dryGainNode=o.context.createGain(),this.delayNode=o.context.createDelay(),this.oscillatorNode=o.context.createOscillator(),this.gainNode=o.context.createGain(),this.feedbackNode=o.context.createGain(),this.oscillatorNode.type="sine",this.inputNode.connect(this.inputFeedbackNode),this.inputNode.connect(this.dryGainNode),this.inputFeedbackNode.connect(this.delayNode),this.inputFeedbackNode.connect(this.wetGainNode),this.delayNode.connect(this.wetGainNode),this.delayNode.connect(this.feedbackNode),this.feedbackNode.connect(this.inputFeedbackNode),this.oscillatorNode.connect(this.gainNode),this.gainNode.connect(this.delayNode.delayTime),this.dryGainNode.connect(this.outputNode),this.wetGainNode.connect(this.outputNode),this.oscillatorNode.start(0);for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]},o.Effects.Flanger.prototype=Object.create(null,{time:{enumberable:!0,get:function(){return this.options.time},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.time=t,this.delayNode.delayTime.value=s.Util.normalize(t,.001,.02))}},speed:{enumberable:!0,get:function(){return this.options.speed},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.speed=t,this.oscillatorNode.frequency.value=s.Util.normalize(t,.5,5))}},depth:{enumberable:!0,get:function(){return this.options.depth},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.depth=t,this.gainNode.gain.value=s.Util.normalize(t,5e-4,.005))}},feedback:{enumberable:!0,get:function(){return this.options.feedback},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.feedback=t,this.feedbackNode.gain.value=s.Util.normalize(t,0,.8))}},mix:{enumberable:!0,get:function(){return this.options.mix},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.mix=t,this.dryGainNode.gain.value=o.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=o.Util.getWetLevel(this.mix))}}}),o.Effects.StereoPanner=function(t){this.options={},t=t||this.options;var e={pan:0};this.inputNode=o.context.createGain(),this.outputNode=o.context.createGain(),o.context.createStereoPanner?(this.pannerNode=o.context.createStereoPanner(),this.inputNode.connect(this.pannerNode),this.pannerNode.connect(this.outputNode)):this.inputNode.connect(this.outputNode);for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]},o.Effects.StereoPanner.prototype=Object.create(null,{pan:{enumerable:!0,get:function(){return this.options.pan},set:function(t){s.Util.isInRange(t,-1,1)&&(this.options.pan=t,this.pannerNode&&(this.pannerNode.pan.value=t))}}}),o.Effects.Convolver=function(t,e){this.options={},t=t||this.options;var i=this,n=new XMLHttpRequest,a={mix:.5};this.callback=e,this.inputNode=o.context.createGain(),this.convolverNode=o.context.createConvolver(),this.outputNode=o.context.createGain(),this.wetGainNode=o.context.createGain(),this.dryGainNode=o.context.createGain(),this.inputNode.connect(this.convolverNode),this.convolverNode.connect(this.wetGainNode),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.wetGainNode.connect(this.outputNode);for(var r in a)this[r]=t[r],this[r]=void 0===this[r]||null===this[r]?a[r]:this[r];return t.impulse?(n.open("GET",t.impulse,!0),n.responseType="arraybuffer",n.onload=function(t){var e=t.target.response;o.context.decodeAudioData(e,function(t){i.convolverNode.buffer=t,i.callback&&s.Util.isFunction(i.callback)&&i.callback()},function(t){t=t||new Error("Error decoding impulse file"),i.callback&&s.Util.isFunction(i.callback)&&i.callback(t)})},n.onreadystatechange=function(e){4===n.readyState&&200!==n.status&&console.error("Error while fetching "+t.impulse+". "+n.statusText)},void n.send()):void console.error("No impulse file specified.")},o.Effects.Convolver.prototype=Object.create(null,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.mix=t,this.dryGainNode.gain.value=o.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=o.Util.getWetLevel(this.mix))}}}),o.Effects.PingPongDelay=function(t){this.options={},t=t||this.options;var e={feedback:.5,time:.3,mix:.5};this.inputNode=o.context.createGain(),this.outputNode=o.context.createGain(),this.delayNodeLeft=o.context.createDelay(),this.delayNodeRight=o.context.createDelay(),this.dryGainNode=o.context.createGain(),this.wetGainNode=o.context.createGain(),this.feedbackGainNode=o.context.createGain(),this.channelMerger=o.context.createChannelMerger(2),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.delayNodeLeft.connect(this.channelMerger,0,0),this.delayNodeRight.connect(this.channelMerger,0,1),this.delayNodeLeft.connect(this.delayNodeRight),this.feedbackGainNode.connect(this.delayNodeLeft),this.delayNodeRight.connect(this.feedbackGainNode),this.inputNode.connect(this.feedbackGainNode),this.channelMerger.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]},o.Effects.PingPongDelay.prototype=Object.create(null,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.mix=t,this.dryGainNode.gain.value=o.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=o.Util.getWetLevel(this.mix))}},time:{enumerable:!0,get:function(){return this.options.time},set:function(t){s.Util.isInRange(t,0,180)&&(this.options.time=t,this.delayNodeLeft.delayTime.value=t,this.delayNodeRight.delayTime.value=t)}},feedback:{enumerable:!0,get:function(){return this.options.feedback},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.feedback=parseFloat(t,10),this.feedbackGainNode.gain.value=this.feedback)}}}),o.Effects.Reverb=function(t){this.options={},t=t||this.options;var e={mix:.5,time:.01,decay:.01,reverse:!1};this.inputNode=o.context.createGain(),this.reverbNode=o.context.createConvolver(),this.outputNode=o.context.createGain(),this.wetGainNode=o.context.createGain(),this.dryGainNode=o.context.createGain(),this.inputNode.connect(this.reverbNode),this.reverbNode.connect(this.wetGainNode),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.wetGainNode.connect(this.outputNode);for(var n in e)this[n]=t[n],this[n]=void 0===this[n]||null===this[n]?e[n]:this[n];i.bind(this)()},o.Effects.Reverb.prototype=Object.create(null,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.mix=t,this.dryGainNode.gain.value=o.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=o.Util.getWetLevel(this.mix))}},time:{enumerable:!0,get:function(){return this.options.time},set:function(t){s.Util.isInRange(t,1e-4,10)&&(this.options.time=t,i.bind(this)())}},decay:{enumerable:!0,get:function(){return this.options.decay},set:function(t){s.Util.isInRange(t,1e-4,10)&&(this.options.decay=t,i.bind(this)())}},reverse:{enumerable:!0,get:function(){return this.options.reverse},set:function(t){s.Util.isBool(t)&&(this.options.reverse=t,i.bind(this)())}}}),o.Effects.DubDelay=function(t){this.options={},t=t||this.options;var e={feedback:.6,time:.7,mix:.5,cutoff:700};this.inputNode=o.context.createGain(),this.outputNode=o.context.createGain(),this.dryGainNode=o.context.createGain(),this.wetGainNode=o.context.createGain(),this.feedbackGainNode=o.context.createGain(),this.delayNode=o.context.createDelay(),this.bqFilterNode=o.context.createBiquadFilter(),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.inputNode.connect(this.wetGainNode),this.inputNode.connect(this.feedbackGainNode),this.feedbackGainNode.connect(this.bqFilterNode),this.bqFilterNode.connect(this.delayNode),this.delayNode.connect(this.feedbackGainNode),this.delayNode.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]},o.Effects.DubDelay.prototype=Object.create(null,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.mix=t,this.dryGainNode.gain.value=o.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=o.Util.getWetLevel(this.mix))}},time:{enumerable:!0,get:function(){return this.options.time},set:function(t){s.Util.isInRange(t,0,180)&&(this.options.time=t,this.delayNode.delayTime.value=t)}},feedback:{enumerable:!0,get:function(){return this.options.feedback},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.feedback=parseFloat(t,10),this.feedbackGainNode.gain.value=this.feedback)}},cutoff:{enumerable:!0,get:function(){return this.options.cutoff},set:function(t){s.Util.isInRange(t,0,4e3)&&(this.options.cutoff=t,this.bqFilterNode.frequency.value=this.cutoff)}}}),o.Effects.RingModulator=function(t){this.options={},t=t||this.options;var e={speed:30,distortion:1,mix:.5};this.inputNode=o.context.createGain(),this.outputNode=o.context.createGain(),this.dryGainNode=o.context.createGain(),this.wetGainNode=o.context.createGain(),this.vIn=o.context.createOscillator(),this.vIn.start(0),this.vInGain=o.context.createGain(),this.vInGain.gain.value=.5,this.vInInverter1=o.context.createGain(),this.vInInverter1.gain.value=-1,this.vInInverter2=o.context.createGain(),this.vInInverter2.gain.value=-1,this.vInDiode1=new d(o.context),this.vInDiode2=new d(o.context),this.vInInverter3=o.context.createGain(),this.vInInverter3.gain.value=-1,this.vcInverter1=o.context.createGain(),this.vcInverter1.gain.value=-1,this.vcDiode3=new d(o.context),this.vcDiode4=new d(o.context),this.outGain=o.context.createGain(),this.outGain.gain.value=3,this.compressor=o.context.createDynamicsCompressor(),this.compressor.threshold.value=-24,this.compressor.ratio.value=16,this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.inputNode.connect(this.vcInverter1),this.inputNode.connect(this.vcDiode4.node),this.vcInverter1.connect(this.vcDiode3.node),this.vIn.connect(this.vInGain),this.vInGain.connect(this.vInInverter1),this.vInGain.connect(this.vcInverter1),this.vInGain.connect(this.vcDiode4.node),this.vInInverter1.connect(this.vInInverter2),this.vInInverter1.connect(this.vInDiode2.node),this.vInInverter2.connect(this.vInDiode1.node),this.vInDiode1.connect(this.vInInverter3),this.vInDiode2.connect(this.vInInverter3),this.vInInverter3.connect(this.compressor),this.vcDiode3.connect(this.compressor),this.vcDiode4.connect(this.compressor),this.compressor.connect(this.outGain),this.outGain.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]};var d=function(t){this.context=t,this.node=this.context.createWaveShaper(),this.vb=.2,this.vl=.4,this.h=1,this.setCurve()};return d.prototype.setDistortion=function(t){return this.h=t,this.setCurve()},d.prototype.setCurve=function(){var t,e,i,n,o,s,a,r;for(e=1024,o=new Float32Array(e),t=s=0,a=o.length;a>=0?a>s:s>a;t=a>=0?++s:--s)i=(t-e/2)/(e/2),i=Math.abs(i),n=i<=this.vb?0:this.vb<i&&i<=this.vl?this.h*(Math.pow(i-this.vb,2)/(2*this.vl-2*this.vb)):this.h*i-this.h*this.vl+this.h*(Math.pow(this.vl-this.vb,2)/(2*this.vl-2*this.vb)),o[t]=n;return r=this.node.curve=o},d.prototype.connect=function(t){return this.node.connect(t)},o.Effects.RingModulator.prototype=Object.create(null,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.mix=t,this.dryGainNode.gain.value=o.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=o.Util.getWetLevel(this.mix))}},speed:{enumerable:!0,get:function(){return this.options.speed},set:function(t){s.Util.isInRange(t,0,2e3)&&(this.options.speed=t,this.vIn.frequency.value=t)}},distortion:{enumerable:!0,get:function(){return this.options.distortion},set:function(t){if(s.Util.isInRange(t,.2,50)){this.options.distortion=parseFloat(t,10);for(var e=[this.vInDiode1,this.vInDiode2,this.vcDiode3,this.vcDiode4],i=0,n=e.length;n>i;i++)e[i].setDistortion(t)}}}}),o.Effects.PitchShifter=function(t){this.options={},t=t||this.options;var e={pitch:.5,grainSize:256,mix:1};this.inputNode=o.context.createGain(),this.outputNode=o.context.createGain(),this.wetGainNode=o.context.createGain(),this.dryGainNode=o.context.createGain(),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode);for(var i in e)this[i]=t[i],this[i]=void 0===this[i]||null===this[i]?e[i]:this[i]},o.Effects.PitchShifter.prototype=Object.create(null,{grainSize:{enumberable:!0,get:function(){return this.options.grainSize},set:function(t){this.options.grainSize=t,this.hannWindow=new Float32Array(t);for(var e=0;e<this.hannWindow.length;e++)this.hannWindow[e]=.5*(1-Math.cos(2*Math.PI*e/(t-1)));this.buffer=new Float32Array(2*this.grainSize),this.processorNode&&this.processorNode.disconnect(),this.processorNode=s.context.createScriptProcessor(this.grainSize,1,1),this.processorNode.onaudioprocess=n.bind(this),this.wetGainNode.disconnect(),this.inputNode.connect(this.processorNode),this.processorNode.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode)}},mix:{enumberable:!0,get:function(){return this.options.mix},set:function(t){s.Util.isInRange(t,0,1)&&(this.options.mix=t,this.dryGainNode.gain.value=o.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=o.Util.getWetLevel(this.mix))}}}),o}("undefined"!=typeof window?window:global); | ||
!function(e){"use strict";function t(e,t){this.options={},e=e||this.options;var i={frequency:350,peak:1};this.inputNode=this.filterNode=o.context.createBiquadFilter(),this.filterNode.type=t,this.outputNode=n.context.createGain(),this.filterNode.connect(this.outputNode);for(var s in i)this[s]=e[s],this[s]=void 0===this[s]||null===this[s]?i[s]:this[s]}function i(){var e,t,i=o.context.sampleRate*this.time,s=n.context.createBuffer(2,i,o.context.sampleRate),a=s.getChannelData(0),r=s.getChannelData(1);for(t=0;i>t;t++)e=this.reverse?i-t:t,a[t]=(2*Math.random()-1)*Math.pow(1-e/i,this.decay),r[t]=(2*Math.random()-1)*Math.pow(1-e/i,this.decay);this.reverbNode.buffer=s}var n={},o=n,s="object"==typeof module&&module.exports,a="function"==typeof define&&define.amd;s?module.exports=n:a?define([],n):e.Pizzicato=e.Pz=n;var r=e.AudioContext||e.webkitAudioContext;if(!r)return void console.error("No AudioContext found in this environment. Please ensure your window or global object contains a working AudioContext constructor function.");n.context=new r;var c=n.context.createGain();c.connect(n.context.destination),n.Util={isString:function(e){return"[object String]"===toString.call(e)},isObject:function(e){return"[object Object]"===toString.call(e)},isFunction:function(e){return"[object Function]"===toString.call(e)},isNumber:function(e){return"[object Number]"===toString.call(e)&&e===+e},isArray:function(e){return"[object Array]"===toString.call(e)},isInRange:function(e,t,i){return o.Util.isNumber(e)&&o.Util.isNumber(t)&&o.Util.isNumber(i)?e>=t&&i>=e:!1},isBool:function(e){return"boolean"==typeof e},isOscillator:function(e){return e&&"[object OscillatorNode]"===e.toString()},isEffect:function(e){for(var t in n.Effects)if(e instanceof n.Effects[t])return!0;return!1},normalize:function(e,t,i){return o.Util.isNumber(e)&&o.Util.isNumber(t)&&o.Util.isNumber(i)?(i-t)*e/1+t:void 0},getDryLevel:function(e){return!o.Util.isNumber(e)||e>1||0>e?0:.5>=e?1:1-2*(e-.5)},getWetLevel:function(e){return!o.Util.isNumber(e)||e>1||0>e?0:e>=.5?1:1-2*(.5-e)}};var h=n.context.createGain(),u=Object.getPrototypeOf(Object.getPrototypeOf(h)),d=u.connect;u.connect=function(e){var t=o.Util.isEffect(e)?e.inputNode:e;return d.call(this,t),e},Object.defineProperty(n,"volume",{enumerable:!0,get:function(){return c.gain.value},set:function(e){o.Util.isInRange(e,0,1)&&c&&(c.gain.value=e)}}),Object.defineProperty(n,"masterGainNode",{enumerable:!1,get:function(){return c},set:function(e){console.error("Can't set the master gain node")}}),n.Events={on:function(e,t,i){if(e&&t){this._events=this._events||{};var n=this._events[e]||(this._events[e]=[]);n.push({callback:t,context:i||this,handler:this})}},trigger:function(e){if(e){var t,i,n,o;if(this._events=this._events||{},t=this._events[e]||(this._events[e]=[])){for(i=Math.max(0,arguments.length-1),n=[],o=0;i>o;o++)n[o]=arguments[o+1];for(o=0;o<t.length;o++)t[o].callback.apply(t[o].context,n)}}},off:function(e){e?this._events[e]=void 0:this._events={}}},n.Sound=function(e,t){function i(e){var t=["wave","file","input","script","sound"];if(e&&!d.isFunction(e)&&!d.isString(e)&&!d.isObject(e))return"Description type not supported. Initialize a sound using an object, a function or a string.";if(d.isObject(e)){if(!d.isString(e.source)||-1===t.indexOf(e.source))return"Specified source not supported. Sources can be wave, file, input or script";if(!("file"!==e.source||e.options&&e.options.path))return"A path is needed for sounds with a file source";if(!("script"!==e.source||e.options&&e.options.audioFunction))return"An audio function is needed for sounds with a script source"}}function s(e,t){e=e||{},this.getRawSourceNode=function(){var t=this.sourceNode?this.sourceNode.frequency.value:e.frequency,i=n.context.createOscillator();return i.type=e.type||"sine",i.frequency.value=t||440,i},this.sourceNode=this.getRawSourceNode(),d.isFunction(t)&&t()}function a(e,t){e=d.isArray(e)?e:[e];var i=new XMLHttpRequest;i.open("GET",e[0],!0),i.responseType="arraybuffer",i.onload=function(i){n.context.decodeAudioData(i.target.response,function(e){u.getRawSourceNode=function(){var t=n.context.createBufferSource();return t.loop=this.loop,t.buffer=e,t},d.isFunction(t)&&t()}.bind(u),function(i){return console.error("Error decoding audio file "+e[0]),e.length>1?(e.shift(),void a(e,t)):(i=i||new Error("Error decoding audio file "+e[0]),void(d.isFunction(t)&&t(i)))}.bind(u))},i.onreadystatechange=function(t){4===i.readyState&&200!==i.status&&console.error("Error while fetching "+e[0]+". "+i.statusText)},i.send()}function r(e,t){navigator.getUserMedia=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia,navigator.getUserMedia&&navigator.getUserMedia({audio:!0},function(e){u.getRawSourceNode=function(){return n.context.createMediaStreamSource(e)},d.isFunction(t)&&t()}.bind(u),function(e){d.isFunction(t)&&t(e)})}function c(e,t){var i=d.isFunction(e)?e:e.audioFunction,o=d.isObject(e)&&e.bufferSize?e.bufferSize:null;if(!o)try{n.context.createScriptProcessor()}catch(s){o=2048}this.getRawSourceNode=function(){var e=n.context.createScriptProcessor(o,1,1);return e.onaudioprocess=i,e}}function h(e,t){this.getRawSourceNode=e.sound.getRawSourceNode,e.sound.sourceNode&&o.Util.isOscillator(e.sound.sourceNode)&&(this.sourceNode=this.getRawSourceNode(),this.frequency=e.sound.frequency)}var u=this,d=n.Util,l=i(e),f=d.isObject(e)&&d.isObject(e.options),p=.04,v=.04;if(l)throw console.error(l),new Error("Error initializing Pizzicato Sound: "+l);this.masterVolume=n.context.createGain(),this.fadeNode=n.context.createGain(),f&&e.options.detached||this.masterVolume.connect(n.masterGainNode),this.lastTimePlayed=0,this.effects=[],this.playing=this.paused=!1,this.loop=f&&e.options.loop,this.attack=f&&d.isNumber(e.options.attack)?e.options.attack:p,this.volume=f&&d.isNumber(e.options.volume)?e.options.volume:1,f&&d.isNumber(e.options.release)?this.release=e.options.release:f&&d.isNumber(e.options.sustain)?(console.warn("'sustain' is deprecated. Use 'release' instead."),this.release=e.options.sustain):this.release=v,e?d.isString(e)?a.bind(this)(e,t):d.isFunction(e)?c.bind(this)(e,t):"file"===e.source?a.bind(this)(e.options.path,t):"wave"===e.source?s.bind(this)(e.options,t):"input"===e.source?r.bind(this)(e,t):"script"===e.source?c.bind(this)(e.options,t):"sound"===e.source&&h.bind(this)(e.options,t):s.bind(this)({},t)},n.Sound.prototype=Object.create(n.Events,{play:{enumerable:!0,value:function(e,t){this.playing||(o.Util.isNumber(t)||(t=this.offsetTime||0),o.Util.isNumber(e)||(e=0),this.playing=!0,this.paused=!1,this.sourceNode=this.getSourceNode(),this.applyAttack(),o.Util.isFunction(this.sourceNode.start)&&(this.lastTimePlayed=n.context.currentTime-t,this.sourceNode.start(o.context.currentTime+e,t)),this.trigger("play"))}},stop:{enumerable:!0,value:function(){(this.paused||this.playing)&&(this.paused=this.playing=!1,this.stopWithRelease(),this.offsetTime=0,this.trigger("stop"))}},pause:{enumerable:!0,value:function(){if(!this.paused&&this.playing){this.paused=!0,this.playing=!1,this.stopWithRelease();var e=o.context.currentTime-this.lastTimePlayed;this.sourceNode.buffer?this.offsetTime=e%(this.sourceNode.buffer.length/o.context.sampleRate):this.offsetTime=e,this.trigger("pause")}}},clone:{enumerable:!0,value:function(){for(var e=new n.Sound({source:"sound",options:{loop:this.loop,attack:this.attack,release:this.release,volume:this.volume,sound:this}}),t=0;t<this.effects.length;t++)e.addEffect(this.effects[t]);return e}},onEnded:{enumerable:!0,value:function(){this.playing&&this.stop(),this.paused||this.trigger("end")}},addEffect:{enumerable:!0,value:function(e){return e&&o.Util.isEffect(e)?(this.effects.push(e),this.connectEffects(),void(this.sourceNode&&(this.fadeNode.disconnect(),this.fadeNode.connect(this.getInputNode())))):void console.warn("Invalid effect.")}},removeEffect:{enumerable:!0,value:function(e){var t=this.effects.indexOf(e);if(-1===t)return void console.warn("Cannot remove effect that is not applied to this sound.");var i=this.playing;i&&this.pause(),this.fadeNode.disconnect();for(var n=0;n<this.effects.length;n++)this.effects[n].outputNode.disconnect();this.effects.splice(t,1),this.connectEffects(),i&&this.play()}},connect:{enumerable:!0,value:function(e){this.masterVolume.connect(e)}},disconnect:{enumerable:!0,value:function(e){this.masterVolume.disconnect(e)}},connectEffects:{enumerable:!0,value:function(){for(var e=0;e<this.effects.length;e++){var t=e===this.effects.length-1,i=t?this.masterVolume:this.effects[e+1].inputNode;this.effects[e].outputNode.disconnect(),this.effects[e].outputNode.connect(i)}}},volume:{enumerable:!0,get:function(){return this.masterVolume?this.masterVolume.gain.value:void 0},set:function(e){o.Util.isInRange(e,0,1)&&this.masterVolume&&(this.masterVolume.gain.value=e)}},frequency:{enumerable:!0,get:function(){return this.sourceNode&&o.Util.isOscillator(this.sourceNode)?this.sourceNode.frequency.value:null},set:function(e){this.sourceNode&&o.Util.isOscillator(this.sourceNode)&&(this.sourceNode.frequency.value=e)}},sustain:{enumerable:!0,get:function(){return console.warn("'sustain' is deprecated. Use 'release' instead."),this.release},set:function(e){console.warn("'sustain' is deprecated. Use 'release' instead."),o.Util.isInRange(e,0,10)&&(this.release=e)}},getSourceNode:{enumerable:!0,value:function(){this.sourceNode&&this.sourceNode.disconnect();var e=this.getRawSourceNode();return e.connect(this.fadeNode),e.onended=this.onEnded.bind(this),this.fadeNode.connect(this.getInputNode()),e}},getInputNode:{enumerable:!0,value:function(){return this.effects.length>0?this.effects[0].inputNode:this.masterVolume}},getAnalyser:{enumerable:!0,value:function(){return console.warn("This method is deprecated. You should manually create an AnalyserNode and use connect() on the Pizzicato Sound."),this.analyser?this.analyser:(this.analyser=n.context.createAnalyser(),this.masterVolume.disconnect(),this.masterVolume.connect(this.analyser),this.analyser.connect(n.masterGainNode),this.analyser)}},applyAttack:{enumerable:!1,value:function(){this.attack&&(this.fadeNode.gain.setValueAtTime(1e-5,n.context.currentTime),this.fadeNode.gain.linearRampToValueAtTime(1,n.context.currentTime+this.attack))}},stopWithRelease:{enumerable:!1,value:function(e){var t=this.sourceNode,i=function(){return o.Util.isFunction(t.stop)?t.stop(0):t.disconnect()};this.release||i(),this.fadeNode.gain.setValueAtTime(this.fadeNode.gain.value,n.context.currentTime),this.fadeNode.gain.linearRampToValueAtTime(1e-5,n.context.currentTime+this.release),window.setTimeout(function(){i()},1e3*this.release)}}}),n.Effects={};var l=Object.create(null,{connect:{enumerable:!0,value:function(e){this.outputNode.connect(e)}},disconnect:{enumerable:!0,value:function(e){this.outputNode.disconnect(e)}}});n.Effects.Delay=function(e){this.options={},e=e||this.options;var t={feedback:.5,time:.3,mix:.5};this.inputNode=n.context.createGain(),this.outputNode=n.context.createGain(),this.dryGainNode=n.context.createGain(),this.wetGainNode=n.context.createGain(),this.feedbackGainNode=n.context.createGain(),this.delayNode=n.context.createDelay(),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.delayNode.connect(this.feedbackGainNode),this.feedbackGainNode.connect(this.delayNode),this.inputNode.connect(this.delayNode),this.delayNode.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]},n.Effects.Delay.prototype=Object.create(l,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.mix=e,this.dryGainNode.gain.value=n.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=n.Util.getWetLevel(this.mix))}},time:{enumerable:!0,get:function(){return this.options.time},set:function(e){o.Util.isInRange(e,0,180)&&(this.options.time=e,this.delayNode.delayTime.value=e)}},feedback:{enumerable:!0,get:function(){return this.options.feedback},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.feedback=parseFloat(e,10),this.feedbackGainNode.gain.value=this.feedback)}}}),n.Effects.Compressor=function(e){this.options={},e=e||this.options;var t={threshold:-24,knee:30,attack:.003,release:.25,ratio:12};this.inputNode=this.compressorNode=n.context.createDynamicsCompressor(),this.outputNode=n.context.createGain(),this.compressorNode.connect(this.outputNode);for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]},n.Effects.Compressor.prototype=Object.create(l,{threshold:{enumerable:!0,get:function(){return this.compressorNode.threshold.value},set:function(e){n.Util.isInRange(e,-100,0)&&(this.compressorNode.threshold.value=e)}},knee:{enumerable:!0,get:function(){return this.compressorNode.knee.value},set:function(e){n.Util.isInRange(e,0,40)&&(this.compressorNode.knee.value=e)}},attack:{enumerable:!0,get:function(){return this.compressorNode.attack.value},set:function(e){n.Util.isInRange(e,0,1)&&(this.compressorNode.attack.value=e)}},release:{enumerable:!0,get:function(){return this.compressorNode.release.value},set:function(e){n.Util.isInRange(e,0,1)&&(this.compressorNode.release.value=e)}},ratio:{enumerable:!0,get:function(){return this.compressorNode.ratio.value},set:function(e){n.Util.isInRange(e,1,20)&&(this.compressorNode.ratio.value=e)}},getCurrentGainReduction:function(){return this.compressorNode.reduction}}),n.Effects.LowPassFilter=function(e){t.call(this,e,"lowpass")},n.Effects.HighPassFilter=function(e){t.call(this,e,"highpass")};var f=Object.create(l,{frequency:{enumerable:!0,get:function(){return this.filterNode.frequency.value},set:function(e){n.Util.isInRange(e,10,22050)&&(this.filterNode.frequency.value=e)}},peak:{enumerable:!0,get:function(){return this.filterNode.Q.value},set:function(e){n.Util.isInRange(e,1e-4,1e3)&&(this.filterNode.Q.value=e)}}});n.Effects.LowPassFilter.prototype=f,n.Effects.HighPassFilter.prototype=f,n.Effects.Distortion=function(e){this.options={},e=e||this.options;var t={gain:.5};this.waveShaperNode=n.context.createWaveShaper(),this.inputNode=this.outputNode=this.waveShaperNode;for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]},n.Effects.Distortion.prototype=Object.create(l,{gain:{enumerable:!0,get:function(){return this.options.gain},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.gain=e,this.adjustGain())}},adjustGain:{writable:!1,configurable:!1,enumerable:!1,value:function(){for(var e,t=o.Util.isNumber(this.options.gain)?parseInt(100*this.options.gain,10):50,i=44100,n=new Float32Array(i),s=Math.PI/180,a=0;i>a;++a)e=2*a/i-1,n[a]=(3+t)*e*20*s/(Math.PI+t*Math.abs(e));this.waveShaperNode.curve=n}}}),n.Effects.Flanger=function(e){this.options={},e=e||this.options;var t={time:.45,speed:.2,depth:.1,feedback:.1,mix:.5};this.inputNode=n.context.createGain(),this.outputNode=n.context.createGain(),this.inputFeedbackNode=n.context.createGain(),this.wetGainNode=n.context.createGain(),this.dryGainNode=n.context.createGain(),this.delayNode=n.context.createDelay(),this.oscillatorNode=n.context.createOscillator(),this.gainNode=n.context.createGain(),this.feedbackNode=n.context.createGain(),this.oscillatorNode.type="sine",this.inputNode.connect(this.inputFeedbackNode),this.inputNode.connect(this.dryGainNode),this.inputFeedbackNode.connect(this.delayNode),this.inputFeedbackNode.connect(this.wetGainNode),this.delayNode.connect(this.wetGainNode),this.delayNode.connect(this.feedbackNode),this.feedbackNode.connect(this.inputFeedbackNode),this.oscillatorNode.connect(this.gainNode),this.gainNode.connect(this.delayNode.delayTime),this.dryGainNode.connect(this.outputNode),this.wetGainNode.connect(this.outputNode),this.oscillatorNode.start(0);for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]},n.Effects.Flanger.prototype=Object.create(l,{time:{enumberable:!0,get:function(){return this.options.time},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.time=e,this.delayNode.delayTime.value=o.Util.normalize(e,.001,.02))}},speed:{enumberable:!0,get:function(){return this.options.speed},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.speed=e,this.oscillatorNode.frequency.value=o.Util.normalize(e,.5,5))}},depth:{enumberable:!0,get:function(){return this.options.depth},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.depth=e,this.gainNode.gain.value=o.Util.normalize(e,5e-4,.005))}},feedback:{enumberable:!0,get:function(){return this.options.feedback},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.feedback=e,this.feedbackNode.gain.value=o.Util.normalize(e,0,.8))}},mix:{enumberable:!0,get:function(){return this.options.mix},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.mix=e,this.dryGainNode.gain.value=n.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=n.Util.getWetLevel(this.mix))}}}),n.Effects.StereoPanner=function(e){this.options={},e=e||this.options;var t={pan:0};this.inputNode=n.context.createGain(),this.outputNode=n.context.createGain(),n.context.createStereoPanner?(this.pannerNode=n.context.createStereoPanner(),this.inputNode.connect(this.pannerNode),this.pannerNode.connect(this.outputNode)):this.inputNode.connect(this.outputNode);for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]},n.Effects.StereoPanner.prototype=Object.create(l,{pan:{enumerable:!0,get:function(){return this.options.pan},set:function(e){o.Util.isInRange(e,-1,1)&&(this.options.pan=e,this.pannerNode&&(this.pannerNode.pan.value=e))}}}),n.Effects.Convolver=function(e,t){this.options={},e=e||this.options;var i=this,s=new XMLHttpRequest,a={mix:.5};this.callback=t,this.inputNode=n.context.createGain(),this.convolverNode=n.context.createConvolver(),this.outputNode=n.context.createGain(),this.wetGainNode=n.context.createGain(),this.dryGainNode=n.context.createGain(),this.inputNode.connect(this.convolverNode),this.convolverNode.connect(this.wetGainNode),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.wetGainNode.connect(this.outputNode);for(var r in a)this[r]=e[r],this[r]=void 0===this[r]||null===this[r]?a[r]:this[r];return e.impulse?(s.open("GET",e.impulse,!0),s.responseType="arraybuffer",s.onload=function(e){var t=e.target.response;n.context.decodeAudioData(t,function(e){i.convolverNode.buffer=e,i.callback&&o.Util.isFunction(i.callback)&&i.callback()},function(e){e=e||new Error("Error decoding impulse file"),i.callback&&o.Util.isFunction(i.callback)&&i.callback(e)})},s.onreadystatechange=function(t){4===s.readyState&&200!==s.status&&console.error("Error while fetching "+e.impulse+". "+s.statusText)},void s.send()):void console.error("No impulse file specified.")},n.Effects.Convolver.prototype=Object.create(l,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.mix=e,this.dryGainNode.gain.value=n.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=n.Util.getWetLevel(this.mix))}}}),n.Effects.PingPongDelay=function(e){this.options={},e=e||this.options;var t={feedback:.5,time:.3,mix:.5};this.inputNode=n.context.createGain(),this.outputNode=n.context.createGain(),this.delayNodeLeft=n.context.createDelay(),this.delayNodeRight=n.context.createDelay(),this.dryGainNode=n.context.createGain(),this.wetGainNode=n.context.createGain(),this.feedbackGainNode=n.context.createGain(),this.channelMerger=n.context.createChannelMerger(2),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.delayNodeLeft.connect(this.channelMerger,0,0),this.delayNodeRight.connect(this.channelMerger,0,1),this.delayNodeLeft.connect(this.delayNodeRight),this.feedbackGainNode.connect(this.delayNodeLeft),this.delayNodeRight.connect(this.feedbackGainNode),this.inputNode.connect(this.feedbackGainNode),this.channelMerger.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]},n.Effects.PingPongDelay.prototype=Object.create(l,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.mix=e,this.dryGainNode.gain.value=n.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=n.Util.getWetLevel(this.mix))}},time:{enumerable:!0,get:function(){return this.options.time},set:function(e){o.Util.isInRange(e,0,180)&&(this.options.time=e,this.delayNodeLeft.delayTime.value=e,this.delayNodeRight.delayTime.value=e)}},feedback:{enumerable:!0,get:function(){return this.options.feedback},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.feedback=parseFloat(e,10),this.feedbackGainNode.gain.value=this.feedback)}}}),n.Effects.Reverb=function(e){this.options={},e=e||this.options;var t={mix:.5,time:.01,decay:.01,reverse:!1};this.inputNode=n.context.createGain(),this.reverbNode=n.context.createConvolver(),this.outputNode=n.context.createGain(),this.wetGainNode=n.context.createGain(),this.dryGainNode=n.context.createGain(),this.inputNode.connect(this.reverbNode),this.reverbNode.connect(this.wetGainNode),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.wetGainNode.connect(this.outputNode);for(var o in t)this[o]=e[o],this[o]=void 0===this[o]||null===this[o]?t[o]:this[o];i.bind(this)()},n.Effects.Reverb.prototype=Object.create(l,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.mix=e,this.dryGainNode.gain.value=n.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=n.Util.getWetLevel(this.mix))}},time:{enumerable:!0,get:function(){return this.options.time},set:function(e){o.Util.isInRange(e,1e-4,10)&&(this.options.time=e,i.bind(this)())}},decay:{enumerable:!0,get:function(){return this.options.decay},set:function(e){o.Util.isInRange(e,1e-4,10)&&(this.options.decay=e,i.bind(this)())}},reverse:{enumerable:!0,get:function(){return this.options.reverse},set:function(e){o.Util.isBool(e)&&(this.options.reverse=e,i.bind(this)())}}}),n.Effects.Tremolo=function(e){this.options={},e=e||this.options;var t={speed:4,depth:1,mix:.8};this.inputNode=n.context.createGain(),this.outputNode=n.context.createGain(),this.dryGainNode=n.context.createGain(),this.wetGainNode=n.context.createGain(),this.tremoloGainNode=n.context.createGain(),this.tremoloGainNode.gain.value=0,this.lfoNode=n.context.createOscillator(),this.shaperNode=n.context.createWaveShaper(),this.shaperNode.curve=new Float32Array([0,1]),this.shaperNode.connect(this.tremoloGainNode.gain),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.lfoNode.connect(this.shaperNode),this.lfoNode.type="sine",this.lfoNode.start(0),this.inputNode.connect(this.tremoloGainNode),this.tremoloGainNode.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]},n.Effects.Tremolo.prototype=Object.create(l,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.mix=e,this.dryGainNode.gain.value=n.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=n.Util.getWetLevel(this.mix))}},speed:{enumerable:!0,get:function(){return this.options.speed},set:function(e){o.Util.isInRange(e,0,20)&&(this.options.speed=e,this.lfoNode.frequency.value=e)}},depth:{enumerable:!0,get:function(){return this.options.depth},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.depth=e,this.shaperNode.curve=new Float32Array([1-e,1]))}}}),n.Effects.DubDelay=function(e){this.options={},e=e||this.options;var t={feedback:.6,time:.7,mix:.5,cutoff:700};this.inputNode=n.context.createGain(),this.outputNode=n.context.createGain(),this.dryGainNode=n.context.createGain(),this.wetGainNode=n.context.createGain(),this.feedbackGainNode=n.context.createGain(),this.delayNode=n.context.createDelay(),this.bqFilterNode=n.context.createBiquadFilter(),this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.inputNode.connect(this.wetGainNode),this.inputNode.connect(this.feedbackGainNode),this.feedbackGainNode.connect(this.bqFilterNode),this.bqFilterNode.connect(this.delayNode),this.delayNode.connect(this.feedbackGainNode),this.delayNode.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]},n.Effects.DubDelay.prototype=Object.create(l,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.mix=e,this.dryGainNode.gain.value=n.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=n.Util.getWetLevel(this.mix))}},time:{enumerable:!0,get:function(){return this.options.time},set:function(e){o.Util.isInRange(e,0,180)&&(this.options.time=e,this.delayNode.delayTime.value=e)}},feedback:{enumerable:!0,get:function(){return this.options.feedback},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.feedback=parseFloat(e,10),this.feedbackGainNode.gain.value=this.feedback)}},cutoff:{enumerable:!0,get:function(){return this.options.cutoff},set:function(e){o.Util.isInRange(e,0,4e3)&&(this.options.cutoff=e,this.bqFilterNode.frequency.value=this.cutoff)}}}),n.Effects.RingModulator=function(e){this.options={},e=e||this.options;var t={speed:30,distortion:1,mix:.5};this.inputNode=n.context.createGain(),this.outputNode=n.context.createGain(),this.dryGainNode=n.context.createGain(),this.wetGainNode=n.context.createGain(),this.vIn=n.context.createOscillator(),this.vIn.start(0),this.vInGain=n.context.createGain(),this.vInGain.gain.value=.5,this.vInInverter1=n.context.createGain(),this.vInInverter1.gain.value=-1,this.vInInverter2=n.context.createGain(),this.vInInverter2.gain.value=-1,this.vInDiode1=new p(n.context),this.vInDiode2=new p(n.context),this.vInInverter3=n.context.createGain(),this.vInInverter3.gain.value=-1,this.vcInverter1=n.context.createGain(),this.vcInverter1.gain.value=-1,this.vcDiode3=new p(n.context),this.vcDiode4=new p(n.context),this.outGain=n.context.createGain(),this.outGain.gain.value=3,this.compressor=n.context.createDynamicsCompressor(),this.compressor.threshold.value=-24,this.compressor.ratio.value=16,this.inputNode.connect(this.dryGainNode),this.dryGainNode.connect(this.outputNode),this.inputNode.connect(this.vcInverter1),this.inputNode.connect(this.vcDiode4.node),this.vcInverter1.connect(this.vcDiode3.node),this.vIn.connect(this.vInGain),this.vInGain.connect(this.vInInverter1),this.vInGain.connect(this.vcInverter1),this.vInGain.connect(this.vcDiode4.node),this.vInInverter1.connect(this.vInInverter2),this.vInInverter1.connect(this.vInDiode2.node),this.vInInverter2.connect(this.vInDiode1.node),this.vInDiode1.connect(this.vInInverter3),this.vInDiode2.connect(this.vInInverter3),this.vInInverter3.connect(this.compressor),this.vcDiode3.connect(this.compressor),this.vcDiode4.connect(this.compressor),this.compressor.connect(this.outGain),this.outGain.connect(this.wetGainNode),this.wetGainNode.connect(this.outputNode);for(var i in t)this[i]=e[i],this[i]=void 0===this[i]||null===this[i]?t[i]:this[i]};var p=function(e){this.context=e,this.node=this.context.createWaveShaper(),this.vb=.2,this.vl=.4,this.h=1,this.setCurve()};return p.prototype.setDistortion=function(e){return this.h=e,this.setCurve()},p.prototype.setCurve=function(){var e,t,i,n,o,s,a,r;for(t=1024,o=new Float32Array(t),e=s=0,a=o.length;a>=0?a>s:s>a;e=a>=0?++s:--s)i=(e-t/2)/(t/2),i=Math.abs(i),n=i<=this.vb?0:this.vb<i&&i<=this.vl?this.h*(Math.pow(i-this.vb,2)/(2*this.vl-2*this.vb)):this.h*i-this.h*this.vl+this.h*(Math.pow(this.vl-this.vb,2)/(2*this.vl-2*this.vb)),o[e]=n;return r=this.node.curve=o},p.prototype.connect=function(e){return this.node.connect(e)},n.Effects.RingModulator.prototype=Object.create(l,{mix:{enumerable:!0,get:function(){return this.options.mix},set:function(e){o.Util.isInRange(e,0,1)&&(this.options.mix=e,this.dryGainNode.gain.value=n.Util.getDryLevel(this.mix),this.wetGainNode.gain.value=n.Util.getWetLevel(this.mix))}},speed:{enumerable:!0,get:function(){return this.options.speed},set:function(e){o.Util.isInRange(e,0,2e3)&&(this.options.speed=e,this.vIn.frequency.value=e)}},distortion:{enumerable:!0,get:function(){return this.options.distortion},set:function(e){if(o.Util.isInRange(e,.2,50)){this.options.distortion=parseFloat(e,10);for(var t=[this.vInDiode1,this.vInDiode2,this.vcDiode3,this.vcDiode4],i=0,n=t.length;n>i;i++)t[i].setDistortion(e)}}}}),n}("undefined"!=typeof window?window:global); |
{ | ||
"name": "pizzicato", | ||
"description": "A web-audio library to simplify using and manipulating sounds.", | ||
"version": "0.3.2", | ||
"version": "0.4.0", | ||
"license": "MIT", | ||
"homepage": "http://alemangui.github.io/pizzicato/", | ||
"homepage": "https://alemangui.github.io/pizzicato/", | ||
"author": { | ||
"name": "Alejandro Mantecon Guillen", | ||
"email": "alemangui@gmail.com", | ||
"url": "http://alemangui.com/" | ||
"url": "https://alemangui.github.io/" | ||
}, | ||
@@ -12,0 +12,0 @@ "repository": { |
120
README.md
<img align="center" src="https://alemangui.github.io/pizzicato/img/horizontal-logo.svg" alt="Pizzicato.js"> | ||
[![Build Status](https://travis-ci.org/alemangui/pizzicato.svg?branch=master)](https://travis-ci.org/alemangui/pizzicato) | ||
[![Build Status](https://travis-ci.org/alemangui/pizzicato.svg?branch=master)](https://travis-ci.org/alemangui/pizzicato) [![npm](https://img.shields.io/npm/v/pizzicato.svg?maxAge=2592000)](https://www.npmjs.com/package/pizzicato) [![Bower](https://img.shields.io/bower/v/pizzicato.svg?maxAge=2592000)]() | ||
@@ -26,4 +26,5 @@ ##A Web Audio library | ||
- [attack](#sounds-attack) | ||
- [sustain](#sounds-sustain) | ||
- [release](#sounds-release) | ||
- [frequency](#sounds-frequency) | ||
- [Connecting sounds to AudioNodes](#sounds-connect) | ||
- [Effects](#effects) | ||
@@ -42,5 +43,7 @@ - [Delay](#delay) | ||
- [Ring Modulator](#ring-modulator) | ||
- [Tremolo](#tremolo) | ||
- [Connecting effects to and from AudioNodes](#effects-connect) | ||
- [Advanced](#advanced) | ||
- [Accessing the audio context](#accessing-the-context) | ||
- [Getting an analyser node for a sound](#analyser-node) | ||
- [Using Pizzicato objects in a web audio graph](#using-graph) | ||
- [General volume](#general-volume) | ||
@@ -114,3 +117,3 @@ - [Support](#support) | ||
Typically, the ```description``` object contains a string ```source``` and an object ```options```. | ||
Typically, the ```description``` object contains a string ```source``` and an object ```options```. The ```options``` object varies depending on the source of the sound being created. | ||
@@ -136,4 +139,5 @@ For example, this objects describes a sine waveform with a frequency of 440: | ||
* ```volume``` _(Optional; min: 0, max: 1, defaults to 1)_: Loudness of the sound. | ||
* ```sustain``` _(Optional; defaults to 0.4)_: Value in seconds that indicates the fade-out time when the sound is stopped. | ||
* ```release``` _(Optional; defaults to 0.4)_: Value in seconds that indicates the fade-out time when the sound is stopped. | ||
* ```attack``` _(Optional; defaults to 0.4)_: Value in seconds that indicates the fade-in time when the sound is played. | ||
* ```detached``` _(Optional; defaults to false)_: If true, the sound will not be connected to the context's destination, and thus, will not be audible. | ||
@@ -159,4 +163,5 @@ ```javascript | ||
* ```volume``` _(Optional; min: 0, max: 1, defaults to 1)_: Loudness of the sound. | ||
* ```sustain``` _(Optional; defaults to 0)_: Value in seconds that indicates the fade-out time once the sound is stopped. | ||
* ```release``` _(Optional; defaults to 0)_: Value in seconds that indicates the fade-out time once the sound is stopped. | ||
* ```attack``` _(Optional; defaults to 0.4)_: Value in seconds that indicates the fade-in time when the sound is played. | ||
* ```detached``` _(Optional; defaults to false)_: If true, the sound will not be connected to the context's destination, and thus, will not be audible. | ||
@@ -191,4 +196,5 @@ ```javascript | ||
* ```volume``` _(Optional; min: 0, max: 1, defaults to 1)_: Loudness of the sound. | ||
* ```sustain``` _(Optional; defaults to 0)_: Value in seconds that indicates the fade-out time once the sound is stopped. | ||
* ```release``` _(Optional; defaults to 0)_: Value in seconds that indicates the fade-out time once the sound is stopped. | ||
* ```attack``` _(Optional; defaults to 0.4)_: Value in seconds that indicates the fade-in time when the sound is played. | ||
* ```detached``` _(Optional; defaults to false)_: If true, the sound will not be connected to the context's destination, and thus, will not be audible. | ||
@@ -208,4 +214,5 @@ ```javascript | ||
* ```volume``` _(Optional; min: 0, max: 1, defaults to 1)_: Loudness of the sound. | ||
* ```sustain``` _(Optional; defaults to 0)_: Value in seconds that indicates the fade-out time once the sound is stopped. | ||
* ```release``` _(Optional; defaults to 0)_: Value in seconds that indicates the fade-out time once the sound is stopped. | ||
* ```attack``` _(Optional; defaults to 0.4)_: Value in seconds that indicates the fade-in time when the sound is played. | ||
* ```detached``` _(Optional; defaults to false)_: If true, the sound will not be connected to the context's destination, and thus, will not be audible. | ||
@@ -314,3 +321,3 @@ For example: | ||
<a name="sounds-attack"/> | ||
### Attack ([example](https://alemangui.github.io/pizzicato/#attack-sustain)) | ||
### Attack ([example](https://alemangui.github.io/pizzicato/#attack-release)) | ||
@@ -327,8 +334,8 @@ Use the sound's ```attack``` property to modify its attack (or fade-in) value. This value eases the beginning of the sound, often avoiding unwanted clicks. | ||
<a name="sounds-sustain"/> | ||
### Sustain ([example](https://alemangui.github.io/pizzicato/#attack-sustain)) | ||
<a name="sounds-release"/> | ||
### Release ([example](https://alemangui.github.io/pizzicato/#attack-release)) | ||
Use the sound's ```sustain``` property to modify its sustain (or fade-out) value. This value eases the end of the sound, often avoiding unwanted clicks. | ||
Use the sound's ```release``` property to modify its release (or fade-out) value. This value eases the end of the sound, often avoiding unwanted clicks. | ||
* ```sustain``` _(min: 0, max: 10, defaults to 0.04)_: The sound's sustain. | ||
* ```release``` _(min: 0, max: 10, defaults to 0.04)_: The sound's release. | ||
@@ -338,3 +345,3 @@ Example: | ||
var sound = new Pizzicato.Sound(); | ||
sound.sustain = 0.9; | ||
sound.release = 0.9; | ||
``` | ||
@@ -352,5 +359,13 @@ | ||
var sound = new Pizzicato.Sound(); | ||
sound.sustain = 0.9; | ||
sound.play(); | ||
// go up an octave | ||
sound.frequency = 880; // a5 | ||
``` | ||
<a name="sounds-connect"> | ||
###Connecting sounds to AudioNodes | ||
It is possible to connect AudioNodes to sound objects by using the ```connect``` method. More details in the [advanced section of this file](#using-graph-sound). | ||
<a name="effects"/> | ||
@@ -594,3 +609,3 @@ ## Effects | ||
### Ring Modulator ([example](https://alemangui.github.io/pizzicato/#ring-modulator)) | ||
The ring modulator effect combines two input signals, where one of the inputs is a sine wave modulating the other. [This article from the BBC](http://webaudio.prototyping.bbc.co.uk/ring-modulator/) goes into deeper detail and explains how to recreate it. The 'ring' in this effect derives from the layout of diode nodes in the original analogue equipment, and also refers to the sound being increasingly modulated as it travels through the ring of diodes. | ||
The ring modulator effect combines two input signals, where one of the inputs is a sine wave modulating the other. [This article from the BBC](http://webaudio.prototyping.bbc.co.uk/ring-modulator/) - from where this effect was inspired from - goes into deeper detail and explains how to recreate it. The 'ring' in this effect derives from the layout of diode nodes in the original analogue equipment, and also refers to the sound being increasingly modulated as it travels through the ring of diodes. | ||
@@ -613,2 +628,26 @@ * ```distortion``` _(min: 0.2, max: 50, defaults to 1)_: Level of distortion applied to the diode nodes. | ||
<a name="tremolo"/> | ||
### Tremolo ([example](https://alemangui.github.io/pizzicato/#tremolo)) | ||
The tremolo effect changes the volume of the sound over time. The outcome would be similar as if you turned the volume node up and down periodically. | ||
* ```speed``` _(min: 0, max: 20, defaults to 4)_: The speed at which the volume will change. | ||
* ```depth``` _(min: 0, max: 1, defaults to 1)_: The intensity of the volume change. | ||
* ```mix``` _(min: 0, max: 1, defaults to 0.5)_: Volume balance between the original audio and the effected output. | ||
Example: | ||
```javascript | ||
var tremolo = new Pizzicato.Effects.Tremolo({ | ||
speed: 5, | ||
depth: 1, | ||
mix: 0.5 | ||
}); | ||
sound.addEffect(tremolo); | ||
sound.play(); | ||
``` | ||
<a name="effects-connect"> | ||
### Connecting effects to and from AudioNodes | ||
It is possible to connect AudioNodes to effects (and viceversa) by using the ```connect``` method. More details in the [advanced section of this file](#using-graph-effect). | ||
<a name="advanced"> | ||
@@ -624,10 +663,49 @@ ## Advanced | ||
<a name="analyser-node"> | ||
### Getting an analyser node for a sound object | ||
You can obtain an analyser node for a particular Pizzicato Sound object by using the function ```getAnalyser```: | ||
<a name="using-graph"> | ||
### Using Pizzicato objects in a web audio graph | ||
You can use effects and sounds as part of an existing web audio graph. | ||
<a name="using-graph-sound"> | ||
#### Connecting nodes to a Pizzicato.Sound object | ||
Using the ```connect``` method, you can connect audio nodes to a Pizzicato.Sound object. For example: | ||
```javascript | ||
var analyser = Pizzicato.context.createAnaliser(); | ||
var sound = new Pizzicato.Sound(); | ||
var analyser = sound.getAnalyser(); | ||
sound.connect(analyser); | ||
``` | ||
<a name="using-graph-sound-detached"> | ||
#### Creating a detached Pizzicato.Sound object | ||
All Pizzicato.Sound objects are connected to the context's destination by default. In the example above, the ```sound``` object will be connected to an analyser node and it will also remain connected to the context's destination node. | ||
To have a Pizzicato.Sound object that is not connected to the context's destination, use the ```detached``` option as follows: | ||
```javascript | ||
var analyser = Pizzicato.context.createAnaliser(); | ||
var sound = new Pizzicato.Sound({ | ||
source: wave, | ||
options: { | ||
detached: true | ||
} | ||
}); | ||
sound.connect(analyser); | ||
``` | ||
<a name="using-graph-effect"> | ||
#### Connecting nodes to effects | ||
Pizzicato effects can also be used in a web audio graph without the need to create Pizzicato.Sound objects by using the ```connect``` method. | ||
Additionally, the ```connect``` method in an AudioNode can receive a Pizzicato effect as a parameter. | ||
```javascript | ||
var oscillator = Pizzicato.context.createOscillator(); | ||
var distortion = new Pizzicato.Effects.Distortion(); | ||
var analyser = Pizzicato.context.createAnalyser(); | ||
oscillator.connect(distortion); | ||
distortion.connect(analyser); | ||
``` | ||
<a name="general-volume"> | ||
@@ -645,3 +723,3 @@ ### General volume | ||
### Browsers | ||
Pizzicato can only work in [browsers with Web Audio support](http://caniuse.com/#feat=audio-api), no shims have been added yet. This means: | ||
Pizzicato can only work in [browsers with Web Audio support](http://caniuse.com/#feat=audio-api). This means: | ||
* Firefox 31+ | ||
@@ -648,0 +726,0 @@ * Chrome 31+ |
@@ -1,1 +0,20 @@ | ||
Pizzicato.Effects = {}; | ||
Pizzicato.Effects = {}; | ||
var baseEffect = Object.create(null, { | ||
connect: { | ||
enumerable: true, | ||
value: function(audioNode) { | ||
this.outputNode.connect(audioNode); | ||
} | ||
}, | ||
disconnect: { | ||
enumerable: true, | ||
value: function(audioNode) { | ||
this.outputNode.disconnect(audioNode); | ||
} | ||
} | ||
}); |
@@ -25,3 +25,3 @@ Pizzicato.Effects.Compressor = function(options) { | ||
Pizzicato.Effects.Compressor.prototype = Object.create(null, { | ||
Pizzicato.Effects.Compressor.prototype = Object.create(baseEffect, { | ||
@@ -28,0 +28,0 @@ /** |
@@ -69,3 +69,3 @@ Pizzicato.Effects.Convolver = function(options, callback) { | ||
Pizzicato.Effects.Convolver.prototype = Object.create(null, { | ||
Pizzicato.Effects.Convolver.prototype = Object.create(baseEffect, { | ||
@@ -72,0 +72,0 @@ mix: { |
@@ -42,3 +42,3 @@ Pizzicato.Effects.Delay = function(options) { | ||
Pizzicato.Effects.Delay.prototype = Object.create(null, { | ||
Pizzicato.Effects.Delay.prototype = Object.create(baseEffect, { | ||
@@ -45,0 +45,0 @@ /** |
@@ -19,3 +19,3 @@ Pizzicato.Effects.Distortion = function(options) { | ||
Pizzicato.Effects.Distortion.prototype = Object.create(null, { | ||
Pizzicato.Effects.Distortion.prototype = Object.create(baseEffect, { | ||
@@ -22,0 +22,0 @@ /** |
@@ -43,3 +43,3 @@ Pizzicato.Effects.DubDelay = function(options) { | ||
Pizzicato.Effects.DubDelay.prototype = Object.create(null, { | ||
Pizzicato.Effects.DubDelay.prototype = Object.create(baseEffect, { | ||
@@ -46,0 +46,0 @@ /** |
@@ -44,3 +44,3 @@ /** | ||
var filterPrototype = Object.create(null, { | ||
var filterPrototype = Object.create(baseEffect, { | ||
@@ -47,0 +47,0 @@ /** |
@@ -50,3 +50,3 @@ Pizzicato.Effects.Flanger = function(options) { | ||
Pizzicato.Effects.Flanger.prototype = Object.create(null, { | ||
Pizzicato.Effects.Flanger.prototype = Object.create(baseEffect, { | ||
@@ -53,0 +53,0 @@ time: { |
@@ -50,3 +50,3 @@ /** | ||
Pizzicato.Effects.PingPongDelay.prototype = Object.create(null, { | ||
Pizzicato.Effects.PingPongDelay.prototype = Object.create(baseEffect, { | ||
@@ -53,0 +53,0 @@ /** |
@@ -71,3 +71,3 @@ Pizzicato.Effects.PitchShifter = function(options) { | ||
Pizzicato.Effects.PitchShifter.prototype = Object.create(null, { | ||
Pizzicato.Effects.PitchShifter.prototype = Object.create(baseEffect, { | ||
@@ -74,0 +74,0 @@ grainSize: { |
@@ -38,3 +38,3 @@ /** | ||
Pizzicato.Effects.Reverb.prototype = Object.create(null, { | ||
Pizzicato.Effects.Reverb.prototype = Object.create(baseEffect, { | ||
@@ -41,0 +41,0 @@ mix: { |
@@ -130,3 +130,3 @@ /** | ||
Pizzicato.Effects.RingModulator.prototype = Object.create(null, { | ||
Pizzicato.Effects.RingModulator.prototype = Object.create(baseEffect, { | ||
@@ -133,0 +133,0 @@ /** |
@@ -29,3 +29,3 @@ Pizzicato.Effects.StereoPanner = function(options) { | ||
Pizzicato.Effects.StereoPanner.prototype = Object.create(null, { | ||
Pizzicato.Effects.StereoPanner.prototype = Object.create(baseEffect, { | ||
@@ -32,0 +32,0 @@ /** |
@@ -29,2 +29,3 @@ (function(root) { | ||
//= require ./Util.js | ||
//= require ./Shims.js | ||
@@ -68,7 +69,7 @@ Object.defineProperty(Pizzicato, 'volume', { | ||
//= require ./Effects/Reverb.js | ||
//= require ./Effects/Tremolo.js | ||
//= require ./Effects/DubDelay.js | ||
//= require ./Effects/RingModulator.js | ||
//= require ./Effects/PitchShifter.js | ||
return Pizzicato; | ||
})(typeof window !== "undefined" ? window : global); |
@@ -7,3 +7,3 @@ Pizzicato.Sound = function(description, callback) { | ||
var defaultAttack = 0.04; | ||
var defaultSustain = 0.04; | ||
var defaultRelease = 0.04; | ||
@@ -16,5 +16,6 @@ if (descriptionError) { | ||
this.masterVolume = Pizzicato.context.createGain(); | ||
this.masterVolume.connect(Pizzicato.masterGainNode); | ||
this.fadeNode = Pizzicato.context.createGain(); | ||
if (!hasOptions || !description.options.detached) | ||
this.masterVolume.connect(Pizzicato.masterGainNode); | ||
@@ -26,5 +27,13 @@ this.lastTimePlayed = 0; | ||
this.attack = hasOptions && util.isNumber(description.options.attack) ? description.options.attack : defaultAttack; | ||
this.sustain = hasOptions && util.isNumber(description.options.sustain) ? description.options.sustain : defaultSustain; | ||
this.volume = hasOptions && util.isNumber(description.options.volume) ? description.options.volume : 1; | ||
if (hasOptions && util.isNumber(description.options.release)) { | ||
this.release = description.options.release; | ||
} else if (hasOptions && util.isNumber(description.options.sustain)) { | ||
console.warn('\'sustain\' is deprecated. Use \'release\' instead.'); | ||
this.release = description.options.sustain; | ||
} else { | ||
this.release = defaultRelease; | ||
} | ||
if (!description) | ||
@@ -231,3 +240,3 @@ (initializeWithWave.bind(this))({}, callback); | ||
this.paused = this.playing = false; | ||
this.stopWithSustain(); | ||
this.stopWithRelease(); | ||
@@ -250,3 +259,3 @@ this.offsetTime = 0; | ||
this.stopWithSustain(); | ||
this.stopWithRelease(); | ||
@@ -276,3 +285,3 @@ var elapsedTime = Pz.context.currentTime - this.lastTimePlayed; | ||
attack: this.attack, | ||
sustain: this.sustain, | ||
release: this.release, | ||
volume: this.volume, | ||
@@ -353,2 +362,20 @@ sound: this | ||
connect: { | ||
enumerable: true, | ||
value: function(audioNode) { | ||
this.masterVolume.connect(audioNode); | ||
} | ||
}, | ||
disconnect: { | ||
enumerable: true, | ||
value: function(audioNode) { | ||
this.masterVolume.disconnect(audioNode); | ||
} | ||
}, | ||
connectEffects: { | ||
@@ -403,3 +430,22 @@ enumerable: true, | ||
/** | ||
* @deprecated - Use "release" | ||
*/ | ||
sustain: { | ||
enumerable: true, | ||
get: function() { | ||
console.warn('\'sustain\' is deprecated. Use \'release\' instead.'); | ||
return this.release; | ||
}, | ||
set: function(sustain){ | ||
console.warn('\'sustain\' is deprecated. Use \'release\' instead.'); | ||
if (Pz.Util.isInRange(sustain, 0, 10)) | ||
this.release = sustain; | ||
} | ||
}, | ||
/** | ||
@@ -443,2 +489,4 @@ * Returns the node that produces the sound. For example, an oscillator | ||
/** | ||
* @deprecated - Use "connect" | ||
* | ||
* Returns an analyser node located right after the master volume. | ||
@@ -452,2 +500,4 @@ * This node is created lazily. | ||
console.warn('This method is deprecated. You should manually create an AnalyserNode and use connect() on the Pizzicato Sound.'); | ||
if (this.analyser) | ||
@@ -483,6 +533,6 @@ return this.analyser; | ||
* Will take the current source node and work down the volume | ||
* gradually in as much time as specified in the sustain property | ||
* gradually in as much time as specified in the release property | ||
* of the sound before stopping the source node. | ||
*/ | ||
stopWithSustain: { | ||
stopWithRelease: { | ||
enumerable: false, | ||
@@ -497,12 +547,12 @@ | ||
if (!this.sustain) | ||
if (!this.release) | ||
stopSound(); | ||
this.fadeNode.gain.setValueAtTime(this.fadeNode.gain.value, Pizzicato.context.currentTime); | ||
this.fadeNode.gain.linearRampToValueAtTime(0.00001, Pizzicato.context.currentTime + this.sustain); | ||
this.fadeNode.gain.linearRampToValueAtTime(0.00001, Pizzicato.context.currentTime + this.release); | ||
window.setTimeout(function() { | ||
stopSound(); | ||
}, this.sustain * 1000); | ||
}, this.release * 1000); | ||
} | ||
} | ||
}); |
@@ -31,6 +31,3 @@ Pizzicato.Util = { | ||
isBool: function(arg) { | ||
if (typeof(arg) !== "boolean") | ||
return false; | ||
return true; | ||
return typeof(arg) === "boolean"; | ||
}, | ||
@@ -50,4 +47,3 @@ | ||
// Takes a number from 0 to 1 and normalizes it | ||
// to fit within range floor to ceiling | ||
// Takes a number from 0 to 1 and normalizes it to fit within range floor to ceiling | ||
normalize: function(num, floor, ceil) { | ||
@@ -54,0 +50,0 @@ if (!Pz.Util.isNumber(num) || !Pz.Util.isNumber(floor) || !Pz.Util.isNumber(ceil)) |
@@ -26,2 +26,54 @@ describe('Sound', function() { | ||
it('should raise a deprecation warning if \'sustain\' is used instead of \'release\'', function() { | ||
spyOn(console, 'warn'); | ||
var sound = new Pizzicato.Sound({ | ||
source: 'wave', | ||
options: { sustain: 0.4 } | ||
}); | ||
expect(console.warn).toHaveBeenCalled(); | ||
}); | ||
describe('context\'s destination', function() { | ||
var gainNode = Pizzicato.context.createGain(); | ||
var audioNode = Object.getPrototypeOf(Object.getPrototypeOf(gainNode)); | ||
beforeAll(function() { | ||
spyOn(audioNode, 'connect'); | ||
}); | ||
beforeEach(function() { | ||
audioNode.connect.calls.reset(); | ||
}); | ||
it('should be attached by default', function() { | ||
var sound = new Pizzicato.Sound(); | ||
var spyArguments = audioNode.connect.calls.allArgs(); | ||
var containsMasterGainNode = false; | ||
for (var i = 0; i < spyArguments.length; i++) | ||
if (spyArguments[0].indexOf(Pizzicato.masterGainNode) >= 0) | ||
containsMasterGainNode = true; | ||
expect(containsMasterGainNode).toBe(true); | ||
}); | ||
it('should be detached if the detached option is specified', function() { | ||
var sound = new Pizzicato.Sound({ | ||
source: 'wave', | ||
options: { detached: true } | ||
}); | ||
var spyArguments = audioNode.connect.calls.allArgs(); | ||
var containsMasterGainNode = false; | ||
for (var i = 0; i < spyArguments.length; i++) | ||
if (spyArguments[0].indexOf(Pizzicato.masterGainNode) >= 0) | ||
containsMasterGainNode = true; | ||
expect(containsMasterGainNode).toBe(false); | ||
}); | ||
}); | ||
describe('wave source', function() { | ||
@@ -39,3 +91,2 @@ | ||
}, 5000); | ||
}); | ||
@@ -63,3 +114,2 @@ | ||
}); | ||
}); | ||
@@ -110,4 +160,3 @@ | ||
expect(navigator.mozGetUserMedia).toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); | ||
@@ -157,2 +206,11 @@ }); | ||
}); | ||
it('should raise deprecation warning', function() { | ||
spyOn(console, 'warn'); | ||
var sound = new Pizzicato.Sound(); | ||
var analyser = sound.getAnalyser(); | ||
expect(console.warn).toHaveBeenCalled(); | ||
}); | ||
}); | ||
@@ -392,2 +450,26 @@ | ||
}); | ||
describe('connectivity', function() { | ||
it('should connect audio nodes when using connect', function(done) { | ||
var analyser = Pz.context.createAnalyser(); | ||
var dataArray = new Float32Array(analyser.frequencyBinCount); | ||
var sound = new Pz.Sound(); | ||
sound.attack = 0; | ||
sound.connect(analyser); | ||
analyser.getFloatFrequencyData(dataArray); | ||
expect(dataArray[0]).toBe(analyser.minDecibels); | ||
sound.play(); | ||
setTimeout(function() { | ||
analyser.getFloatFrequencyData(dataArray); | ||
expect(dataArray[0]).not.toBe(-100); | ||
sound.stop(); | ||
done(); | ||
}, 1500); | ||
}, 5000); | ||
}); | ||
}); |
@@ -82,3 +82,2 @@ describe('Util', function() { | ||
it('contains a working isOscillator function', function() { | ||
@@ -85,0 +84,0 @@ var context = new window.AudioContext(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
713874
51
4625
719