diff --git a/Tone/signal/TimelineSignal.js b/Tone/signal/TimelineSignal.js
index a3304924..a57c63c5 100644
--- a/Tone/signal/TimelineSignal.js
+++ b/Tone/signal/TimelineSignal.js
@@ -1,5 +1,7 @@
define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function (Tone) {
+ "use strict";
+
/**
* @class A signal which adds the method getValueAtTime.
* Code and inspiration from https://github.com/jsantell/web-audio-automation-timeline
@@ -271,7 +273,7 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
* @returns {Tone.TimelineSignal} this
* @method
*/
- Tone.TimelineSignal.prototype.connect = Tone.Signal.prototype.connect;
+ Tone.TimelineSignal.prototype.connect = Tone.SignalBase.prototype.connect;
///////////////////////////////////////////////////////////////////////////
diff --git a/build/Tone.js b/build/p5.Tone.js
similarity index 71%
rename from build/Tone.js
rename to build/p5.Tone.js
index 47f17410..5610076c 100644
--- a/build/Tone.js
+++ b/build/p5.Tone.js
@@ -170,7 +170,7 @@
if (isUndef(param)) {
continue;
}
- if (param instanceof Tone.Signal) {
+ if (Tone.Signal && param instanceof Tone.Signal || Tone.Param && param instanceof Tone.Param) {
if (param.value !== value) {
if (isUndef(rampTime)) {
param.value = value;
@@ -234,7 +234,9 @@
var param = parent[attr];
if (typeof params[attr] === 'object') {
subRet[attr] = param.get();
- } else if (param instanceof Tone.Signal) {
+ } else if (Tone.Signal && param instanceof Tone.Signal) {
+ subRet[attr] = param.value;
+ } else if (Tone.Param && param instanceof Tone.Param) {
subRet[attr] = param.value;
} else if (param instanceof AudioParam) {
subRet[attr] = param.value;
@@ -313,12 +315,12 @@
*/
Tone.prototype.bufferSize = 2048;
/**
- * the delay time of a single buffer frame
+ * The delay time of a single frame (128 samples according to the spec).
* @type {number}
* @static
* @const
*/
- Tone.prototype.bufferTime = Tone.prototype.bufferSize / Tone.context.sampleRate;
+ Tone.prototype.blockTime = 128 / Tone.context.sampleRate;
///////////////////////////////////////////////////////////////////////////
// CONNECTIONS
///////////////////////////////////////////////////////////////////////////
@@ -463,27 +465,39 @@
// UTILITIES / HELPERS / MATHS
///////////////////////////////////////////////////////////////////////////
/**
- * if a the given is undefined, use the fallback.
- * if both given and fallback are objects, given
+ * If a the given is undefined, use the fallback.
+ * If both given and fallback are objects, given
* will be augmented with whatever properties it's
- * missing which are in fallback
- *
- * warning: if object is self referential, it will go into an an
- * infinite recursive loop.
+ * missing which are in fallback. It will recurse nested
+ * objects unless shallowCopy is true.
+ *
+ * WARNING: if object is self referential, it will go into an an
+ * infinite recursive loop if shallowCopy is set to true.
*
* @param {*} given
* @param {*} fallback
+ * @param {Boolean} [shallowCopy=false] Shallow copies avoid recursively
+ * accessing nested objects.
* @return {*}
*/
- Tone.prototype.defaultArg = function (given, fallback) {
- if (typeof given === 'object' && typeof fallback === 'object') {
+ Tone.prototype.defaultArg = function (given, fallback, shallowCopy) {
+ shallowCopy = isUndef(shallowCopy) ? false : shallowCopy;
+ if (typeof given === 'object' && typeof fallback === 'object' && !Array.isArray(given) && !Array.isArray(fallback)) {
var ret = {};
//make a deep copy of the given object
for (var givenProp in given) {
- ret[givenProp] = this.defaultArg(given[givenProp], given[givenProp]);
+ if (shallowCopy) {
+ ret[givenProp] = isUndef(fallback[givenProp]) ? given[givenProp] : fallback[givenProp];
+ } else {
+ ret[givenProp] = this.defaultArg(fallback[givenProp], given[givenProp]);
+ }
}
- for (var prop in fallback) {
- ret[prop] = this.defaultArg(given[prop], fallback[prop]);
+ for (var fallbackProp in fallback) {
+ if (shallowCopy) {
+ ret[fallbackProp] = isUndef(given[fallbackProp]) ? fallback[fallbackProp] : given[fallbackProp];
+ } else {
+ ret[fallbackProp] = this.defaultArg(given[fallbackProp], fallback[fallbackProp]);
+ }
}
return ret;
} else {
@@ -494,7 +508,7 @@
* returns the args as an options object with given arguments
* mapped to the names provided.
*
- * if the args given is an array containing an object, it is assumed
+ * if the args given is an array containing only one object, it is assumed
* that that's already the options object and will just return it.
*
* @param {Array} values the 'arguments' object of the function
@@ -502,11 +516,13 @@
* should appear in the options object
* @param {Object=} defaults optional defaults to mixin to the returned
* options object
+ * @param {Boolean} [shallowCopy=false] Shallow copies avoid recursively
+ * accessing nested objects.
* @return {Object} the options object with the names mapped to the arguments
*/
- Tone.prototype.optionsObject = function (values, keys, defaults) {
+ Tone.prototype.optionsObject = function (values, keys, defaults, shallowCopy) {
var options = {};
- if (values.length === 1 && typeof values[0] === 'object') {
+ if (values.length === 1 && Object.prototype.toString.call(values[0]) === '[object Object]') {
options = values[0];
} else {
for (var i = 0; i < keys.length; i++) {
@@ -514,7 +530,7 @@
}
}
if (!this.isUndef(defaults)) {
- return this.defaultArg(options, defaults);
+ return this.defaultArg(options, defaults, shallowCopy);
} else {
return options;
}
@@ -533,6 +549,28 @@
* @function
*/
Tone.prototype.isFunction = isFunction;
+ /**
+ * Test if the argument is a number.
+ * @param {*} arg the argument to test
+ * @returns {boolean} true if the arg is a number
+ */
+ Tone.prototype.isNumber = function (arg) {
+ return typeof arg === 'number';
+ };
+ /**
+ * Test if the argument is a boolean.
+ * @param {*} arg the argument to test
+ * @returns {boolean} true if the arg is a boolean
+ */
+ Tone.prototype.isBoolean = function (arg) {
+ return typeof arg === 'boolean';
+ };
+ /**
+ * An empty function.
+ * @static
+ */
+ Tone.noOp = function () {
+ };
/**
* Make the property not writable. Internal use only.
* @private
@@ -564,31 +602,39 @@
Object.defineProperty(this, property, { writable: true });
}
};
+ /**
+ * Possible play states.
+ * @enum {string}
+ */
+ Tone.State = {
+ Started: 'started',
+ Stopped: 'stopped',
+ Paused: 'paused'
+ };
///////////////////////////////////////////////////////////////////////////
// GAIN CONVERSIONS
///////////////////////////////////////////////////////////////////////////
/**
- * equal power gain scale
- * good for cross-fading
- * @param {number} percent (0-1)
- * @return {number} output gain (0-1)
+ * Equal power gain scale. Good for cross-fading.
+ * @param {NormalRange} percent (0-1)
+ * @return {Gain} output gain (0-1)
*/
Tone.prototype.equalPowerScale = function (percent) {
var piFactor = 0.5 * Math.PI;
return Math.sin(percent * piFactor);
};
/**
- * convert db scale to gain scale (0-1)
- * @param {number} db
- * @return {number}
+ * Convert decibels into gain.
+ * @param {Decibels} db
+ * @return {Gain}
*/
Tone.prototype.dbToGain = function (db) {
return Math.pow(2, db / 6);
};
/**
- * convert gain scale to decibels
- * @param {number} gain (0-1)
- * @return {number}
+ * Convert gain to decibels.
+ * @param {Gain} gain (0-1)
+ * @return {Decibels}
*/
Tone.prototype.gainToDb = function (gain) {
return 20 * (Math.log(gain) / Math.LN10);
@@ -597,93 +643,14 @@
// TIMING
///////////////////////////////////////////////////////////////////////////
/**
+ * Return the current time of the clock + a single buffer frame.
+ * If this value is used to schedule a value to change, the earliest
+ * it could be scheduled is the following frame.
* @return {number} the currentTime from the AudioContext
*/
Tone.prototype.now = function () {
return this.context.currentTime;
};
- /**
- * convert a sample count to seconds
- * @param {number} samples
- * @return {number}
- */
- Tone.prototype.samplesToSeconds = function (samples) {
- return samples / this.context.sampleRate;
- };
- /**
- * convert a time into samples
- *
- * @param {Tone.time} time
- * @return {number}
- */
- Tone.prototype.toSamples = function (time) {
- var seconds = this.toSeconds(time);
- return Math.round(seconds * this.context.sampleRate);
- };
- /**
- * convert time to seconds
- *
- * this is a simplified version which only handles numbers and
- * 'now' relative numbers. If the Transport is included this
- * method is overridden to include many other features including
- * notationTime, Frequency, and transportTime
- *
- * @param {number=} time
- * @param {number=} now if passed in, this number will be
- * used for all 'now' relative timings
- * @return {number} seconds in the same timescale as the AudioContext
- */
- Tone.prototype.toSeconds = function (time, now) {
- now = this.defaultArg(now, this.now());
- if (typeof time === 'number') {
- return time; //assuming that it's seconds
- } else if (typeof time === 'string') {
- var plusTime = 0;
- if (time.charAt(0) === '+') {
- time = time.slice(1);
- plusTime = now;
- }
- return parseFloat(time) + plusTime;
- } else {
- return now;
- }
- };
- ///////////////////////////////////////////////////////////////////////////
- // FREQUENCY CONVERSION
- ///////////////////////////////////////////////////////////////////////////
- /**
- * true if the input is in the format number+hz
- * i.e.: 10hz
- *
- * @param {number} freq
- * @return {boolean}
- * @function
- */
- Tone.prototype.isFrequency = function () {
- var freqFormat = new RegExp(/\d*\.?\d+hz$/i);
- return function (freq) {
- return freqFormat.test(freq);
- };
- }();
- /**
- * Convert a frequency into seconds.
- * Accepts numbers and strings: i.e. "10hz" or
- * 10 both return 0.1.
- *
- * @param {number|string} freq
- * @return {number}
- */
- Tone.prototype.frequencyToSeconds = function (freq) {
- return 1 / parseFloat(freq);
- };
- /**
- * Convert a number in seconds to a frequency.
- * @param {number} seconds
- * @return {number}
- */
- Tone.prototype.secondsToFrequency = function (seconds) {
- return 1 / seconds;
- };
///////////////////////////////////////////////////////////////////////////
// INHERITANCE
///////////////////////////////////////////////////////////////////////////
@@ -713,123 +680,6 @@
child._super = parent;
};
///////////////////////////////////////////////////////////////////////////
- // TYPES / STATES
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Possible types which a value can take on
- * @enum {string}
- */
- Tone.Type = {
- /**
- * The default value is a number which can take on any value between [-Infinity, Infinity]
- */
- Default: 'number',
- /**
- * Time can be described in a number of ways. Read more [Time](https://github.com/TONEnoTONE/Tone.js/wiki/Time).
- *
- *
- * - Numbers, which will be taken literally as the time (in seconds).
- * - Notation, ("4n", "8t") describes time in BPM and time signature relative values.
- * - TransportTime, ("4:3:2") will also provide tempo and time signature relative times
- * in the form BARS:QUARTERS:SIXTEENTHS.
- * - Frequency, ("8hz") is converted to the length of the cycle in seconds.
- * - Now-Relative, ("+1") prefix any of the above with "+" and it will be interpreted as
- * "the current time plus whatever expression follows".
- * - Expressions, ("3:0 + 2 - (1m / 7)") any of the above can also be combined
- * into a mathematical expression which will be evaluated to compute the desired time.
- * - No Argument, for methods which accept time, no argument will be interpreted as
- * "now" (i.e. the currentTime).
- *
- *
- * @typedef {Time}
- */
- Time: 'time',
- /**
- * Frequency can be described similar to time, except ultimately the
- * values are converted to frequency instead of seconds. A number
- * is taken literally as the value in hertz. Additionally any of the
- * Time encodings can be used. Note names in the form
- * of NOTE OCTAVE (i.e. C4) are also accepted and converted to their
- * frequency value.
- * @typedef {Frequency}
- */
- Frequency: 'frequency',
- /**
- * Gain is the ratio between the input and the output value of a signal.
- * @typedef {Gain}
- */
- Gain: 'gain',
- /**
- * Normal values are within the range [0, 1].
- * @typedef {NormalRange}
- */
- NormalRange: 'normalrange',
- /**
- * AudioRange values are between [-1, 1].
- * @typedef {AudioRange}
- */
- AudioRange: 'audiorange',
- /**
- * Decibels are a logarithmic unit of measurement which is useful for volume
- * because of the logarithmic way that we perceive loudness. 0 decibels
- * means no change in volume. -10db is approximately half as loud and 10db
- * is twice is loud.
- * @typedef {Decibels}
- */
- Decibels: 'db',
- /**
- * Half-step note increments, i.e. 12 is an octave above the root. and 1 is a half-step up.
- * @typedef {Interval}
- */
- Interval: 'interval',
- /**
- * Beats per minute.
- * @typedef {BPM}
- */
- BPM: 'bpm',
- /**
- * The value must be greater than 0.
- * @typedef {Positive}
- */
- Positive: 'positive',
- /**
- * A cent is a hundredth of a semitone.
- * @typedef {Cents}
- */
- Cents: 'cents',
- /**
- * Angle between 0 and 360.
- * @typedef {Degrees}
- */
- Degrees: 'degrees',
- /**
- * A number representing a midi note.
- * @typedef {MIDI}
- */
- MIDI: 'midi',
- /**
- * A colon-separated representation of time in the form of
- * BARS:QUARTERS:SIXTEENTHS.
- * @typedef {TransportTime}
- */
- TransportTime: 'transporttime'
- };
- /**
- * Possible play states.
- * @enum {string}
- */
- Tone.State = {
- Started: 'started',
- Stopped: 'stopped',
- Paused: 'paused'
- };
- /**
- * An empty function.
- * @static
- */
- Tone.noOp = function () {
- };
- ///////////////////////////////////////////////////////////////////////////
// CONTEXT
///////////////////////////////////////////////////////////////////////////
/**
@@ -887,13 +737,13 @@
};
//setup the context
Tone._initAudioContext(function (audioContext) {
- //set the bufferTime
- Tone.prototype.bufferTime = Tone.prototype.bufferSize / audioContext.sampleRate;
+ //set the blockTime
+ Tone.prototype.blockTime = 128 / audioContext.sampleRate;
_silentNode = audioContext.createGain();
_silentNode.gain.value = 0;
_silentNode.connect(audioContext.destination);
});
- Tone.version = 'r5';
+ Tone.version = 'r6-dev';
console.log('%c * Tone.js ' + Tone.version + ' * ', 'background: #000; color: #fff');
return Tone;
});
@@ -921,11 +771,11 @@
*/
Tone.SignalBase.prototype.connect = function (node, outputNumber, inputNumber) {
//zero it out so that the signal can have full control
- if (node.constructor === Tone.Signal) {
+ if (Tone.Signal && Tone.Signal === node.constructor || Tone.Param && Tone.Param === node.constructor || Tone.TimelineSignal && Tone.TimelineSignal === node.constructor) {
//cancel changes
- node._value.cancelScheduledValues(0);
+ node._param.cancelScheduledValues(0);
//reset the value
- node._value.value = 0;
+ node._param.value = 0;
//mark the value as overridden
node.overridden = true;
} else if (node instanceof AudioParam) {
@@ -1021,11 +871,6 @@
return this._shaper.curve;
},
set: function (mapping) {
- //fixes safari WaveShaperNode bug
- if (this._isSafari()) {
- var first = mapping[0];
- mapping.unshift(first);
- }
this._curve = new Float32Array(mapping);
this._shaper.curve = this._curve;
}
@@ -1042,18 +887,17 @@
return this._shaper.oversample;
},
set: function (oversampling) {
- this._shaper.oversample = oversampling;
+ if ([
+ 'none',
+ '2x',
+ '4x'
+ ].indexOf(oversampling) !== -1) {
+ this._shaper.oversample = oversampling;
+ } else {
+ throw new Error('invalid oversampling: ' + oversampling);
+ }
}
});
- /**
- * returns true if the browser is safari
- * @return {boolean}
- * @private
- */
- Tone.WaveShaper.prototype._isSafari = function () {
- var ua = navigator.userAgent.toLowerCase();
- return ua.indexOf('safari') !== -1 && ua.indexOf('chrome') === -1;
- };
/**
* Clean up.
* @returns {Tone.WaveShaper} this
@@ -1069,39 +913,807 @@
});
Module(function (Tone) {
+ ///////////////////////////////////////////////////////////////////////////
+ // TYPES
+ ///////////////////////////////////////////////////////////////////////////
/**
- * @class A signal is an audio-rate value. Tone.Signal is a core component of the library.
- * Unlike a number, Signals can be scheduled with sample-level accuracy. Tone.Signal
- * has all of the methods available to native Web Audio
- * [AudioParam](http://webaudio.github.io/web-audio-api/#the-audioparam-interface)
- * as well as additional conveniences. Read more about working with signals
- * [here](https://github.com/TONEnoTONE/Tone.js/wiki/Signals).
- *
- * @constructor
- * @extends {Tone.SignalBase}
- * @param {Number|AudioParam} [value] Initial value of the signal. If an AudioParam
- * is passed in, that parameter will be wrapped
- * and controlled by the Signal.
- * @param {string} [units=Number] unit The units the signal is in.
- * @example
- * var signal = new Tone.Signal(10);
+ * Units which a value can take on.
+ * @enum {String}
*/
- Tone.Signal = function () {
- var options = this.optionsObject(arguments, [
- 'value',
- 'units'
- ], Tone.Signal.defaults);
+ Tone.Type = {
+ /**
+ * The default value is a number which can take on any value between [-Infinity, Infinity]
+ */
+ Default: 'number',
/**
- * The units of the signal.
- * @type {string}
+ * Time can be described in a number of ways. Read more [Time](https://github.com/Tonejs/Tone.js/wiki/Time).
+ *
+ *
+ * - Numbers, which will be taken literally as the time (in seconds).
+ * - Notation, ("4n", "8t") describes time in BPM and time signature relative values.
+ * - TransportTime, ("4:3:2") will also provide tempo and time signature relative times
+ * in the form BARS:QUARTERS:SIXTEENTHS.
+ * - Frequency, ("8hz") is converted to the length of the cycle in seconds.
+ * - Now-Relative, ("+1") prefix any of the above with "+" and it will be interpreted as
+ * "the current time plus whatever expression follows".
+ * - Expressions, ("3:0 + 2 - (1m / 7)") any of the above can also be combined
+ * into a mathematical expression which will be evaluated to compute the desired time.
+ * - No Argument, for methods which accept time, no argument will be interpreted as
+ * "now" (i.e. the currentTime).
+ *
+ *
+ * @typedef {Time}
+ */
+ Time: 'time',
+ /**
+ * Frequency can be described similar to time, except ultimately the
+ * values are converted to frequency instead of seconds. A number
+ * is taken literally as the value in hertz. Additionally any of the
+ * Time encodings can be used. Note names in the form
+ * of NOTE OCTAVE (i.e. C4) are also accepted and converted to their
+ * frequency value.
+ * @typedef {Frequency}
+ */
+ Frequency: 'frequency',
+ /**
+ * Gain is the ratio between the input and the output value of a signal.
+ * @typedef {Gain}
+ */
+ Gain: 'gain',
+ /**
+ * Normal values are within the range [0, 1].
+ * @typedef {NormalRange}
+ */
+ NormalRange: 'normalRange',
+ /**
+ * AudioRange values are between [-1, 1].
+ * @typedef {AudioRange}
+ */
+ AudioRange: 'audioRange',
+ /**
+ * Decibels are a logarithmic unit of measurement which is useful for volume
+ * because of the logarithmic way that we perceive loudness. 0 decibels
+ * means no change in volume. -10db is approximately half as loud and 10db
+ * is twice is loud.
+ * @typedef {Decibels}
+ */
+ Decibels: 'db',
+ /**
+ * Half-step note increments, i.e. 12 is an octave above the root. and 1 is a half-step up.
+ * @typedef {Interval}
+ */
+ Interval: 'interval',
+ /**
+ * Beats per minute.
+ * @typedef {BPM}
+ */
+ BPM: 'bpm',
+ /**
+ * The value must be greater than 0.
+ * @typedef {Positive}
+ */
+ Positive: 'positive',
+ /**
+ * A cent is a hundredth of a semitone.
+ * @typedef {Cents}
+ */
+ Cents: 'cents',
+ /**
+ * Angle between 0 and 360.
+ * @typedef {Degrees}
+ */
+ Degrees: 'degrees',
+ /**
+ * A number representing a midi note.
+ * @typedef {MIDI}
+ */
+ MIDI: 'midi',
+ /**
+ * A colon-separated representation of time in the form of
+ * BARS:QUARTERS:SIXTEENTHS.
+ * @typedef {TransportTime}
+ */
+ TransportTime: 'transportTime',
+ /**
+ * Ticks are the basic subunit of the Transport. They are
+ * the smallest unit of time that the Transport supports.
+ * @typedef {Ticks}
+ */
+ Ticks: 'tick',
+ /**
+ * A frequency represented by a letter name,
+ * accidental and octave. This system is known as
+ * [Scientific Pitch Notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation).
+ * @typedef {Note}
+ */
+ Note: 'note',
+ /**
+ * One millisecond is a thousandth of a second.
+ * @typedef {Milliseconds}
+ */
+ Milliseconds: 'milliseconds',
+ /**
+ * A string representing a duration relative to a measure.
+ *
+ * - "4n" = quarter note
+ * - "2m" = two measures
+ * - "8t" = eighth-note triplet
+ *
+ * @typedef {Notation}
+ */
+ Notation: 'notation'
+ };
+ ///////////////////////////////////////////////////////////////////////////
+ // MATCHING TESTS
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Test if a function is "now-relative", i.e. starts with "+".
+ *
+ * @param {String} str The string to test
+ * @return {boolean}
+ * @method isNowRelative
+ * @lends Tone.prototype.isNowRelative
+ */
+ Tone.prototype.isNowRelative = function () {
+ var nowRelative = new RegExp(/^\W*\+(.)+/i);
+ return function (note) {
+ return nowRelative.test(note);
+ };
+ }();
+ /**
+ * Tests if a string is in Ticks notation.
+ *
+ * @param {String} str The string to test
+ * @return {boolean}
+ * @method isTicks
+ * @lends Tone.prototype.isTicks
+ */
+ Tone.prototype.isTicks = function () {
+ var tickFormat = new RegExp(/^\d+i$/i);
+ return function (note) {
+ return tickFormat.test(note);
+ };
+ }();
+ /**
+ * Tests if a string is musical notation.
+ * i.e.:
+ *
+ * - 4n = quarter note
+ * - 2m = two measures
+ * - 8t = eighth-note triplet
+ *
+ *
+ * @param {String} str The string to test
+ * @return {boolean}
+ * @method isNotation
+ * @lends Tone.prototype.isNotation
+ */
+ Tone.prototype.isNotation = function () {
+ var notationFormat = new RegExp(/^[0-9]+[mnt]$/i);
+ return function (note) {
+ return notationFormat.test(note);
+ };
+ }();
+ /**
+ * Test if a string is in the transportTime format.
+ * "Bars:Beats:Sixteenths"
+ * @param {String} transportTime
+ * @return {boolean}
+ * @method isTransportTime
+ * @lends Tone.prototype.isTransportTime
+ */
+ Tone.prototype.isTransportTime = function () {
+ var transportTimeFormat = new RegExp(/^(\d+(\.\d+)?\:){1,2}(\d+(\.\d+)?)?$/i);
+ return function (transportTime) {
+ return transportTimeFormat.test(transportTime);
+ };
+ }();
+ /**
+ * Test if a string is in Scientific Pitch Notation: i.e. "C4".
+ * @param {String} note The note to test
+ * @return {boolean} true if it's in the form of a note
+ * @method isNote
+ * @lends Tone.prototype.isNote
+ * @function
+ */
+ Tone.prototype.isNote = function () {
+ var noteFormat = new RegExp(/^[a-g]{1}(b|#|x|bb)?-?[0-9]+$/i);
+ return function (note) {
+ return noteFormat.test(note);
+ };
+ }();
+ /**
+ * Test if the input is in the format of number + hz
+ * i.e.: 10hz
+ *
+ * @param {String} freq
+ * @return {boolean}
+ * @function
+ */
+ Tone.prototype.isFrequency = function () {
+ var freqFormat = new RegExp(/^\d*\.?\d+hz$/i);
+ return function (freq) {
+ return freqFormat.test(freq);
+ };
+ }();
+ ///////////////////////////////////////////////////////////////////////////
+ // TO SECOND CONVERSIONS
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * @private
+ * @return {Object} The Transport's BPM if the Transport exists,
+ * otherwise returns reasonable defaults.
+ */
+ function getTransportBpm() {
+ if (Tone.Transport && Tone.Transport.bpm) {
+ return Tone.Transport.bpm.value;
+ } else {
+ return 120;
+ }
+ }
+ /**
+ * @private
+ * @return {Object} The Transport's Time Signature if the Transport exists,
+ * otherwise returns reasonable defaults.
+ */
+ function getTransportTimeSignature() {
+ if (Tone.Transport && Tone.Transport.timeSignature) {
+ return Tone.Transport.timeSignature;
+ } else {
+ return 4;
+ }
+ }
+ /**
+ *
+ * convert notation format strings to seconds
+ *
+ * @param {String} notation
+ * @param {BPM=} bpm
+ * @param {number=} timeSignature
+ * @return {number}
+ *
+ */
+ Tone.prototype.notationToSeconds = function (notation, bpm, timeSignature) {
+ bpm = this.defaultArg(bpm, getTransportBpm());
+ timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
+ var beatTime = 60 / bpm;
+ //special case: 1n = 1m
+ if (notation === '1n') {
+ notation = '1m';
+ }
+ var subdivision = parseInt(notation, 10);
+ var beats = 0;
+ if (subdivision === 0) {
+ beats = 0;
+ }
+ var lastLetter = notation.slice(-1);
+ if (lastLetter === 't') {
+ beats = 4 / subdivision * 2 / 3;
+ } else if (lastLetter === 'n') {
+ beats = 4 / subdivision;
+ } else if (lastLetter === 'm') {
+ beats = subdivision * timeSignature;
+ } else {
+ beats = 0;
+ }
+ return beatTime * beats;
+ };
+ /**
+ * convert transportTime into seconds.
+ *
+ * ie: 4:2:3 == 4 measures + 2 quarters + 3 sixteenths
+ *
+ * @param {TransportTime} transportTime
+ * @param {BPM=} bpm
+ * @param {number=} timeSignature
+ * @return {number} seconds
+ *
+ * @lends Tone.prototype.transportTimeToSeconds
+ */
+ Tone.prototype.transportTimeToSeconds = function (transportTime, bpm, timeSignature) {
+ bpm = this.defaultArg(bpm, getTransportBpm());
+ timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
+ var measures = 0;
+ var quarters = 0;
+ var sixteenths = 0;
+ var split = transportTime.split(':');
+ if (split.length === 2) {
+ measures = parseFloat(split[0]);
+ quarters = parseFloat(split[1]);
+ } else if (split.length === 1) {
+ quarters = parseFloat(split[0]);
+ } else if (split.length === 3) {
+ measures = parseFloat(split[0]);
+ quarters = parseFloat(split[1]);
+ sixteenths = parseFloat(split[2]);
+ }
+ var beats = measures * timeSignature + quarters + sixteenths / 4;
+ return beats * this.notationToSeconds('4n', bpm, timeSignature);
+ };
+ /**
+ * convert ticks into seconds
+ *
+ * @param {Ticks} ticks
+ * @param {BPM=} bpm
+ * @param {number=} timeSignature
+ * @return {number} seconds
+ * @private
+ */
+ Tone.prototype.ticksToSeconds = function (ticks, bpm, timeSignature) {
+ if (this.isUndef(Tone.Transport)) {
+ return 0;
+ }
+ ticks = parseInt(ticks);
+ var quater = this.notationToSeconds('4n', bpm, timeSignature);
+ return quater * ticks / Tone.Transport.PPQ;
+ };
+ /**
+ * Convert a frequency into seconds.
+ * Accepts numbers and strings: i.e. "10hz" or
+ * 10 both return 0.1.
+ *
+ * @param {Frequency} freq
+ * @return {number}
+ */
+ Tone.prototype.frequencyToSeconds = function (freq) {
+ return 1 / parseFloat(freq);
+ };
+ /**
+ * Convert a sample count to seconds.
+ * @param {number} samples
+ * @return {number}
+ */
+ Tone.prototype.samplesToSeconds = function (samples) {
+ return samples / this.context.sampleRate;
+ };
+ /**
+ * Convert from seconds to samples.
+ * @param {number} seconds
+ * @return {number} The number of samples
+ */
+ Tone.prototype.secondsToSamples = function (seconds) {
+ return seconds * this.context.sampleRate;
+ };
+ ///////////////////////////////////////////////////////////////////////////
+ // FROM SECOND CONVERSIONS
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Convert seconds to transportTime in the form
+ * "measures:quarters:sixteenths"
+ *
+ * @param {Number} seconds
+ * @param {BPM=} bpm
+ * @param {Number=} timeSignature
+ * @return {TransportTime}
+ */
+ Tone.prototype.secondsToTransportTime = function (seconds, bpm, timeSignature) {
+ bpm = this.defaultArg(bpm, getTransportBpm());
+ timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
+ var quarterTime = this.notationToSeconds('4n', bpm, timeSignature);
+ var quarters = seconds / quarterTime;
+ var measures = Math.floor(quarters / timeSignature);
+ var sixteenths = quarters % 1 * 4;
+ quarters = Math.floor(quarters) % timeSignature;
+ var progress = [
+ measures,
+ quarters,
+ sixteenths
+ ];
+ return progress.join(':');
+ };
+ /**
+ * Convert a number in seconds to a frequency.
+ * @param {number} seconds
+ * @return {number}
+ */
+ Tone.prototype.secondsToFrequency = function (seconds) {
+ return 1 / seconds;
+ };
+ ///////////////////////////////////////////////////////////////////////////
+ // GENERALIZED CONVERSIONS
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Convert seconds to the closest transportTime in the form
+ * measures:quarters:sixteenths
+ *
+ * @method toTransportTime
+ *
+ * @param {Time} time
+ * @param {BPM=} bpm
+ * @param {number=} timeSignature
+ * @return {TransportTime}
+ *
+ * @lends Tone.prototype.toTransportTime
+ */
+ Tone.prototype.toTransportTime = function (time, bpm, timeSignature) {
+ var seconds = this.toSeconds(time, bpm, timeSignature);
+ return this.secondsToTransportTime(seconds, bpm, timeSignature);
+ };
+ /**
+ * Convert a frequency representation into a number.
+ *
+ * @param {Frequency} freq
+ * @param {number=} now if passed in, this number will be
+ * used for all 'now' relative timings
+ * @return {number} the frequency in hertz
+ */
+ Tone.prototype.toFrequency = function (freq, now) {
+ if (this.isFrequency(freq)) {
+ return parseFloat(freq);
+ } else if (this.isNotation(freq) || this.isTransportTime(freq)) {
+ return this.secondsToFrequency(this.toSeconds(freq, now));
+ } else if (this.isNote(freq)) {
+ return this.noteToFrequency(freq);
+ } else {
+ return freq;
+ }
+ };
+ /**
+ * Convert the time representation into ticks.
+ * Now-Relative timing will be relative to the current
+ * Tone.Transport.ticks.
+ * @param {Time} time
+ * @return {Ticks}
+ * @private
+ */
+ Tone.prototype.toTicks = function (time, bpm, timeSignature) {
+ if (this.isUndef(Tone.Transport)) {
+ return 0;
+ }
+ //get the seconds
+ var plusNow = 0;
+ if (this.isNowRelative(time)) {
+ time = time.replace(/^\W*/, '');
+ plusNow = Tone.Transport.ticks;
+ } else if (this.isUndef(time)) {
+ return Tone.Transport.ticks;
+ }
+ var seconds = this.toSeconds(time);
+ var quarter = this.notationToSeconds('4n', bpm, timeSignature);
+ var quarters = seconds / quarter;
+ var tickNum = quarters * Tone.Transport.PPQ;
+ //quantize to tick value
+ return Math.round(tickNum) + plusNow;
+ };
+ /**
+ * convert a time into samples
+ *
+ * @param {Time} time
+ * @return {number}
+ */
+ Tone.prototype.toSamples = function (time) {
+ var seconds = this.toSeconds(time);
+ return Math.round(seconds * this.context.sampleRate);
+ };
+ /**
+ * Convert Time into seconds.
+ *
+ * Unlike the method which it overrides, this takes into account
+ * transporttime and musical notation.
+ *
+ * Time : 1.40
+ * Notation: 4n|1m|2t
+ * TransportTime: 2:4:1 (measure:quarters:sixteens)
+ * Now Relative: +3n
+ * Math: 3n+16n or even very complicated expressions ((3n*2)/6 + 1)
+ *
+ * @override
+ * @param {Time} time
+ * @param {number=} now if passed in, this number will be
+ * used for all 'now' relative timings
+ * @return {number}
+ */
+ Tone.prototype.toSeconds = function (time, now) {
+ now = this.defaultArg(now, this.now());
+ if (typeof time === 'number') {
+ return time; //assuming that it's seconds
+ } else if (typeof time === 'string') {
+ var plusTime = 0;
+ if (this.isNowRelative(time)) {
+ time = time.replace(/^\W*/, '');
+ plusTime = now;
+ }
+ var components = time.split(/[\(\)\-\+\/\*]/);
+ if (components.length > 1) {
+ var originalTime = time;
+ for (var i = 0; i < components.length; i++) {
+ var symb = components[i].trim();
+ if (symb !== '') {
+ var val = this.toSeconds(symb);
+ time = time.replace(symb, val);
+ }
+ }
+ try {
+ //eval is evil, but i think it's safe here
+ time = eval(time); // jshint ignore:line
+ } catch (e) {
+ throw new EvalError('problem evaluating Time: ' + originalTime);
+ }
+ } else if (this.isNotation(time)) {
+ time = this.notationToSeconds(time);
+ } else if (this.isTransportTime(time)) {
+ time = this.transportTimeToSeconds(time);
+ } else if (this.isFrequency(time)) {
+ time = this.frequencyToSeconds(time);
+ } else if (this.isTicks(time)) {
+ time = this.ticksToSeconds(time);
+ } else {
+ time = parseFloat(time);
+ }
+ return time + plusTime;
+ } else {
+ return now;
+ }
+ };
+ /**
+ * Convert a Time to Notation. Values will be thresholded to the nearest 128th note.
+ * @param {Time} time
+ * @param {BPM=} bpm
+ * @param {number=} timeSignature
+ * @return {Notation}
+ */
+ Tone.prototype.toNotation = function (time, bpm, timeSignature) {
+ var testNotations = [
+ '1m',
+ '2n',
+ '4n',
+ '8n',
+ '16n',
+ '32n',
+ '64n',
+ '128n'
+ ];
+ var retNotation = toNotationHelper.call(this, time, bpm, timeSignature, testNotations);
+ //try the same thing but with tripelets
+ var testTripletNotations = [
+ '1m',
+ '2n',
+ '2t',
+ '4n',
+ '4t',
+ '8n',
+ '8t',
+ '16n',
+ '16t',
+ '32n',
+ '32t',
+ '64n',
+ '64t',
+ '128n'
+ ];
+ var retTripletNotation = toNotationHelper.call(this, time, bpm, timeSignature, testTripletNotations);
+ //choose the simpler expression of the two
+ if (retTripletNotation.split('+').length < retNotation.split('+').length) {
+ return retTripletNotation;
+ } else {
+ return retNotation;
+ }
+ };
+ /**
+ * Helper method for Tone.toNotation
+ * @private
+ */
+ function toNotationHelper(time, bpm, timeSignature, testNotations) {
+ var seconds = this.toSeconds(time);
+ var threshold = this.notationToSeconds(testNotations[testNotations.length - 1], bpm, timeSignature);
+ var retNotation = '';
+ for (var i = 0; i < testNotations.length; i++) {
+ var notationTime = this.notationToSeconds(testNotations[i], bpm, timeSignature);
+ //account for floating point errors (i.e. round up if the value is 0.999999)
+ var multiple = seconds / notationTime;
+ var floatingPointError = 0.000001;
+ if (1 - multiple % 1 < floatingPointError) {
+ multiple += floatingPointError;
+ }
+ multiple = Math.floor(multiple);
+ if (multiple > 0) {
+ if (multiple === 1) {
+ retNotation += testNotations[i];
+ } else {
+ retNotation += multiple.toString() + '*' + testNotations[i];
+ }
+ seconds -= multiple * notationTime;
+ if (seconds < threshold) {
+ break;
+ } else {
+ retNotation += ' + ';
+ }
+ }
+ }
+ return retNotation;
+ }
+ ///////////////////////////////////////////////////////////////////////////
+ // FREQUENCY CONVERSIONS
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Note to scale index
+ * @type {Object}
+ */
+ var noteToScaleIndex = {
+ 'cbb': -2,
+ 'cb': -1,
+ 'c': 0,
+ 'c#': 1,
+ 'cx': 2,
+ 'dbb': 0,
+ 'db': 1,
+ 'd': 2,
+ 'd#': 3,
+ 'dx': 4,
+ 'ebb': 2,
+ 'eb': 3,
+ 'e': 4,
+ 'e#': 5,
+ 'ex': 6,
+ 'fbb': 3,
+ 'fb': 4,
+ 'f': 5,
+ 'f#': 6,
+ 'fx': 7,
+ 'gbb': 5,
+ 'gb': 6,
+ 'g': 7,
+ 'g#': 8,
+ 'gx': 9,
+ 'abb': 7,
+ 'ab': 8,
+ 'a': 9,
+ 'a#': 10,
+ 'ax': 11,
+ 'bbb': 9,
+ 'bb': 10,
+ 'b': 11,
+ 'b#': 12,
+ 'bx': 13
+ };
+ /**
+ * scale index to note (sharps)
+ * @type {Array}
+ */
+ var scaleIndexToNote = [
+ 'C',
+ 'C#',
+ 'D',
+ 'D#',
+ 'E',
+ 'F',
+ 'F#',
+ 'G',
+ 'G#',
+ 'A',
+ 'A#',
+ 'B'
+ ];
+ /**
+ * The [concert pitch](https://en.wikipedia.org/wiki/Concert_pitch,
+ * A4's values in Hertz.
+ * @type {Frequency}
+ * @static
+ */
+ Tone.A4 = 440;
+ /**
+ * Convert a note name to frequency.
+ * @param {String} note
+ * @return {number}
+ * @example
+ * var freq = tone.noteToFrequency("A4"); //returns 440
+ */
+ Tone.prototype.noteToFrequency = function (note) {
+ //break apart the note by frequency and octave
+ var parts = note.split(/(-?\d+)/);
+ if (parts.length === 3) {
+ var index = noteToScaleIndex[parts[0].toLowerCase()];
+ var octave = parts[1];
+ var noteNumber = index + (parseInt(octave, 10) + 1) * 12;
+ return this.midiToFrequency(noteNumber);
+ } else {
+ return 0;
+ }
+ };
+ /**
+ * Convert a frequency to a note name (i.e. A4, C#5).
+ * @param {number} freq
+ * @return {String}
+ */
+ Tone.prototype.frequencyToNote = function (freq) {
+ var log = Math.log(freq / Tone.A4) / Math.LN2;
+ var noteNumber = Math.round(12 * log) + 57;
+ var octave = Math.floor(noteNumber / 12);
+ if (octave < 0) {
+ noteNumber += -12 * octave;
+ }
+ var noteName = scaleIndexToNote[noteNumber % 12];
+ return noteName + octave.toString();
+ };
+ /**
+ * Convert an interval (in semitones) to a frequency ratio.
+ *
+ * @param {Interval} interval the number of semitones above the base note
+ * @return {number} the frequency ratio
+ * @example
+ * tone.intervalToFrequencyRatio(0); // returns 1
+ * tone.intervalToFrequencyRatio(12); // returns 2
+ */
+ Tone.prototype.intervalToFrequencyRatio = function (interval) {
+ return Math.pow(2, interval / 12);
+ };
+ /**
+ * Convert a midi note number into a note name.
+ *
+ * @param {MIDI} midiNumber the midi note number
+ * @return {String} the note's name and octave
+ * @example
+ * tone.midiToNote(60); // returns "C3"
+ */
+ Tone.prototype.midiToNote = function (midiNumber) {
+ var octave = Math.floor(midiNumber / 12) - 1;
+ var note = midiNumber % 12;
+ return scaleIndexToNote[note] + octave;
+ };
+ /**
+ * Convert a note to it's midi value.
+ *
+ * @param {String} note the note name (i.e. "C3")
+ * @return {MIDI} the midi value of that note
+ * @example
+ * tone.noteToMidi("C3"); // returns 60
+ */
+ Tone.prototype.noteToMidi = function (note) {
+ //break apart the note by frequency and octave
+ var parts = note.split(/(\d+)/);
+ if (parts.length === 3) {
+ var index = noteToScaleIndex[parts[0].toLowerCase()];
+ var octave = parts[1];
+ return index + (parseInt(octave, 10) + 1) * 12;
+ } else {
+ return 0;
+ }
+ };
+ /**
+ * Convert a MIDI note to frequency value.
+ *
+ * @param {MIDI} midi The midi number to convert.
+ * @return {Frequency} the corresponding frequency value
+ * @example
+ * tone.midiToFrequency(57); // returns 440
+ */
+ Tone.prototype.midiToFrequency = function (midi) {
+ return Tone.A4 * Math.pow(2, (midi - 69) / 12);
+ };
+ return Tone;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class Tone.Param wraps the native Web Audio's AudioParam to provide
+ * additional unit conversion functionality. It also
+ * serves as a base-class for classes which have a single,
+ * automatable parameter.
+ * @extends {Tone}
+ * @param {AudioParam} param The parameter to wrap.
+ * @param {Tone.Type} units The units of the audio param.
+ * @param {Boolean} convert If the param should be converted.
+ */
+ Tone.Param = function () {
+ var options = this.optionsObject(arguments, [
+ 'param',
+ 'units',
+ 'convert'
+ ], Tone.Param.defaults);
+ /**
+ * The native parameter to control
+ * @type {AudioParam}
+ * @private
+ */
+ this._param = this.input = options.param;
+ /**
+ * The units of the parameter
+ * @type {Tone.Type}
*/
this.units = options.units;
/**
- * When true, converts the set value
- * based on the units given. When false,
- * applies no conversion and the units
- * are merely used as a label.
- * @type {boolean}
+ * If the value should be converted or not
+ * @type {Boolean}
*/
this.convert = options.convert;
/**
@@ -1112,68 +1724,44 @@
* @private
*/
this.overridden = false;
- /**
- * The node where the constant signal value is scaled.
- * @type {GainNode}
- * @private
- */
- this.output = this._scaler = this.context.createGain();
- /**
- * The node where the value is set.
- * @type {AudioParam}
- * @private
- */
- this.input = this._value = this._scaler.gain;
- if (options.value instanceof AudioParam) {
- this._scaler.connect(options.value);
- //zero out the value
- options.value.value = 0;
- } else {
- if (!this.isUndef(options.param)) {
- this._scaler.connect(options.param);
- options.param.value = 0;
- }
+ if (!this.isUndef(options.value)) {
this.value = options.value;
}
- //connect the constant 1 output to the node output
- Tone.Signal._constant.chain(this._scaler);
};
- Tone.extend(Tone.Signal, Tone.SignalBase);
+ Tone.extend(Tone.Param);
/**
- * The default values
+ * Defaults
* @type {Object}
- * @static
* @const
*/
- Tone.Signal.defaults = {
- 'value': 0,
- 'param': undefined,
+ Tone.Param.defaults = {
'units': Tone.Type.Default,
- 'convert': true
+ 'convert': true,
+ 'param': undefined
};
/**
- * The current value of the signal.
- * @memberOf Tone.Signal#
+ * The current value of the parameter.
+ * @memberOf Tone.Param#
* @type {Number}
* @name value
*/
- Object.defineProperty(Tone.Signal.prototype, 'value', {
+ Object.defineProperty(Tone.Param.prototype, 'value', {
get: function () {
- return this._toUnits(this._value.value);
+ return this._toUnits(this._param.value);
},
set: function (value) {
var convertedVal = this._fromUnits(value);
- //is this what you want?
- this.cancelScheduledValues(0);
- this._value.value = convertedVal;
+ this._param.value = convertedVal;
}
});
/**
- * @private
- * @param {*} val the value to convert
- * @return {number} the number which the value should be set to
+ * Convert the given value from the type specified by Tone.Param.units
+ * into the destination value (such as Gain or Frequency).
+ * @private
+ * @param {*} val the value to convert
+ * @return {number} the number which the value should be set to
*/
- Tone.Signal.prototype._fromUnits = function (val) {
+ Tone.Param.prototype._fromUnits = function (val) {
if (this.convert || this.isUndef(this.convert)) {
switch (this.units) {
case Tone.Type.Time:
@@ -1196,12 +1784,12 @@
}
};
/**
- * convert to the desired units
+ * Convert the parameters value into the units specified by Tone.Param.units.
* @private
* @param {number} val the value to convert
* @return {number}
*/
- Tone.Signal.prototype._toUnits = function (val) {
+ Tone.Param.prototype._toUnits = function (val) {
if (this.convert || this.isUndef(this.convert)) {
switch (this.units) {
case Tone.Type.Decibels:
@@ -1213,18 +1801,24 @@
return val;
}
};
+ /**
+ * the minimum output value
+ * @type {Number}
+ * @private
+ */
+ Tone.Param.prototype._minOutput = 0.00001;
/**
* Schedules a parameter value change at the given time.
* @param {*} value The value to set the signal.
* @param {Time} time The time when the change should occur.
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
* @example
* //set the frequency to "G4" in exactly 1 second from now.
* freq.setValueAtTime("G4", "+1");
*/
- Tone.Signal.prototype.setValueAtTime = function (value, time) {
+ Tone.Param.prototype.setValueAtTime = function (value, time) {
value = this._fromUnits(value);
- this._value.setValueAtTime(value, this.toSeconds(time));
+ this._param.setValueAtTime(value, this.toSeconds(time));
return this;
};
/**
@@ -1233,13 +1827,12 @@
* schedule changes from the current value.
*
* @param {number=} now (Optionally) pass the now value in.
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
*/
- Tone.Signal.prototype.setCurrentValueNow = function (now) {
+ Tone.Param.prototype.setRampPoint = function (now) {
now = this.defaultArg(now, this.now());
- var currentVal = this._value.value;
- this.cancelScheduledValues(now);
- this._value.setValueAtTime(currentVal, now);
+ var currentVal = this._param.value;
+ this._param.setValueAtTime(currentVal, now);
return this;
};
/**
@@ -1248,11 +1841,11 @@
*
* @param {number} value
* @param {Time} endTime
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
*/
- Tone.Signal.prototype.linearRampToValueAtTime = function (value, endTime) {
+ Tone.Param.prototype.linearRampToValueAtTime = function (value, endTime) {
value = this._fromUnits(value);
- this._value.linearRampToValueAtTime(value, this.toSeconds(endTime));
+ this._param.linearRampToValueAtTime(value, this.toSeconds(endTime));
return this;
};
/**
@@ -1261,50 +1854,52 @@
*
* @param {number} value
* @param {Time} endTime
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
*/
- Tone.Signal.prototype.exponentialRampToValueAtTime = function (value, endTime) {
+ Tone.Param.prototype.exponentialRampToValueAtTime = function (value, endTime) {
value = this._fromUnits(value);
- value = Math.max(0.00001, value);
- this._value.exponentialRampToValueAtTime(value, this.toSeconds(endTime));
+ value = Math.max(this._minOutput, value);
+ this._param.exponentialRampToValueAtTime(value, this.toSeconds(endTime));
return this;
};
/**
* Schedules an exponential continuous change in parameter value from
- * the current time and current value to the given value.
+ * the current time and current value to the given value over the
+ * duration of the rampTime.
*
- * @param {number} value
+ * @param {number} value The value to ramp to.
* @param {Time} rampTime the time that it takes the
* value to ramp from it's current value
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
* @example
* //exponentially ramp to the value 2 over 4 seconds.
- * signal.exponentialRampToValueNow(2, 4);
+ * signal.exponentialRampToValue(2, 4);
*/
- Tone.Signal.prototype.exponentialRampToValueNow = function (value, rampTime) {
+ Tone.Param.prototype.exponentialRampToValue = function (value, rampTime) {
var now = this.now();
// exponentialRampToValueAt cannot ever ramp from 0, apparently.
// More info: https://bugzilla.mozilla.org/show_bug.cgi?id=1125600#c2
var currentVal = this.value;
- this.setValueAtTime(Math.max(currentVal, 0.0001), now);
+ this.setValueAtTime(Math.max(currentVal, this._minOutput), now);
this.exponentialRampToValueAtTime(value, now + this.toSeconds(rampTime));
return this;
};
/**
* Schedules an linear continuous change in parameter value from
- * the current time and current value to the given value at the given time.
+ * the current time and current value to the given value over the
+ * duration of the rampTime.
*
- * @param {number} value
+ * @param {number} value The value to ramp to.
* @param {Time} rampTime the time that it takes the
* value to ramp from it's current value
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
* @example
* //linearly ramp to the value 4 over 3 seconds.
- * signal.linearRampToValueNow(4, 3);
+ * signal.linearRampToValue(4, 3);
*/
- Tone.Signal.prototype.linearRampToValueNow = function (value, rampTime) {
+ Tone.Param.prototype.linearRampToValue = function (value, rampTime) {
var now = this.now();
- this.setCurrentValueNow(now);
+ this.setRampPoint(now);
this.linearRampToValueAtTime(value, now + this.toSeconds(rampTime));
return this;
};
@@ -1314,15 +1909,16 @@
* @param {number} value
* @param {Time} startTime
* @param {number} timeConstant
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
*/
- Tone.Signal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
+ Tone.Param.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
value = this._fromUnits(value);
// The value will never be able to approach without timeConstant > 0.
// http://www.w3.org/TR/webaudio/#dfn-setTargetAtTime, where the equation
// is described. 0 results in a division by 0.
- timeConstant = Math.max(0.00001, timeConstant);
- this._value.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
+ value = Math.max(this._minOutput, value);
+ timeConstant = Math.max(this._minOutput, timeConstant);
+ this._param.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
return this;
};
/**
@@ -1332,13 +1928,13 @@
* @param {Array} values
* @param {Time} startTime
* @param {Time} duration
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
*/
- Tone.Signal.prototype.setValueCurveAtTime = function (values, startTime, duration) {
+ Tone.Param.prototype.setValueCurveAtTime = function (values, startTime, duration) {
for (var i = 0; i < values.length; i++) {
values[i] = this._fromUnits(values[i]);
}
- this._value.setValueCurveAtTime(values, this.toSeconds(startTime), this.toSeconds(duration));
+ this._param.setValueCurveAtTime(values, this.toSeconds(startTime), this.toSeconds(duration));
return this;
};
/**
@@ -1346,10 +1942,10 @@
* equal to startTime.
*
* @param {Time} startTime
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
*/
- Tone.Signal.prototype.cancelScheduledValues = function (startTime) {
- this._value.cancelScheduledValues(this.toSeconds(startTime));
+ Tone.Param.prototype.cancelScheduledValues = function (startTime) {
+ this._param.cancelScheduledValues(this.toSeconds(startTime));
return this;
};
/**
@@ -1360,66 +1956,757 @@
* @param {number} value
* @param {Time} rampTime the time that it takes the
* value to ramp from it's current value
- * @returns {Tone.Signal} this
+ * @returns {Tone.Param} this
* @example
* //ramp to the value either linearly or exponentially
* //depending on the "units" value of the signal
* signal.rampTo(0, 10);
*/
- Tone.Signal.prototype.rampTo = function (value, rampTime) {
+ Tone.Param.prototype.rampTo = function (value, rampTime) {
rampTime = this.defaultArg(rampTime, 0);
if (this.units === Tone.Type.Frequency || this.units === Tone.Type.BPM) {
- this.exponentialRampToValueNow(value, rampTime);
+ this.exponentialRampToValue(value, rampTime);
} else {
- this.linearRampToValueNow(value, rampTime);
+ this.linearRampToValue(value, rampTime);
}
return this;
};
+ /**
+ * Clean up
+ * @returns {Tone.Param} this
+ */
+ Tone.Param.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ this._param = null;
+ return this;
+ };
+ return Tone.Param;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class A thin wrapper around the Native Web Audio GainNode.
+ * The GainNode is a basic building block of the Web Audio
+ * API and is useful for routing audio and adjusting gains.
+ * @extends {Tone}
+ * @param {Number=} value The initial gain of the GainNode
+ * @param {Tone.Type=} units The units of the gain parameter.
+ */
+ Tone.Gain = function () {
+ var options = this.optionsObject(arguments, [
+ 'value',
+ 'units'
+ ], Tone.Gain.defaults);
+ /**
+ * The GainNode
+ * @type {GainNode}
+ * @private
+ */
+ this._gainNode = this.context.createGain();
+ options.param = this._gainNode.gain;
+ Tone.Param.call(this, options);
+ this.input = this.output = this._gainNode;
+ /**
+ * The gain parameter of the gain node.
+ * @type {AudioParam}
+ * @signal
+ */
+ this.gain = this._param;
+ this._readOnly('gain');
+ };
+ Tone.extend(Tone.Gain, Tone.Param);
+ /**
+ * The defaults
+ * @const
+ * @type {Object}
+ */
+ Tone.Gain.defaults = {
+ 'value': 1,
+ 'units': Tone.Type.Gain,
+ 'convert': true
+ };
+ /**
+ * Clean up.
+ * @return {Tone.Gain} this
+ */
+ Tone.Gain.prototype.dispose = function () {
+ Tone.Param.prototype.dispose.call(this);
+ this._gainNode.disconnect();
+ this._gainNode = null;
+ this._writable('gain');
+ this.gain = null;
+ };
+ return Tone.Gain;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class A signal is an audio-rate value. Tone.Signal is a core component of the library.
+ * Unlike a number, Signals can be scheduled with sample-level accuracy. Tone.Signal
+ * has all of the methods available to native Web Audio
+ * [AudioParam](http://webaudio.github.io/web-audio-api/#the-audioparam-interface)
+ * as well as additional conveniences. Read more about working with signals
+ * [here](https://github.com/Tonejs/Tone.js/wiki/Signals).
+ *
+ * @constructor
+ * @extends {Tone.SignalBase}
+ * @param {Number|AudioParam} [value] Initial value of the signal. If an AudioParam
+ * is passed in, that parameter will be wrapped
+ * and controlled by the Signal.
+ * @param {string} [units=Number] unit The units the signal is in.
+ * @example
+ * var signal = new Tone.Signal(10);
+ */
+ Tone.Signal = function () {
+ var options = this.optionsObject(arguments, [
+ 'value',
+ 'units'
+ ], Tone.Signal.defaults);
+ /**
+ * The node where the constant signal value is scaled.
+ * @type {GainNode}
+ * @private
+ */
+ this.output = this._gain = new Tone.Gain(options);
+ options.param = this._gain.gain;
+ Tone.Param.call(this, options);
+ /**
+ * The node where the value is set.
+ * @type {Tone.Param}
+ * @private
+ */
+ this.input = this._param = this._gain.gain;
+ //connect the const output to the node output
+ Tone.Signal._constant.chain(this._gain);
+ };
+ Tone.extend(Tone.Signal, Tone.Param);
+ /**
+ * The default values
+ * @type {Object}
+ * @static
+ * @const
+ */
+ Tone.Signal.defaults = {
+ 'value': 0,
+ 'units': Tone.Type.Default,
+ 'convert': true
+ };
+ /**
+ * When signals connect to other signals or AudioParams,
+ * they take over the output value of that signal or AudioParam.
+ * For all other nodes, the behavior is the same as a default connect
.
+ *
+ * @override
+ * @param {AudioParam|AudioNode|Tone.Signal|Tone} node
+ * @param {number} [outputNumber=0] The output number to connect from.
+ * @param {number} [inputNumber=0] The input number to connect to.
+ * @returns {Tone.SignalBase} this
+ * @method
+ */
+ Tone.Signal.prototype.connect = Tone.SignalBase.prototype.connect;
/**
* dispose and disconnect
* @returns {Tone.Signal} this
*/
Tone.Signal.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
- this._value = null;
- this._scaler = null;
+ Tone.Param.prototype.dispose.call(this);
+ this._param = null;
+ this._gain.dispose();
+ this._gain = null;
return this;
};
///////////////////////////////////////////////////////////////////////////
// STATIC
///////////////////////////////////////////////////////////////////////////
/**
- * the constant signal generator
+ * Generates a constant output of 1.
* @static
* @private
* @const
- * @type {OscillatorNode}
- */
- Tone.Signal._generator = null;
- /**
- * the signal generator waveshaper. makes the incoming signal
- * only output 1 for all inputs.
- * @static
- * @private
- * @const
- * @type {Tone.WaveShaper}
+ * @type {AudioBufferSourceNode}
*/
Tone.Signal._constant = null;
/**
* initializer function
*/
Tone._initAudioContext(function (audioContext) {
- Tone.Signal._generator = audioContext.createOscillator();
- Tone.Signal._constant = new Tone.WaveShaper([
- 1,
- 1
- ]);
- Tone.Signal._generator.connect(Tone.Signal._constant);
- Tone.Signal._generator.start(0);
- Tone.Signal._generator.noGC();
+ var buffer = audioContext.createBuffer(1, 128, audioContext.sampleRate);
+ var arr = buffer.getChannelData(0);
+ for (var i = 0; i < arr.length; i++) {
+ arr[i] = 1;
+ }
+ Tone.Signal._constant = audioContext.createBufferSource();
+ Tone.Signal._constant.channelCount = 1;
+ Tone.Signal._constant.channelCountMode = 'explicit';
+ Tone.Signal._constant.buffer = buffer;
+ Tone.Signal._constant.loop = true;
+ Tone.Signal._constant.start(0);
+ Tone.Signal._constant.noGC();
});
return Tone.Signal;
});
+ Module(function (Tone) {
+
+ /**
+ * @class A Timeline class for scheduling and maintaining state
+ * along a timeline. All events must have a "time" property.
+ * Internally, events are stored in time order for fast
+ * retrieval.
+ * @extends {Tone}
+ */
+ Tone.Timeline = function () {
+ /**
+ * The array of scheduled timeline events
+ * @type {Array}
+ * @private
+ */
+ this._timeline = [];
+ };
+ Tone.extend(Tone.Timeline);
+ /**
+ * The number of items in the timeline.
+ * @type {Number}
+ * @memberOf Tone.Timeline#
+ * @name length
+ * @readOnly
+ */
+ Object.defineProperty(Tone.Timeline.prototype, 'length', {
+ get: function () {
+ return this._timeline.length;
+ }
+ });
+ /**
+ * Insert an event object onto the timeline. Events must have a "time" attribute.
+ * @param {Object} event The event object to insert into the
+ * timeline.
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.addEvent = function (event) {
+ //the event needs to have a time attribute
+ if (this.isUndef(event.time)) {
+ throw new Error('events must have a time attribute');
+ }
+ event.time = this.toSeconds(event.time);
+ if (this._timeline.length) {
+ var index = this._search(event.time);
+ this._timeline.splice(index + 1, 0, event);
+ } else {
+ this._timeline.push(event);
+ }
+ return this;
+ };
+ /**
+ * Remove an event from the timeline.
+ * @param {Object} event The event object to remove from the list.
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.removeEvent = function (event) {
+ this.forEachAtTime(event.time, function (testEvent, index) {
+ if (testEvent === event) {
+ this._timeline.splice(index, 1);
+ }
+ }.bind(this));
+ return this;
+ };
+ /**
+ * Get the event whose time is less than or equal to the given time.
+ * @param {Number} time The time to query.
+ * @returns {Object} The event object set after that time.
+ */
+ Tone.Timeline.prototype.getEvent = function (time) {
+ time = this.toSeconds(time);
+ var index = this._search(time);
+ if (index !== -1) {
+ return this._timeline[index];
+ } else {
+ return null;
+ }
+ };
+ /**
+ * Get the event which is scheduled after the given time.
+ * @param {Number} time The time to query.
+ * @returns {Object} The event object after the given time
+ */
+ Tone.Timeline.prototype.getEventAfter = function (time) {
+ time = this.toSeconds(time);
+ var index = this._search(time);
+ if (index + 1 < this._timeline.length) {
+ return this._timeline[index + 1];
+ } else {
+ return null;
+ }
+ };
+ /**
+ * Get the event before the event at the given time.
+ * @param {Number} time The time to query.
+ * @returns {Object} The event object before the given time
+ */
+ Tone.Timeline.prototype.getEventBefore = function (time) {
+ time = this.toSeconds(time);
+ var index = this._search(time);
+ if (index - 1 >= 0) {
+ return this._timeline[index - 1];
+ } else {
+ return null;
+ }
+ };
+ /**
+ * Cancel events after the given time
+ * @param {Time} time The time to query.
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.cancel = function (after) {
+ if (this._timeline.length) {
+ after = this.toSeconds(after);
+ var index = this._search(after);
+ if (index >= 0) {
+ this._timeline = this._timeline.slice(0, index);
+ } else {
+ this._timeline = [];
+ }
+ }
+ return this;
+ };
+ /**
+ * Cancel events before or equal to the given time.
+ * @param {Time} time The time to cancel before.
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.cancelBefore = function (time) {
+ if (this._timeline.length) {
+ time = this.toSeconds(time);
+ var index = this._search(time);
+ if (index >= 0) {
+ this._timeline = this._timeline.slice(index + 1);
+ }
+ }
+ return this;
+ };
+ /**
+ * Does a binary serach on the timeline array and returns the
+ * event which is after or equal to the time.
+ * @param {Number} time
+ * @return {Number} the index in the timeline array
+ * @private
+ */
+ Tone.Timeline.prototype._search = function (time) {
+ var beginning = 0;
+ var len = this._timeline.length;
+ var end = len;
+ // continue searching while [imin,imax] is not empty
+ while (beginning <= end && beginning < len) {
+ // calculate the midpoint for roughly equal partition
+ var midPoint = Math.floor(beginning + (end - beginning) / 2);
+ var event = this._timeline[midPoint];
+ if (event.time === time) {
+ //choose the last one that has the same time
+ for (var i = midPoint; i < this._timeline.length; i++) {
+ var testEvent = this._timeline[i];
+ if (testEvent.time === time) {
+ midPoint = i;
+ }
+ }
+ return midPoint;
+ } else if (event.time > time) {
+ //search lower
+ end = midPoint - 1;
+ } else if (event.time < time) {
+ //search upper
+ beginning = midPoint + 1;
+ }
+ }
+ return beginning - 1;
+ };
+ /**
+ * Iterate over everything in the array
+ * @param {Function} callback The callback to invoke with every item
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.forEach = function (callback) {
+ //iterate over the items in reverse so that removing an item doesn't break things
+ for (var i = this._timeline.length - 1; i >= 0; i--) {
+ callback(this._timeline[i], i);
+ }
+ return this;
+ };
+ /**
+ * Iterate over everything in the array at or before the given time.
+ * @param {Time} time The time to check if items are before
+ * @param {Function} callback The callback to invoke with every item
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.forEachBefore = function (time, callback) {
+ //iterate over the items in reverse so that removing an item doesn't break things
+ time = this.toSeconds(time);
+ var startIndex = this._search(time);
+ if (startIndex !== -1) {
+ for (var i = startIndex; i >= 0; i--) {
+ callback(this._timeline[i], i);
+ }
+ }
+ return this;
+ };
+ /**
+ * Iterate over everything in the array after the given time.
+ * @param {Time} time The time to check if items are before
+ * @param {Function} callback The callback to invoke with every item
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.forEachAfter = function (time, callback) {
+ //iterate over the items in reverse so that removing an item doesn't break things
+ time = this.toSeconds(time);
+ var endIndex = this._search(time);
+ for (var i = this._timeline.length - 1; i > endIndex; i--) {
+ callback(this._timeline[i], i);
+ }
+ return this;
+ };
+ /**
+ * Iterate over everything in the array at or after the given time. Similar to
+ * forEachAfter, but includes the item(s) at the given time.
+ * @param {Time} time The time to check if items are before
+ * @param {Function} callback The callback to invoke with every item
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.forEachFrom = function (time, callback) {
+ //iterate over the items in reverse so that removing an item doesn't break things
+ time = this.toSeconds(time);
+ var endIndex = this._search(time);
+ //work backwards until the event time is less than time
+ while (endIndex >= 0 && this._timeline[endIndex].time >= time) {
+ endIndex--;
+ }
+ for (var i = this._timeline.length - 1; i > endIndex; i--) {
+ callback(this._timeline[i], i);
+ }
+ return this;
+ };
+ /**
+ * Iterate over everything in the array at the given time
+ * @param {Time} time The time to check if items are before
+ * @param {Function} callback The callback to invoke with every item
+ * @returns {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.forEachAtTime = function (time, callback) {
+ //iterate over the items in reverse so that removing an item doesn't break things
+ time = this.toSeconds(time);
+ var index = this._search(time);
+ if (index !== -1) {
+ for (var i = index; i >= 0; i--) {
+ var event = this._timeline[i];
+ if (event.time === time) {
+ callback(event, i);
+ } else {
+ break;
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * Clean up.
+ * @return {Tone.Timeline} this
+ */
+ Tone.Timeline.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ this._timeline = null;
+ };
+ return Tone.Timeline;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class A signal which adds the method getValueAtTime.
+ * Code and inspiration from https://github.com/jsantell/web-audio-automation-timeline
+ */
+ Tone.TimelineSignal = function () {
+ var options = this.optionsObject(arguments, [
+ 'value',
+ 'units'
+ ], Tone.Signal.defaults);
+ //constructors
+ Tone.Signal.apply(this, options);
+ options.param = this._param;
+ Tone.Param.call(this, options);
+ /**
+ * The scheduled events
+ * @type {Tone.Timeline}
+ * @private
+ */
+ this._events = new Tone.Timeline();
+ /**
+ * The initial scheduled value
+ * @type {Number}
+ * @private
+ */
+ this._initial = this._fromUnits(this._param.value);
+ };
+ Tone.extend(Tone.TimelineSignal, Tone.Param);
+ /**
+ * The event types of a schedulable signal.
+ * @enum {String}
+ */
+ Tone.TimelineSignal.Type = {
+ Linear: 'linear',
+ Exponential: 'exponential',
+ Target: 'target',
+ Set: 'set'
+ };
+ /**
+ * The current value of the signal.
+ * @memberOf Tone.TimelineSignal#
+ * @type {Number}
+ * @name value
+ */
+ Object.defineProperty(Tone.TimelineSignal.prototype, 'value', {
+ get: function () {
+ return this._toUnits(this._param.value);
+ },
+ set: function (value) {
+ var convertedVal = this._fromUnits(value);
+ this._initial = convertedVal;
+ this._param.value = convertedVal;
+ }
+ });
+ ///////////////////////////////////////////////////////////////////////////
+ // SCHEDULING
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Schedules a parameter value change at the given time.
+ * @param {*} value The value to set the signal.
+ * @param {Time} time The time when the change should occur.
+ * @returns {Tone.TimelineSignal} this
+ * @example
+ * //set the frequency to "G4" in exactly 1 second from now.
+ * freq.setValueAtTime("G4", "+1");
+ */
+ Tone.TimelineSignal.prototype.setValueAtTime = function (value, startTime) {
+ value = this._fromUnits(value);
+ startTime = this.toSeconds(startTime);
+ this._events.addEvent({
+ 'type': Tone.TimelineSignal.Type.Set,
+ 'value': value,
+ 'time': startTime
+ });
+ //invoke the original event
+ this._param.setValueAtTime(value, startTime);
+ return this;
+ };
+ /**
+ * Schedules a linear continuous change in parameter value from the
+ * previous scheduled parameter value to the given value.
+ *
+ * @param {number} value
+ * @param {Time} endTime
+ * @returns {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.linearRampToValueAtTime = function (value, endTime) {
+ value = this._fromUnits(value);
+ endTime = this.toSeconds(endTime);
+ this._events.addEvent({
+ 'type': Tone.TimelineSignal.Type.Linear,
+ 'value': value,
+ 'time': endTime
+ });
+ this._param.linearRampToValueAtTime(value, endTime);
+ return this;
+ };
+ /**
+ * Schedules an exponential continuous change in parameter value from
+ * the previous scheduled parameter value to the given value.
+ *
+ * @param {number} value
+ * @param {Time} endTime
+ * @returns {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.exponentialRampToValueAtTime = function (value, endTime) {
+ value = this._fromUnits(value);
+ value = Math.max(this._minOutput, value);
+ endTime = this.toSeconds(endTime);
+ this._events.addEvent({
+ 'type': Tone.TimelineSignal.Type.Exponential,
+ 'value': value,
+ 'time': endTime
+ });
+ this._param.exponentialRampToValueAtTime(value, endTime);
+ return this;
+ };
+ /**
+ * Start exponentially approaching the target value at the given time with
+ * a rate having the given time constant.
+ * @param {number} value
+ * @param {Time} startTime
+ * @param {number} timeConstant
+ * @returns {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
+ value = this._fromUnits(value);
+ value = Math.max(this._minOutput, value);
+ startTime = this.toSeconds(startTime);
+ this._events.addEvent({
+ 'type': Tone.TimelineSignal.Type.Target,
+ 'value': value,
+ 'time': startTime,
+ 'constant': timeConstant
+ });
+ this._param.setTargetAtTime(value, startTime, timeConstant);
+ return this;
+ };
+ /**
+ * Cancels all scheduled parameter changes with times greater than or
+ * equal to startTime.
+ *
+ * @param {Time} startTime
+ * @returns {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.cancelScheduledValues = function (after) {
+ this._events.clear(after);
+ this._param.cancelScheduledValues(this.toSeconds(after));
+ return this;
+ };
+ /**
+ * Sets the computed value at the given time. This provides
+ * a point from which a linear or exponential curve
+ * can be scheduled after.
+ * @param {Time} time When to set the ramp point
+ * @returns {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.setRampPoint = function (time) {
+ time = this.toSeconds(time);
+ //get the value at the given time
+ var val = this.getValueAtTime(time);
+ this.setValueAtTime(val, time);
+ return this;
+ };
+ /**
+ * Do a linear ramp to the given value between the start and finish times.
+ * @param {Number} value The value to ramp to.
+ * @param {Time} start The beginning anchor point to do the linear ramp
+ * @param {Time} finish The ending anchor point by which the value of
+ * the signal will equal the given value.
+ * @returns {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.linearRampToValueBetween = function (value, start, finish) {
+ this.setRampPoint(start);
+ this.linearRampToValueAtTime(value, finish);
+ return this;
+ };
+ /**
+ * Do a exponential ramp to the given value between the start and finish times.
+ * @param {Number} value The value to ramp to.
+ * @param {Time} start The beginning anchor point to do the exponential ramp
+ * @param {Time} finish The ending anchor point by which the value of
+ * the signal will equal the given value.
+ * @returns {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.exponentialRampToValueBetween = function (value, start, finish) {
+ this.setRampPoint(start);
+ this.exponentialRampToValueAtTime(value, finish);
+ return this;
+ };
+ ///////////////////////////////////////////////////////////////////////////
+ // GETTING SCHEDULED VALUES
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns the value before or equal to the given time
+ * @param {Number} time The time to query
+ * @return {Object} The event at or before the given time.
+ * @private
+ */
+ Tone.TimelineSignal.prototype._searchBefore = function (time) {
+ return this._events.getEvent(time);
+ };
+ /**
+ * The event after the given time
+ * @param {Number} time The time to query.
+ * @return {Object} The next event after the given time
+ * @private
+ */
+ Tone.TimelineSignal.prototype._searchAfter = function (time) {
+ return this._events.getEventAfter(time);
+ };
+ /**
+ * Get the scheduled value at the given time.
+ * @param {Number} time The time in seconds.
+ * @return {Number} The scheduled value at the given time.
+ */
+ Tone.TimelineSignal.prototype.getValueAtTime = function (time) {
+ var after = this._searchAfter(time);
+ var before = this._searchBefore(time);
+ //if it was set by
+ if (before === null) {
+ return this._initial;
+ } else if (before.type === Tone.TimelineSignal.Type.Target) {
+ var previous = this._searchBefore(before.time - 0.0001);
+ var previouVal;
+ if (previous === null) {
+ previouVal = this._initial;
+ } else {
+ previouVal = previous.value;
+ }
+ return this._exponentialApproach(before.time, previouVal, before.value, before.constant, time);
+ } else if (after === null) {
+ return before.value;
+ } else if (after.type === Tone.TimelineSignal.Type.Linear) {
+ return this._linearInterpolate(before.time, before.value, after.time, after.value, time);
+ } else if (after.type === Tone.TimelineSignal.Type.Exponential) {
+ return this._exponentialInterpolate(before.time, before.value, after.time, after.value, time);
+ } else {
+ return before.value;
+ }
+ return this._param.getValueAtTime(time);
+ };
+ /**
+ * When signals connect to other signals or AudioParams,
+ * they take over the output value of that signal or AudioParam.
+ * For all other nodes, the behavior is the same as a default connect
.
+ *
+ * @override
+ * @param {AudioParam|AudioNode|Tone.Signal|Tone} node
+ * @param {number} [outputNumber=0] The output number to connect from.
+ * @param {number} [inputNumber=0] The input number to connect to.
+ * @returns {Tone.TimelineSignal} this
+ * @method
+ */
+ Tone.TimelineSignal.prototype.connect = Tone.SignalBase.prototype.connect;
+ ///////////////////////////////////////////////////////////////////////////
+ // AUTOMATION CURVE CALCULATIONS
+ // MIT License, copyright (c) 2014 Jordan Santell
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Calculates the the value along the curve produced by setTargetAtTime
+ * @private
+ */
+ Tone.TimelineSignal.prototype._exponentialApproach = function (t0, v0, v1, timeConstant, t) {
+ return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
+ };
+ /**
+ * Calculates the the value along the curve produced by linearRampToValueAtTime
+ * @private
+ */
+ Tone.TimelineSignal.prototype._linearInterpolate = function (t0, v0, t1, v1, t) {
+ return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
+ };
+ /**
+ * Calculates the the value along the curve produced by exponentialRampToValueAtTime
+ * @private
+ */
+ Tone.TimelineSignal.prototype._exponentialInterpolate = function (t0, v0, t1, v1, t) {
+ v0 = Math.max(this._minOutput, v0);
+ return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
+ };
+ /**
+ * Clean up.
+ * @return {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.dispose = function () {
+ Tone.Signal.prototype.dispose.call(this);
+ Tone.Param.prototype.dispose.call(this);
+ this._events.dispose();
+ this._events = null;
+ };
+ return Tone.TimelineSignal;
+ });
Module(function (Tone) {
/**
@@ -1428,7 +2715,7 @@
*
* @extends {Tone.SignalBase}
* @constructor
- * @param {number} exp The exponent to apply to the incoming signal, must be at least 2.
+ * @param {Positive} exp The exponent to apply to the incoming signal, must be at least 2.
* @example
* var pow = new Tone.Pow(2);
* var sig = new Tone.Signal(0.5).connect(pow);
@@ -1548,62 +2835,34 @@
* @type {Time}
*/
this.release = options.release;
- /**
- * the next time the envelope is attacked
- * @type {number}
- * @private
- */
- this._nextAttack = Infinity;
- /**
- * the next time the envelope is decayed
- * @type {number}
- * @private
- */
- this._nextDecay = Infinity;
- /**
- * the next time the envelope is sustain
- * @type {number}
- * @private
- */
- this._nextSustain = Infinity;
- /**
- * the next time the envelope is released
- * @type {number}
- * @private
- */
- this._nextRelease = Infinity;
- /**
- * the next time the envelope is at standby
- * @type {number}
- * @private
- */
- this._nextStandby = Infinity;
/**
* the next time the envelope is at standby
* @type {number}
* @private
*/
this._attackCurve = Tone.Envelope.Type.Linear;
- /**
- * the last recorded velocity value
+ /**
+ * the next time the envelope is at standby
* @type {number}
* @private
*/
- this._peakValue = 1;
+ this._releaseCurve = Tone.Envelope.Type.Exponential;
/**
* the minimum output value
* @type {number}
* @private
*/
- this._minOutput = 0.0001;
+ this._minOutput = 0.00001;
/**
* the signal
- * @type {Tone.Signal}
+ * @type {Tone.TimelineSignal}
* @private
*/
- this._sig = this.output = new Tone.Signal(0);
+ this._sig = this.output = new Tone.TimelineSignal();
+ this._sig.setValueAtTime(0, 0);
//set the attackCurve initially
this.attackCurve = options.attackCurve;
+ this.releaseCurve = options.releaseCurve;
};
Tone.extend(Tone.Envelope);
/**
@@ -1616,7 +2875,8 @@
'decay': 0.1,
'sustain': 0.5,
'release': 1,
- 'attackCurve': 'linear'
+ 'attackCurve': 'linear',
+ 'releaseCurve': 'exponential'
};
/**
* the envelope time multipler
@@ -1658,77 +2918,25 @@
}
});
/**
- * Get the phase of the envelope at the specified time.
- * @param {number} time
- * @return {Tone.Envelope.Phase}
- * @private
+ * The slope of the Release. Either "linear" or "exponential".
+ * @memberOf Tone.Envelope#
+ * @type {string}
+ * @name releaseCurve
+ * @example
+ * env.releaseCurve = "linear";
*/
- Tone.Envelope.prototype._phaseAtTime = function (time) {
- if (this._nextRelease > time) {
- if (this._nextAttack <= time && this._nextDecay > time) {
- return Tone.Envelope.Phase.Attack;
- } else if (this._nextDecay <= time && this._nextSustain > time) {
- return Tone.Envelope.Phase.Decay;
- } else if (this._nextSustain <= time && this._nextRelease > time) {
- return Tone.Envelope.Phase.Sustain;
+ Object.defineProperty(Tone.Envelope.prototype, 'releaseCurve', {
+ get: function () {
+ return this._releaseCurve;
+ },
+ set: function (type) {
+ if (type === Tone.Envelope.Type.Linear || type === Tone.Envelope.Type.Exponential) {
+ this._releaseCurve = type;
} else {
- return Tone.Envelope.Phase.Standby;
+ throw Error('releaseCurve must be either "linear" or "exponential". Invalid type: ', type);
}
- } else if (this._nextRelease < time && this._nextStandby > time) {
- return Tone.Envelope.Phase.Release;
- } else {
- return Tone.Envelope.Phase.Standby;
}
- };
- /**
- * https://github.com/jsantell/web-audio-automation-timeline
- * MIT License, copyright (c) 2014 Jordan Santell
- * @private
- */
- Tone.Envelope.prototype._exponentialApproach = function (t0, v0, v1, timeConstant, t) {
- return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
- };
- /**
- * @private
- */
- Tone.Envelope.prototype._linearInterpolate = function (t0, v0, t1, v1, t) {
- return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
- };
- /**
- * @private
- */
- Tone.Envelope.prototype._exponentialInterpolate = function (t0, v0, t1, v1, t) {
- return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
- };
- /**
- * Get the envelopes value at the given time
- * @param {number} time
- * @param {number} velocity
- * @return {number}
- * @private
- */
- Tone.Envelope.prototype._valueAtTime = function (time) {
- var attack = this.toSeconds(this.attack);
- var decay = this.toSeconds(this.decay);
- var release = this.toSeconds(this.release);
- switch (this._phaseAtTime(time)) {
- case Tone.Envelope.Phase.Attack:
- if (this._attackCurve === Tone.Envelope.Type.Linear) {
- return this._linearInterpolate(this._nextAttack, this._minOutput, this._nextAttack + attack, this._peakValue, time);
- } else {
- return this._exponentialInterpolate(this._nextAttack, this._minOutput, this._nextAttack + attack, this._peakValue, time);
- }
- break;
- case Tone.Envelope.Phase.Decay:
- return this._exponentialApproach(this._nextDecay, this._peakValue, this.sustain * this._peakValue, decay * this._timeMult, time);
- case Tone.Envelope.Phase.Release:
- return this._exponentialApproach(this._nextRelease, this._peakValue, this._minOutput, release * this._timeMult, time);
- case Tone.Envelope.Phase.Sustain:
- return this.sustain * this._peakValue;
- case Tone.Envelope.Phase.Standby:
- return this._minOutput;
- }
- };
+ });
/**
* Trigger the attack/decay portion of the ADSR envelope.
* @param {Time} [time=now] When the attack should start.
@@ -1741,30 +2949,19 @@
*/
Tone.Envelope.prototype.triggerAttack = function (time, velocity) {
//to seconds
- time = this.toSeconds(time);
- var attack = this.toSeconds(this.attack);
+ var now = this.now() + this.blockTime;
+ time = this.toSeconds(time, now);
+ var attack = this.toSeconds(this.attack) + time;
var decay = this.toSeconds(this.decay);
- //get the phase and position
- var valueAtTime = this._valueAtTime(time);
- var attackPast = valueAtTime * attack;
- //compute the timing
- this._nextAttack = time - attackPast;
- this._nextDecay = this._nextAttack + attack;
- this._nextSustain = this._nextDecay + decay;
- this._nextRelease = Infinity;
- //get the values
- this._peakValue = this.defaultArg(velocity, 1);
- var scaledMax = this._peakValue;
- var sustainVal = this.sustain * scaledMax;
- //set the curve
- this._sig.cancelScheduledValues(time);
- this._sig.setValueAtTime(valueAtTime, time);
+ velocity = this.defaultArg(velocity, 1);
+ //attack
if (this._attackCurve === Tone.Envelope.Type.Linear) {
- this._sig.linearRampToValueAtTime(scaledMax, this._nextDecay);
+ this._sig.linearRampToValueBetween(velocity, time, attack);
} else {
- this._sig.exponentialRampToValueAtTime(scaledMax, this._nextDecay);
+ this._sig.exponentialRampToValueBetween(velocity, time, attack);
}
- this._sig.setTargetAtTime(sustainVal, this._nextDecay, decay * this._timeMult);
+ //decay
+ this._sig.setTargetAtTime(this.sustain * velocity, attack, decay * this._timeMult);
return this;
};
/**
@@ -1776,28 +2973,14 @@
* env.triggerRelease();
*/
Tone.Envelope.prototype.triggerRelease = function (time) {
- time = this.toSeconds(time);
- var phase = this._phaseAtTime(time);
+ var now = this.now() + this.blockTime;
+ time = this.toSeconds(time, now);
var release = this.toSeconds(this.release);
- //computer the value at the start of the next release
- var valueAtTime = this._valueAtTime(time);
- this._peakValue = valueAtTime;
- this._nextRelease = time;
- this._nextStandby = this._nextRelease + release;
- //set the values
- this._sig.cancelScheduledValues(this._nextRelease);
- //if the phase is in the attack still, must reschedule the rest of the attack
- if (phase === Tone.Envelope.Phase.Attack) {
- this._sig.setCurrentValueNow();
- if (this.attackCurve === Tone.Envelope.Type.Linear) {
- this._sig.linearRampToValueAtTime(this._peakValue, this._nextRelease);
- } else {
- this._sig.exponentialRampToValueAtTime(this._peakValue, this._nextRelease);
- }
+ if (this._releaseCurve === Tone.Envelope.Type.Linear) {
+ this._sig.linearRampToValueBetween(this._minOutput, time, time + release);
} else {
- this._sig.setValueAtTime(this._peakValue, this._nextRelease);
+ this._sig.setTargetAtTime(this._minOutput, time, release * this._timeMult);
}
- this._sig.setTargetAtTime(this._minOutput, this._nextRelease, release * this._timeMult);
return this;
};
/**
@@ -1890,12 +3073,229 @@
* @type {GainNode}
* @private
*/
- this.input = this.output = this.context.createGain();
+ this.input = this.output = new Tone.Gain();
this._sig.connect(this.output.gain);
};
Tone.extend(Tone.AmplitudeEnvelope, Tone.Envelope);
+ /**
+ * Clean up
+ * @return {Tone.AmplitudeEnvelope} this
+ */
+ Tone.AmplitudeEnvelope.prototype.dispose = function () {
+ this.input.dispose();
+ this.input = null;
+ Tone.Envelope.prototype.dispose.call(this);
+ return this;
+ };
return Tone.AmplitudeEnvelope;
});
+ Module(function (Tone) {
+
+ /**
+ * @class Wrapper around the native Web Audio's
+ * [AnalyserNode](http://webaudio.github.io/web-audio-api/#idl-def-AnalyserNode).
+ * Extracts FFT or Waveform data from the incoming signal.
+ * @extends {Tone}
+ * @param {Number=} size The size of the FFT. Value must be a power of
+ * two in the range 32 to 32768.
+ * @param {String=} type The return type of the analysis, either "fft", or "waveform".
+ */
+ Tone.Analyser = function () {
+ var options = this.optionsObject(arguments, [
+ 'size',
+ 'type'
+ ], Tone.Analyser.defaults);
+ /**
+ * The analyser node.
+ * @private
+ * @type {AnalyserNode}
+ */
+ this._analyser = this.input = this.context.createAnalyser();
+ /**
+ * The analysis type
+ * @type {String}
+ * @private
+ */
+ this._type = options.type;
+ /**
+ * The return type of the analysis
+ * @type {String}
+ * @private
+ */
+ this._returnType = options.returnType;
+ /**
+ * The buffer that the FFT data is written to
+ * @type {TypedArray}
+ * @private
+ */
+ this._buffer = null;
+ //set the values initially
+ this.size = options.size;
+ this.type = options.type;
+ this.returnType = options.returnType;
+ this.minDecibels = options.minDecibels;
+ this.maxDecibels = options.maxDecibels;
+ };
+ Tone.extend(Tone.Analyser);
+ /**
+ * The default values.
+ * @type {Object}
+ * @const
+ */
+ Tone.Analyser.defaults = {
+ 'size': 2048,
+ 'returnType': 'byte',
+ 'type': 'fft',
+ 'smoothing': 0.8,
+ 'maxDecibels': -30,
+ 'minDecibels': -100
+ };
+ /**
+ * Possible return types of Tone.Analyser.value
+ * @enum {String}
+ */
+ Tone.Analyser.Type = {
+ Waveform: 'waveform',
+ FFT: 'fft'
+ };
+ /**
+ * Possible return types of Tone.Analyser.value
+ * @enum {String}
+ */
+ Tone.Analyser.ReturnType = {
+ Byte: 'byte',
+ Float: 'float'
+ };
+ /**
+ * Run the analysis given the current settings and return the
+ * result as a TypedArray.
+ * @returns {TypedArray}
+ */
+ Tone.Analyser.prototype.analyse = function () {
+ if (this._type === Tone.Analyser.Type.FFT) {
+ if (this._returnType === Tone.Analyser.ReturnType.Byte) {
+ this._analyser.getByteFrequencyData(this._buffer);
+ } else {
+ this._analyser.getFloatFrequencyData(this._buffer);
+ }
+ } else if (this._type === Tone.Analyser.Type.Waveform) {
+ if (this._returnType === Tone.Analyser.ReturnType.Byte) {
+ this._analyser.getByteTimeDomainData(this._buffer);
+ } else {
+ this._analyser.getFloatTimeDomainData(this._buffer);
+ }
+ }
+ return this._buffer;
+ };
+ /**
+ * The size of analysis. This must be a power of two in the range 32 to 32768.
+ * @memberOf Tone.Analyser#
+ * @type {Number}
+ * @name size
+ */
+ Object.defineProperty(Tone.Analyser.prototype, 'size', {
+ get: function () {
+ return this._analyser.frequencyBinCount;
+ },
+ set: function (size) {
+ this._analyser.fftSize = size * 2;
+ this.type = this._type;
+ }
+ });
+ /**
+ * The return type of Tone.Analyser.value, either "byte" or "float".
+ * When the type is set to "byte" the range of values returned in the array
+ * are between 0-255, when set to "float" the values are between 0-1.
+ * @memberOf Tone.Analyser#
+ * @type {String}
+ * @name type
+ */
+ Object.defineProperty(Tone.Analyser.prototype, 'returnType', {
+ get: function () {
+ return this._returnType;
+ },
+ set: function (type) {
+ if (type === Tone.Analyser.ReturnType.Byte) {
+ this._buffer = new Uint8Array(this._analyser.frequencyBinCount);
+ } else if (type === Tone.Analyser.ReturnType.Float) {
+ this._buffer = new Float32Array(this._analyser.frequencyBinCount);
+ } else {
+ throw new Error('Invalid Return Type: ' + type);
+ }
+ this._returnType = type;
+ }
+ });
+ /**
+ * The analysis function returned by Tone.Analyser.value, either "fft" or "waveform".
+ * @memberOf Tone.Analyser#
+ * @type {String}
+ * @name type
+ */
+ Object.defineProperty(Tone.Analyser.prototype, 'type', {
+ get: function () {
+ return this._type;
+ },
+ set: function (type) {
+ if (type !== Tone.Analyser.Type.Waveform && type !== Tone.Analyser.Type.FFT) {
+ throw new Error('Invalid Type: ' + type);
+ }
+ this._type = type;
+ }
+ });
+ /**
+ * 0 represents no time averaging with the last analysis frame.
+ * @memberOf Tone.Analyser#
+ * @type {NormalRange}
+ * @name smoothing
+ */
+ Object.defineProperty(Tone.Analyser.prototype, 'smoothing', {
+ get: function () {
+ return this._analyser.smoothingTimeConstant;
+ },
+ set: function (val) {
+ this._analyser.smoothingTimeConstant = val;
+ }
+ });
+ /**
+ * The smallest decibel value which is analysed by the FFT.
+ * @memberOf Tone.Analyser#
+ * @type {Decibels}
+ * @name minDecibels
+ */
+ Object.defineProperty(Tone.Analyser.prototype, 'minDecibels', {
+ get: function () {
+ return this._analyser.minDecibels;
+ },
+ set: function (val) {
+ this._analyser.minDecibels = val;
+ }
+ });
+ /**
+ * The largest decibel value which is analysed by the FFT.
+ * @memberOf Tone.Analyser#
+ * @type {Decibels}
+ * @name maxDecibels
+ */
+ Object.defineProperty(Tone.Analyser.prototype, 'maxDecibels', {
+ get: function () {
+ return this._analyser.maxDecibels;
+ },
+ set: function (val) {
+ this._analyser.maxDecibels = val;
+ }
+ });
+ /**
+ * Clean up.
+ * @return {Tone.Analyser} this
+ */
+ Tone.Analyser.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ this._analyser.disconnect();
+ this._analyser = null;
+ this._buffer = null;
+ };
+ return Tone.Analyser;
+ });
Module(function (Tone) {
/**
@@ -1934,13 +3334,13 @@
* @type {Time}
* @signal
*/
- this.attack = new Tone.Signal(this._compressor.attack, Tone.Type.Time);
+ this.attack = new Tone.Param(this._compressor.attack, Tone.Type.Time);
/**
* The release parameter
* @type {Time}
* @signal
*/
- this.release = new Tone.Signal(this._compressor.release, Tone.Type.Time);
+ this.release = new Tone.Param(this._compressor.release, Tone.Type.Time);
/**
* The knee parameter
* @type {Decibels}
@@ -2039,8 +3439,8 @@
* @private
* @type {Tone.Signal}
*/
- this._value = this.input[1] = new Tone.Signal(value);
- this._value.connect(this._sum);
+ this._param = this.input[1] = new Tone.Signal(value);
+ this._param.connect(this._sum);
};
Tone.extend(Tone.Add, Tone.Signal);
/**
@@ -2051,8 +3451,8 @@
Tone.prototype.dispose.call(this);
this._sum.disconnect();
this._sum = null;
- this._value.dispose();
- this._value = null;
+ this._param.dispose();
+ this._param = null;
return this;
};
return Tone.Add;
@@ -2094,8 +3494,8 @@
* @type {AudioParam}
* @private
*/
- this._value = this.input[1] = this.output.gain;
- this._value.value = this.defaultArg(value, 0);
+ this._param = this.input[1] = this.output.gain;
+ this._param.value = this.defaultArg(value, 0);
};
Tone.extend(Tone.Multiply, Tone.Signal);
/**
@@ -2106,7 +3506,7 @@
Tone.prototype.dispose.call(this);
this._mult.disconnect();
this._mult = null;
- this._value = null;
+ this._param = null;
return this;
};
return Tone.Multiply;
@@ -2187,8 +3587,8 @@
* @private
* @type {Tone.Signal}
*/
- this._value = this.input[1] = new Tone.Signal(value);
- this._value.chain(this._neg, this._sum);
+ this._param = this.input[1] = new Tone.Signal(value);
+ this._param.chain(this._neg, this._sum);
};
Tone.extend(Tone.Subtract, Tone.Signal);
/**
@@ -2201,8 +3601,8 @@
this._neg = null;
this._sum.disconnect();
this._sum = null;
- this._value.dispose();
- this._value = null;
+ this._param.dispose();
+ this._param = null;
return this;
};
return Tone.Subtract;
@@ -2659,8 +4059,8 @@
* @type {Tone.Subtract}
* @private
*/
- this._value = this.input[0] = new Tone.Subtract(value);
- this.input[1] = this._value.input[1];
+ this._param = this.input[0] = new Tone.Subtract(value);
+ this.input[1] = this._param.input[1];
/**
* compare that amount to zero
* @type {Tone.GreaterThanZero}
@@ -2668,7 +4068,7 @@
*/
this._gtz = this.output = new Tone.GreaterThanZero();
//connect
- this._value.connect(this._gtz);
+ this._param.connect(this._gtz);
};
Tone.extend(Tone.GreaterThan, Tone.Signal);
/**
@@ -2677,8 +4077,8 @@
*/
Tone.GreaterThan.prototype.dispose = function () {
Tone.prototype.dispose.call(this);
- this._value.dispose();
- this._value = null;
+ this._param.dispose();
+ this._param = null;
this._gtz.dispose();
this._gtz = null;
return this;
@@ -2726,10 +4126,10 @@
* @private
* @type {Tone.Signal}
*/
- this._value = this.input[1] = new Tone.Signal(value);
+ this._param = this.input[1] = new Tone.Signal(value);
//connect
this._neg.connect(this._gt);
- this._value.connect(this._rhNeg);
+ this._param.connect(this._rhNeg);
this._rhNeg.connect(this._gt, 0, 1);
};
Tone.extend(Tone.LessThan, Tone.Signal);
@@ -2745,8 +4145,8 @@
this._gt = null;
this._rhNeg.dispose();
this._rhNeg = null;
- this._value.dispose();
- this._value = null;
+ this._param.dispose();
+ this._param = null;
return this;
};
return Tone.LessThan;
@@ -2837,7 +4237,7 @@
* @type {Tone.Signal}
* @private
*/
- this._value = this.input[1] = new Tone.Signal(max);
+ this._param = this.input[1] = new Tone.Signal(max);
/**
* @type {Tone.Select}
* @private
@@ -2851,8 +4251,8 @@
//connections
this.input[0].chain(this._gt, this._ifThenElse.if);
this.input[0].connect(this._ifThenElse.then);
- this._value.connect(this._ifThenElse.else);
- this._value.connect(this._gt, 0, 1);
+ this._param.connect(this._ifThenElse.else);
+ this._param.connect(this._gt, 0, 1);
};
Tone.extend(Tone.Max, Tone.Signal);
/**
@@ -2861,10 +4261,10 @@
*/
Tone.Max.prototype.dispose = function () {
Tone.prototype.dispose.call(this);
- this._value.dispose();
+ this._param.dispose();
this._ifThenElse.dispose();
this._gt.dispose();
- this._value = null;
+ this._param = null;
this._ifThenElse = null;
this._gt = null;
return this;
@@ -2912,12 +4312,12 @@
* @type {Tone.Signal}
* @private
*/
- this._value = this.input[1] = new Tone.Signal(min);
+ this._param = this.input[1] = new Tone.Signal(min);
//connections
this.input[0].chain(this._lt, this._ifThenElse.if);
this.input[0].connect(this._ifThenElse.then);
- this._value.connect(this._ifThenElse.else);
- this._value.connect(this._lt, 0, 1);
+ this._param.connect(this._ifThenElse.else);
+ this._param.connect(this._lt, 0, 1);
};
Tone.extend(Tone.Min, Tone.Signal);
/**
@@ -2926,10 +4326,10 @@
*/
Tone.Min.prototype.dispose = function () {
Tone.prototype.dispose.call(this);
- this._value.dispose();
+ this._param.dispose();
this._ifThenElse.dispose();
this._lt.dispose();
- this._value = null;
+ this._param = null;
this._ifThenElse = null;
this._lt = null;
return this;
@@ -3028,6 +4428,39 @@
};
return Tone.Modulo;
});
+ Module(function (Tone) {
+
+ /**
+ * @class AudioToGain converts an input in AudioRange [-1,1] to NormalRange [0,1].
+ * See Tone.GainToAudio.
+ *
+ * @extends {Tone.SignalBase}
+ * @constructor
+ * @example
+ * var a2g = new Tone.AudioToGain();
+ */
+ Tone.AudioToGain = function () {
+ /**
+ * @type {WaveShaperNode}
+ * @private
+ */
+ this._norm = this.input = this.output = new Tone.WaveShaper(function (x) {
+ return (x + 1) / 2;
+ });
+ };
+ Tone.extend(Tone.AudioToGain, Tone.SignalBase);
+ /**
+ * clean up
+ * @returns {Tone.AudioToGain} this
+ */
+ Tone.AudioToGain.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ this._norm.dispose();
+ this._norm = null;
+ return this;
+ };
+ return Tone.AudioToGain;
+ });
Module(function (Tone) {
/**
@@ -3176,6 +4609,14 @@
self._eval(args[0]).connect(op);
return op;
}
+ },
+ 'a2g': {
+ regexp: /^a2g/,
+ method: function (args, self) {
+ var op = new Tone.AudioToGain();
+ self._eval(args[0]).connect(op);
+ return op;
+ }
}
},
//binary expressions
@@ -3666,7 +5107,7 @@
*/
this.gain = new Tone.Signal({
'value': options.gain,
- 'units': Tone.Type.Decibels,
+ 'units': Tone.Type.Gain,
'convert': false
});
/**
@@ -3733,7 +5174,7 @@
'peaking'
];
if (types.indexOf(type) === -1) {
- throw new TypeError('Tone.Filter does not have filter type ' + type);
+ throw new Error('Tone.Filter does not have filter type ' + type);
}
this._type = type;
for (var i = 0; i < this._filters.length; i++) {
@@ -3744,7 +5185,7 @@
/**
* The rolloff of the filter which is the drop in db
* per octave. Implemented internally by cascading filters.
- * Only accepts the values -12, -24, and -48.
+ * Only accepts the values -12, -24, -48 and -96.
* @memberOf Tone.Filter#
* @type {number}
* @name rolloff
@@ -3754,17 +5195,19 @@
return this._rolloff;
},
set: function (rolloff) {
+ rolloff = parseInt(rolloff, 10);
var possibilities = [
-12,
-24,
- -48
+ -48,
+ -96
];
var cascadingCount = possibilities.indexOf(rolloff);
//check the rolloff is valid
if (cascadingCount === -1) {
- throw new RangeError('Filter rolloff can only be -12, -24, or -48');
+ throw new Error('Filter rolloff can only be -12, -24, -48 or -96');
}
- cascadingCount++;
+ cascadingCount += 1;
this._rolloff = rolloff;
//first disconnect the filters and throw them away
this.input.disconnect();
@@ -3981,42 +5424,24 @@
'lowFrequency': options.lowFrequency,
'highFrequency': options.highFrequency
});
- /**
- * the low gain
- * @type {GainNode}
- * @private
- */
- this._lowGain = this.context.createGain();
- /**
- * the mid gain
- * @type {GainNode}
- * @private
- */
- this._midGain = this.context.createGain();
- /**
- * the high gain
- * @type {GainNode}
- * @private
- */
- this._highGain = this.context.createGain();
/**
* The gain in decibels of the low part
* @type {Decibels}
* @signal
*/
- this.low = new Tone.Signal(this._lowGain.gain, Tone.Type.Decibels);
+ this.low = new Tone.Gain(options.low, Tone.Type.Decibels);
/**
* The gain in decibels of the mid part
* @type {Decibels}
* @signal
*/
- this.mid = new Tone.Signal(this._midGain.gain, Tone.Type.Decibels);
+ this.mid = new Tone.Gain(options.mid, Tone.Type.Decibels);
/**
* The gain in decibels of the high part
* @type {Decibels}
* @signal
*/
- this.high = new Tone.Signal(this._highGain.gain, Tone.Type.Decibels);
+ this.high = new Tone.Gain(options.high, Tone.Type.Decibels);
/**
* The Q value for all of the filters.
* @type {Positive}
@@ -4036,13 +5461,9 @@
*/
this.highFrequency = this._multibandSplit.highFrequency;
//the frequency bands
- this._multibandSplit.low.chain(this._lowGain, this.output);
- this._multibandSplit.mid.chain(this._midGain, this.output);
- this._multibandSplit.high.chain(this._highGain, this.output);
- //set the gains
- this.low.value = options.low;
- this.mid.value = options.mid;
- this.high.value = options.high;
+ this._multibandSplit.low.chain(this.low, this.output);
+ this._multibandSplit.mid.chain(this.mid, this.output);
+ this._multibandSplit.high.chain(this.high, this.output);
this._readOnly([
'low',
'mid',
@@ -4079,12 +5500,6 @@
this._multibandSplit = null;
this.lowFrequency = null;
this.highFrequency = null;
- this._lowGain.disconnect();
- this._lowGain = null;
- this._midGain.disconnect();
- this._midGain = null;
- this._highGain.disconnect();
- this._highGain = null;
this.low.dispose();
this.low = null;
this.mid.dispose();
@@ -4301,12 +5716,6 @@
'delayTime',
'resonance'
], Tone.FeedbackCombFilter.defaults);
- /**
- * The amount of feedback of the delayed signal.
- * @type {NormalRange}
- * @signal
- */
- this.resonance = new Tone.Signal(options.resonance, Tone.Type.NormalRange);
/**
* the delay node
* @type {DelayNode}
@@ -4318,16 +5727,28 @@
* @type {Time}
* @signal
*/
- this.delayTime = new Tone.Signal(options.delayTime, Tone.Type.Time);
+ this.delayTime = new Tone.Param({
+ 'param': this._delay.delayTime,
+ 'value': options.delayTime,
+ 'units': Tone.Type.Time
+ });
/**
* the feedback node
* @type {GainNode}
* @private
*/
this._feedback = this.context.createGain();
+ /**
+ * The amount of feedback of the delayed signal.
+ * @type {NormalRange}
+ * @signal
+ */
+ this.resonance = new Tone.Param({
+ 'param': this._feedback.gain,
+ 'value': options.resonance,
+ 'units': Tone.Type.NormalRange
+ });
this._delay.chain(this._feedback, this._delay);
- this.resonance.connect(this._feedback.gain);
- this.delayTime.connect(this._delay.delayTime);
this._readOnly([
'resonance',
'delayTime'
@@ -4418,7 +5839,7 @@
* @private
*/
this._delay = this.context.createDelay();
- this._delay.delayTime.value = this.bufferTime;
+ this._delay.delayTime.value = this.blockTime;
/**
* this keeps it far from 0, even for very small differences
* @type {Tone.Multiply}
@@ -4461,7 +5882,7 @@
* @private
*/
Tone.Follower.prototype._setAttackRelease = function (attack, release) {
- var minTime = this.bufferTime;
+ var minTime = this.blockTime;
attack = this.secondsToFrequency(this.toSeconds(attack));
release = this.secondsToFrequency(this.toSeconds(release));
attack = Math.max(attack, minTime);
@@ -4641,6 +6062,54 @@
};
return Tone.Gate;
});
+ Module(function (Tone) {
+
+ /**
+ * @class A Timeline State. Provides the methods: setStateAtTime("state", time)
+ * and getStateAtTime(time)
.
+ *
+ * @extends {Tone.Timeline}
+ * @param {String} initial The initial state of the TimelineState.
+ * Defaults to undefined
+ */
+ Tone.TimelineState = function (initial) {
+ Tone.Timeline.call(this);
+ /**
+ * The initial state
+ * @private
+ * @type {String}
+ */
+ this._initial = initial;
+ };
+ Tone.extend(Tone.TimelineState, Tone.Timeline);
+ /**
+ * Returns the scheduled state scheduled before or at
+ * the given time.
+ * @param {Time} time The time to query.
+ * @return {String} The name of the state input in setStateAtTime.
+ */
+ Tone.TimelineState.prototype.getStateAtTime = function (time) {
+ var event = this.getEvent(time);
+ if (event !== null) {
+ return event.state;
+ } else {
+ return this._initial;
+ }
+ };
+ /**
+ * Returns the scheduled state scheduled before or at
+ * the given time.
+ * @param {String} state The name of the state to set.
+ * @param {Time} time The time to query.
+ */
+ Tone.TimelineState.prototype.setStateAtTime = function (state, time) {
+ this.addEvent({
+ 'state': state,
+ 'time': this.toSeconds(time)
+ });
+ };
+ return Tone.TimelineState;
+ });
Module(function (Tone) {
/**
@@ -4648,168 +6117,963 @@
* While the callback is not sample-accurate (it is still susceptible to
* loose JS timing), the time passed in as the argument to the callback
* is precise. For most applications, it is better to use Tone.Transport
- * instead of the clock.
+ * instead of the Clock by itself since you can synchronize multiple callbacks.
*
* @constructor
* @extends {Tone}
- * @param {Frequency} frequency The rate of the callback
* @param {function} callback The callback to be invoked with the time of the audio event
+ * @param {Frequency} frequency The rate of the callback
* @example
* //the callback will be invoked approximately once a second
* //and will print the time exactly once a second apart.
- * var clock = new Tone.Clock(1, function(time){
+ * var clock = new Tone.Clock(function(time){
* console.log(time);
- * });
+ * }, 1);
*/
- Tone.Clock = function (frequency, callback) {
+ Tone.Clock = function () {
+ var options = this.optionsObject(arguments, [
+ 'callback',
+ 'frequency'
+ ], Tone.Clock.defaults);
/**
- * the oscillator
- * @type {OscillatorNode}
+ * The callback function to invoke at the scheduled tick.
+ * @type {Function}
+ */
+ this.callback = options.callback;
+ /**
+ * The time which the clock will schedule events in advance
+ * of the current time. Scheduling notes in advance improves
+ * performance and decreases the chance for clicks caused
+ * by scheduling events in the past. If set to "auto",
+ * this value will be automatically computed based on the
+ * rate of requestAnimationFrame (0.016 seconds). Larger values
+ * will yeild better performance, but at the cost of latency.
+ * Values less than 0.016 are not recommended.
+ * @type {Number|String}
+ */
+ this._lookAhead = 'auto';
+ /**
+ * The lookahead value which was automatically
+ * computed using a time-based averaging.
+ * @type {Number}
* @private
*/
- this._oscillator = null;
+ this._computedLookAhead = 1 / 60;
/**
- * the script processor which listens to the oscillator
- * @type {ScriptProcessorNode}
+ * The value afterwhich events are thrown out
+ * @type {Number}
* @private
*/
- this._jsNode = this.context.createScriptProcessor(this.bufferSize, 1, 1);
- this._jsNode.onaudioprocess = this._processBuffer.bind(this);
+ this._threshold = 0.5;
/**
- * The frequency in which the callback will be invoked.
- * @type {Frequency}
+ * The next time the callback is scheduled.
+ * @type {Number}
+ * @private
+ */
+ this._nextTick = -1;
+ /**
+ * The last time the callback was invoked
+ * @type {Number}
+ * @private
+ */
+ this._lastUpdate = 0;
+ /**
+ * The id of the requestAnimationFrame
+ * @type {Number}
+ * @private
+ */
+ this._loopID = -1;
+ /**
+ * The rate the callback function should be invoked.
+ * @type {BPM}
* @signal
*/
- this.frequency = new Tone.Signal(frequency, Tone.Type.Frequency);
+ this.frequency = new Tone.TimelineSignal(options.frequency, Tone.Type.Frequency);
/**
- * whether the tick is on the up or down
- * @type {boolean}
+ * The number of times the callback was invoked. Starts counting at 0
+ * and increments after the callback was invoked.
+ * @type {Ticks}
+ * @readOnly
+ */
+ this.ticks = 0;
+ /**
+ * The state timeline
+ * @type {Tone.TimelineState}
* @private
*/
- this._upTick = false;
+ this._state = new Tone.TimelineState(Tone.State.Stopped);
/**
- * The callback which is invoked on every tick
- * with the time of that tick as the argument
- * @type {function(number)}
+ * A pre-binded loop function to save a tiny bit of overhead
+ * of rebinding the function on every frame.
+ * @type {Function}
+ * @private
*/
- this.tick = callback;
- /**
- * Callback is invoked when the clock is stopped.
- * @type {function}
- * @example
- * clock.onended = function(){
- * console.log("the clock is stopped");
- * }
- */
- this.onended = Tone.noOp;
- //setup
- this._jsNode.noGC();
+ this._boundLoop = this._loop.bind(this);
+ this._readOnly('frequency');
+ //start the loop
+ this._loop();
};
Tone.extend(Tone.Clock);
/**
- * Start the clock.
- * @param {Time} [time=now] the time when the clock should start
- * @returns {Tone.Clock} this
- * @example
- * clock.start();
+ * The defaults
+ * @const
+ * @type {Object}
*/
- Tone.Clock.prototype.start = function (time) {
- if (!this._oscillator) {
- this._oscillator = this.context.createOscillator();
- this._oscillator.type = 'square';
- this._oscillator.connect(this._jsNode);
- //connect it up
- this.frequency.connect(this._oscillator.frequency);
- this._upTick = false;
- var startTime = this.toSeconds(time);
- this._oscillator.start(startTime);
+ Tone.Clock.defaults = {
+ 'callback': Tone.noOp,
+ 'frequency': 1,
+ 'lookAhead': 'auto'
+ };
+ /**
+ * Returns the playback state of the source, either "started", "stopped" or "paused".
+ * @type {Tone.State}
+ * @readOnly
+ * @memberOf Tone.Clock#
+ * @name state
+ */
+ Object.defineProperty(Tone.Clock.prototype, 'state', {
+ get: function () {
+ return this._state.getStateAtTime(this.now());
+ }
+ });
+ /**
+ * The time which the clock will schedule events in advance
+ * of the current time. Scheduling notes in advance improves
+ * performance and decreases the chance for clicks caused
+ * by scheduling events in the past. If set to "auto",
+ * this value will be automatically computed based on the
+ * rate of requestAnimationFrame (0.016 seconds). Larger values
+ * will yeild better performance, but at the cost of latency.
+ * Values less than 0.016 are not recommended.
+ * @type {Number|String}
+ * @memberOf Tone.Clock#
+ * @name lookAhead
+ */
+ Object.defineProperty(Tone.Clock.prototype, 'lookAhead', {
+ get: function () {
+ return this._lookAhead;
+ },
+ set: function (val) {
+ if (val === 'auto') {
+ this._lookAhead = 'auto';
+ } else {
+ this._lookAhead = this.toSeconds(val);
+ }
+ }
+ });
+ /**
+ * Start the clock at the given time. Optionally pass in an offset
+ * of where to start the tick counter from.
+ * @param {Time} time The time the clock should start
+ * @param {Ticks=} offset Where the tick counter starts counting from.
+ * @return {Tone.Clock} this
+ */
+ Tone.Clock.prototype.start = function (time, offset) {
+ time = this.toSeconds(time);
+ if (this._state.getStateAtTime(time) !== Tone.State.Started) {
+ this._state.addEvent({
+ 'state': Tone.State.Started,
+ 'time': time,
+ 'offset': offset
+ });
}
return this;
};
/**
- * Stop the clock.
+ * Stop the clock. Stopping the clock resets the tick counter to 0.
* @param {Time} [time=now] The time when the clock should stop.
* @returns {Tone.Clock} this
* @example
* clock.stop();
*/
Tone.Clock.prototype.stop = function (time) {
- if (this._oscillator) {
- var now = this.now();
- var stopTime = this.toSeconds(time, now);
- this._oscillator.stop(stopTime);
- this._oscillator = null;
- if (time) {
- //set a timeout for when it stops
- setTimeout(this.onended, (stopTime - now) * 1000);
- } else {
- this.onended();
- }
+ time = this.toSeconds(time);
+ if (this._state.getStateAtTime(time) !== Tone.State.Stopped) {
+ this._state.setStateAtTime(Tone.State.Stopped, time);
}
return this;
};
/**
- * @private
- * @param {AudioProcessingEvent} event
+ * Pause the clock. Pausing does not reset the tick counter.
+ * @param {Time} [time=now] The time when the clock should stop.
+ * @returns {Tone.Clock} this
*/
- Tone.Clock.prototype._processBuffer = function (event) {
- var now = this.defaultArg(event.playbackTime, this.now());
- var bufferSize = this._jsNode.bufferSize;
- var incomingBuffer = event.inputBuffer.getChannelData(0);
- var upTick = this._upTick;
- var self = this;
- for (var i = 0; i < bufferSize; i++) {
- var sample = incomingBuffer[i];
- if (sample > 0 && !upTick) {
- upTick = true;
- //get the callback out of audio thread
- setTimeout(function () {
- //to account for the double buffering
- var tickTime = now + self.samplesToSeconds(i + bufferSize * 2);
- return function () {
- if (self.tick) {
- self.tick(tickTime);
- }
- };
- }(), 0); // jshint ignore:line
- } else if (sample < 0 && upTick) {
- upTick = false;
- }
+ Tone.Clock.prototype.pause = function (time) {
+ time = this.toSeconds(time);
+ if (this._state.getStateAtTime(time) === Tone.State.Started) {
+ this._state.setStateAtTime(Tone.State.Paused, time);
}
- this._upTick = upTick;
+ return this;
};
/**
- * Clean up.
+ * The scheduling loop.
+ * @param {Number} time The current page time starting from 0
+ * when the page was loaded.
+ * @private
+ */
+ Tone.Clock.prototype._loop = function (time) {
+ this._loopID = requestAnimationFrame(this._boundLoop);
+ //compute the look ahead
+ if (this._lookAhead === 'auto') {
+ if (!this.isUndef(time)) {
+ var diff = (time - this._lastUpdate) / 1000;
+ this._lastUpdate = time;
+ //throw away large differences
+ if (diff < this._threshold) {
+ //averaging
+ this._computedLookAhead = (9 * this._computedLookAhead + diff) / 10;
+ }
+ }
+ } else {
+ this._computedLookAhead = this._lookAhead;
+ }
+ //get the frequency value to compute the value of the next loop
+ var now = this.now();
+ //if it's started
+ var lookAhead = this._computedLookAhead * 2;
+ var event = this._state.getEvent(now + lookAhead);
+ var state = Tone.State.Stopped;
+ if (event) {
+ state = event.state;
+ //if it was stopped and now started
+ if (this._nextTick === -1 && state === Tone.State.Started) {
+ this._nextTick = event.time;
+ if (!this.isUndef(event.offset)) {
+ this.ticks = event.offset;
+ }
+ }
+ }
+ if (state === Tone.State.Started) {
+ while (now + lookAhead > this._nextTick) {
+ //catch up
+ if (now > this._nextTick + this._threshold) {
+ this._nextTick = now;
+ }
+ var tickTime = this._nextTick;
+ this._nextTick += 1 / this.frequency.getValueAtTime(this._nextTick);
+ this.callback(tickTime);
+ this.ticks++;
+ }
+ } else if (state === Tone.State.Stopped) {
+ this._nextTick = -1;
+ this.ticks = 0;
+ }
+ };
+ /**
+ * Returns the scheduled state at the given time.
+ * @param {Time} time The time to query.
+ * @return {String} The name of the state input in setStateAtTime.
+ * @example
+ * clock.start("+0.1");
+ * clock.getStateAtTime("+0.1"); //returns "started"
+ */
+ Tone.Clock.prototype.getStateAtTime = function (time) {
+ return this._state.getStateAtTime(time);
+ };
+ /**
+ * Clean up
* @returns {Tone.Clock} this
*/
Tone.Clock.prototype.dispose = function () {
- this._jsNode.disconnect();
+ cancelAnimationFrame(this._loopID);
+ Tone.TimelineState.prototype.dispose.call(this);
+ this._writable('frequency');
this.frequency.dispose();
this.frequency = null;
- if (this._oscillator) {
- this._oscillator.disconnect();
- this._oscillator = null;
- }
- this._jsNode.onaudioprocess = Tone.noOp;
- this._jsNode = null;
- this.tick = null;
- this.onended = Tone.noOp;
- return this;
+ this._boundLoop = Tone.noOp;
+ this._nextTick = Infinity;
+ this.callback = null;
+ this._state.dispose();
+ this._state = null;
};
return Tone.Clock;
});
Module(function (Tone) {
/**
- * @class Oscillator-based transport allows for timing musical events.
- * Supports tempo curves and time changes. A single transport is created
- * on initialization. Unlike browser-based timing (setInterval, requestAnimationFrame)
+ * @class Tone.EventEmitter gives classes which extend it
+ * the ability to listen for and trigger events.
+ * Inspiration and reference from Jerome Etienne's [MicroEvent](https://github.com/jeromeetienne/microevent.js).
+ * MIT (c) 2011 Jerome Etienne.
+ *
+ * @extends {Tone}
+ */
+ Tone.EventEmitter = function () {
+ /**
+ * Contains all of the events.
+ * @private
+ * @type {Object}
+ */
+ this._events = {};
+ };
+ Tone.extend(Tone.EventEmitter);
+ /**
+ * Bind a callback to a specific event.
+ * @param {String} event The name of the event to listen for.
+ * @param {Function} callback The callback to invoke when the
+ * event is triggered
+ * @return {Tone.EventEmitter} this
+ */
+ Tone.EventEmitter.prototype.on = function (event, callback) {
+ //split the event
+ var events = event.split(/\W+/);
+ for (var i = 0; i < events.length; i++) {
+ var eventName = events[i];
+ if (!this._events.hasOwnProperty(eventName)) {
+ this._events[eventName] = [];
+ }
+ this._events[eventName].push(callback);
+ }
+ return this;
+ };
+ /**
+ * Remove the event listener.
+ * @param {String} event The event to stop listening to.
+ * @param {Function=} callback The callback which was bound to
+ * the event with Tone.EventEmitter.on.
+ * If no callback is given, all callbacks
+ * events are removed.
+ * @return {Tone.EventEmitter} this
+ */
+ Tone.EventEmitter.prototype.off = function (event, callback) {
+ var events = event.split(/\W+/);
+ for (var ev = 0; ev < events.length; ev++) {
+ event = events[ev];
+ if (this._events.hasOwnProperty(event)) {
+ if (this.isUndef(callback)) {
+ this._events[event] = [];
+ } else {
+ var eventList = this._events[event];
+ for (var i = 0; i < eventList.length; i++) {
+ if (eventList[i] === callback) {
+ eventList.splice(i, 1);
+ }
+ }
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * Invoke all of the callbacks bound to the event
+ * with any arguments passed in.
+ * @param {String} event The name of the event.
+ * @param {*...} args The arguments to pass to the functions listening.
+ * @return {Tone.EventEmitter} this
+ */
+ Tone.EventEmitter.prototype.trigger = function (event) {
+ if (this._events) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (this._events.hasOwnProperty(event)) {
+ var eventList = this._events[event];
+ for (var i = 0, len = eventList.length; i < len; i++) {
+ eventList[i].apply(this, args);
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * Add EventEmitter functions (on/off/trigger) to the object
+ * @param {Object|Function} object The object or class to extend.
+ */
+ Tone.EventEmitter.mixin = function (object) {
+ var functions = [
+ 'on',
+ 'off',
+ 'trigger'
+ ];
+ object._events = {};
+ for (var i = 0; i < functions.length; i++) {
+ var func = functions[i];
+ var emitterFunc = Tone.EventEmitter.prototype[func];
+ object[func] = emitterFunc;
+ }
+ };
+ /**
+ * Clean up
+ * @return {Tone.EventEmitter} this
+ */
+ Tone.EventEmitter.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ this._events = null;
+ return this;
+ };
+ return Tone.EventEmitter;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class Similar to Tone.Timeline, but all events represent
+ * intervals with both "time" and "duration" times. The
+ * events are placed in a tree structure optimized
+ * for querying an intersection point with the timeline
+ * events. Internally uses an [Interval Tree](https://en.wikipedia.org/wiki/Interval_tree)
+ * to represent the data.
+ * @extends {Tone}
+ */
+ Tone.IntervalTimeline = function () {
+ /**
+ * The root node of the inteval tree
+ * @type {IntervalNode}
+ * @private
+ */
+ this._root = null;
+ /**
+ * Keep track of the length of the timeline.
+ * @type {Number}
+ * @private
+ */
+ this._length = 0;
+ };
+ Tone.extend(Tone.IntervalTimeline);
+ /**
+ * The event to add to the timeline. All events must
+ * have a time and duration value
+ * @param {Object} event The event to add to the timeline
+ * @return {Tone.IntervalTimeline} this
+ */
+ Tone.IntervalTimeline.prototype.addEvent = function (event) {
+ if (this.isUndef(event.time) || this.isUndef(event.duration)) {
+ throw new Error('events must have time and duration parameters');
+ }
+ var node = new IntervalNode(event.time, event.time + event.duration, event);
+ if (this._root === null) {
+ this._root = node;
+ } else {
+ this._root.insert(node);
+ }
+ this._length++;
+ // Restructure tree to be balanced
+ while (node !== null) {
+ node.updateHeight();
+ node.updateMax();
+ this._rebalance(node);
+ node = node.parent;
+ }
+ return this;
+ };
+ /**
+ * Remove an event from the timeline.
+ * @param {Object} event The event to remove from the timeline
+ * @return {Tone.IntervalTimeline} this
+ */
+ Tone.IntervalTimeline.prototype.removeEvent = function (event) {
+ if (this._root !== null) {
+ var results = [];
+ this._root.search(event.time, results);
+ for (var i = 0; i < results.length; i++) {
+ var node = results[i];
+ if (node.event === event) {
+ this._removeNode(node);
+ this._length--;
+ break;
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * The number of items in the timeline.
+ * @type {Number}
+ * @memberOf Tone.IntervalTimeline#
+ * @name length
+ * @readOnly
+ */
+ Object.defineProperty(Tone.IntervalTimeline.prototype, 'length', {
+ get: function () {
+ return this._length;
+ }
+ });
+ /**
+ * Remove events whose time time is after the given time
+ * @param {Time} time The time to query.
+ * @returns {Tone.IntervalTimeline} this
+ */
+ Tone.IntervalTimeline.prototype.cancel = function (after) {
+ after = this.toSeconds(after);
+ this.forEachAfter(after, function (event) {
+ this.removeEvent(event);
+ }.bind(this));
+ return this;
+ };
+ /**
+ * Set the root node as the given node
+ * @param {IntervalNode} node
+ * @private
+ */
+ Tone.IntervalTimeline.prototype._setRoot = function (node) {
+ this._root = node;
+ if (this._root !== null) {
+ this._root.parent = null;
+ }
+ };
+ /**
+ * Replace the references to the node in the node's parent
+ * with the replacement node.
+ * @param {IntervalNode} node
+ * @param {IntervalNode} replacement
+ * @private
+ */
+ Tone.IntervalTimeline.prototype._replaceNodeInParent = function (node, replacement) {
+ if (node.parent !== null) {
+ if (node.isLeftChild()) {
+ node.parent.left = replacement;
+ } else {
+ node.parent.right = replacement;
+ }
+ this._rebalance(node.parent);
+ } else {
+ this._setRoot(replacement);
+ }
+ };
+ /**
+ * Remove the node from the tree and replace it with
+ * a successor which follows the schema.
+ * @param {IntervalNode} node
+ * @private
+ */
+ Tone.IntervalTimeline.prototype._removeNode = function (node) {
+ if (node.left === null && node.right === null) {
+ this._replaceNodeInParent(node, null);
+ } else if (node.right === null) {
+ this._replaceNodeInParent(node, node.left);
+ } else if (node.left === null) {
+ this._replaceNodeInParent(node, node.right);
+ } else {
+ var balance = node.getBalance();
+ var replacement, temp;
+ if (balance > 0) {
+ if (node.left.right === null) {
+ replacement = node.left;
+ replacement.right = node.right;
+ temp = replacement;
+ } else {
+ replacement = node.left.right;
+ while (replacement.right !== null) {
+ replacement = replacement.right;
+ }
+ replacement.parent.right = replacement.left;
+ temp = replacement.parent;
+ replacement.left = node.left;
+ replacement.right = node.right;
+ }
+ } else {
+ if (node.right.left === null) {
+ replacement = node.right;
+ replacement.left = node.left;
+ temp = replacement;
+ } else {
+ replacement = node.right.left;
+ while (replacement.left !== null) {
+ replacement = replacement.left;
+ }
+ replacement.parent = replacement.parent;
+ replacement.parent.left = replacement.right;
+ temp = replacement.parent;
+ replacement.left = node.left;
+ replacement.right = node.right;
+ }
+ }
+ if (node.parent !== null) {
+ if (node.isLeftChild()) {
+ node.parent.left = replacement;
+ } else {
+ node.parent.right = replacement;
+ }
+ } else {
+ this._setRoot(replacement);
+ }
+ // this._replaceNodeInParent(node, replacement);
+ this._rebalance(temp);
+ }
+ node.dispose();
+ };
+ /**
+ * Rotate the tree to the left
+ * @param {IntervalNode} node
+ * @private
+ */
+ Tone.IntervalTimeline.prototype._rotateLeft = function (node) {
+ var parent = node.parent;
+ var isLeftChild = node.isLeftChild();
+ // Make node.right the new root of this sub tree (instead of node)
+ var pivotNode = node.right;
+ node.right = pivotNode.left;
+ pivotNode.left = node;
+ if (parent !== null) {
+ if (isLeftChild) {
+ parent.left = pivotNode;
+ } else {
+ parent.right = pivotNode;
+ }
+ } else {
+ this._setRoot(pivotNode);
+ }
+ };
+ /**
+ * Rotate the tree to the right
+ * @param {IntervalNode} node
+ * @private
+ */
+ Tone.IntervalTimeline.prototype._rotateRight = function (node) {
+ var parent = node.parent;
+ var isLeftChild = node.isLeftChild();
+ // Make node.left the new root of this sub tree (instead of node)
+ var pivotNode = node.left;
+ node.left = pivotNode.right;
+ pivotNode.right = node;
+ if (parent !== null) {
+ if (isLeftChild) {
+ parent.left = pivotNode;
+ } else {
+ parent.right = pivotNode;
+ }
+ } else {
+ this._setRoot(pivotNode);
+ }
+ };
+ /**
+ * Balance the BST
+ * @param {IntervalNode} node
+ * @private
+ */
+ Tone.IntervalTimeline.prototype._rebalance = function (node) {
+ var balance = node.getBalance();
+ if (balance > 1) {
+ if (node.left.getBalance() < 0) {
+ this._rotateLeft(node.left);
+ } else {
+ this._rotateRight(node);
+ }
+ } else if (balance < -1) {
+ if (node.right.getBalance() > 0) {
+ this._rotateRight(node.right);
+ } else {
+ this._rotateLeft(node);
+ }
+ }
+ };
+ /**
+ * Get an event whose time and duration span the give time. Will
+ * return the match whose "time" value is closest to the given time.
+ * @param {Object} event The event to add to the timeline
+ * @return {Object} The event which spans the desired time
+ */
+ Tone.IntervalTimeline.prototype.getEvent = function (time) {
+ if (this._root !== null) {
+ var results = [];
+ this._root.search(time, results);
+ if (results.length > 0) {
+ var max = results[0];
+ for (var i = 1; i < results.length; i++) {
+ if (results[i].low > max.low) {
+ max = results[i];
+ }
+ }
+ return max.event;
+ }
+ }
+ return null;
+ };
+ /**
+ * Iterate over everything in the timeline.
+ * @param {Function} callback The callback to invoke with every item
+ * @returns {Tone.IntervalTimeline} this
+ */
+ Tone.IntervalTimeline.prototype.forEach = function (callback) {
+ if (this._root !== null) {
+ var allNodes = [];
+ if (this._root !== null) {
+ this._root.traverse(function (node) {
+ allNodes.push(node);
+ });
+ }
+ for (var i = 0; i < allNodes.length; i++) {
+ callback(allNodes[i].event);
+ }
+ }
+ return this;
+ };
+ /**
+ * Iterate over everything in the array in which the given time
+ * overlaps with the time and duration time of the event.
+ * @param {Time} time The time to check if items are overlapping
+ * @param {Function} callback The callback to invoke with every item
+ * @returns {Tone.IntervalTimeline} this
+ */
+ Tone.IntervalTimeline.prototype.forEachOverlap = function (time, callback) {
+ //iterate over the items in reverse so that removing an item doesn't break things
+ time = this.toSeconds(time);
+ if (this._root !== null) {
+ var results = [];
+ this._root.search(time, results);
+ for (var i = results.length - 1; i >= 0; i--) {
+ callback(results[i].event);
+ }
+ }
+ return this;
+ };
+ /**
+ * Iterate over everything in the array in which the time is greater
+ * than the given time.
+ * @param {Time} time The time to check if items are before
+ * @param {Function} callback The callback to invoke with every item
+ * @returns {Tone.IntervalTimeline} this
+ */
+ Tone.IntervalTimeline.prototype.forEachAfter = function (time, callback) {
+ //iterate over the items in reverse so that removing an item doesn't break things
+ time = this.toSeconds(time);
+ if (this._root !== null) {
+ var results = [];
+ this._root.searchAfter(time, results);
+ for (var i = results.length - 1; i >= 0; i--) {
+ callback(results[i].event);
+ }
+ }
+ return this;
+ };
+ /**
+ * Clean up
+ * @return {Tone.IntervalTimeline} this
+ */
+ Tone.IntervalTimeline.prototype.dispose = function () {
+ var allNodes = [];
+ if (this._root !== null) {
+ this._root.traverse(function (node) {
+ allNodes.push(node);
+ });
+ }
+ for (var i = 0; i < allNodes.length; i++) {
+ allNodes[i].dispose();
+ }
+ allNodes = null;
+ this._root = null;
+ return this;
+ };
+ ///////////////////////////////////////////////////////////////////////////
+ // INTERVAL NODE HELPER
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Represents a node in the binary search tree, with the addition
+ * of a "high" value which keeps track of the highest value of
+ * its children.
+ * References:
+ * https://brooknovak.wordpress.com/2013/12/07/augmented-interval-tree-in-c/
+ * http://www.mif.vu.lt/~valdas/ALGORITMAI/LITERATURA/Cormen/Cormen.pdf
+ * @param {Number} low
+ * @param {Number} high
+ * @private
+ */
+ var IntervalNode = function (low, high, event) {
+ //the event container
+ this.event = event;
+ //the low value
+ this.low = low;
+ //the high value
+ this.high = high;
+ //the high value for this and all child nodes
+ this.max = this.high;
+ //the nodes to the left
+ this._left = null;
+ //the nodes to the right
+ this._right = null;
+ //the parent node
+ this.parent = null;
+ //the number of child nodes
+ this.height = 0;
+ };
+ /**
+ * Insert a node into the correct spot in the tree
+ * @param {IntervalNode} node
+ */
+ IntervalNode.prototype.insert = function (node) {
+ if (node.low <= this.low) {
+ if (this.left === null) {
+ this.left = node;
+ } else {
+ this.left.insert(node);
+ }
+ } else {
+ if (this.right === null) {
+ this.right = node;
+ } else {
+ this.right.insert(node);
+ }
+ }
+ };
+ /**
+ * Search the tree for nodes which overlap
+ * with the given point
+ * @param {Number} point The point to query
+ * @param {Array} results The array to put the results
+ */
+ IntervalNode.prototype.search = function (point, results) {
+ // If p is to the right of the rightmost point of any interval
+ // in this node and all children, there won't be any matches.
+ if (point > this.max) {
+ return;
+ }
+ // Search left children
+ if (this.left !== null) {
+ this.left.search(point, results);
+ }
+ // Check this node
+ if (this.low <= point && this.high >= point) {
+ results.push(this);
+ }
+ // If p is to the left of the time of this interval,
+ // then it can't be in any child to the right.
+ if (this.low > point) {
+ return;
+ }
+ // Search right children
+ if (this.right !== null) {
+ this.right.search(point, results);
+ }
+ };
+ /**
+ * Search the tree for nodes which are less
+ * than the given point
+ * @param {Number} point The point to query
+ * @param {Array} results The array to put the results
+ */
+ IntervalNode.prototype.searchAfter = function (point, results) {
+ // Check this node
+ if (this.low >= point) {
+ results.push(this);
+ if (this.left !== null) {
+ this.left.searchAfter(point, results);
+ }
+ }
+ // search the right side
+ if (this.right !== null) {
+ this.right.searchAfter(point, results);
+ }
+ };
+ /**
+ * Invoke the callback on this element and both it's branches
+ * @param {Function} callback
+ */
+ IntervalNode.prototype.traverse = function (callback) {
+ callback(this);
+ if (this.left !== null) {
+ this.left.traverse(callback);
+ }
+ if (this.right !== null) {
+ this.right.traverse(callback);
+ }
+ };
+ /**
+ * Update the height of the node
+ */
+ IntervalNode.prototype.updateHeight = function () {
+ if (this.left !== null && this.right !== null) {
+ this.height = Math.max(this.left.height, this.right.height) + 1;
+ } else if (this.right !== null) {
+ this.height = this.right.height + 1;
+ } else if (this.left !== null) {
+ this.height = this.left.height + 1;
+ } else {
+ this.height = 0;
+ }
+ };
+ /**
+ * Update the height of the node
+ */
+ IntervalNode.prototype.updateMax = function () {
+ this.max = this.high;
+ if (this.left !== null) {
+ this.max = Math.max(this.max, this.left.max);
+ }
+ if (this.right !== null) {
+ this.max = Math.max(this.max, this.right.max);
+ }
+ };
+ /**
+ * The balance is how the leafs are distributed on the node
+ * @return {Number} Negative numbers are balanced to the right
+ */
+ IntervalNode.prototype.getBalance = function () {
+ var balance = 0;
+ if (this.left !== null && this.right !== null) {
+ balance = this.left.height - this.right.height;
+ } else if (this.left !== null) {
+ balance = this.left.height + 1;
+ } else if (this.right !== null) {
+ balance = -(this.right.height + 1);
+ }
+ return balance;
+ };
+ /**
+ * @returns {Boolean} true if this node is the left child
+ * of its parent
+ */
+ IntervalNode.prototype.isLeftChild = function () {
+ return this.parent !== null && this.parent.left === this;
+ };
+ /**
+ * get/set the left node
+ * @type {IntervalNode}
+ */
+ Object.defineProperty(IntervalNode.prototype, 'left', {
+ get: function () {
+ return this._left;
+ },
+ set: function (node) {
+ this._left = node;
+ if (node !== null) {
+ node.parent = this;
+ }
+ this.updateHeight();
+ this.updateMax();
+ }
+ });
+ /**
+ * get/set the right node
+ * @type {IntervalNode}
+ */
+ Object.defineProperty(IntervalNode.prototype, 'right', {
+ get: function () {
+ return this._right;
+ },
+ set: function (node) {
+ this._right = node;
+ if (node !== null) {
+ node.parent = this;
+ }
+ this.updateHeight();
+ this.updateMax();
+ }
+ });
+ /**
+ * null out references.
+ */
+ IntervalNode.prototype.dispose = function () {
+ this.parent = null;
+ this._left = null;
+ this._right = null;
+ this.event = null;
+ };
+ ///////////////////////////////////////////////////////////////////////////
+ // END INTERVAL NODE HELPER
+ ///////////////////////////////////////////////////////////////////////////
+ return Tone.IntervalTimeline;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class Transport for timing musical events.
+ * Supports tempo curves and time changes. Unlike browser-based timing (setInterval, requestAnimationFrame)
* Tone.Transport timing events pass in the exact time of the scheduled event
* in the argument of the callback function. Pass that time value to the object
* you're scheduling.
* A single transport is created for you when the library is initialized.
+ *
+ * The transport emits the events: "start", "stop", "pause", and "loop" which are
+ * called with the time of that event as the argument.
*
- * @extends {Tone}
+ * @extends {Tone.EventEmitter}
* @singleton
* @example
* //repeated event every 8th note
@@ -4828,20 +7092,46 @@
* }, "16:0:0");
*/
Tone.Transport = function () {
- /**
- * watches the main oscillator for timing ticks
- * initially starts at 120bpm
- *
- * @private
- * @type {Tone.Clock}
- */
- this._clock = new Tone.Clock(0, this._processTick.bind(this));
- this._clock.onended = this._onended.bind(this);
+ Tone.EventEmitter.call(this);
+ ///////////////////////////////////////////////////////////////////////
+ // LOOPING
+ //////////////////////////////////////////////////////////////////////
/**
* If the transport loops or not.
* @type {boolean}
*/
this.loop = false;
+ /**
+ * The loop start position in ticks
+ * @type {Ticks}
+ * @private
+ */
+ this._loopStart = 0;
+ /**
+ * The loop end position in ticks
+ * @type {Ticks}
+ * @private
+ */
+ this._loopEnd = 0;
+ ///////////////////////////////////////////////////////////////////////
+ // CLOCK/TEMPO
+ //////////////////////////////////////////////////////////////////////
+ /**
+ * Pulses per quarter is the number of ticks per quarter note.
+ * @private
+ * @type {Number}
+ */
+ this._ppq = TransportConstructor.defaults.PPQ;
+ /**
+ * watches the main oscillator for timing ticks
+ * initially starts at 120bpm
+ * @private
+ * @type {Tone.Clock}
+ */
+ this._clock = new Tone.Clock({
+ 'callback': this._processTick.bind(this),
+ 'frequency': 0
+ });
/**
* The Beats Per Minute of the Transport.
* @type {BPM}
@@ -4851,22 +7141,75 @@
* //ramp the bpm to 120 over 10 seconds
* Tone.Transport.bpm.rampTo(120, 10);
*/
- this.bpm = new Tone.Signal(120, Tone.Type.BPM);
+ this.bpm = this._clock.frequency;
+ this.bpm._toUnits = this._toUnits.bind(this);
+ this.bpm._fromUnits = this._fromUnits.bind(this);
+ this.bpm.units = Tone.Type.BPM;
+ this.bpm.value = TransportConstructor.defaults.bpm;
+ this._readOnly('bpm');
/**
- * the signal scalar
- * @type {Tone.Multiply}
+ * The time signature, or more accurately the numerator
+ * of the time signature over a denominator of 4.
+ * @type {Number}
* @private
*/
- this._bpmMult = new Tone.Multiply(1 / 60 * tatum);
+ this._timeSignature = TransportConstructor.defaults.timeSignature;
+ ///////////////////////////////////////////////////////////////////////
+ // TIMELINE EVENTS
+ //////////////////////////////////////////////////////////////////////
/**
- * The state of the transport. READ ONLY.
- * @type {Tone.State}
+ * All the events in an object to keep track by ID
+ * @type {Object}
+ * @private
*/
- this.state = Tone.State.Stopped;
- //connect it all up
- this.bpm.chain(this._bpmMult, this._clock.frequency);
+ this._scheduledEvents = {};
+ /**
+ * The event ID counter
+ * @type {Number}
+ * @private
+ */
+ this._eventID = 0;
+ /**
+ * The scheduled events.
+ * @type {Tone.Timeline}
+ * @private
+ */
+ this._timeline = new Tone.Timeline();
+ /**
+ * Repeated events
+ * @type {Array}
+ * @private
+ */
+ this._repeatedEvents = new Tone.IntervalTimeline();
+ /**
+ * Events that occur once
+ * @type {Array}
+ * @private
+ */
+ this._onceEvents = new Tone.Timeline();
+ /**
+ * All of the synced Signals
+ * @private
+ * @type {Array}
+ */
+ this._syncedSignals = [];
+ ///////////////////////////////////////////////////////////////////////
+ // SWING
+ //////////////////////////////////////////////////////////////////////
+ /**
+ * The subdivision of the swing
+ * @type {Ticks}
+ * @private
+ */
+ this._swingTicks = this.toTicks(TransportConstructor.defaults.swingSubdivision, TransportConstructor.defaults.bpm, TransportConstructor.defaults.timeSignature);
+ /**
+ * The swing amount
+ * @type {NormalRange}
+ * @private
+ */
+ this._swingAmount = 0;
};
- Tone.extend(Tone.Transport);
+ Tone.extend(Tone.Transport, Tone.EventEmitter);
/**
* the defaults
* @type {Object}
@@ -4879,90 +7222,9 @@
'swingSubdivision': '16n',
'timeSignature': 4,
'loopStart': 0,
- 'loopEnd': '4m'
+ 'loopEnd': '4m',
+ 'PPQ': 48
};
- /**
- * @private
- * @type {number}
- */
- var tatum = 12;
- /**
- * @private
- * @type {number}
- */
- var timelineTicks = 0;
- /**
- * @private
- * @type {number}
- */
- var transportTicks = 0;
- /**
- * Which subdivision the swing is applied to.
- * defaults to an 16th note
- * @private
- * @type {number}
- */
- var swingSubdivision = '16n';
- /**
- * controls which beat the swing is applied to
- * defaults to an 16th note
- * @private
- * @type {number}
- */
- var swingTatum = 3;
- /**
- * controls which beat the swing is applied to
- * @private
- * @type {number}
- */
- var swingAmount = 0;
- /**
- * @private
- * @type {number}
- */
- var transportTimeSignature = 4;
- /**
- * @private
- * @type {number}
- */
- var loopStart = 0;
- /**
- * @private
- * @type {number}
- */
- var loopEnd = tatum * 4;
- /**
- * @private
- * @type {Array}
- */
- var intervals = [];
- /**
- * @private
- * @type {Array}
- */
- var timeouts = [];
- /**
- * @private
- * @type {Array}
- */
- var transportTimeline = [];
- /**
- * @private
- * @type {number}
- */
- var timelineProgress = 0;
- /**
- * All of the synced components
- * @private
- * @type {Array}
- */
- var SyncedSources = [];
- /**
- * All of the synced Signals
- * @private
- * @type {Array}
- */
- var SyncedSignals = [];
///////////////////////////////////////////////////////////////////////////////
// TICKS
///////////////////////////////////////////////////////////////////////////////
@@ -4972,282 +7234,146 @@
* @private
*/
Tone.Transport.prototype._processTick = function (tickTime) {
- if (this.state === Tone.State.Started) {
- if (swingAmount > 0 && timelineTicks % tatum !== 0 && //not on a downbeat
- timelineTicks % swingTatum === 0) {
- //add some swing
- tickTime += this._ticksToSeconds(swingTatum) * swingAmount;
- }
- processIntervals(tickTime);
- processTimeouts(tickTime);
- processTimeline(tickTime);
- transportTicks += 1;
- timelineTicks += 1;
- if (this.loop) {
- if (timelineTicks === loopEnd) {
- this._setTicks(loopStart);
- }
+ //handle swing
+ if (this._swingAmount > 0 && this._clock.ticks % this._ppq !== 0 && //not on a downbeat
+ this._clock.ticks % this._swingTicks === 0) {
+ //add some swing
+ tickTime += this.ticksToSeconds(this._swingTicks) * this._swingAmount;
+ }
+ //do the loop test
+ if (this.loop) {
+ if (this._clock.ticks === this._loopEnd) {
+ this.ticks = this._loopStart;
+ this.trigger('loop', tickTime);
}
}
- };
- /**
- * jump to a specific tick in the timeline
- * updates the timeline callbacks
- *
- * @param {number} ticks the tick to jump to
- * @private
- */
- Tone.Transport.prototype._setTicks = function (ticks) {
- timelineTicks = ticks;
- for (var i = 0; i < transportTimeline.length; i++) {
- var timeout = transportTimeline[i];
- if (timeout.callbackTick() >= ticks) {
- timelineProgress = i;
- break;
+ var ticks = this._clock.ticks;
+ //fire the next tick events if their time has come
+ this._timeline.forEachAtTime(ticks, function (event) {
+ event.callback(tickTime);
+ });
+ //process the repeated events
+ this._repeatedEvents.forEachOverlap(ticks, function (event) {
+ if ((ticks - event.time) % event.interval === 0) {
+ event.callback(tickTime);
}
- }
+ });
+ //process the single occurrence events
+ this._onceEvents.forEachBefore(ticks, function (event) {
+ event.callback(tickTime);
+ });
+ //and clear the single occurrence timeline
+ this._onceEvents.cancelBefore(ticks);
};
///////////////////////////////////////////////////////////////////////////////
- // EVENT PROCESSING
+ // SCHEDULABLE EVENTS
///////////////////////////////////////////////////////////////////////////////
/**
- * process the intervals
- * @param {number} time
- */
- var processIntervals = function (time) {
- for (var i = 0, len = intervals.length; i < len; i++) {
- var interval = intervals[i];
- if (interval.testInterval(transportTicks)) {
- interval.doCallback(time);
- }
- }
- };
- /**
- * process the timeouts
- * @param {number} time
- */
- var processTimeouts = function (time) {
- var removeTimeouts = 0;
- for (var i = 0, len = timeouts.length; i < len; i++) {
- var timeout = timeouts[i];
- var callbackTick = timeout.callbackTick();
- if (callbackTick <= transportTicks) {
- timeout.doCallback(time);
- removeTimeouts++;
- } else if (callbackTick > transportTicks) {
- break;
- }
- }
- //remove the timeouts off the front of the array after they've been called
- timeouts.splice(0, removeTimeouts);
- };
- /**
- * process the transportTimeline events
- * @param {number} time
- */
- var processTimeline = function (time) {
- for (var i = timelineProgress, len = transportTimeline.length; i < len; i++) {
- var evnt = transportTimeline[i];
- var callbackTick = evnt.callbackTick();
- if (callbackTick === timelineTicks) {
- timelineProgress = i;
- evnt.doCallback(time);
- } else if (callbackTick > timelineTicks) {
- break;
- }
- }
- };
- ///////////////////////////////////////////////////////////////////////////////
- // INTERVAL
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * Set a callback for a recurring event.
- * @param {function} callback
- * @param {Time} interval
- * @return {number} the id of the interval
+ * Schedule an event along the timeline.
+ * @param {TimelineEvent} event
+ * @param {Time} time
+ * @return {Number} The id of the event which can be used for canceling the event.
* @example
- * //triggers a callback every 8th note with the exact time of the event
- * Tone.Transport.setInterval(function(time){
+ * //trigger the callback when the Transport reaches the desired time
+ * Tone.Transport.schedule(function(time){
* envelope.triggerAttack(time);
- * }, "8n");
+ * }, "128i");
*/
- Tone.Transport.prototype.setInterval = function (callback, interval, ctx) {
- var tickTime = this._toTicks(interval);
- var timeout = new TimelineEvent(callback, ctx, tickTime, transportTicks);
- intervals.push(timeout);
- return timeout.id;
+ Tone.Transport.prototype.schedule = function (callback, time) {
+ var event = {
+ 'time': this.toTicks(time),
+ 'callback': callback
+ };
+ var id = this._eventID++;
+ this._scheduledEvents[id.toString()] = {
+ 'event': event,
+ 'timeline': this._timeline
+ };
+ this._timeline.addEvent(event);
+ return id;
};
/**
- * Stop and ongoing interval.
- * @param {number} intervalID The ID of interval to remove. The interval
- * ID is given as the return value in Tone.Transport.setInterval.
- * @return {boolean} true if the event was removed
+ * Schedule a repeated event along the timeline.
+ * @param {Function} callback The callback to invoke.
+ * @param {Time} interval The duration between successive
+ * callbacks.
+ * @param {Time=} startTime When along the timeline the events should
+ * start being invoked.
+ * @param {Time} [duration=Infinity] How long the event should repeat.
+ * @return {Number} The ID of the scheduled event. Use this to cancel
+ * the event.
*/
- Tone.Transport.prototype.clearInterval = function (rmInterval) {
- for (var i = 0; i < intervals.length; i++) {
- var interval = intervals[i];
- if (interval.id === rmInterval) {
- intervals.splice(i, 1);
- return true;
- }
+ Tone.Transport.prototype.scheduleRepeat = function (callback, interval, startTime, duration) {
+ if (interval <= 0) {
+ throw new Error('repeat events must have an interval larger than 0');
}
- return false;
+ var event = {
+ 'time': this.toTicks(startTime),
+ 'duration': this.toTicks(this.defaultArg(duration, Infinity)),
+ 'interval': this.toTicks(interval),
+ 'callback': callback
+ };
+ var id = this._eventID++;
+ this._scheduledEvents[id.toString()] = {
+ 'event': event,
+ 'timeline': this._repeatedEvents
+ };
+ this._repeatedEvents.addEvent(event);
+ return id;
};
/**
- * Removes all of the intervals that are currently set.
- * @return {boolean} true if the event was removed
+ * Schedule an event that will be removed after it is invoked.
+ * Note that if the given time is less than the current transport time,
+ * the event will be invoked immediately.
+ * @param {Function} callback The callback to invoke once.
+ * @param {Time} time The time the callback should be invoked.
+ * @returns {Number} The ID of the scheduled event.
*/
- Tone.Transport.prototype.clearIntervals = function () {
- var willRemove = intervals.length > 0;
- intervals = [];
- return willRemove;
+ Tone.Transport.prototype.scheduleOnce = function (callback, time) {
+ var event = {
+ 'time': this.toTicks(time),
+ 'callback': callback
+ };
+ var id = this._eventID++;
+ this._scheduledEvents[id.toString()] = {
+ 'event': event,
+ 'timeline': this._onceEvents
+ };
+ this._onceEvents.addEvent(event);
+ return id;
};
- ///////////////////////////////////////////////////////////////////////////////
- // TIMEOUT
- ///////////////////////////////////////////////////////////////////////////////
/**
- * Set a timeout to occur after time from now. NB: the transport must be
- * running for this to be triggered. All timeout events are cleared when the
- * transport is stopped.
- *
- * @param {function} callback
- * @param {Time} time The time (from now) that the callback will be invoked.
- * @return {number} The id of the timeout.
- * @example
- * //trigger an event to happen 1 second from now
- * Tone.Transport.setTimeout(function(time){
- * player.start(time);
- * }, 1)
+ * Clear the passed in event id from the timeline
+ * @param {Number} eventId The id of the event.
+ * @returns {Tone.Transport} this
*/
- Tone.Transport.prototype.setTimeout = function (callback, time, ctx) {
- var ticks = this._toTicks(time);
- var timeout = new TimelineEvent(callback, ctx, ticks + transportTicks, 0);
- //put it in the right spot
- for (var i = 0, len = timeouts.length; i < len; i++) {
- var testEvnt = timeouts[i];
- if (testEvnt.callbackTick() > timeout.callbackTick()) {
- timeouts.splice(i, 0, timeout);
- return timeout.id;
- }
+ Tone.Transport.prototype.clear = function (eventId) {
+ if (this._scheduledEvents.hasOwnProperty(eventId)) {
+ var item = this._scheduledEvents[eventId.toString()];
+ item.timeline.removeEvent(item.event);
+ delete this._scheduledEvents[eventId.toString()];
}
- //otherwise push it on the end
- timeouts.push(timeout);
- return timeout.id;
+ return this;
};
/**
- * Clear a timeout using it's ID.
- * @param {number} intervalID The ID of timeout to remove. The timeout
- * ID is given as the return value in Tone.Transport.setTimeout.
- * @return {boolean} true if the timeout was removed
+ * Remove scheduled events from the timeline after
+ * the given time. Repeated events will be removed
+ * if their startTime is after the given time
+ * @param {Time} [after=0] Clear all events after
+ * this time.
+ * @returns {Tone.Transport} this
*/
- Tone.Transport.prototype.clearTimeout = function (timeoutID) {
- for (var i = 0; i < timeouts.length; i++) {
- var testTimeout = timeouts[i];
- if (testTimeout.id === timeoutID) {
- timeouts.splice(i, 1);
- return true;
- }
- }
- return false;
- };
- /**
- * Removes all of the timeouts that are currently set.
- * @return {boolean} true if the event was removed
- */
- Tone.Transport.prototype.clearTimeouts = function () {
- var willRemove = timeouts.length > 0;
- timeouts = [];
- return willRemove;
+ Tone.Transport.prototype.cancel = function (after) {
+ after = this.defaultArg(after, 0);
+ after = this.toTicks(after);
+ this._timeline.cancel(after);
+ this._onceEvents.cancel(after);
+ this._repeatedEvents.cancel(after);
+ return this;
};
///////////////////////////////////////////////////////////////////////////////
- // TIMELINE
+ // QUANTIZATION
///////////////////////////////////////////////////////////////////////////////
- /**
- * Timeline events are synced to the timeline of the Tone.Transport.
- * Unlike Timeout, Timeline events will restart after the
- * Tone.Transport has been stopped and restarted.
- *
- * @param {function} callback
- * @param {Tome.Time} timeout
- * @return {number} the id for clearing the transportTimeline event
- * @example
- * //trigger the start of a part on the 16th measure
- * Tone.Transport.setTimeline(function(time){
- * part.start(time);
- * }, "16m");
- */
- Tone.Transport.prototype.setTimeline = function (callback, timeout, ctx) {
- var ticks = this._toTicks(timeout);
- var timelineEvnt = new TimelineEvent(callback, ctx, ticks, 0);
- //put it in the right spot
- for (var i = timelineProgress, len = transportTimeline.length; i < len; i++) {
- var testEvnt = transportTimeline[i];
- if (testEvnt.callbackTick() > timelineEvnt.callbackTick()) {
- transportTimeline.splice(i, 0, timelineEvnt);
- return timelineEvnt.id;
- }
- }
- //otherwise push it on the end
- transportTimeline.push(timelineEvnt);
- return timelineEvnt.id;
- };
- /**
- * Clear the timeline event.
- * @param {number} timelineID
- * @return {boolean} true if it was removed
- */
- Tone.Transport.prototype.clearTimeline = function (timelineID) {
- for (var i = 0; i < transportTimeline.length; i++) {
- var testTimeline = transportTimeline[i];
- if (testTimeline.id === timelineID) {
- transportTimeline.splice(i, 1);
- return true;
- }
- }
- return false;
- };
- /**
- * Remove all events from the timeline.
- * @returns {boolean} true if the events were removed
- */
- Tone.Transport.prototype.clearTimelines = function () {
- timelineProgress = 0;
- var willRemove = transportTimeline.length > 0;
- transportTimeline = [];
- return willRemove;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // TIME CONVERSIONS
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * turns the time into
- * @param {Time} time
- * @return {number}
- * @private
- */
- Tone.Transport.prototype._toTicks = function (time) {
- //get the seconds
- var seconds = this.toSeconds(time);
- var quarter = this.notationToSeconds('4n');
- var quarters = seconds / quarter;
- var tickNum = quarters * tatum;
- //quantize to tick value
- return Math.round(tickNum);
- };
- /**
- * convert ticks into seconds
- *
- * @param {number} ticks
- * @param {number=} bpm
- * @param {number=} timeSignature
- * @return {number} seconds
- * @private
- */
- Tone.Transport.prototype._ticksToSeconds = function (ticks, bpm, timeSignature) {
- ticks = Math.floor(ticks);
- var quater = this.notationToSeconds('4n', bpm, timeSignature);
- return quater * ticks / tatum;
- };
/**
* Returns the time of the next beat.
* @param {string} [subdivision="4n"]
@@ -5255,17 +7381,24 @@
*/
Tone.Transport.prototype.nextBeat = function (subdivision) {
subdivision = this.defaultArg(subdivision, '4n');
- var tickNum = this._toTicks(subdivision);
+ var tickNum = this.toTicks(subdivision);
var remainingTicks = transportTicks % tickNum;
- var nextTick = remainingTicks;
- if (remainingTicks > 0) {
- nextTick = tickNum - remainingTicks;
- }
- return this._ticksToSeconds(nextTick);
};
///////////////////////////////////////////////////////////////////////////////
// START/STOP/PAUSE
///////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns the playback state of the source, either "started", "stopped", or "paused"
+ * @type {String}
+ * @readOnly
+ * @memberOf Tone.State#
+ * @name state
+ */
+ Object.defineProperty(Tone.Transport.prototype, 'state', {
+ get: function () {
+ return this._clock.getStateAtTime(this.now());
+ }
+ });
/**
* Start the transport and all sources synced to the transport.
* @param {Time} [time=now] The time when the transport should start.
@@ -5276,20 +7409,15 @@
* Tone.Transport.start("+1", "4:0:0");
*/
Tone.Transport.prototype.start = function (time, offset) {
- if (this.state === Tone.State.Stopped || this.state === Tone.State.Paused) {
- if (!this.isUndef(offset)) {
- this._setTicks(this._toTicks(offset));
- }
- this.state = Tone.State.Started;
- var startTime = this.toSeconds(time);
- this._clock.start(startTime);
- //call start on each of the synced sources
- for (var i = 0; i < SyncedSources.length; i++) {
- var source = SyncedSources[i].source;
- var delay = SyncedSources[i].delay;
- source.start(startTime + delay);
- }
+ time = this.toSeconds(time);
+ if (!this.isUndef(offset)) {
+ offset = this.toTicks(offset);
+ } else {
+ offset = this.defaultArg(offset, this._clock.ticks);
}
+ //start the clock
+ this._clock.start(time, offset);
+ this.trigger('start', time, this.ticksToSeconds(offset));
return this;
};
/**
@@ -5300,45 +7428,20 @@
* Tone.Transport.stop();
*/
Tone.Transport.prototype.stop = function (time) {
- if (this.state === Tone.State.Started || this.state === Tone.State.Paused) {
- var stopTime = this.toSeconds(time);
- this._clock.stop(stopTime);
- //call start on each of the synced sources
- for (var i = 0; i < SyncedSources.length; i++) {
- var source = SyncedSources[i].source;
- source.stop(stopTime);
- }
- } else {
- this._onended();
- }
+ time = this.toSeconds(time);
+ this._clock.stop(time);
+ this.trigger('stop', time);
return this;
};
- /**
- * invoked when the transport is stopped
- * @private
- */
- Tone.Transport.prototype._onended = function () {
- transportTicks = 0;
- this._setTicks(0);
- this.clearTimeouts();
- this.state = Tone.State.Stopped;
- };
/**
* Pause the transport and all sources synced to the transport.
* @param {Time} [time=now]
* @returns {Tone.Transport} this
*/
Tone.Transport.prototype.pause = function (time) {
- if (this.state === Tone.State.Started) {
- this.state = Tone.State.Paused;
- var stopTime = this.toSeconds(time);
- this._clock.stop(stopTime);
- //call pause on each of the synced sources
- for (var i = 0; i < SyncedSources.length; i++) {
- var source = SyncedSources[i].source;
- source.pause(stopTime);
- }
- }
+ time = this.toSeconds(time);
+ this._clock.pause(time);
+ this.trigger('pause', time);
return this;
};
///////////////////////////////////////////////////////////////////////////////
@@ -5358,10 +7461,13 @@
*/
Object.defineProperty(Tone.Transport.prototype, 'timeSignature', {
get: function () {
- return transportTimeSignature;
+ return this._timeSignature;
},
- set: function (numerator) {
- transportTimeSignature = numerator;
+ set: function (timeSig) {
+ if (Array.isArray(timeSig)) {
+ timeSig = timeSig[0] / timeSig[1] * 4;
+ }
+ this._timeSignature = timeSig;
}
});
/**
@@ -5372,10 +7478,10 @@
*/
Object.defineProperty(Tone.Transport.prototype, 'loopStart', {
get: function () {
- return this._ticksToSeconds(loopStart);
+ return this.ticksToSeconds(this._loopStart);
},
set: function (startPosition) {
- loopStart = this._toTicks(startPosition);
+ this._loopStart = this.toTicks(startPosition);
}
});
/**
@@ -5386,10 +7492,10 @@
*/
Object.defineProperty(Tone.Transport.prototype, 'loopEnd', {
get: function () {
- return this._ticksToSeconds(loopEnd);
+ return this.ticksToSeconds(this._loopEnd);
},
set: function (endPosition) {
- loopEnd = this._toTicks(endPosition);
+ this._loopEnd = this.toTicks(endPosition);
}
});
/**
@@ -5416,11 +7522,11 @@
*/
Object.defineProperty(Tone.Transport.prototype, 'swing', {
get: function () {
- return swingAmount * 2;
+ return this._swingAmount * 2;
},
set: function (amount) {
//scale the values to a normal range
- swingAmount = amount * 0.5;
+ this._swingAmount = amount * 0.5;
}
});
/**
@@ -5434,12 +7540,10 @@
*/
Object.defineProperty(Tone.Transport.prototype, 'swingSubdivision', {
get: function () {
- return swingSubdivision;
+ return this.toNotation(this._swingTicks + 'i');
},
set: function (subdivision) {
- //scale the values to a normal range
- swingSubdivision = subdivision;
- swingTatum = this._toTicks(subdivision);
+ this._swingTicks = this.toTicks(subdivision);
}
});
/**
@@ -5452,10 +7556,14 @@
*/
Object.defineProperty(Tone.Transport.prototype, 'position', {
get: function () {
- var quarters = timelineTicks / tatum;
- var measures = Math.floor(quarters / transportTimeSignature);
- var sixteenths = Math.floor(quarters % 1 * 4);
- quarters = Math.floor(quarters) % transportTimeSignature;
+ var quarters = this.ticks / this._ppq;
+ var measures = Math.floor(quarters / this._timeSignature);
+ var sixteenths = quarters % 1 * 4;
+ //if the sixteenths aren't a whole number, fix their length
+ if (sixteenths % 1 > 0) {
+ sixteenths = sixteenths.toFixed(3);
+ }
+ quarters = Math.floor(quarters) % this._timeSignature;
var progress = [
measures,
quarters,
@@ -5464,44 +7572,81 @@
return progress.join(':');
},
set: function (progress) {
- var ticks = this._toTicks(progress);
- this._setTicks(ticks);
+ var ticks = this.toTicks(progress);
+ this.ticks = ticks;
}
});
+ /**
+ * The Transport's loop position as a normalized value. Always
+ * returns 0 if the transport if loop is not true.
+ * @memberOf Tone.Transport#
+ * @name progress
+ * @type {NormalRange}
+ */
+ Object.defineProperty(Tone.Transport.prototype, 'progress', {
+ get: function () {
+ if (this.loop) {
+ return (this.ticks - this._loopStart) / (this._loopEnd - this._loopStart);
+ } else {
+ return 0;
+ }
+ }
+ });
+ /**
+ * The transports current tick position.
+ *
+ * @memberOf Tone.Transport#
+ * @type {Ticks}
+ * @name ticks
+ */
+ Object.defineProperty(Tone.Transport.prototype, 'ticks', {
+ get: function () {
+ return this._clock.ticks;
+ },
+ set: function (t) {
+ this._clock.ticks = t;
+ }
+ });
+ /**
+ * Pulses Per Quarter note. This is the smallest resolution
+ * the Transport timing supports. This should be set once
+ * on initialization and not set again. Changing this value
+ * after other objects have been created can cause problems.
+ *
+ * @memberOf Tone.Transport#
+ * @type {Number}
+ * @name PPQ
+ */
+ Object.defineProperty(Tone.Transport.prototype, 'PPQ', {
+ get: function () {
+ return this._ppq;
+ },
+ set: function (ppq) {
+ this._ppq = ppq;
+ this.bpm.value = this.bpm.value;
+ }
+ });
+ /**
+ * Convert from BPM to frequency (factoring in PPQ)
+ * @param {BPM} bpm The BPM value to convert to frequency
+ * @return {Frequency} The BPM as a frequency with PPQ factored in.
+ * @private
+ */
+ Tone.Transport.prototype._fromUnits = function (bpm) {
+ return 1 / (60 / bpm / this.PPQ);
+ };
+ /**
+ * Convert from frequency (with PPQ) into BPM
+ * @param {Frequency} freq The clocks frequency to convert to BPM
+ * @return {BPM} The frequency value as BPM.
+ * @private
+ */
+ Tone.Transport.prototype._toUnits = function (freq) {
+ return freq / this.PPQ * 60;
+ };
///////////////////////////////////////////////////////////////////////////////
// SYNCING
///////////////////////////////////////////////////////////////////////////////
- /**
- * Sync a source to the transport so that
- * @param {Tone.Source} source the source to sync to the transport
- * @param {Time} delay (optionally) start the source with a delay from the transport
- * @returns {Tone.Transport} this
- * @example
- * Tone.Transport.syncSource(player, "1m");
- * Tone.Transport.start();
- * //the player will start 1 measure after the transport starts
- */
- Tone.Transport.prototype.syncSource = function (source, startDelay) {
- SyncedSources.push({
- source: source,
- delay: this.toSeconds(this.defaultArg(startDelay, 0))
- });
- return this;
- };
- /**
- * Unsync the source from the transport. See Tone.Transport.syncSource.
- *
- * @param {Tone.Source} source [description]
- * @returns {Tone.Transport} this
- */
- Tone.Transport.prototype.unsyncSource = function (source) {
- for (var i = 0; i < SyncedSources.length; i++) {
- if (SyncedSources[i].source === source) {
- SyncedSources.splice(i, 1);
- }
- }
- return this;
- };
/**
* Attaches the signal to the tempo control signal so that
* any changes in the tempo will change the signal in the same
@@ -5516,21 +7661,20 @@
Tone.Transport.prototype.syncSignal = function (signal, ratio) {
if (!ratio) {
//get the sync ratio
- if (signal._value.value !== 0) {
- ratio = signal._value.value / this.bpm.value;
+ if (signal._param.value !== 0) {
+ ratio = signal._param.value / this.bpm._param.value;
} else {
ratio = 0;
}
}
- var ratioSignal = this.context.createGain();
- ratioSignal.gain.value = ratio;
- this.bpm.chain(ratioSignal, signal._value);
- SyncedSignals.push({
+ var ratioSignal = new Tone.Gain(ratio);
+ this.bpm.chain(ratioSignal, signal._param);
+ this._syncedSignals.push({
'ratio': ratioSignal,
'signal': signal,
- 'initial': signal._value.value
+ 'initial': signal._param.value
});
- signal._value.value = 0;
+ signal._param.value = 0;
return this;
};
/**
@@ -5540,12 +7684,12 @@
* @returns {Tone.Transport} this
*/
Tone.Transport.prototype.unsyncSignal = function (signal) {
- for (var i = 0; i < SyncedSignals.length; i++) {
- var syncedSignal = SyncedSignals[i];
+ for (var i = this._syncedSignals.length - 1; i >= 0; i--) {
+ var syncedSignal = this._syncedSignals[i];
if (syncedSignal.signal === signal) {
- syncedSignal.ratio.disconnect();
- syncedSignal.signal._value.value = syncedSignal.initial;
- SyncedSignals.splice(i, 1);
+ syncedSignal.ratio.dispose();
+ syncedSignal.signal._param.value = syncedSignal.initial;
+ this._syncedSignals.splice(i, 1);
}
}
return this;
@@ -5556,268 +7700,112 @@
* @private
*/
Tone.Transport.prototype.dispose = function () {
+ Tone.EventEmitter.prototype.dispose.call(this);
this._clock.dispose();
this._clock = null;
- this.bpm.dispose();
+ this._writable('bpm');
this.bpm = null;
- this._bpmMult.dispose();
- this._bpmMult = null;
+ this._timeline.dispose();
+ this._timeline = null;
+ this._onceEvents.dispose();
+ this._onceEvents = null;
+ this._repeatedEvents.dispose();
+ this._repeatedEvents = null;
return this;
};
///////////////////////////////////////////////////////////////////////////////
- // TIMELINE EVENT
+ // DEPRECATED FUNCTIONS
+ // (will be removed in r7)
///////////////////////////////////////////////////////////////////////////////
/**
- * @static
- * @type {number}
+ * @deprecated Use Tone.scheduleRepeat instead.
+ * Set a callback for a recurring event.
+ * @param {function} callback
+ * @param {Time} interval
+ * @return {number} the id of the interval
+ * @example
+ * //triggers a callback every 8th note with the exact time of the event
+ * Tone.Transport.setInterval(function(time){
+ * envelope.triggerAttack(time);
+ * }, "8n");
*/
- var TimelineEventIDCounter = 0;
+ Tone.Transport.prototype.setInterval = function (callback, interval) {
+ console.warn('This method is deprecated. Use Tone.Transport.scheduleRepeat instead.');
+ return Tone.Transport.scheduleRepeat(callback, interval);
+ };
/**
- * A Timeline event
+ * @deprecated Use Tone.cancel instead.
+ * Stop and ongoing interval.
+ * @param {number} intervalID The ID of interval to remove. The interval
+ * ID is given as the return value in Tone.Transport.setInterval.
+ * @return {boolean} true if the event was removed
+ */
+ Tone.Transport.prototype.clearInterval = function (id) {
+ console.warn('This method is deprecated. Use Tone.Transport.clear instead.');
+ return Tone.Transport.clear(id);
+ };
+ /**
+ * @deprecated Use Tone.Note instead.
+ * Set a timeout to occur after time from now. NB: the transport must be
+ * running for this to be triggered. All timeout events are cleared when the
+ * transport is stopped.
*
- * @constructor
- * @private
- * @param {function(number)} callback
- * @param {Object} context
- * @param {number} tickTime
- * @param {number} startTicks
+ * @param {function} callback
+ * @param {Time} time The time (from now) that the callback will be invoked.
+ * @return {number} The id of the timeout.
+ * @example
+ * //trigger an event to happen 1 second from now
+ * Tone.Transport.setTimeout(function(time){
+ * player.start(time);
+ * }, 1)
*/
- var TimelineEvent = function (callback, context, tickTime, startTicks) {
- this.startTicks = startTicks;
- this.tickTime = tickTime;
- this.callback = callback;
- this.context = context;
- this.id = TimelineEventIDCounter++;
+ Tone.Transport.prototype.setTimeout = function (callback, timeout) {
+ console.warn('This method is deprecated. Use Tone.Transport.scheduleOnce instead.');
+ return Tone.Transport.scheduleOnce(callback, timeout);
};
/**
- * invoke the callback in the correct context
- * passes in the playback time
- *
- * @param {number} playbackTime
+ * @deprecated Use Tone.Note instead.
+ * Clear a timeout using it's ID.
+ * @param {number} intervalID The ID of timeout to remove. The timeout
+ * ID is given as the return value in Tone.Transport.setTimeout.
+ * @return {boolean} true if the timeout was removed
*/
- TimelineEvent.prototype.doCallback = function (playbackTime) {
- this.callback.call(this.context, playbackTime);
+ Tone.Transport.prototype.clearTimeout = function (id) {
+ console.warn('This method is deprecated. Use Tone.Transport.clear instead.');
+ return Tone.Transport.clear(id);
};
/**
- * get the tick which the callback is supposed to occur on
- *
- * @return {number}
+ * @deprecated Use Tone.Note instead.
+ * Timeline events are synced to the timeline of the Tone.Transport.
+ * Unlike Timeout, Timeline events will restart after the
+ * Tone.Transport has been stopped and restarted.
+ *
+ * @param {function} callback
+ * @param {Time} time
+ * @return {number} the id for clearing the transportTimeline event
+ * @example
+ * //trigger the start of a part on the 16th measure
+ * Tone.Transport.setTimeline(function(time){
+ * part.start(time);
+ * }, "16m");
*/
- TimelineEvent.prototype.callbackTick = function () {
- return this.startTicks + this.tickTime;
+ Tone.Transport.prototype.setTimeline = function (callback, time) {
+ console.warn('This method is deprecated. Use Tone.Transport.schedule instead.');
+ return Tone.Transport.schedule(callback, time);
};
/**
- * test if the tick occurs on the interval
- *
- * @param {number} tick
- * @return {boolean}
+ * @deprecated Use Tone.Note instead.
+ * Clear the timeline event.
+ * @param {number} id
+ * @return {boolean} true if it was removed
*/
- TimelineEvent.prototype.testInterval = function (tick) {
- return (tick - this.startTicks) % this.tickTime === 0;
+ Tone.Transport.prototype.clearTimeline = function (id) {
+ console.warn('This method is deprecated. Use Tone.Transport.clear instead.');
+ return Tone.Transport.clear(id);
};
///////////////////////////////////////////////////////////////////////////////
- // AUGMENT TONE'S PROTOTYPE TO INCLUDE TRANSPORT TIMING
+ // INITIALIZATION
///////////////////////////////////////////////////////////////////////////////
- /**
- * tests if a string is musical notation
- * i.e.:
- * 4n = quarter note
- * 2m = two measures
- * 8t = eighth-note triplet
- *
- * @return {boolean}
- * @method isNotation
- * @lends Tone.prototype.isNotation
- */
- Tone.prototype.isNotation = function () {
- var notationFormat = new RegExp(/[0-9]+[mnt]$/i);
- return function (note) {
- return notationFormat.test(note);
- };
- }();
- /**
- * tests if a string is transportTime
- * i.e. :
- * 1:2:0 = 1 measure + two quarter notes + 0 sixteenth notes
- *
- * @return {boolean}
- *
- * @method isTransportTime
- * @lends Tone.prototype.isTransportTime
- */
- Tone.prototype.isTransportTime = function () {
- var transportTimeFormat = new RegExp(/^\d+(\.\d+)?:\d+(\.\d+)?(:\d+(\.\d+)?)?$/i);
- return function (transportTime) {
- return transportTimeFormat.test(transportTime);
- };
- }();
- /**
- *
- * convert notation format strings to seconds
- *
- * @param {string} notation
- * @param {number=} bpm
- * @param {number=} timeSignature
- * @return {number}
- *
- */
- Tone.prototype.notationToSeconds = function (notation, bpm, timeSignature) {
- bpm = this.defaultArg(bpm, Tone.Transport.bpm.value);
- timeSignature = this.defaultArg(timeSignature, transportTimeSignature);
- var beatTime = 60 / bpm;
- var subdivision = parseInt(notation, 10);
- var beats = 0;
- if (subdivision === 0) {
- beats = 0;
- }
- var lastLetter = notation.slice(-1);
- if (lastLetter === 't') {
- beats = 4 / subdivision * 2 / 3;
- } else if (lastLetter === 'n') {
- beats = 4 / subdivision;
- } else if (lastLetter === 'm') {
- beats = subdivision * timeSignature;
- } else {
- beats = 0;
- }
- return beatTime * beats;
- };
- /**
- * convert transportTime into seconds.
- *
- * ie: 4:2:3 == 4 measures + 2 quarters + 3 sixteenths
- *
- * @param {string} transportTime
- * @param {number=} bpm
- * @param {number=} timeSignature
- * @return {number} seconds
- *
- * @lends Tone.prototype.transportTimeToSeconds
- */
- Tone.prototype.transportTimeToSeconds = function (transportTime, bpm, timeSignature) {
- bpm = this.defaultArg(bpm, Tone.Transport.bpm.value);
- timeSignature = this.defaultArg(timeSignature, transportTimeSignature);
- var measures = 0;
- var quarters = 0;
- var sixteenths = 0;
- var split = transportTime.split(':');
- if (split.length === 2) {
- measures = parseFloat(split[0]);
- quarters = parseFloat(split[1]);
- } else if (split.length === 1) {
- quarters = parseFloat(split[0]);
- } else if (split.length === 3) {
- measures = parseFloat(split[0]);
- quarters = parseFloat(split[1]);
- sixteenths = parseFloat(split[2]);
- }
- var beats = measures * timeSignature + quarters + sixteenths / 4;
- return beats * this.notationToSeconds('4n');
- };
- /**
- * Convert seconds to the closest transportTime in the form
- * measures:quarters:sixteenths
- *
- * @method toTransportTime
- *
- * @param {Time} seconds
- * @param {number=} bpm
- * @param {number=} timeSignature
- * @return {string}
- *
- * @lends Tone.prototype.toTransportTime
- */
- Tone.prototype.toTransportTime = function (time, bpm, timeSignature) {
- var seconds = this.toSeconds(time, bpm, timeSignature);
- bpm = this.defaultArg(bpm, Tone.Transport.bpm.value);
- timeSignature = this.defaultArg(timeSignature, transportTimeSignature);
- var quarterTime = this.notationToSeconds('4n');
- var quarters = seconds / quarterTime;
- var measures = Math.floor(quarters / timeSignature);
- var sixteenths = Math.floor(quarters % 1 * 4);
- quarters = Math.floor(quarters) % timeSignature;
- var progress = [
- measures,
- quarters,
- sixteenths
- ];
- return progress.join(':');
- };
- /**
- * Convert a frequency representation into a number.
- *
- * @param {Frequency} freq
- * @param {number=} now if passed in, this number will be
- * used for all 'now' relative timings
- * @return {number} the frequency in hertz
- */
- Tone.prototype.toFrequency = function (freq, now) {
- if (this.isFrequency(freq)) {
- return parseFloat(freq);
- } else if (this.isNotation(freq) || this.isTransportTime(freq)) {
- return this.secondsToFrequency(this.toSeconds(freq, now));
- } else {
- return freq;
- }
- };
- /**
- * Convert Time into seconds.
- *
- * Unlike the method which it overrides, this takes into account
- * transporttime and musical notation.
- *
- * Time : 1.40
- * Notation: 4n|1m|2t
- * TransportTime: 2:4:1 (measure:quarters:sixteens)
- * Now Relative: +3n
- * Math: 3n+16n or even very complicated expressions ((3n*2)/6 + 1)
- *
- * @override
- * @param {Time} time
- * @param {number=} now if passed in, this number will be
- * used for all 'now' relative timings
- * @return {number}
- */
- Tone.prototype.toSeconds = function (time, now) {
- now = this.defaultArg(now, this.now());
- if (typeof time === 'number') {
- return time; //assuming that it's seconds
- } else if (typeof time === 'string') {
- var plusTime = 0;
- if (time.charAt(0) === '+') {
- plusTime = now;
- time = time.slice(1);
- }
- var components = time.split(/[\(\)\-\+\/\*]/);
- if (components.length > 1) {
- var originalTime = time;
- for (var i = 0; i < components.length; i++) {
- var symb = components[i].trim();
- if (symb !== '') {
- var val = this.toSeconds(symb);
- time = time.replace(symb, val);
- }
- }
- try {
- //i know eval is evil, but i think it's safe here
- time = eval(time); // jshint ignore:line
- } catch (e) {
- throw new EvalError('problem evaluating Tone.Type.Time: ' + originalTime);
- }
- } else if (this.isNotation(time)) {
- time = this.notationToSeconds(time);
- } else if (this.isTransportTime(time)) {
- time = this.transportTimeToSeconds(time);
- } else if (this.isFrequency(time)) {
- time = this.frequencyToSeconds(time);
- } else {
- time = parseFloat(time);
- }
- return time + plusTime;
- } else {
- return now;
- }
- };
var TransportConstructor = Tone.Transport;
Tone._initAudioContext(function () {
if (typeof Tone.Transport === 'function') {
@@ -5826,14 +7814,14 @@
} else {
//stop the clock
Tone.Transport.stop();
- //get the previous bpm
- var bpm = Tone.Transport.bpm.value;
- //destory the old clock
- Tone.Transport._clock.dispose();
+ //get the previous values
+ var prevSettings = Tone.Transport.get();
+ //destory the old transport
+ Tone.Transport.dispose();
//make new Transport insides
TransportConstructor.call(Tone.Transport);
- //set the bpm
- Tone.Transport.bpm.value = bpm;
+ //set the previous config
+ Tone.Transport.set(prevSettings);
}
});
return Tone.Transport;
@@ -5841,179 +7829,57 @@
Module(function (Tone) {
/**
- * @class A single master output which is connected to the
- * AudioDestinationNode (aka your speakers).
- * It provides useful conveniences such as the ability
- * to set the volume and mute the entire application.
- * It also gives you the ability to apply master effects to your application.
- *
- * Like Tone.Transport, A single Tone.Master is created
- * on initialization and you do not need to explicitly construct one.
+ * @class Tone.Volume is a simple volume node, useful for creating a volume fader.
*
- * @constructor
* @extends {Tone}
- * @singleton
+ * @constructor
+ * @param {Decibels} [volume=0] the initial volume
* @example
- * //the audio will go from the oscillator to the speakers
- * oscillator.connect(Tone.Master);
- * //a convenience for connecting to the master output is also provided:
- * oscillator.toMaster();
- * //the above two examples are equivalent.
+ * var vol = new Tone.Volume(-12);
+ * instrument.chain(vol, Tone.Master);
*/
- Tone.Master = function () {
- Tone.call(this);
- /**
- * the unmuted volume
- * @type {number}
- * @private
- */
- this._unmutedVolume = 1;
- /**
- * if the master is muted
- * @type {boolean}
- * @private
- */
- this._muted = false;
- /**
- * The volume of the master output.
- * @type {Decibels}
- * @signal
- */
- this.volume = new Tone.Signal(this.output.gain, Tone.Type.Decibels);
- //connections
- this.input.chain(this.output, this.context.destination);
+ Tone.Volume = function () {
+ var options = this.optionsObject(arguments, ['value'], Tone.Volume.defaults);
+ Tone.Gain.call(this, options.value, Tone.Type.Decibels);
};
- Tone.extend(Tone.Master);
+ Tone.extend(Tone.Volume, Tone.Gain);
/**
- * @type {Object}
+ * Defaults
+ * @type {Object}
* @const
+ * @static
*/
- Tone.Master.defaults = {
- 'volume': 0,
- 'mute': false
- };
- /**
- * Mute the output.
- * @memberOf Tone.Master#
- * @type {boolean}
- * @name mute
- * @example
- * //mute the output
- * Tone.Master.mute = true;
- */
- Object.defineProperty(Tone.Master.prototype, 'mute', {
- get: function () {
- return this._muted;
- },
- set: function (mute) {
- if (!this._muted && mute) {
- this._unmutedVolume = this.volume.value;
- //maybe it should ramp here?
- this.volume.value = -Infinity;
- } else if (this._muted && !mute) {
- this.volume.value = this._unmutedVolume;
- }
- this._muted = mute;
- }
- });
- /**
- * Add a master effects chain. NOTE: this will disconnect any nodes which were previously
- * chained in the master effects chain.
- * @param {AudioNode|Tone...} args All arguments will be connected in a row
- * and the Master will be routed through it.
- * @return {Tone.Master} this
- * @example
- * //some overall compression to keep the levels in check
- * var masterCompressor = new Tone.Compressor({
- * "threshold" : -6,
- * "ratio" : 3,
- * "attack" : 0.5,
- * "release" : 0.1
- * });
- * //give a little boost to the lows
- * var lowBump = new Tone.Filter(200, "lowshelf");
- * //route everything through the filter
- * //and compressor before going to the speakers
- * Tone.Master.chain(lowBump, masterCompressor);
- */
- Tone.Master.prototype.chain = function () {
- this.input.disconnect();
- this.input.chain.apply(this.input, arguments);
- arguments[arguments.length - 1].connect(this.output);
- };
- ///////////////////////////////////////////////////////////////////////////
- // AUGMENT TONE's PROTOTYPE
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Connect 'this' to the master output. Shorthand for this.connect(Tone.Master)
- * @returns {Tone} this
- * @example
- * //connect an oscillator to the master output
- * var osc = new Tone.Oscillator().toMaster();
- */
- Tone.prototype.toMaster = function () {
- this.connect(Tone.Master);
- return this;
- };
- /**
- * Also augment AudioNode's prototype to include toMaster
- * as a convenience
- * @returns {AudioNode} this
- */
- AudioNode.prototype.toMaster = function () {
- this.connect(Tone.Master);
- return this;
- };
- var MasterConstructor = Tone.Master;
- /**
- * initialize the module and listen for new audio contexts
- */
- Tone._initAudioContext(function () {
- //a single master output
- if (!Tone.prototype.isUndef(Tone.Master)) {
- Tone.Master = new MasterConstructor();
- } else {
- MasterConstructor.prototype.dispose.call(Tone.Master);
- MasterConstructor.call(Tone.Master);
- }
- });
- return Tone.Master;
+ Tone.Volume.defaults = { 'value': 0 };
+ return Tone.Volume;
});
Module(function (Tone) {
/**
* @class Base class for sources. Sources have start/stop methods
* and the ability to be synced to the
- * start/stop of Tone.Transport.
+ * start/stop of Tone.Transport.
*
* @constructor
* @extends {Tone}
+ * @example
+ * //Multiple state change events can be chained together,
+ * //but must be set in the correct order and with ascending times
+ *
+ * // OK
+ * state.start().stop("+0.2");
+ * // AND
+ * state.start().stop("+0.2").start("+0.4").stop("+0.7")
+ *
+ * // BAD
+ * state.stop("+0.2").start();
+ * // OR
+ * state.start("+0.3").stop("+0.2");
+ *
*/
Tone.Source = function (options) {
- //unlike most ToneNodes, Sources only have an output and no input
- Tone.call(this, 0, 1);
+ //Sources only have an output and no input
+ Tone.call(this);
options = this.defaultArg(options, Tone.Source.defaults);
- /**
- * Callback is invoked when the source is done playing.
- * @type {function}
- * @example
- * source.onended = function(){
- * console.log("the source is done playing");
- * }
- */
- this.onended = options.onended;
- /**
- * the next time the source is started
- * @type {number}
- * @private
- */
- this._nextStart = Infinity;
- /**
- * the next time the source is stopped
- * @type {number}
- * @private
- */
- this._nextStop = Infinity;
/**
* The volume of the output in decibels.
* @type {Decibels}
@@ -6021,19 +7887,36 @@
* @example
* source.volume.value = -6;
*/
- this.volume = new Tone.Signal({
- 'param': this.output.gain,
- 'value': options.volume,
- 'units': Tone.Type.Decibels
- });
+ this.volume = this.output = new Tone.Volume(options.volume);
this._readOnly('volume');
/**
- * keeps track of the timeout for chaning the state
- * and calling the onended
- * @type {number}
+ * Keep track of the scheduled state.
+ * @type {Tone.TimelineState}
* @private
*/
- this._timeout = -1;
+ this._state = new Tone.TimelineState(Tone.State.Stopped);
+ /**
+ * The synced `start` callback function from the transport
+ * @type {Function}
+ * @private
+ */
+ this._syncStart = function (time, offset) {
+ time = this.toSeconds(time);
+ time += this.toSeconds(this._startDelay);
+ this.start(time, offset);
+ }.bind(this);
+ /**
+ * The synced `stop` callback function from the transport
+ * @type {Function}
+ * @private
+ */
+ this._syncStop = this.stop.bind(this);
+ /**
+ * The offset from the start of the Transport `start`
+ * @type {Time}
+ * @private
+ */
+ this._startDelay = 0;
//make the output explicitly stereo
this.output.channelCount = 2;
this.output.channelCountMode = 'explicit';
@@ -6045,10 +7928,7 @@
* @const
* @type {Object}
*/
- Tone.Source.defaults = {
- 'onended': Tone.noOp,
- 'volume': 0
- };
+ Tone.Source.defaults = { 'volume': 0 };
/**
* Returns the playback state of the source, either "started" or "stopped".
* @type {Tone.State}
@@ -6058,25 +7938,9 @@
*/
Object.defineProperty(Tone.Source.prototype, 'state', {
get: function () {
- return this._stateAtTime(this.now());
+ return this._state.getStateAtTime(this.now());
}
});
- /**
- * Get the state of the source at the specified time.
- * @param {Time} time
- * @return {Tone.State}
- * @private
- */
- Tone.Source.prototype._stateAtTime = function (time) {
- time = this.toSeconds(time);
- if (this._nextStart <= time && this._nextStop > time) {
- return Tone.State.Started;
- } else if (this._nextStop <= time) {
- return Tone.State.Stopped;
- } else {
- return Tone.State.Stopped;
- }
- };
/**
* Start the source at the specified time. If no time is given,
* start the source now.
@@ -6087,10 +7951,11 @@
*/
Tone.Source.prototype.start = function (time) {
time = this.toSeconds(time);
- if (this._stateAtTime(time) !== Tone.State.Started || this.retrigger) {
- this._nextStart = time;
- this._nextStop = Infinity;
- this._start.apply(this, arguments);
+ if (this._state.getStateAtTime(time) !== Tone.State.Started || this.retrigger) {
+ this._state.setStateAtTime(Tone.State.Started, time);
+ if (this._start) {
+ this._start.apply(this, arguments);
+ }
}
return this;
};
@@ -6103,34 +7968,15 @@
* source.stop(); // stops the source immediately
*/
Tone.Source.prototype.stop = function (time) {
- var now = this.now();
- time = this.toSeconds(time, now);
- if (this._stateAtTime(time) === Tone.State.Started) {
- this._nextStop = this.toSeconds(time);
- clearTimeout(this._timeout);
- var diff = time - now;
- if (diff > 0) {
- //add a small buffer before invoking the callback
- this._timeout = setTimeout(this.onended, diff * 1000 + 20);
- } else {
- this.onended();
+ time = this.toSeconds(time);
+ if (this._state.getStateAtTime(time) === Tone.State.Started) {
+ this._state.setStateAtTime(Tone.State.Stopped, time);
+ if (this._stop) {
+ this._stop.apply(this, arguments);
}
- this._stop.apply(this, arguments);
}
return this;
};
- /**
- * Not ready yet.
- * @private
- * @abstract
- * @param {Time} time
- * @returns {Tone.Source} this
- */
- Tone.Source.prototype.pause = function (time) {
- //if there is no pause, just stop it
- this.stop(time);
- return this;
- };
/**
* Sync the source to the Transport so that when the transport
* is started, this source is started and when the transport is stopped
@@ -6146,7 +7992,9 @@
* Tone.Transport.start();
*/
Tone.Source.prototype.sync = function (delay) {
- Tone.Transport.syncSource(this, delay);
+ this._startDelay = this.defaultArg(delay, 0);
+ Tone.Transport.on('start', this._syncStart);
+ Tone.Transport.on('stop pause', this._syncStop);
return this;
};
/**
@@ -6154,7 +8002,9 @@
* @returns {Tone.Source} this
*/
Tone.Source.prototype.unsync = function () {
- Tone.Transport.unsyncSource(this);
+ this._startDelay = 0;
+ Tone.Transport.off('start', this._syncStart);
+ Tone.Transport.off('stop pause', this._syncStop);
return this;
};
/**
@@ -6162,13 +8012,16 @@
* @return {Tone.Source} this
*/
Tone.Source.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
this.stop();
- clearTimeout(this._timeout);
- this.onended = Tone.noOp;
+ Tone.prototype.dispose.call(this);
+ this.unsync();
this._writable('volume');
this.volume.dispose();
this.volume = null;
+ this._state.dispose();
+ this._state = null;
+ this._syncStart = null;
+ this._syncStart = null;
};
return Tone.Source;
});
@@ -6217,6 +8070,12 @@
* @private
*/
this._wave = null;
+ /**
+ * The partials of the oscillator
+ * @type {Array}
+ * @private
+ */
+ this._partials = this.defaultArg(options.partials, [1]);
/**
* the phase of the oscillator
* between 0 - 360
@@ -6249,6 +8108,17 @@
'detune': 0,
'phase': 0
};
+ /**
+ * The Oscillator types
+ * @enum {String}
+ */
+ Tone.Oscillator.Type = {
+ Sine: 'sine',
+ Triangle: 'triangle',
+ Sawtooth: 'sawtooth',
+ Square: 'square',
+ Custom: 'custom'
+ };
/**
* start the oscillator
* @param {Time} [time=now]
@@ -6328,57 +8198,135 @@
return this._type;
},
set: function (type) {
- var originalType = type;
- var fftSize = 4096;
- var periodicWaveSize = fftSize / 2;
- var real = new Float32Array(periodicWaveSize);
- var imag = new Float32Array(periodicWaveSize);
- var partialCount = 1;
- var partial = /(sine|triangle|square|sawtooth)(\d+)$/.exec(type);
- if (partial) {
- partialCount = parseInt(partial[2]);
- type = partial[1];
- partialCount = Math.max(partialCount, 2);
- periodicWaveSize = partialCount;
- }
- var shift = this._phase;
- for (var n = 1; n < periodicWaveSize; ++n) {
- var piFactor = 2 / (n * Math.PI);
- var b;
- switch (type) {
- case 'sine':
- b = n <= partialCount ? 1 : 0;
- break;
- case 'square':
- b = n & 1 ? 2 * piFactor : 0;
- break;
- case 'sawtooth':
- b = piFactor * (n & 1 ? 1 : -1);
- break;
- case 'triangle':
- if (n & 1) {
- b = 2 * (piFactor * piFactor) * (n - 1 >> 1 & 1 ? -1 : 1);
- } else {
- b = 0;
- }
- break;
- default:
- throw new TypeError('invalid oscillator type: ' + type);
- }
- if (b !== 0) {
- real[n] = -b * Math.sin(shift * n);
- imag[n] = b * Math.cos(shift * n);
- } else {
- real[n] = 0;
- imag[n] = 0;
- }
- }
- var periodicWave = this.context.createPeriodicWave(real, imag);
+ var coefs = this._getRealImaginary(type, this._phase);
+ var periodicWave = this.context.createPeriodicWave(coefs[0], coefs[1]);
this._wave = periodicWave;
if (this._oscillator !== null) {
this._oscillator.setPeriodicWave(this._wave);
}
- this._type = originalType;
+ this._type = type;
+ }
+ });
+ /**
+ * Returns the real and imaginary components based
+ * on the oscillator type.
+ * @returns {Array} [real, imaginary]
+ * @private
+ */
+ Tone.Oscillator.prototype._getRealImaginary = function (type, phase) {
+ var fftSize = 4096;
+ var periodicWaveSize = fftSize / 2;
+ var real = new Float32Array(periodicWaveSize);
+ var imag = new Float32Array(periodicWaveSize);
+ var partialCount = 1;
+ if (type === Tone.Oscillator.Type.Custom) {
+ partialCount = this._partials.length + 1;
+ periodicWaveSize = partialCount;
+ } else {
+ var partial = /^(sine|triangle|square|sawtooth)(\d+)$/.exec(type);
+ if (partial) {
+ partialCount = parseInt(partial[2]) + 1;
+ type = partial[1];
+ partialCount = Math.max(partialCount, 2);
+ periodicWaveSize = partialCount;
+ }
+ }
+ for (var n = 1; n < periodicWaveSize; ++n) {
+ var piFactor = 2 / (n * Math.PI);
+ var b;
+ switch (type) {
+ case Tone.Oscillator.Type.Sine:
+ b = n <= partialCount ? 1 : 0;
+ break;
+ case Tone.Oscillator.Type.Square:
+ b = n & 1 ? 2 * piFactor : 0;
+ break;
+ case Tone.Oscillator.Type.Sawtooth:
+ b = piFactor * (n & 1 ? 1 : -1);
+ break;
+ case Tone.Oscillator.Type.Triangle:
+ if (n & 1) {
+ b = 2 * (piFactor * piFactor) * (n - 1 >> 1 & 1 ? -1 : 1);
+ } else {
+ b = 0;
+ }
+ break;
+ case Tone.Oscillator.Type.Custom:
+ b = this._partials[n - 1];
+ break;
+ default:
+ throw new Error('invalid oscillator type: ' + type);
+ }
+ if (b !== 0) {
+ real[n] = -b * Math.sin(phase * n);
+ imag[n] = b * Math.cos(phase * n);
+ } else {
+ real[n] = 0;
+ imag[n] = 0;
+ }
+ }
+ return [
+ real,
+ imag
+ ];
+ };
+ /**
+ * Compute the inverse FFT for a given phase.
+ * @param {Float32Array} real
+ * @param {Float32Array} imag
+ * @param {NormalRange} phase
+ * @return {AudioRange}
+ * @private
+ */
+ Tone.Oscillator.prototype._inverseFFT = function (real, imag, phase) {
+ var sum = 0;
+ var len = real.length;
+ for (var i = 0; i < len; i++) {
+ sum += real[i] * Math.cos(i * phase) + imag[i] * Math.sin(i * phase);
+ }
+ return sum;
+ };
+ /**
+ * Returns the initial value of the oscillator.
+ * @return {AudioRange}
+ * @private
+ */
+ Tone.Oscillator.prototype._getInitialValue = function () {
+ var coefs = this._getRealImaginary(this._type, 0);
+ var real = coefs[0];
+ var imag = coefs[1];
+ var maxValue = 0;
+ var twoPi = Math.PI * 2;
+ //check for peaks in 8 places
+ for (var i = 0; i < 8; i++) {
+ maxValue = Math.max(this._inverseFFT(real, imag, i / 8 * twoPi), maxValue);
+ }
+ return -this._inverseFFT(real, imag, this._phase) / maxValue;
+ };
+ /**
+ * The partials of the waveform. A partial represents
+ * the amplitude at a harmonic. The first harmonic is the
+ * fundamental frequency, the second is the octave and so on
+ * following the harmonic series.
+ * Setting this value will automatically set the type to "custom".
+ * The value is an empty array when the type is not "custom".
+ * @memberOf Tone.Oscillator#
+ * @type {Array}
+ * @name partials
+ * @example
+ * osc.partials = [1, 0.2, 0.01];
+ */
+ Object.defineProperty(Tone.Oscillator.prototype, 'partials', {
+ get: function () {
+ if (this._type !== Tone.Oscillator.Type.Custom) {
+ return [];
+ } else {
+ return this._partials;
+ }
+ },
+ set: function (partials) {
+ this._partials = partials;
+ this.type = Tone.Oscillator.Type.Custom;
}
});
/**
@@ -6418,43 +8366,11 @@
this.frequency = null;
this.detune.dispose();
this.detune = null;
+ this._partials = null;
return this;
};
return Tone.Oscillator;
});
- Module(function (Tone) {
-
- /**
- * @class AudioToGain converts an input in AudioRange [-1,1] to NormalRange [0,1].
- * See Tone.GainToAudio.
- *
- * @extends {Tone.SignalBase}
- * @constructor
- * @example
- * var a2g = new Tone.AudioToGain();
- */
- Tone.AudioToGain = function () {
- /**
- * @type {WaveShaperNode}
- * @private
- */
- this._norm = this.input = this.output = new Tone.WaveShaper(function (x) {
- return (x + 1) / 2;
- });
- };
- Tone.extend(Tone.AudioToGain, Tone.SignalBase);
- /**
- * clean up
- * @returns {Tone.AudioToGain} this
- */
- Tone.AudioToGain.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
- this._norm.dispose();
- this._norm = null;
- return this;
- };
- return Tone.AudioToGain;
- });
Module(function (Tone) {
/**
@@ -6467,8 +8383,7 @@
* @extends {Tone.Oscillator}
* @param {Frequency|Object} [frequency] The frequency of the oscillation. Typically, LFOs will be
* in the frequency range of 0.1 to 10 hertz.
- * @param {number=} min The minimum output value of the LFO. The LFO starts
- * at it's minimum value.
+ * @param {number=} min The minimum output value of the LFO.
* @param {number=} max The maximum value of the LFO.
* @example
* var lfo = new Tone.LFO("4n", 400, 4000);
@@ -6485,17 +8400,16 @@
* @type {Tone.Oscillator}
* @private
*/
- this.oscillator = new Tone.Oscillator({
+ this._oscillator = new Tone.Oscillator({
'frequency': options.frequency,
- 'type': options.type,
- 'phase': options.phase + 90
+ 'type': options.type
});
/**
* the lfo's frequency
* @type {Frequency}
* @signal
*/
- this.frequency = this.oscillator.frequency;
+ this.frequency = this._oscillator.frequency;
/**
* The amplitude of the LFO, which controls the output range between
* the min and max output. For example if the min is -10 and the max
@@ -6504,9 +8418,21 @@
* @type {Number}
* @signal
*/
- this.amplitude = this.oscillator.volume;
+ this.amplitude = this._oscillator.volume;
this.amplitude.units = Tone.Type.NormalRange;
this.amplitude.value = options.amplitude;
+ /**
+ * The signal which is output when the LFO is stopped
+ * @type {Tone.Signal}
+ * @private
+ */
+ this._stoppedSignal = new Tone.Signal(0, Tone.Type.AudioRange);
+ /**
+ * The value that the LFO outputs when it's stopped
+ * @type {AudioRange}
+ * @private
+ */
+ this._stoppedValue = 0;
/**
* @type {Tone.AudioToGain}
* @private
@@ -6519,17 +8445,19 @@
this._scaler = this.output = new Tone.Scale(options.min, options.max);
/**
* the units of the LFO (used for converting)
- * @type {string}
+ * @type {Tone.Type}
* @private
*/
this._units = Tone.Type.Default;
+ this.units = options.units;
//connect it up
- this.oscillator.chain(this._a2g, this._scaler);
+ this._oscillator.chain(this._a2g, this._scaler);
+ this._stoppedSignal.connect(this._a2g);
this._readOnly([
'amplitude',
- 'frequency',
- 'oscillator'
+ 'frequency'
]);
+ this.phase = options.phase;
};
Tone.extend(Tone.LFO, Tone.Oscillator);
/**
@@ -6545,7 +8473,8 @@
'max': 1,
'phase': 0,
'frequency': '4n',
- 'amplitude': 1
+ 'amplitude': 1,
+ 'units': Tone.Type.Default
};
/**
* Start the LFO.
@@ -6553,7 +8482,9 @@
* @returns {Tone.LFO} this
*/
Tone.LFO.prototype.start = function (time) {
- this.oscillator.start(time);
+ time = this.toSeconds(time);
+ this._stoppedSignal.setValueAtTime(0, time);
+ this._oscillator.start(time);
return this;
};
/**
@@ -6562,7 +8493,9 @@
* @returns {Tone.LFO} this
*/
Tone.LFO.prototype.stop = function (time) {
- this.oscillator.stop(time);
+ time = this.toSeconds(time);
+ this._stoppedSignal.setValueAtTime(this._stoppedValue, time);
+ this._oscillator.stop(time);
return this;
};
/**
@@ -6579,8 +8512,8 @@
* //even as the tempo changes
*/
Tone.LFO.prototype.sync = function (delay) {
- this.oscillator.sync(delay);
- this.oscillator.syncFrequency();
+ this._oscillator.sync(delay);
+ this._oscillator.syncFrequency();
return this;
};
/**
@@ -6588,8 +8521,8 @@
* @returns {Tone.LFO} this
*/
Tone.LFO.prototype.unsync = function () {
- this.oscillator.unsync();
- this.oscillator.unsyncFrequency();
+ this._oscillator.unsync();
+ this._oscillator.unsyncFrequency();
return this;
};
/**
@@ -6630,10 +8563,12 @@
*/
Object.defineProperty(Tone.LFO.prototype, 'type', {
get: function () {
- return this.oscillator.type;
+ return this._oscillator.type;
},
set: function (type) {
- this.oscillator.type = type;
+ this._oscillator.type = type;
+ this._stoppedValue = this._oscillator._getInitialValue();
+ this._stoppedSignal.value = this._stoppedValue;
}
});
/**
@@ -6644,10 +8579,12 @@
*/
Object.defineProperty(Tone.LFO.prototype, 'phase', {
get: function () {
- return this.oscillator.phase - 90;
+ return this._oscillator.phase;
},
set: function (phase) {
- this.oscillator.phase = phase + 90;
+ this._oscillator.phase = phase;
+ this._stoppedValue = this._oscillator._getInitialValue();
+ this._stoppedSignal.value = this._stoppedValue;
}
});
/**
@@ -6670,7 +8607,20 @@
}
});
/**
- * Connect the output of a ToneNode to an AudioParam, AudioNode, or Tone Node.
+ * Returns the playback state of the source, either "started" or "stopped".
+ * @type {Tone.State}
+ * @readOnly
+ * @memberOf Tone.LFO#
+ * @name state
+ */
+ Object.defineProperty(Tone.LFO.prototype, 'state', {
+ get: function () {
+ return this._oscillator.state;
+ }
+ });
+ /**
+ * Connect the output of the LFO to an AudioParam, AudioNode, or Tone Node.
+ * Tone.LFO will automatically convert to the destination units of the
* will get the units from the connected node.
* @param {Tone | AudioParam | AudioNode} node
* @param {number} [outputNum=0] optionally which output to connect from
@@ -6679,7 +8629,7 @@
* @private
*/
Tone.LFO.prototype.connect = function (node) {
- if (node.constructor === Tone.Signal) {
+ if (node.constructor === Tone.Signal || node.constructor === Tone.Param || node.constructor === Tone.TimelineSignal) {
this.convert = node.convert;
this.units = node.units;
}
@@ -6687,19 +8637,19 @@
return this;
};
/**
- * private method borroed from Signal converts
+ * private method borrowed from Param converts
* units from their destination value
* @function
* @private
*/
- Tone.LFO.prototype._fromUnits = Tone.Signal.prototype._fromUnits;
+ Tone.LFO.prototype._fromUnits = Tone.Param.prototype._fromUnits;
/**
- * private method borroed from Signal converts
+ * private method borrowed from Param converts
* units to their destination value
* @function
* @private
*/
- Tone.LFO.prototype._toUnits = Tone.Signal.prototype._toUnits;
+ Tone.LFO.prototype._toUnits = Tone.Param.prototype._toUnits;
/**
* disconnect and dispose
* @returns {Tone.LFO} this
@@ -6708,11 +8658,12 @@
Tone.prototype.dispose.call(this);
this._writable([
'amplitude',
- 'frequency',
- 'oscillator'
+ 'frequency'
]);
- this.oscillator.dispose();
- this.oscillator = null;
+ this._oscillator.dispose();
+ this._oscillator = null;
+ this._stoppedSignal.dispose();
+ this._stoppedSignal = null;
this._scaler.dispose();
this._scaler = null;
this._a2g.dispose();
@@ -6739,7 +8690,8 @@
* @example
* var limiter = new Tone.Limiter(-6);
*/
- Tone.Limiter = function (threshold) {
+ Tone.Limiter = function () {
+ var options = this.optionsObject(arguments, ['threshold'], Tone.Limiter.defaults);
/**
* the compressor
* @private
@@ -6748,7 +8700,7 @@
this._compressor = this.input = this.output = new Tone.Compressor({
'attack': 0.001,
'decay': 0.001,
- 'threshold': threshold
+ 'threshold': options.threshold
});
/**
* The threshold of of the limiter
@@ -6759,6 +8711,13 @@
this._readOnly('threshold');
};
Tone.extend(Tone.Limiter);
+ /**
+ * The default value
+ * @type {Object}
+ * @const
+ * @static
+ */
+ Tone.Limiter.defaults = { 'threshold': -12 };
/**
* Clean up.
* @returns {Tone.Limiter} this
@@ -6818,8 +8777,11 @@
* @type {Frequency}
* @signal
*/
- this.dampening = new Tone.Signal(this._lowpass.frequency, Tone.Type.Frequency);
- this.dampening.value = options.dampening;
+ this.dampening = new Tone.Param({
+ 'param': this._lowpass.frequency,
+ 'units': Tone.Type.Frequency,
+ 'value': options.dampening
+ });
/**
* the feedback gain
* @type {GainNode}
@@ -6831,12 +8793,14 @@
* @type {NormalRange}
* @signal
*/
- this.resonance = new Tone.Signal(options.resonance, Tone.Type.NormalRange);
+ this.resonance = new Tone.Param({
+ 'param': this._feedback.gain,
+ 'units': Tone.Type.NormalRange,
+ 'value': options.resonance
+ });
//connections
this._delay.chain(this._lowpass, this._feedback, this._delay);
this.delayTime.connect(this._delay.delayTime);
- this.resonance.connect(this._feedback.gain);
- this.dampening.connect(this._lowpass.frequency);
this._readOnly([
'dampening',
'resonance',
@@ -6941,6 +8905,158 @@
};
return Tone.Merge;
});
+ Module(function (Tone) {
+
+ /**
+ * @class A single master output which is connected to the
+ * AudioDestinationNode (aka your speakers).
+ * It provides useful conveniences such as the ability
+ * to set the volume and mute the entire application.
+ * It also gives you the ability to apply master effects to your application.
+ *
+ * Like Tone.Transport, A single Tone.Master is created
+ * on initialization and you do not need to explicitly construct one.
+ *
+ * @constructor
+ * @extends {Tone}
+ * @singleton
+ * @example
+ * //the audio will go from the oscillator to the speakers
+ * oscillator.connect(Tone.Master);
+ * //a convenience for connecting to the master output is also provided:
+ * oscillator.toMaster();
+ * //the above two examples are equivalent.
+ */
+ Tone.Master = function () {
+ Tone.call(this);
+ /**
+ * the unmuted volume
+ * @type {number}
+ * @private
+ */
+ this._unmutedVolume = 1;
+ /**
+ * if the master is muted
+ * @type {boolean}
+ * @private
+ */
+ this._muted = false;
+ /**
+ * The volume of the master output.
+ * @type {Decibels}
+ * @signal
+ */
+ this.volume = this.output = new Tone.Volume();
+ this._readOnly('volume');
+ //connections
+ this.input.chain(this.output, this.context.destination);
+ };
+ Tone.extend(Tone.Master);
+ /**
+ * @type {Object}
+ * @const
+ */
+ Tone.Master.defaults = {
+ 'volume': 0,
+ 'mute': false
+ };
+ /**
+ * Mute the output.
+ * @memberOf Tone.Master#
+ * @type {boolean}
+ * @name mute
+ * @example
+ * //mute the output
+ * Tone.Master.mute = true;
+ */
+ Object.defineProperty(Tone.Master.prototype, 'mute', {
+ get: function () {
+ return this._muted;
+ },
+ set: function (mute) {
+ if (!this._muted && mute) {
+ this._unmutedVolume = this.volume.value;
+ //maybe it should ramp here?
+ this.volume.value = -Infinity;
+ } else if (this._muted && !mute) {
+ this.volume.value = this._unmutedVolume;
+ }
+ this._muted = mute;
+ }
+ });
+ /**
+ * Add a master effects chain. NOTE: this will disconnect any nodes which were previously
+ * chained in the master effects chain.
+ * @param {AudioNode|Tone...} args All arguments will be connected in a row
+ * and the Master will be routed through it.
+ * @return {Tone.Master} this
+ * @example
+ * //some overall compression to keep the levels in check
+ * var masterCompressor = new Tone.Compressor({
+ * "threshold" : -6,
+ * "ratio" : 3,
+ * "attack" : 0.5,
+ * "release" : 0.1
+ * });
+ * //give a little boost to the lows
+ * var lowBump = new Tone.Filter(200, "lowshelf");
+ * //route everything through the filter
+ * //and compressor before going to the speakers
+ * Tone.Master.chain(lowBump, masterCompressor);
+ */
+ Tone.Master.prototype.chain = function () {
+ this.input.disconnect();
+ this.input.chain.apply(this.input, arguments);
+ arguments[arguments.length - 1].connect(this.output);
+ };
+ /**
+ * Clean up
+ * @return {Tone.Master} this
+ */
+ Tone.Master.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ this._writable('volume');
+ this.volume.dispose();
+ this.volume = null;
+ };
+ ///////////////////////////////////////////////////////////////////////////
+ // AUGMENT TONE's PROTOTYPE
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * Connect 'this' to the master output. Shorthand for this.connect(Tone.Master)
+ * @returns {Tone} this
+ * @example
+ * //connect an oscillator to the master output
+ * var osc = new Tone.Oscillator().toMaster();
+ */
+ Tone.prototype.toMaster = function () {
+ this.connect(Tone.Master);
+ return this;
+ };
+ /**
+ * Also augment AudioNode's prototype to include toMaster
+ * as a convenience
+ * @returns {AudioNode} this
+ */
+ AudioNode.prototype.toMaster = function () {
+ this.connect(Tone.Master);
+ return this;
+ };
+ var MasterConstructor = Tone.Master;
+ /**
+ * initialize the module and listen for new audio contexts
+ */
+ Tone._initAudioContext(function () {
+ //a single master output
+ if (!Tone.prototype.isUndef(Tone.Master)) {
+ Tone.Master = new MasterConstructor();
+ } else {
+ MasterConstructor.prototype.dispose.call(Tone.Master);
+ MasterConstructor.call(Tone.Master);
+ }
+ });
+ return Tone.Master;
+ });
Module(function (Tone) {
/**
@@ -6966,7 +9082,11 @@
* //to access meter level
* meter.getLevel();
*/
- Tone.Meter = function (channels, smoothing, clipMemory) {
+ Tone.Meter = function () {
+ var options = this.optionsObject(arguments, [
+ 'channels',
+ 'smoothing'
+ ], Tone.Meter.defaults);
//extends Unit
Tone.call(this);
/**
@@ -6974,19 +9094,23 @@
* @type {number}
* @private
*/
- this._channels = this.defaultArg(channels, 1);
- /**
- * the smoothing value
- * @type {number}
- * @private
+ this._channels = options.channels;
+ /**
+ * The amount which the decays of the meter are smoothed. Small values
+ * will follow the contours of the incoming envelope more closely than large values.
+ * @type {NormalRange}
*/
- this._smoothing = this.defaultArg(smoothing, 0.8);
+ this.smoothing = options.smoothing;
/**
- * the amount of time a clip is remember for.
- * @type {number}
- * @private
+ * The amount of time a clip is remember for.
+ * @type {Time}
*/
- this._clipMemory = this.defaultArg(clipMemory, 0.5) * 1000;
+ this.clipMemory = options.clipMemory;
+ /**
+ * The value above which the signal is considered clipped.
+ * @type {Number}
+ */
+ this.clipLevel = options.clipLevel;
/**
* the rms for each of the channels
* @private
@@ -7007,14 +9131,18 @@
/**
* last time the values clipped
* @private
- * @type {number}
+ * @type {Array}
*/
- this._lastClip = 0;
+ this._lastClip = new Array(this._channels);
+ //zero out the clip array
+ for (var j = 0; j < this._lastClip.length; j++) {
+ this._lastClip[j] = 0;
+ }
/**
* @private
* @type {ScriptProcessorNode}
*/
- this._jsNode = this.context.createScriptProcessor(this.bufferSize, this._channels, 1);
+ this._jsNode = this.context.createScriptProcessor(options.bufferSize, this._channels, 1);
this._jsNode.onaudioprocess = this._onprocess.bind(this);
//so it doesn't get garbage collected
this._jsNode.noGC();
@@ -7023,6 +9151,19 @@
this.input.connect(this._jsNode);
};
Tone.extend(Tone.Meter);
+ /**
+ * The defaults
+ * @type {Object}
+ * @static
+ * @const
+ */
+ Tone.Meter.defaults = {
+ 'smoothing': 0.8,
+ 'bufferSize': 1024,
+ 'clipMemory': 0.5,
+ 'clipLevel': 0.9,
+ 'channels': 1
+ };
/**
* called on each processing frame
* @private
@@ -7030,24 +9171,22 @@
*/
Tone.Meter.prototype._onprocess = function (event) {
var bufferSize = this._jsNode.bufferSize;
- var smoothing = this._smoothing;
+ var smoothing = this.smoothing;
for (var channel = 0; channel < this._channels; channel++) {
var input = event.inputBuffer.getChannelData(channel);
var sum = 0;
var total = 0;
var x;
- var clipped = false;
for (var i = 0; i < bufferSize; i++) {
x = input[i];
- if (!clipped && x > 0.95) {
- clipped = true;
- this._lastClip = Date.now();
- }
total += x;
sum += x * x;
}
var average = total / bufferSize;
var rms = Math.sqrt(sum / bufferSize);
+ if (rms > 0.9) {
+ this._lastClip[channel] = Date.now();
+ }
this._volume[channel] = Math.max(rms, this._volume[channel] * smoothing);
this._values[channel] = average;
}
@@ -7087,8 +9226,9 @@
* @returns {boolean} if the audio has clipped. The value resets
* based on the clipMemory defined.
*/
- Tone.Meter.prototype.isClipped = function () {
- return Date.now() - this._lastClip < this._clipMemory;
+ Tone.Meter.prototype.isClipped = function (channel) {
+ channel = this.defaultArg(channel, 0);
+ return Date.now() - this._lastClip[channel] < this._clipMemory * 1000;
};
/**
* Clean up.
@@ -7098,8 +9238,10 @@
Tone.prototype.dispose.call(this);
this._jsNode.disconnect();
this._jsNode.onaudioprocess = null;
+ this._jsNode = null;
this._volume = null;
this._values = null;
+ this._lastClip = null;
return this;
};
return Tone.Meter;
@@ -7685,48 +9827,6 @@
};
return Tone.Panner;
});
- Module(function (Tone) {
-
- /**
- * @class Tone.Volume is a simple volume node, useful for creating a volume fader.
- *
- * @extends {Tone}
- * @constructor
- * @param {Decibels} [volume=0] the initial volume
- * @example
- * var vol = new Tone.Volume(-12);
- * instrument.chain(vol, Tone.Master);
- */
- Tone.Volume = function (volume) {
- /**
- * the output node
- * @type {GainNode}
- * @private
- */
- this.output = this.input = this.context.createGain();
- /**
- * The volume control in decibels.
- * @type {Decibels}
- * @signal
- */
- this.volume = new Tone.Signal(this.output.gain, Tone.Type.Decibels);
- this.volume.value = this.defaultArg(volume, 0);
- this._readOnly('volume');
- };
- Tone.extend(Tone.Volume);
- /**
- * clean up
- * @returns {Tone.Volume} this
- */
- Tone.Volume.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
- this._writable('volume');
- this.volume.dispose();
- this.volume = null;
- return this;
- };
- return Tone.Volume;
- });
Module(function (Tone) {
/**
@@ -7740,40 +9840,47 @@
* //pan the incoming signal left and drop the volume
* var panVol = new Tone.PanVol(0.25, -12);
*/
- Tone.PanVol = function (pan, volume) {
+ Tone.PanVol = function () {
+ var options = this.optionsObject(arguments, [
+ 'pan',
+ 'volume'
+ ], Tone.PanVol.defaults);
/**
* The panning node
* @type {Tone.Panner}
* @private
*/
- this._panner = this.input = new Tone.Panner(pan);
+ this._panner = this.input = new Tone.Panner(options.pan);
/**
* The L/R panning control.
* @type {NormalRange}
* @signal
*/
this.pan = this._panner.pan;
- /**
- * The volume object.
- * @type {Tone.Volume}
- * @signal
- * @private
- */
- this._volume = this.output = new Tone.Volume(volume);
/**
* The volume control in decibels.
* @type {Decibels}
* @signal
*/
- this.volume = this._volume.volume;
+ this.volume = this.output = new Tone.Volume(options.volume);
//connections
- this._panner.connect(this._volume);
+ this._panner.connect(this.volume);
this._readOnly([
'pan',
'volume'
]);
};
Tone.extend(Tone.PanVol);
+ /**
+ * The defaults
+ * @type {Object}
+ * @const
+ * @static
+ */
+ Tone.PanVol.defaults = {
+ 'pan': 0.5,
+ 'volume': 0
+ };
/**
* clean up
* @returns {Tone.PanVol} this
@@ -7786,9 +9893,8 @@
]);
this._panner.dispose();
this._panner = null;
- this._volume.dispose();
- this._volume = null;
this.pan = null;
+ this.volume.dispose();
this.volume = null;
return this;
};
@@ -7908,733 +10014,6 @@
};
return Tone.ScaledEnvelope;
});
- Module(function (Tone) {
-
- /**
- * @class Buffer loading and storage. Tone.Buffer is used internally by all
- * classes that make requests for audio files such as Tone.Player,
- * Tone.Sampler and Tone.Convolver.
- *
- * Aside from load callbacks from individual buffers, Tone.Buffer
- * provides static methods which keep track of the loading progress
- * of all of the buffers. These methods are Tone.Buffer.onload, Tone.Buffer.onprogress,
- * and Tone.Buffer.onerror.
- *
- * @constructor
- * @extends {Tone}
- * @param {AudioBuffer|string} url The url to load, or the audio buffer to set.
- * @param {function=} onload A callback which is invoked after the buffer is loaded.
- * It's recommended to use Tone.Buffer.onload instead
- * since it will give you a callback when ALL buffers are loaded.
- * @example
- * var buffer = new Tone.Buffer("path/to/sound.mp3", function(){
- * //the buffer is now available.
- * var buff = buffer.get();
- * });
- */
- Tone.Buffer = function () {
- var options = this.optionsObject(arguments, [
- 'url',
- 'onload'
- ], Tone.Buffer.defaults);
- /**
- * stores the loaded AudioBuffer
- * @type {AudioBuffer}
- * @private
- */
- this._buffer = null;
- /**
- * indicates if the buffer should be reversed or not
- * @type {boolean}
- * @private
- */
- this._reversed = options.reverse;
- /**
- * The url of the buffer. undefined
if it was
- * constructed with a buffer
- * @type {string}
- * @readOnly
- */
- this.url = undefined;
- /**
- * Indicates if the buffer is loaded or not.
- * @type {boolean}
- * @readOnly
- */
- this.loaded = false;
- /**
- * The callback to invoke when everything is loaded.
- * @type {function}
- */
- this.onload = options.onload.bind(this, this);
- if (options.url instanceof AudioBuffer) {
- this._buffer.set(options.url);
- this.onload(this);
- } else if (typeof options.url === 'string') {
- this.url = options.url;
- Tone.Buffer._addToQueue(options.url, this);
- }
- };
- Tone.extend(Tone.Buffer);
- /**
- * the default parameters
- * @type {Object}
- */
- Tone.Buffer.defaults = {
- 'url': undefined,
- 'onload': Tone.noOp,
- 'reverse': false
- };
- /**
- * Pass in an AudioBuffer or Tone.Buffer to set the value
- * of this buffer.
- * @param {AudioBuffer|Tone.Buffer} buffer the buffer
- * @returns {Tone.Buffer} this
- */
- Tone.Buffer.prototype.set = function (buffer) {
- if (buffer instanceof Tone.Buffer) {
- this._buffer = buffer.get();
- } else {
- this._buffer = buffer;
- }
- this.loaded = true;
- return this;
- };
- /**
- * @return {AudioBuffer} The audio buffer stored in the object.
- */
- Tone.Buffer.prototype.get = function () {
- return this._buffer;
- };
- /**
- * Load url into the buffer.
- * @param {String} url The url to load
- * @param {Function=} callback The callback to invoke on load.
- * don't need to set if `onload` is
- * already set.
- * @returns {Tone.Buffer} this
- */
- Tone.Buffer.prototype.load = function (url, callback) {
- this.url = url;
- this.onload = this.defaultArg(callback, this.onload);
- Tone.Buffer._addToQueue(url, this);
- return this;
- };
- /**
- * dispose and disconnect
- * @returns {Tone.Buffer} this
- */
- Tone.Buffer.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
- Tone.Buffer._removeFromQueue(this);
- this._buffer = null;
- this.onload = Tone.Buffer.defaults.onload;
- return this;
- };
- /**
- * The duration of the buffer.
- * @memberOf Tone.Buffer#
- * @type {number}
- * @name duration
- * @readOnly
- */
- Object.defineProperty(Tone.Buffer.prototype, 'duration', {
- get: function () {
- if (this._buffer) {
- return this._buffer.duration;
- } else {
- return 0;
- }
- }
- });
- /**
- * Reverse the buffer.
- * @private
- * @return {Tone.Buffer} this
- */
- Tone.Buffer.prototype._reverse = function () {
- if (this.loaded) {
- for (var i = 0; i < this._buffer.numberOfChannels; i++) {
- Array.prototype.reverse.call(this._buffer.getChannelData(i));
- }
- }
- return this;
- };
- /**
- * Reverse the buffer.
- * @memberOf Tone.Buffer#
- * @type {boolean}
- * @name reverse
- */
- Object.defineProperty(Tone.Buffer.prototype, 'reverse', {
- get: function () {
- return this._reversed;
- },
- set: function (rev) {
- if (this._reversed !== rev) {
- this._reversed = rev;
- this._reverse();
- }
- }
- });
- ///////////////////////////////////////////////////////////////////////////
- // STATIC METHODS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * the static queue for all of the xhr requests
- * @type {Array}
- * @private
- */
- Tone.Buffer._queue = [];
- /**
- * the array of current downloads
- * @type {Array}
- * @private
- */
- Tone.Buffer._currentDownloads = [];
- /**
- * the total number of downloads
- * @type {number}
- * @private
- */
- Tone.Buffer._totalDownloads = 0;
- /**
- * the maximum number of simultaneous downloads
- * @static
- * @type {number}
- */
- Tone.Buffer.MAX_SIMULTANEOUS_DOWNLOADS = 6;
- /**
- * Adds a file to be loaded to the loading queue
- * @param {string} url the url to load
- * @param {function} callback the callback to invoke once it's loaded
- * @private
- */
- Tone.Buffer._addToQueue = function (url, buffer) {
- Tone.Buffer._queue.push({
- url: url,
- Buffer: buffer,
- progress: 0,
- xhr: null
- });
- this._totalDownloads++;
- Tone.Buffer._next();
- };
- /**
- * Remove an object from the queue's (if it's still there)
- * Abort the XHR if it's in progress
- * @param {Tone.Buffer} buffer the buffer to remove
- * @private
- */
- Tone.Buffer._removeFromQueue = function (buffer) {
- var i;
- for (i = 0; i < Tone.Buffer._queue.length; i++) {
- var q = Tone.Buffer._queue[i];
- if (q.Buffer === buffer) {
- Tone.Buffer._queue.splice(i, 1);
- }
- }
- for (i = 0; i < Tone.Buffer._currentDownloads.length; i++) {
- var dl = Tone.Buffer._currentDownloads[i];
- if (dl.Buffer === buffer) {
- Tone.Buffer._currentDownloads.splice(i, 1);
- dl.xhr.abort();
- dl.xhr.onprogress = null;
- dl.xhr.onload = null;
- dl.xhr.onerror = null;
- }
- }
- };
- /**
- * load the next buffer in the queue
- * @private
- */
- Tone.Buffer._next = function () {
- if (Tone.Buffer._queue.length > 0) {
- if (Tone.Buffer._currentDownloads.length < Tone.Buffer.MAX_SIMULTANEOUS_DOWNLOADS) {
- var next = Tone.Buffer._queue.shift();
- Tone.Buffer._currentDownloads.push(next);
- next.xhr = Tone.Buffer.load(next.url, function (buffer) {
- //remove this one from the queue
- var index = Tone.Buffer._currentDownloads.indexOf(next);
- Tone.Buffer._currentDownloads.splice(index, 1);
- next.Buffer.set(buffer);
- if (next.Buffer._reversed) {
- next.Buffer._reverse();
- }
- next.Buffer.onload(next.Buffer);
- Tone.Buffer._onprogress();
- Tone.Buffer._next();
- });
- next.xhr.onprogress = function (event) {
- next.progress = event.loaded / event.total;
- Tone.Buffer._onprogress();
- };
- next.xhr.onerror = Tone.Buffer.onerror;
- }
- } else if (Tone.Buffer._currentDownloads.length === 0) {
- Tone.Buffer.onload();
- //reset the downloads
- Tone.Buffer._totalDownloads = 0;
- }
- };
- /**
- * internal progress event handler
- * @private
- */
- Tone.Buffer._onprogress = function () {
- var curretDownloadsProgress = 0;
- var currentDLLen = Tone.Buffer._currentDownloads.length;
- var inprogress = 0;
- if (currentDLLen > 0) {
- for (var i = 0; i < currentDLLen; i++) {
- var dl = Tone.Buffer._currentDownloads[i];
- curretDownloadsProgress += dl.progress;
- }
- inprogress = curretDownloadsProgress;
- }
- var currentDownloadProgress = currentDLLen - inprogress;
- var completed = Tone.Buffer._totalDownloads - Tone.Buffer._queue.length - currentDownloadProgress;
- Tone.Buffer.onprogress(completed / Tone.Buffer._totalDownloads);
- };
- /**
- * Makes an xhr reqest for the selected url then decodes
- * the file as an audio buffer. Invokes
- * the callback once the audio buffer loads.
- * @param {string} url The url of the buffer to load.
- * filetype support depends on the
- * browser.
- * @param {function} callback The function to invoke when the url is loaded.
- * @returns {XMLHttpRequest} returns the XHR
- */
- Tone.Buffer.load = function (url, callback) {
- var request = new XMLHttpRequest();
- request.open('GET', url, true);
- request.responseType = 'arraybuffer';
- // decode asynchronously
- request.onload = function () {
- Tone.context.decodeAudioData(request.response, function (buff) {
- if (!buff) {
- throw new Error('could not decode audio data:' + url);
- }
- callback(buff);
- });
- };
- //send the request
- request.send();
- return request;
- };
- /**
- * Callback when all of the buffers in the queue have loaded
- * @static
- * @function
- * @example
- * //invoked when all of the queued samples are done loading
- * Tone.Buffer.onload = function(){
- * console.log("everything is loaded");
- * };
- */
- Tone.Buffer.onload = Tone.noOp;
- /**
- * Callback function is invoked with the progress of all of the loads in the queue.
- * The value passed to the callback is between 0-1.
- * @static
- * @param {Number} percent The progress between 0 and 1.
- * @function
- * @example
- * Tone.Buffer.onprogress = function(percent){
- * console.log("progress:" + (percent * 100).toFixed(1) + "%");
- * };
- */
- Tone.Buffer.onprogress = Tone.noOp;
- /**
- * Callback if one of the buffers in the queue encounters an error. The error
- * is passed in as the argument.
- * @static
- * @param {Error} err
- * @function
- * @example
- * Tone.Buffer.onerror = function(e){
- * console.log("there was an error while loading the buffers: "+e);
- * }
- */
- Tone.Buffer.onerror = Tone.noOp;
- return Tone.Buffer;
- });
- Module(function (Tone) {
-
- /**
- * buses are another way of routing audio
- *
- * augments Tone.prototype to include send and recieve
- */
- /**
- * All of the routes
- *
- * @type {Object}
- * @static
- * @private
- */
- var Buses = {};
- /**
- * Send this signal to the channel name.
- * @param {string} channelName A named channel to send the signal to.
- * @param {Decibels} amount The amount of the source to send to the bus.
- * @return {GainNode} The gain node which connects this node to the desired channel.
- * Can be used to adjust the levels of the send.
- * @example
- * source.send("reverb", -12);
- */
- Tone.prototype.send = function (channelName, amount) {
- if (!Buses.hasOwnProperty(channelName)) {
- Buses[channelName] = this.context.createGain();
- }
- var sendKnob = this.context.createGain();
- sendKnob.gain.value = this.dbToGain(this.defaultArg(amount, 1));
- this.output.chain(sendKnob, Buses[channelName]);
- return sendKnob;
- };
- /**
- * Recieve the input from the desired channelName to the input
- *
- * @param {string} channelName A named channel to send the signal to.
- * @param {AudioNode} [input] If no input is selected, the
- * input of the current node is
- * chosen.
- * @returns {Tone} this
- * @example
- * reverbEffect.receive("reverb");
- */
- Tone.prototype.receive = function (channelName, input) {
- if (!Buses.hasOwnProperty(channelName)) {
- Buses[channelName] = this.context.createGain();
- }
- if (this.isUndef(input)) {
- input = this.input;
- }
- Buses[channelName].connect(input);
- return this;
- };
- return Tone;
- });
- Module(function (Tone) {
-
- /**
- * @class A timed note. Creating a note will register a callback
- * which will be invoked on the channel at the time with
- * whatever value was specified.
- *
- * @constructor
- * @param {number|string} channel the channel name of the note
- * @param {Time} time the time when the note will occur
- * @param {string|number|Object|Array} value the value of the note
- */
- Tone.Note = function (channel, time, value) {
- /**
- * the value of the note. This value is returned
- * when the channel callback is invoked.
- *
- * @type {string|number|Object}
- */
- this.value = value;
- /**
- * the channel name or number
- *
- * @type {string|number}
- * @private
- */
- this._channel = channel;
- /**
- * an internal reference to the id of the timeline
- * callback which is set.
- *
- * @type {number}
- * @private
- */
- this._timelineID = Tone.Transport.setTimeline(this._trigger.bind(this), time);
- };
- /**
- * invoked by the timeline
- * @private
- * @param {number} time the time at which the note should play
- */
- Tone.Note.prototype._trigger = function (time) {
- //invoke the callback
- channelCallbacks(this._channel, time, this.value);
- };
- /**
- * clean up
- * @returns {Tone.Note} this
- */
- Tone.Note.prototype.dispose = function () {
- Tone.Tranport.clearTimeline(this._timelineID);
- this.value = null;
- return this;
- };
- /**
- * @private
- * @static
- * @type {Object}
- */
- var NoteChannels = {};
- /**
- * invoke all of the callbacks on a specific channel
- * @private
- */
- function channelCallbacks(channel, time, value) {
- if (NoteChannels.hasOwnProperty(channel)) {
- var callbacks = NoteChannels[channel];
- for (var i = 0, len = callbacks.length; i < len; i++) {
- var callback = callbacks[i];
- if (Array.isArray(value)) {
- callback.apply(window, [time].concat(value));
- } else {
- callback(time, value);
- }
- }
- }
- }
- /**
- * listen to a specific channel, get all of the note callbacks
- * @static
- * @param {string|number} channel the channel to route note events from
- * @param {function(*)} callback callback to be invoked when a note will occur
- * on the specified channel
- */
- Tone.Note.route = function (channel, callback) {
- if (NoteChannels.hasOwnProperty(channel)) {
- NoteChannels[channel].push(callback);
- } else {
- NoteChannels[channel] = [callback];
- }
- };
- /**
- * Remove a previously routed callback from a channel.
- * @static
- * @param {string|number} channel The channel to unroute note events from
- * @param {function(*)} callback Callback which was registered to the channel.
- */
- Tone.Note.unroute = function (channel, callback) {
- if (NoteChannels.hasOwnProperty(channel)) {
- var channelCallback = NoteChannels[channel];
- var index = channelCallback.indexOf(callback);
- if (index !== -1) {
- NoteChannels[channel].splice(index, 1);
- }
- }
- };
- /**
- * Parses a score and registers all of the notes along the timeline.
- *
- * Scores are a JSON object with instruments at the top level
- * and an array of time and values. The value of a note can be 0 or more
- * parameters.
- *
- * The only requirement for the score format is that the time is the first (or only)
- * value in the array. All other values are optional and will be passed into the callback
- * function registered using `Note.route(channelName, callback)`.
- *
- * To convert MIDI files to score notation, take a look at utils/MidiToScore.js
- *
- * @example
- * //an example JSON score which sets up events on channels
- * var score = {
- * "synth" : [["0", "C3"], ["0:1", "D3"], ["0:2", "E3"], ... ],
- * "bass" : [["0", "C2"], ["1:0", "A2"], ["2:0", "C2"], ["3:0", "A2"], ... ],
- * "kick" : ["0", "0:2", "1:0", "1:2", "2:0", ... ],
- * //...
- * };
- * //parse the score into Notes
- * Tone.Note.parseScore(score);
- * //route all notes on the "synth" channel
- * Tone.Note.route("synth", function(time, note){
- * //trigger synth
- * });
- * @static
- * @param {Object} score
- * @return {Array} an array of all of the notes that were created
- */
- Tone.Note.parseScore = function (score) {
- var notes = [];
- for (var inst in score) {
- var part = score[inst];
- if (inst === 'tempo') {
- Tone.Transport.bpm.value = part;
- } else if (inst === 'timeSignature') {
- Tone.Transport.timeSignature = part[0] / (part[1] / 4);
- } else if (Array.isArray(part)) {
- for (var i = 0; i < part.length; i++) {
- var noteDescription = part[i];
- var note;
- if (Array.isArray(noteDescription)) {
- var time = noteDescription[0];
- var value = noteDescription.slice(1);
- note = new Tone.Note(inst, time, value);
- } else {
- note = new Tone.Note(inst, noteDescription);
- }
- notes.push(note);
- }
- } else {
- throw new TypeError('score parts must be Arrays');
- }
- }
- return notes;
- };
- ///////////////////////////////////////////////////////////////////////////
- // MUSIC NOTES
- //
- // Augments Tone.prototype to include note methods
- ///////////////////////////////////////////////////////////////////////////
- var noteToIndex = {
- 'c': 0,
- 'c#': 1,
- 'db': 1,
- 'd': 2,
- 'd#': 3,
- 'eb': 3,
- 'e': 4,
- 'f': 5,
- 'f#': 6,
- 'gb': 6,
- 'g': 7,
- 'g#': 8,
- 'ab': 8,
- 'a': 9,
- 'a#': 10,
- 'bb': 10,
- 'b': 11
- };
- var noteIndexToNote = [
- 'C',
- 'C#',
- 'D',
- 'D#',
- 'E',
- 'F',
- 'F#',
- 'G',
- 'G#',
- 'A',
- 'A#',
- 'B'
- ];
- var middleC = 261.6255653005986;
- /**
- * Convert a note name to frequency.
- * @param {string} note
- * @return {number}
- * @example
- * var freq = tone.noteToFrequency("A4"); //returns 440
- */
- Tone.prototype.noteToFrequency = function (note) {
- //break apart the note by frequency and octave
- var parts = note.split(/(\d+)/);
- if (parts.length === 3) {
- var index = noteToIndex[parts[0].toLowerCase()];
- var octave = parts[1];
- var noteNumber = index + parseInt(octave, 10) * 12;
- return Math.pow(2, (noteNumber - 48) / 12) * middleC;
- } else {
- return 0;
- }
- };
- /**
- * Test if a string is in note format: i.e. "C4".
- * @param {string|number} note The note to test
- * @return {boolean} true if it's in the form of a note
- * @method isNotation
- * @lends Tone.prototype.isNote
- * @function
- */
- Tone.prototype.isNote = function () {
- var noteFormat = new RegExp(/[a-g]{1}([b#]{1}|[b#]{0})[0-9]+$/i);
- return function (note) {
- if (typeof note === 'string') {
- note = note.toLowerCase();
- }
- return noteFormat.test(note);
- };
- }();
- /**
- * A pointer to the previous toFrequency method
- * @private
- * @function
- */
- Tone.prototype._overwrittenToFrequency = Tone.prototype.toFrequency;
- /**
- * A method which accepts frequencies in the form
- * of notes (`"C#4"`), frequencies as strings ("49hz"), frequency numbers,
- * or Time and converts them to their frequency as a number in hertz.
- * @param {Frequency} note the note name or notation
- * @param {number=} now if passed in, this number will be
- * used for all 'now' relative timings
- * @return {number} the frequency as a number
- */
- Tone.prototype.toFrequency = function (note, now) {
- if (this.isNote(note)) {
- note = this.noteToFrequency(note);
- }
- return this._overwrittenToFrequency(note, now);
- };
- /**
- * Convert a frequency to a note name (i.e. A4, C#5).
- * @param {number} freq
- * @return {string}
- */
- Tone.prototype.frequencyToNote = function (freq) {
- var log = Math.log(freq / middleC) / Math.LN2;
- var noteNumber = Math.round(12 * log) + 48;
- var octave = Math.floor(noteNumber / 12);
- var noteName = noteIndexToNote[noteNumber % 12];
- return noteName + octave.toString();
- };
- /**
- * Convert an interval (in semitones) to a frequency ratio.
- *
- * @param {Interval} interval the number of semitones above the base note
- * @return {number} the frequency ratio
- * @example
- * tone.intervalToFrequencyRatio(0); // returns 1
- * tone.intervalToFrequencyRatio(12); // returns 2
- */
- Tone.prototype.intervalToFrequencyRatio = function (interval) {
- return Math.pow(2, interval / 12);
- };
- /**
- * Convert a midi note number into a note name.
- *
- * @param {MIDI} midiNumber the midi note number
- * @return {string} the note's name and octave
- * @example
- * tone.midiToNote(60); // returns "C3"
- */
- Tone.prototype.midiToNote = function (midiNumber) {
- var octave = Math.floor(midiNumber / 12) - 2;
- var note = midiNumber % 12;
- return noteIndexToNote[note] + octave;
- };
- /**
- * Convert a note to it's midi value.
- *
- * @param {string} note the note name (i.e. "C3")
- * @return {MIDI} the midi value of that note
- * @example
- * tone.noteToMidi("C3"); // returns 60
- */
- Tone.prototype.noteToMidi = function (note) {
- //break apart the note by frequency and octave
- var parts = note.split(/(\d+)/);
- if (parts.length === 3) {
- var index = noteToIndex[parts[0].toLowerCase()];
- var octave = parts[1];
- return index + (parseInt(octave, 10) + 2) * 12;
- } else {
- return 0;
- }
- };
- return Tone.Note;
- });
Module(function (Tone) {
/**
@@ -8836,7 +10215,8 @@
*/
this._modulator = new Tone.Oscillator({
'frequency': options.frequency,
- 'detune': options.detune
+ 'detune': options.detune,
+ 'phase': options.phase
});
/**
* Scale the oscillator so it doesn't go silent
@@ -8882,6 +10262,7 @@
Tone.PWMOscillator.defaults = {
'frequency': 440,
'detune': 0,
+ 'phase': 0,
'modulationFrequency': 0.4
};
/**
@@ -9004,6 +10385,7 @@
this._oscillator = null;
//set the oscillator
this.type = options.type;
+ this.phase = options.phase;
this._readOnly([
'frequency',
'detune'
@@ -9020,6 +10402,7 @@
'frequency': 440,
'detune': 0,
'type': 'sine',
+ 'phase': 0,
'width': 0.4,
//only applies if the oscillator is set to "pulse",
'modulationFrequency': 0.4
@@ -9077,7 +10460,7 @@
this._createNewOscillator(Tone.PulseOscillator);
}
} else {
- throw new TypeError('Tone.OmniOscillator does not support type ' + type);
+ throw new Error('Tone.OmniOscillator does not support type ' + type);
}
}
});
@@ -9087,14 +10470,15 @@
*/
Tone.OmniOscillator.prototype._createNewOscillator = function (OscillatorConstructor) {
//short delay to avoid clicks on the change
- var now = this.now() + this.bufferTime;
+ var now = this.now() + this.blockTime;
if (this._oscillator !== null) {
var oldOsc = this._oscillator;
oldOsc.stop(now);
- oldOsc.onended = function () {
+ //dispose the old one
+ setTimeout(function () {
oldOsc.dispose();
oldOsc = null;
- };
+ }, this.blockTime * 1000);
}
this._oscillator = new OscillatorConstructor();
this.frequency.connect(this._oscillator.frequency);
@@ -9188,22 +10572,14 @@
//get the defaults
options = this.defaultArg(options, Tone.Instrument.defaults);
/**
- * the output
- * @type {GainNode}
- * @private
- */
- this.output = this.context.createGain();
- /**
- * The volume of the instrument.
+ * The volume of the output in decibels.
* @type {Decibels}
* @signal
+ * @example
+ * source.volume.value = -6;
*/
- this.volume = new Tone.Signal({
- 'param': this.output.gain,
- 'units': Tone.Type.Decibels,
- 'value': options.volume
- });
- this._readOnly(['volume']);
+ this.volume = this.output = new Tone.Volume(options.volume);
+ this._readOnly('volume');
};
Tone.extend(Tone.Instrument);
/**
@@ -9261,7 +10637,9 @@
Module(function (Tone) {
/**
- * @class This is a base class for monophonic instruments.
+ * @class This is an abstract base class for other monophonic instruments to
+ * extend. IMPORTANT: It does not make any sound on its own and
+ * shouldn't be directly instantiated.
*
* @constructor
* @abstract
@@ -9868,8 +11246,11 @@
* @type {Gain}
* @signal
*/
- this.vibratoAmount = new Tone.Signal(this._vibratoGain.gain, Tone.Type.Gain);
- this.vibratoAmount.value = options.vibratoAmount;
+ this.vibratoAmount = new Tone.Param({
+ 'param': this._vibratoGain.gain,
+ 'units': Tone.Type.Gain,
+ 'value': options.vibratoAmount
+ });
/**
* the delay before the vibrato starts
* @type {number}
@@ -10281,11 +11662,11 @@
this._buffer = _brownNoise;
break;
default:
- this._buffer = _whiteNoise;
+ throw new Error('invalid noise type: ' + type);
}
//if it's playing, stop and restart it
if (this.state === Tone.State.Started) {
- var now = this.now() + this.bufferTime;
+ var now = this.now() + this.blockTime;
//remove the listener
this._source.onended = undefined;
this._stop(now);
@@ -10304,7 +11685,7 @@
this._source = this.context.createBufferSource();
this._source.buffer = this._buffer;
this._source.loop = true;
- this.connectSeries(this._source, this.output);
+ this._source.connect(this.output);
this._source.start(this.toSeconds(time));
this._source.onended = this.onended;
};
@@ -10680,6 +12061,12 @@
* @type {Array}
*/
this.voices = new Array(options.polyphony);
+ /**
+ * If there are no more voices available,
+ * should an active voice be stolen to play the new note?
+ * @type {Boolean}
+ */
+ this.stealVoices = true;
/**
* the queue of free voices
* @private
@@ -10730,12 +12117,20 @@
for (var i = 0; i < notes.length; i++) {
var val = notes[i];
var stringified = JSON.stringify(val);
- if (this._activeVoices[stringified]) {
+ //retrigger the same note if possible
+ if (this._activeVoices.hasOwnProperty(stringified)) {
this._activeVoices[stringified].triggerAttack(val, time, velocity);
} else if (this._freeVoices.length > 0) {
var voice = this._freeVoices.shift();
voice.triggerAttack(val, time, velocity);
this._activeVoices[stringified] = voice;
+ } else if (this.stealVoices) {
+ //steal a voice
+ //take the first voice
+ for (var voiceName in this._activeVoices) {
+ this._activeVoices[voiceName].triggerAttack(val, time, velocity);
+ break;
+ }
}
}
return this;
@@ -10767,7 +12162,7 @@
* @param {Time} [time=now] When the release will be triggered.
* @returns {Tone.PolySynth} this
* @example
- * poly.triggerAttack(["Ab3", "C4", "F5"]);
+ * poly.triggerRelease(["Ab3", "C4", "F5"], "+2n");
*/
Tone.PolySynth.prototype.triggerRelease = function (notes, time) {
if (!Array.isArray(notes)) {
@@ -10831,6 +12226,17 @@
}
return this;
};
+ /**
+ * Trigger the release portion of all the currently active voices.
+ * @param {Time} [time=now] When the notes should be released.
+ * @return {Tone.PolySynth} this
+ */
+ Tone.PolySynth.prototype.releaseAll = function (time) {
+ for (var i = 0; i < this.voices.length; i++) {
+ this.voices[i].triggerRelease(time);
+ }
+ return this;
+ };
/**
* Clean up.
* @returns {Tone.PolySynth} this
@@ -10848,6 +12254,359 @@
};
return Tone.PolySynth;
});
+ Module(function (Tone) {
+
+ /**
+ * @class Buffer loading and storage. Tone.Buffer is used internally by all
+ * classes that make requests for audio files such as Tone.Player,
+ * Tone.Sampler and Tone.Convolver.
+ *
+ * Aside from load callbacks from individual buffers, Tone.Buffer
+ * provides static methods which keep track of the loading progress
+ * of all of the buffers. These methods are Tone.Buffer.onload, Tone.Buffer.onprogress,
+ * and Tone.Buffer.onerror.
+ *
+ * @constructor
+ * @extends {Tone}
+ * @param {AudioBuffer|string} url The url to load, or the audio buffer to set.
+ * @param {function=} onload A callback which is invoked after the buffer is loaded.
+ * It's recommended to use Tone.Buffer.onload instead
+ * since it will give you a callback when ALL buffers are loaded.
+ * @example
+ * var buffer = new Tone.Buffer("path/to/sound.mp3", function(){
+ * //the buffer is now available.
+ * var buff = buffer.get();
+ * });
+ */
+ Tone.Buffer = function (url) {
+ var options = this.optionsObject(arguments, [
+ 'url',
+ 'onload'
+ ], Tone.Buffer.defaults);
+ /**
+ * stores the loaded AudioBuffer
+ * @type {AudioBuffer}
+ * @private
+ */
+ this._buffer = null;
+ /**
+ * indicates if the buffer should be reversed or not
+ * @type {boolean}
+ * @private
+ */
+ this._reversed = options.reverse;
+ /**
+ * The url of the buffer. undefined
if it was
+ * constructed with a buffer
+ * @type {string}
+ * @readOnly
+ */
+ this.url = undefined;
+ /**
+ * Indicates if the buffer is loaded or not.
+ * @type {boolean}
+ * @readOnly
+ */
+ this.loaded = false;
+ /**
+ * The callback to invoke when everything is loaded.
+ * @type {function}
+ */
+ this.onload = options.onload.bind(this, this);
+ if (url instanceof AudioBuffer || url instanceof Tone.Buffer) {
+ this.set(url);
+ this.onload(this);
+ } else if (typeof options.url === 'string') {
+ this.url = options.url;
+ Tone.Buffer._addToQueue(options.url, this);
+ }
+ };
+ Tone.extend(Tone.Buffer);
+ /**
+ * the default parameters
+ * @type {Object}
+ */
+ Tone.Buffer.defaults = {
+ 'url': undefined,
+ 'onload': Tone.noOp,
+ 'reverse': false
+ };
+ /**
+ * Pass in an AudioBuffer or Tone.Buffer to set the value
+ * of this buffer.
+ * @param {AudioBuffer|Tone.Buffer} buffer the buffer
+ * @returns {Tone.Buffer} this
+ */
+ Tone.Buffer.prototype.set = function (buffer) {
+ if (buffer instanceof Tone.Buffer) {
+ this._buffer = buffer.get();
+ } else {
+ this._buffer = buffer;
+ }
+ this.loaded = true;
+ return this;
+ };
+ /**
+ * @return {AudioBuffer} The audio buffer stored in the object.
+ */
+ Tone.Buffer.prototype.get = function () {
+ return this._buffer;
+ };
+ /**
+ * Load url into the buffer.
+ * @param {String} url The url to load
+ * @param {Function=} callback The callback to invoke on load.
+ * don't need to set if `onload` is
+ * already set.
+ * @returns {Tone.Buffer} this
+ */
+ Tone.Buffer.prototype.load = function (url, callback) {
+ this.url = url;
+ this.onload = this.defaultArg(callback, this.onload);
+ Tone.Buffer._addToQueue(url, this);
+ return this;
+ };
+ /**
+ * dispose and disconnect
+ * @returns {Tone.Buffer} this
+ */
+ Tone.Buffer.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ Tone.Buffer._removeFromQueue(this);
+ this._buffer = null;
+ this.onload = Tone.Buffer.defaults.onload;
+ return this;
+ };
+ /**
+ * The duration of the buffer.
+ * @memberOf Tone.Buffer#
+ * @type {number}
+ * @name duration
+ * @readOnly
+ */
+ Object.defineProperty(Tone.Buffer.prototype, 'duration', {
+ get: function () {
+ if (this._buffer) {
+ return this._buffer.duration;
+ } else {
+ return 0;
+ }
+ }
+ });
+ /**
+ * Reverse the buffer.
+ * @private
+ * @return {Tone.Buffer} this
+ */
+ Tone.Buffer.prototype._reverse = function () {
+ if (this.loaded) {
+ for (var i = 0; i < this._buffer.numberOfChannels; i++) {
+ Array.prototype.reverse.call(this._buffer.getChannelData(i));
+ }
+ }
+ return this;
+ };
+ /**
+ * Reverse the buffer.
+ * @memberOf Tone.Buffer#
+ * @type {boolean}
+ * @name reverse
+ */
+ Object.defineProperty(Tone.Buffer.prototype, 'reverse', {
+ get: function () {
+ return this._reversed;
+ },
+ set: function (rev) {
+ if (this._reversed !== rev) {
+ this._reversed = rev;
+ this._reverse();
+ }
+ }
+ });
+ ///////////////////////////////////////////////////////////////////////////
+ // STATIC METHODS
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * the static queue for all of the xhr requests
+ * @type {Array}
+ * @private
+ */
+ Tone.Buffer._queue = [];
+ /**
+ * the array of current downloads
+ * @type {Array}
+ * @private
+ */
+ Tone.Buffer._currentDownloads = [];
+ /**
+ * the total number of downloads
+ * @type {number}
+ * @private
+ */
+ Tone.Buffer._totalDownloads = 0;
+ /**
+ * the maximum number of simultaneous downloads
+ * @static
+ * @type {number}
+ */
+ Tone.Buffer.MAX_SIMULTANEOUS_DOWNLOADS = 6;
+ /**
+ * Adds a file to be loaded to the loading queue
+ * @param {string} url the url to load
+ * @param {function} callback the callback to invoke once it's loaded
+ * @private
+ */
+ Tone.Buffer._addToQueue = function (url, buffer) {
+ Tone.Buffer._queue.push({
+ url: url,
+ Buffer: buffer,
+ progress: 0,
+ xhr: null
+ });
+ this._totalDownloads++;
+ Tone.Buffer._next();
+ };
+ /**
+ * Remove an object from the queue's (if it's still there)
+ * Abort the XHR if it's in progress
+ * @param {Tone.Buffer} buffer the buffer to remove
+ * @private
+ */
+ Tone.Buffer._removeFromQueue = function (buffer) {
+ var i;
+ for (i = 0; i < Tone.Buffer._queue.length; i++) {
+ var q = Tone.Buffer._queue[i];
+ if (q.Buffer === buffer) {
+ Tone.Buffer._queue.splice(i, 1);
+ }
+ }
+ for (i = 0; i < Tone.Buffer._currentDownloads.length; i++) {
+ var dl = Tone.Buffer._currentDownloads[i];
+ if (dl.Buffer === buffer) {
+ Tone.Buffer._currentDownloads.splice(i, 1);
+ dl.xhr.abort();
+ dl.xhr.onprogress = null;
+ dl.xhr.onload = null;
+ dl.xhr.onerror = null;
+ }
+ }
+ };
+ /**
+ * load the next buffer in the queue
+ * @private
+ */
+ Tone.Buffer._next = function () {
+ if (Tone.Buffer._queue.length > 0) {
+ if (Tone.Buffer._currentDownloads.length < Tone.Buffer.MAX_SIMULTANEOUS_DOWNLOADS) {
+ var next = Tone.Buffer._queue.shift();
+ Tone.Buffer._currentDownloads.push(next);
+ next.xhr = Tone.Buffer.load(next.url, function (buffer) {
+ //remove this one from the queue
+ var index = Tone.Buffer._currentDownloads.indexOf(next);
+ Tone.Buffer._currentDownloads.splice(index, 1);
+ next.Buffer.set(buffer);
+ if (next.Buffer._reversed) {
+ next.Buffer._reverse();
+ }
+ next.Buffer.onload(next.Buffer);
+ Tone.Buffer._onprogress();
+ Tone.Buffer._next();
+ });
+ next.xhr.onprogress = function (event) {
+ next.progress = event.loaded / event.total;
+ Tone.Buffer._onprogress();
+ };
+ next.xhr.onerror = Tone.Buffer.onerror;
+ }
+ } else if (Tone.Buffer._currentDownloads.length === 0) {
+ Tone.Buffer.onload();
+ //reset the downloads
+ Tone.Buffer._totalDownloads = 0;
+ }
+ };
+ /**
+ * internal progress event handler
+ * @private
+ */
+ Tone.Buffer._onprogress = function () {
+ var curretDownloadsProgress = 0;
+ var currentDLLen = Tone.Buffer._currentDownloads.length;
+ var inprogress = 0;
+ if (currentDLLen > 0) {
+ for (var i = 0; i < currentDLLen; i++) {
+ var dl = Tone.Buffer._currentDownloads[i];
+ curretDownloadsProgress += dl.progress;
+ }
+ inprogress = curretDownloadsProgress;
+ }
+ var currentDownloadProgress = currentDLLen - inprogress;
+ var completed = Tone.Buffer._totalDownloads - Tone.Buffer._queue.length - currentDownloadProgress;
+ Tone.Buffer.onprogress(completed / Tone.Buffer._totalDownloads);
+ };
+ /**
+ * Makes an xhr reqest for the selected url then decodes
+ * the file as an audio buffer. Invokes
+ * the callback once the audio buffer loads.
+ * @param {string} url The url of the buffer to load.
+ * filetype support depends on the
+ * browser.
+ * @param {function} callback The function to invoke when the url is loaded.
+ * @returns {XMLHttpRequest} returns the XHR
+ */
+ Tone.Buffer.load = function (url, callback) {
+ var request = new XMLHttpRequest();
+ request.open('GET', url, true);
+ request.responseType = 'arraybuffer';
+ // decode asynchronously
+ request.onload = function () {
+ Tone.context.decodeAudioData(request.response, function (buff) {
+ if (!buff) {
+ throw new Error('could not decode audio data:' + url);
+ }
+ callback(buff);
+ });
+ };
+ //send the request
+ request.send();
+ return request;
+ };
+ /**
+ * Callback when all of the buffers in the queue have loaded
+ * @static
+ * @function
+ * @example
+ * //invoked when all of the queued samples are done loading
+ * Tone.Buffer.onload = function(){
+ * console.log("everything is loaded");
+ * };
+ */
+ Tone.Buffer.onload = Tone.noOp;
+ /**
+ * Callback function is invoked with the progress of all of the loads in the queue.
+ * The value passed to the callback is between 0-1.
+ * @static
+ * @param {Number} percent The progress between 0 and 1.
+ * @function
+ * @example
+ * Tone.Buffer.onprogress = function(percent){
+ * console.log("progress:" + (percent * 100).toFixed(1) + "%");
+ * };
+ */
+ Tone.Buffer.onprogress = Tone.noOp;
+ /**
+ * Callback if one of the buffers in the queue encounters an error. The error
+ * is passed in as the argument.
+ * @static
+ * @param {Error} err
+ * @function
+ * @example
+ * Tone.Buffer.onerror = function(e){
+ * console.log("there was an error while loading the buffers: "+e);
+ * }
+ */
+ Tone.Buffer.onerror = Tone.noOp;
+ return Tone.Buffer;
+ });
Module(function (Tone) {
/**
@@ -10865,11 +12624,17 @@
* player.start();
* }
*/
- Tone.Player = function () {
- var options = this.optionsObject(arguments, [
- 'url',
- 'onload'
- ], Tone.Player.defaults);
+ Tone.Player = function (url) {
+ var options;
+ if (url instanceof Tone.Buffer) {
+ url = url.get();
+ options = Tone.Player.defaults;
+ } else {
+ options = this.optionsObject(arguments, [
+ 'url',
+ 'onload'
+ ], Tone.Player.defaults);
+ }
Tone.Source.call(this, options);
/**
* @private
@@ -10898,6 +12663,9 @@
'onload': this._onload.bind(this, options.onload),
'reverse': options.reverse
});
+ if (url instanceof AudioBuffer) {
+ this._buffer.set(url);
+ }
/**
* if the buffer should loop once it's over
* @type {boolean}
@@ -10952,7 +12720,7 @@
* Load the audio file as an audio buffer.
* Decodes the audio asynchronously and invokes
* the callback once the audio buffer loads.
- * Note: this does not need to be called, if a url
+ * Note: this does not need to be called if a url
* was passed in to the constructor. Only use this
* if you want to manually load a new url.
* @param {string} url The url of the buffer to load.
@@ -11010,18 +12778,19 @@
this._source.loop = this._loop;
this._source.loopStart = this.toSeconds(this._loopStart);
this._source.loopEnd = this.toSeconds(this._loopEnd);
- // this fixes a bug in chrome 42 that breaks looping
- // https://code.google.com/p/chromium/issues/detail?id=457099
- duration = 65536;
} else {
- this._nextStop = startTime + duration;
+ //if it's not looping, set the state change at the end of the sample
+ this._state.setStateAtTime(Tone.State.Stopped, startTime + duration);
}
//and other properties
this._source.playbackRate.value = this._playbackRate;
- this._source.onended = this.onended;
this._source.connect(this.output);
//start it
- this._source.start(startTime, offset, duration);
+ if (this._loop) {
+ this._source.start(startTime, offset);
+ } else {
+ this._source.start(startTime, offset, duration);
+ }
} else {
throw Error('tried to start Player before the buffer was loaded');
}
@@ -11093,7 +12862,7 @@
/**
* The audio buffer belonging to the player.
* @memberOf Tone.Player#
- * @type {AudioBuffer}
+ * @type {Tone.Buffer}
* @name buffer
*/
Object.defineProperty(Tone.Player.prototype, 'buffer', {
@@ -11188,7 +12957,7 @@
* @example
* var sampler = new Sampler({
* A : {
- * 1 : {"./audio/casio/A1.mp3",
+ * 1 : "./audio/casio/A1.mp3",
* 2 : "./audio/casio/A2.mp3",
* },
* "B.1" : "./audio/casio/B1.mp3",
@@ -11885,6 +13654,289 @@
};
return Tone.SimpleFM;
});
+ Module(function (Tone) {
+
+ /**
+ * buses are another way of routing audio
+ *
+ * augments Tone.prototype to include send and recieve
+ */
+ /**
+ * All of the routes
+ *
+ * @type {Object}
+ * @static
+ * @private
+ */
+ var Buses = {};
+ /**
+ * Send this signal to the channel name.
+ * @param {string} channelName A named channel to send the signal to.
+ * @param {Decibels} amount The amount of the source to send to the bus.
+ * @return {GainNode} The gain node which connects this node to the desired channel.
+ * Can be used to adjust the levels of the send.
+ * @example
+ * source.send("reverb", -12);
+ */
+ Tone.prototype.send = function (channelName, amount) {
+ if (!Buses.hasOwnProperty(channelName)) {
+ Buses[channelName] = this.context.createGain();
+ }
+ var sendKnob = this.context.createGain();
+ sendKnob.gain.value = this.dbToGain(this.defaultArg(amount, 1));
+ this.output.chain(sendKnob, Buses[channelName]);
+ return sendKnob;
+ };
+ /**
+ * Recieve the input from the desired channelName to the input
+ *
+ * @param {string} channelName A named channel to send the signal to.
+ * @param {AudioNode} [input] If no input is selected, the
+ * input of the current node is
+ * chosen.
+ * @returns {Tone} this
+ * @example
+ * reverbEffect.receive("reverb");
+ */
+ Tone.prototype.receive = function (channelName, input) {
+ if (!Buses.hasOwnProperty(channelName)) {
+ Buses[channelName] = this.context.createGain();
+ }
+ if (this.isUndef(input)) {
+ input = this.input;
+ }
+ Buses[channelName].connect(input);
+ return this;
+ };
+ return Tone;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class Wrapper around Web Audio's native [DelayNode](http://webaudio.github.io/web-audio-api/#the-delaynode-interface).
+ * @extends {Tone}
+ * @param {Time=} value The delay applied to the incoming signal.
+ * @param {Time=} maxDelay The maximum delay time.
+ */
+ Tone.Delay = function () {
+ var options = this.optionsObject(arguments, [
+ 'value',
+ 'maxDelay'
+ ], Tone.Delay.defaults);
+ /**
+ * The native delay node
+ * @type {DelayNode}
+ * @private
+ */
+ this._delayNode = this.context.createDelay(this.toSeconds(options.maxDelay));
+ Tone.Param.call(this, {
+ 'param': this._delayNode.delayTime,
+ 'units': Tone.Type.Time,
+ 'value': options.value
+ });
+ //set the input and output
+ this.input = this.output = this._delayNode;
+ /**
+ * The amount of time the incoming signal is
+ * delayed.
+ * @type {AudioParam}
+ * @signal
+ */
+ this.delayTime = this._param;
+ this._readOnly('delayTime');
+ };
+ Tone.extend(Tone.Delay, Tone.Param);
+ /**
+ * The defaults
+ * @const
+ * @type {Object}
+ */
+ Tone.Delay.defaults = {
+ 'maxDelay': 1,
+ 'value': 0
+ };
+ /**
+ * Clean up.
+ * @return {Tone.Delay} this
+ */
+ Tone.Delay.prototype.dispose = function () {
+ Tone.Param.prototype.dispose.call(this);
+ this._delayNode.disconnect();
+ this._delayNode = null;
+ this._writable('delayTime');
+ this.delayTime = null;
+ return this;
+ };
+ return Tone.Delay;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class A timed note. Creating a note will register a callback
+ * which will be invoked on the channel at the time with
+ * whatever value was specified.
+ *
+ * @constructor
+ * @param {number|string} channel the channel name of the note
+ * @param {Time} time the time when the note will occur
+ * @param {string|number|Object|Array} value the value of the note
+ */
+ Tone.Note = function (channel, time, value) {
+ /**
+ * the value of the note. This value is returned
+ * when the channel callback is invoked.
+ *
+ * @type {string|number|Object}
+ */
+ this.value = value;
+ /**
+ * the channel name or number
+ *
+ * @type {string|number}
+ * @private
+ */
+ this._channel = channel;
+ /**
+ * an internal reference to the id of the timeline
+ * callback which is set.
+ *
+ * @type {number}
+ * @private
+ */
+ this._timelineID = Tone.Transport.setTimeline(this._trigger.bind(this), time);
+ };
+ /**
+ * invoked by the timeline
+ * @private
+ * @param {number} time the time at which the note should play
+ */
+ Tone.Note.prototype._trigger = function (time) {
+ //invoke the callback
+ channelCallbacks(this._channel, time, this.value);
+ };
+ /**
+ * clean up
+ * @returns {Tone.Note} this
+ */
+ Tone.Note.prototype.dispose = function () {
+ Tone.Transport.clearTimeline(this._timelineID);
+ this.value = null;
+ return this;
+ };
+ /**
+ * @private
+ * @static
+ * @type {Object}
+ */
+ var NoteChannels = {};
+ /**
+ * invoke all of the callbacks on a specific channel
+ * @private
+ */
+ function channelCallbacks(channel, time, value) {
+ if (NoteChannels.hasOwnProperty(channel)) {
+ var callbacks = NoteChannels[channel];
+ for (var i = 0, len = callbacks.length; i < len; i++) {
+ var callback = callbacks[i];
+ if (Array.isArray(value)) {
+ callback.apply(window, [time].concat(value));
+ } else {
+ callback(time, value);
+ }
+ }
+ }
+ }
+ /**
+ * listen to a specific channel, get all of the note callbacks
+ * @static
+ * @param {string|number} channel the channel to route note events from
+ * @param {function(*)} callback callback to be invoked when a note will occur
+ * on the specified channel
+ */
+ Tone.Note.route = function (channel, callback) {
+ if (NoteChannels.hasOwnProperty(channel)) {
+ NoteChannels[channel].push(callback);
+ } else {
+ NoteChannels[channel] = [callback];
+ }
+ };
+ /**
+ * Remove a previously routed callback from a channel.
+ * @static
+ * @param {string|number} channel The channel to unroute note events from
+ * @param {function(*)} callback Callback which was registered to the channel.
+ */
+ Tone.Note.unroute = function (channel, callback) {
+ if (NoteChannels.hasOwnProperty(channel)) {
+ var channelCallback = NoteChannels[channel];
+ var index = channelCallback.indexOf(callback);
+ if (index !== -1) {
+ NoteChannels[channel].splice(index, 1);
+ }
+ }
+ };
+ /**
+ * Parses a score and registers all of the notes along the timeline.
+ *
+ * Scores are a JSON object with instruments at the top level
+ * and an array of time and values. The value of a note can be 0 or more
+ * parameters.
+ *
+ * The only requirement for the score format is that the time is the first (or only)
+ * value in the array. All other values are optional and will be passed into the callback
+ * function registered using `Note.route(channelName, callback)`.
+ *
+ * To convert MIDI files to score notation, take a look at utils/MidiToScore.js
+ *
+ * @example
+ * //an example JSON score which sets up events on channels
+ * var score = {
+ * "synth" : [["0", "C3"], ["0:1", "D3"], ["0:2", "E3"], ... ],
+ * "bass" : [["0", "C2"], ["1:0", "A2"], ["2:0", "C2"], ["3:0", "A2"], ... ],
+ * "kick" : ["0", "0:2", "1:0", "1:2", "2:0", ... ],
+ * //...
+ * };
+ * //parse the score into Notes
+ * Tone.Note.parseScore(score);
+ * //route all notes on the "synth" channel
+ * Tone.Note.route("synth", function(time, note){
+ * //trigger synth
+ * });
+ * @static
+ * @param {Object} score
+ * @return {Array} an array of all of the notes that were created
+ */
+ Tone.Note.parseScore = function (score) {
+ var notes = [];
+ for (var inst in score) {
+ var part = score[inst];
+ if (inst === 'tempo') {
+ Tone.Transport.bpm.value = part;
+ } else if (inst === 'timeSignature') {
+ Tone.Transport.timeSignature = part[0] / (part[1] / 4);
+ } else if (Array.isArray(part)) {
+ for (var i = 0; i < part.length; i++) {
+ var noteDescription = part[i];
+ var note;
+ if (Array.isArray(noteDescription)) {
+ var time = noteDescription[0];
+ var value = noteDescription.slice(1);
+ note = new Tone.Note(inst, time, value);
+ } else if (typeof noteDescription === 'object') {
+ note = new Tone.Note(inst, noteDescription.time, noteDescription);
+ } else {
+ note = new Tone.Note(inst, noteDescription);
+ }
+ notes.push(note);
+ }
+ } else {
+ throw new TypeError('score parts must be Arrays');
+ }
+ }
+ return notes;
+ };
+ return Tone.Note;
+ });
Module(function (Tone) {
/**
@@ -12000,8 +14052,8 @@
this._lfo = new Tone.LFO({
'frequency': options.frequency,
'amplitude': options.depth,
- 'min': options.min,
- 'max': options.max
+ 'min': this.toFrequency(options.min),
+ 'max': this.toFrequency(options.max)
});
/**
* The range of the filter modulating between the min and max frequency.
@@ -12111,7 +14163,7 @@
return this._lfo.min;
},
set: function (min) {
- this._lfo.min = min;
+ this._lfo.min = this.toFrequency(min);
}
});
/**
@@ -12125,7 +14177,7 @@
return this._lfo.max;
},
set: function (max) {
- this._lfo.max = max;
+ this._lfo.max = this.toFrequency(max);
}
});
/**
@@ -12175,9 +14227,7 @@
'frequency': options.frequency,
'amplitude': options.depth,
'min': 0,
- 'max': 1,
- //start at the middle of the cycle
- 'phase': 90
+ 'max': 1
});
/**
* The amount of panning between left and right.
@@ -12586,7 +14636,7 @@
*/
Tone.Chebyshev = function () {
var options = this.optionsObject(arguments, ['order'], Tone.Chebyshev.defaults);
- Tone.Effect.call(this);
+ Tone.Effect.call(this, options);
/**
* @type {WaveShaperNode}
* @private
@@ -12902,7 +14952,7 @@
* @constructor
* @extends {Tone.StereoXFeedbackEffect}
* @param {Frequency|Object} [frequency] The frequency of the LFO.
- * @param {Number} [delayTime] The delay of the chorus effect in ms.
+ * @param {Milliseconds} [delayTime] The delay of the chorus effect in ms.
* @param {NormalRange} [depth] The depth of the chorus.
* @example
* var chorus = new Tone.Chorus(4, 2.5, 0.5);
@@ -12933,14 +14983,22 @@
* @type {Tone.LFO}
* @private
*/
- this._lfoL = new Tone.LFO(options.rate, 0, 1);
+ this._lfoL = new Tone.LFO({
+ 'frequency': options.frequency,
+ 'min': 0,
+ 'max': 1
+ });
/**
* another LFO for the right side with a 180 degree phase diff
* @type {Tone.LFO}
* @private
*/
- this._lfoR = new Tone.LFO(options.rate, 0, 1);
- this._lfoR.phase = 180;
+ this._lfoR = new Tone.LFO({
+ 'frequency': options.frequency,
+ 'min': 0,
+ 'max': 1,
+ 'phase': 180
+ });
/**
* delay for left
* @type {DelayNode}
@@ -12960,10 +15018,11 @@
*/
this.frequency = this._lfoL.frequency;
//connections
- this.connectSeries(this.effectSendL, this._delayNodeL, this.effectReturnL);
- this.connectSeries(this.effectSendR, this._delayNodeR, this.effectReturnR);
+ this.effectSendL.chain(this._delayNodeL, this.effectReturnL);
+ this.effectSendR.chain(this._delayNodeR, this.effectReturnR);
//and pass through to make the detune apparent
- this.input.connect(this.output);
+ this.effectSendL.connect(this.effectReturnL);
+ this.effectSendR.connect(this.effectReturnR);
//lfo setup
this._lfoL.connect(this._delayNodeL.delayTime);
this._lfoR.connect(this._delayNodeR.delayTime);
@@ -13015,7 +15074,7 @@
* will give a more pronounced effect. Nominal range a delayTime
* is between 2 and 20ms.
* @memberOf Tone.Chorus#
- * @type {Number}
+ * @type {Milliseconds}
* @name delayTime
*/
Object.defineProperty(Tone.Chorus.prototype, 'delayTime', {
@@ -13207,9 +15266,9 @@
'oversample': 'none'
};
/**
- * The amount of distortion. Range between 0-1.
+ * The amount of distortion.
* @memberOf Tone.Distortion#
- * @type {number}
+ * @type {NormalRange}
* @name distortion
*/
Object.defineProperty(Tone.Distortion.prototype, 'distortion', {
@@ -13637,7 +15696,7 @@
* @constructor
*/
Tone.MidSideEffect = function () {
- Tone.Effect.call(this);
+ Tone.Effect.apply(this, arguments);
/**
* The mid/side split
* @type {Tone.MidSideSplit}
@@ -13978,6 +16037,208 @@
};
return Tone.PingPongDelay;
});
+ Module(function (Tone) {
+
+ /**
+ * @class Tone.PitchShift does near-realtime pitch shifting to the incoming signal.
+ * The effect is achieved by speeding up or slowing down the delayTime
+ * of a DelayNode using a sawtooth wave.
+ * Algorithm found in [this pdf](http://dsp-book.narod.ru/soundproc.pdf).
+ * Additional reference by [Miller Pucket](http://msp.ucsd.edu/techniques/v0.11/book-html/node115.html).
+ *
+ * @extends {Tone.FeedbackEffect}
+ * @param {Interval=} pitch The interval to transpose the incoming signal by.
+ */
+ Tone.PitchShift = function () {
+ var options = this.optionsObject(arguments, ['pitch'], Tone.PitchShift.defaults);
+ Tone.FeedbackEffect.call(this, options);
+ /**
+ * The pitch signal
+ * @type {Tone.Signal}
+ * @private
+ */
+ this._frequency = new Tone.Signal(0);
+ /**
+ * Uses two DelayNodes to cover up the jump in
+ * the sawtooth wave.
+ * @type {DelayNode}
+ * @private
+ */
+ this._delayA = new Tone.Delay(0, 1);
+ /**
+ * The first LFO.
+ * @type {Tone.LFO}
+ * @private
+ */
+ this._lfoA = new Tone.LFO({
+ 'min': 0,
+ 'max': 0.1,
+ 'type': 'sawtooth'
+ }).connect(this._delayA.delayTime);
+ /**
+ * The second DelayNode
+ * @type {DelayNode}
+ * @private
+ */
+ this._delayB = new Tone.Delay(0, 1);
+ /**
+ * The first LFO.
+ * @type {Tone.LFO}
+ * @private
+ */
+ this._lfoB = new Tone.LFO({
+ 'min': 0,
+ 'max': 0.1,
+ 'type': 'sawtooth',
+ 'phase': 180
+ }).connect(this._delayB.delayTime);
+ /**
+ * Crossfade quickly between the two delay lines
+ * to cover up the jump in the sawtooth wave
+ * @type {Tone.CrossFade}
+ * @private
+ */
+ this._crossFade = new Tone.CrossFade();
+ /**
+ * LFO which alternates between the two
+ * delay lines to cover up the disparity in the
+ * sawtooth wave.
+ * @type {Tone.LFO}
+ */
+ this._crossFadeLFO = new Tone.LFO({
+ 'min': 0,
+ 'max': 1,
+ 'type': 'triangle',
+ 'phase': 90
+ }).connect(this._crossFade.fade);
+ /**
+ * The amount of delay on the input signal
+ * @type {Time}
+ * @signal
+ */
+ this.delayTime = new Tone.Delay(options.delayTime);
+ this._readOnly('delayTime');
+ /**
+ * Hold the current pitch
+ * @type {Number}
+ * @private
+ */
+ this._pitch = options.pitch;
+ /**
+ * Hold the current windowSize
+ * @type {Number}
+ * @private
+ */
+ this._windowSize = options.windowSize;
+ //connect the two delay lines up
+ this._delayA.connect(this._crossFade.a);
+ this._delayB.connect(this._crossFade.b);
+ //connect the frequency
+ this._frequency.fan(this._lfoA.frequency, this._lfoB.frequency, this._crossFadeLFO.frequency);
+ //route the input
+ this.effectSend.fan(this._delayA, this._delayB);
+ this._crossFade.chain(this.delayTime, this.effectReturn);
+ //start the LFOs at the same time
+ var now = this.now();
+ this._lfoA.start(now);
+ this._lfoB.start(now);
+ this._crossFadeLFO.start(now);
+ //set the initial value
+ this.windowSize = this._windowSize;
+ };
+ Tone.extend(Tone.PitchShift, Tone.FeedbackEffect);
+ /**
+ * default values
+ * @static
+ * @type {Object}
+ * @const
+ */
+ Tone.PitchShift.defaults = {
+ 'pitch': 0,
+ 'windowSize': 0.1,
+ 'delayTime': 0,
+ 'feedback': 0
+ };
+ /**
+ * Repitch the incoming signal by some interval (measured
+ * in semi-tones).
+ * @memberOf Tone.PitchShift#
+ * @type {Interval}
+ * @name pitch
+ * @example
+ * pitchShift.pitch = -12; //down one octave
+ * pitchShift.pitch = 7; //up a fifth
+ */
+ Object.defineProperty(Tone.PitchShift.prototype, 'pitch', {
+ get: function () {
+ return this._pitch;
+ },
+ set: function (interval) {
+ this._pitch = interval;
+ var factor = 0;
+ if (interval < 0) {
+ this._lfoA.min = 0;
+ this._lfoA.max = this._windowSize;
+ this._lfoB.min = 0;
+ this._lfoB.max = this._windowSize;
+ factor = this.intervalToFrequencyRatio(interval - 1) + 1;
+ } else {
+ this._lfoA.min = this._windowSize;
+ this._lfoA.max = 0;
+ this._lfoB.min = this._windowSize;
+ this._lfoB.max = 0;
+ factor = this.intervalToFrequencyRatio(interval) - 1;
+ }
+ this._frequency.value = factor * (1.2 / this._windowSize);
+ }
+ });
+ /**
+ * The window size corresponds roughly to the sample length in a looping sampler.
+ * Smaller values are desirable for a less noticeable delay time of the pitch shifted
+ * signal, but larger values will result in smoother pitch shifting for larger intervals.
+ * A nominal range of 0.03 to 0.1 is recommended.
+ * @memberOf Tone.PitchShift#
+ * @type {Time}
+ * @name windowSize
+ * @example
+ * pitchShift.windowSize = 0.1;
+ */
+ Object.defineProperty(Tone.PitchShift.prototype, 'windowSize', {
+ get: function () {
+ return this._windowSize;
+ },
+ set: function (size) {
+ this._windowSize = this.toSeconds(size);
+ this.pitch = this._pitch;
+ }
+ });
+ /**
+ * Clean up.
+ * @return {Tone.PitchShift} this
+ */
+ Tone.PitchShift.prototype.dispose = function () {
+ Tone.FeedbackEffect.prototype.dispose.call(this);
+ this._frequency.dispose();
+ this._frequency = null;
+ this._delayA.disconnect();
+ this._delayA = null;
+ this._delayB.disconnect();
+ this._delayB = null;
+ this._lfoA.dispose();
+ this._lfoA = null;
+ this._lfoB.dispose();
+ this._lfoB = null;
+ this._crossFade.dispose();
+ this._crossFade = null;
+ this._crossFadeLFO.dispose();
+ this._crossFadeLFO = null;
+ this._writable('delayTime');
+ this.delayTime.dispose();
+ this.delayTime = null;
+ return this;
+ };
+ return Tone.PitchShift;
+ });
Module(function (Tone) {
/**
@@ -14056,7 +16317,7 @@
* @type {NormalRange}
* @signal
*/
- this.width = new Tone.Signal(0.5, Tone.Type.NormalRange);
+ this.width = new Tone.Signal(options.width, Tone.Type.NormalRange);
/**
* Mid multiplier
* @type {Tone.Expr}
@@ -14254,6 +16515,104 @@
};
return Tone.Tremolo;
});
+ Module(function (Tone) {
+
+ /**
+ * @class A Vibrato effect composed of a Tone.Delay and a Tone.LFO. The LFO
+ * modulates the delayTime of the delay, causing the pitch to rise
+ * and fall.
+ * @extends {Tone.Effect}
+ * @param {Frequency} frequency The frequency of the vibrato.
+ * @param {NormalRange} depth The amount the pitch is modulated.
+ */
+ Tone.Vibrato = function () {
+ var options = this.optionsObject(arguments, [
+ 'frequency',
+ 'depth'
+ ], Tone.Vibrato.defaults);
+ Tone.Effect.call(this, options);
+ /**
+ * The delay node used for the vibrato effect
+ * @type {Tone.Delay}
+ * @private
+ */
+ this._delayNode = new Tone.Delay(options.maxDelay);
+ /**
+ * The LFO used to control the vibrato
+ * @type {Tone.LFO}
+ * @private
+ */
+ this._lfo = new Tone.LFO({
+ 'type': options.type,
+ 'min': 0,
+ 'max': options.maxDelay,
+ 'frequency': options.frequency,
+ 'phase': -90 //offse the phase so the resting position is in the center
+ }).start().connect(this._delayNode.delayTime);
+ /**
+ * The frequency of the vibrato
+ * @type {Frequency}
+ * @signal
+ */
+ this.frequency = this._lfo.frequency;
+ /**
+ * The depth of the vibrato.
+ * @type {NormalRange}
+ * @signal
+ */
+ this.depth = this._lfo.amplitude;
+ this.depth.value = options.depth;
+ this._readOnly([
+ 'frequency',
+ 'depth'
+ ]);
+ this.effectSend.chain(this._delayNode, this.effectReturn);
+ };
+ Tone.extend(Tone.Vibrato, Tone.Effect);
+ /**
+ * The defaults
+ * @type {Object}
+ * @const
+ */
+ Tone.Vibrato.defaults = {
+ 'maxDelay': 0.005,
+ 'frequency': 5,
+ 'depth': 0.1,
+ 'type': 'sine'
+ };
+ /**
+ * Type of oscillator attached to the Vibrato.
+ * @memberOf Tone.Vibrato#
+ * @type {string}
+ * @name type
+ */
+ Object.defineProperty(Tone.Vibrato.prototype, 'type', {
+ get: function () {
+ return this._lfo.type;
+ },
+ set: function (type) {
+ this._lfo.type = type;
+ }
+ });
+ /**
+ * Clean up.
+ * @returns {Tone.Vibrato} this
+ */
+ Tone.Vibrato.prototype.dispose = function () {
+ Tone.Effect.prototype.dispose.call(this);
+ this._delayNode.dispose();
+ this._delayNode = null;
+ this._lfo.dispose();
+ this._lfo = null;
+ this._writable([
+ 'frequency',
+ 'depth'
+ ]);
+ this.frequency = null;
+ this.depth = null;
+ };
+ return Tone.Vibrato;
+ });
Module(function (Tone) {
/**
@@ -14590,98 +16949,1358 @@
});
Module(function (Tone) {
+ //polyfill for getUserMedia
+ navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
/**
- * @class Tone.Microphone is a WebRTC Microphone. Check
+ * @class Tone.ExternalInput is a WebRTC Audio Input. Check
* [Media Stream API Support](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream_API)
- * to see which browsers are supported.
- *
+ * to see which browsers are supported. As of
+ * writing this, Chrome, Firefox, and Opera
+ * support Media Stream. Chrome allows enumeration
+ * of the sources, and access to device name over a
+ * secure (HTTPS) connection. See [https://simpl.info](https://simpl.info/getusermedia/sources/index.html)
+ * vs [http://simple.info](https://simpl.info/getusermedia/sources/index.html)
+ * on a Chrome browser for the difference.
+ *
* @constructor
* @extends {Tone.Source}
- * @param {number} [inputNum=0] If multiple inputs are present, select the input number.
+ * @param {number} [inputNum=0] If multiple inputs are present, select the input number. Chrome only.
* @example
- * //mic will feedback if played through master
- * var mic = new Tone.Microphone();
- * mic.start();
+ * var motu = new Tone.ExternalInput(3);
+ *
+ * motu.open(function(){
+ * motu.start(10);
+ * });
*/
- Tone.Microphone = function (inputNum) {
- Tone.Source.call(this);
+ Tone.ExternalInput = function () {
+ var options = this.optionsObject(arguments, ['inputNum'], Tone.ExternalInput.defaults);
+ Tone.Source.call(this, options);
/**
+ * The MediaStreamNode
* @type {MediaStreamAudioSourceNode}
* @private
*/
this._mediaStream = null;
/**
+ * The media stream created by getUserMedia.
* @type {LocalMediaStream}
* @private
*/
this._stream = null;
/**
+ * The constraints argument for getUserMedia
* @type {Object}
* @private
*/
this._constraints = { 'audio': true };
- //get the option
- var self = this;
- MediaStreamTrack.getSources(function (media_sources) {
- if (inputNum < media_sources.length) {
- self.constraints.audio = { optional: [{ sourceId: media_sources[inputNum].id }] };
- }
+ /**
+ * The input source position in Tone.ExternalInput.sources.
+ * Set before ExternalInput.open().
+ * @type {Number}
+ * @private
+ */
+ this._inputNum = options.inputNum;
+ /**
+ * Gates the input signal for start/stop.
+ * Initially closed.
+ * @type {GainNode}
+ * @private
+ */
+ this._gate = new Tone.Gain(0).connect(this.output);
+ };
+ Tone.extend(Tone.ExternalInput, Tone.Source);
+ /**
+ * the default parameters
+ * @type {Object}
+ */
+ Tone.ExternalInput.defaults = { 'inputNum': 0 };
+ /**
+ * wrapper for getUserMedia function
+ * @param {function} callback
+ * @private
+ */
+ Tone.ExternalInput.prototype._getUserMedia = function (callback) {
+ if (!Tone.ExternalInput.supported) {
+ throw new Error('browser does not support \'getUserMedia\'');
+ }
+ if (Tone.ExternalInput.sources[this._inputNum]) {
+ this._constraints = { audio: { optional: [{ sourceId: Tone.ExternalInput.sources[this._inputNum].id }] } };
+ }
+ navigator.getUserMedia(this._constraints, function (stream) {
+ this._onStream(stream);
+ callback();
+ }.bind(this), function (err) {
+ callback(err);
});
};
- Tone.extend(Tone.Microphone, Tone.Source);
/**
- * start the stream.
- * @private
+ * called when the stream is successfully setup
+ * @param {LocalMediaStream} stream
+ * @private
*/
- Tone.Microphone.prototype._start = function () {
- navigator.getUserMedia(this._constraints, this._onStream.bind(this), this._onStreamError.bind(this));
+ Tone.ExternalInput.prototype._onStream = function (stream) {
+ if (!this.isFunction(this.context.createMediaStreamSource)) {
+ throw new Error('browser does not support the \'MediaStreamSourceNode\'');
+ }
+ //can only start a new source if the previous one is closed
+ if (!this._stream) {
+ this._stream = stream;
+ //Wrap a MediaStreamSourceNode around the live input stream.
+ this._mediaStream = this.context.createMediaStreamSource(stream);
+ //Connect the MediaStreamSourceNode to a gate gain node
+ this._mediaStream.connect(this._gate);
+ }
};
/**
- * stop the stream.
- * @private
+ * Open the media stream
+ * @param {function=} callback The callback function to
+ * execute when the stream is open
+ * @return {Tone.ExternalInput} this
*/
- Tone.Microphone.prototype._stop = function () {
- this._stream.stop();
+ Tone.ExternalInput.prototype.open = function (callback) {
+ callback = this.defaultArg(callback, Tone.noOp);
+ Tone.ExternalInput.getSources(function () {
+ this._getUserMedia(callback);
+ }.bind(this));
return this;
};
/**
- * called when the stream is successfully setup
- * @param {LocalMediaStream} stream
- * @private
+ * Close the media stream
+ * @return {Tone.ExternalInput} this
*/
- Tone.Microphone.prototype._onStream = function (stream) {
- this._stream = stream;
- // Wrap a MediaStreamSourceNode around the live input stream.
- this._mediaStream = this.context.createMediaStreamSource(stream);
- this._mediaStream.connect(this.output);
+ Tone.ExternalInput.prototype.close = function () {
+ if (this._stream) {
+ var track = this._stream.getTracks()[this._inputNum];
+ if (!this.isUndef(track)) {
+ track.stop();
+ }
+ this._stream = null;
+ }
+ return this;
};
/**
- * called on error
- * @param {Error} e
+ * Start the stream
* @private
*/
- Tone.Microphone.prototype._onStreamError = function (e) {
- console.error(e);
+ Tone.ExternalInput.prototype._start = function (time) {
+ time = this.toSeconds(time);
+ this._gate.gain.setValueAtTime(1, time);
+ return this;
};
/**
- * Clean up.
- * @return {Tone.Microphone} this
+ * Stops the stream.
+ * @private
*/
- Tone.Microphone.prototype.dispose = function () {
+ Tone.ExternalInput.prototype._stop = function (time) {
+ time = this.toSeconds(time);
+ this._gate.gain.setValueAtTime(0, time);
+ return this;
+ };
+ /**
+ * Clean up.
+ * @return {Tone.ExternalInput} this
+ */
+ Tone.ExternalInput.prototype.dispose = function () {
Tone.Source.prototype.dispose.call(this);
+ this.close();
if (this._mediaStream) {
this._mediaStream.disconnect();
this._mediaStream = null;
}
- this._stream = null;
this._constraints = null;
+ this._gate.dispose();
+ this._gate = null;
return this;
};
- //polyfill
- navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+ ///////////////////////////////////////////////////////////////////////////
+ // STATIC METHODS
+ ///////////////////////////////////////////////////////////////////////////
+ /**
+ * The array of available sources, different depending on whether connection is secure
+ * @type {Array}
+ * @static
+ */
+ Tone.ExternalInput.sources = [];
+ /**
+ * indicates whether browser supports MediaStreamTrack.getSources (i.e. Chrome vs Firefox)
+ * @type {Boolean}
+ * @private
+ */
+ Tone.ExternalInput._canGetSources = !Tone.prototype.isUndef(window.MediaStreamTrack) && Tone.prototype.isFunction(MediaStreamTrack.getSources);
+ /**
+ * If getUserMedia is supported by the browser.
+ * @type {Boolean}
+ * @memberOf Tone.ExternalInput#
+ * @name supported
+ * @static
+ * @readOnly
+ */
+ Object.defineProperty(Tone.ExternalInput, 'supported', {
+ get: function () {
+ return Tone.prototype.isFunction(navigator.getUserMedia);
+ }
+ });
+ /**
+ * Populates the source list. Invokes the callback with an array of
+ * possible audio sources.
+ * @param {function=} callback Callback to be executed after populating list
+ * @return {Tone.ExternalInput} this
+ * @static
+ * @example
+ * var soundflower = new Tone.ExternalInput();
+ * Tone.ExternalInput.getSources(selectSoundflower);
+ *
+ * function selectSoundflower(sources){
+ * for(var i = 0; i < sources.length; i++){
+ * if(sources[i].label === "soundflower"){
+ * soundflower.inputNum = i;
+ * soundflower.open(function(){
+ * soundflower.start();
+ * });
+ * break;
+ * }
+ * }
+ * };
+ */
+ Tone.ExternalInput.getSources = function (callback) {
+ if (Tone.ExternalInput.sources.length === 0 && Tone.ExternalInput._canGetSources) {
+ MediaStreamTrack.getSources(function (media_sources) {
+ for (var i = 0; i < media_sources.length; i++) {
+ if (media_sources[i].kind === 'audio') {
+ Tone.ExternalInput.sources[i] = media_sources[i];
+ }
+ }
+ callback(Tone.ExternalInput.sources);
+ });
+ } else {
+ callback(Tone.ExternalInput.sources);
+ }
+ return this;
+ };
+ return Tone.ExternalInput;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class Opens up the default source (typically the microphone).
+ *
+ * @constructor
+ * @extends {Tone.ExternalInput}
+ * @example
+ * //mic will feedback if played through master
+ * var mic = new Tone.Microphone();
+ * mic.open(function(){
+ * //start the mic at ten seconds
+ * mic.start(10);
+ * });
+ * //stop the mic
+ * mic.stop(20);
+ */
+ Tone.Microphone = function () {
+ Tone.ExternalInput.call(this, 0);
+ };
+ Tone.extend(Tone.Microphone, Tone.ExternalInput);
+ /**
+ * If getUserMedia is supported by the browser.
+ * @type {Boolean}
+ * @memberOf Tone.Microphone#
+ * @name supported
+ * @static
+ * @readOnly
+ */
+ Object.defineProperty(Tone.Microphone, 'supported', {
+ get: function () {
+ return Tone.ExternalInput.supported;
+ }
+ });
return Tone.Microphone;
});
-
+ Module(function (Tone) {
+
+ /**
+ * @class Tone.Note provides a callback for a single, repeatable
+ * event along the timeline.
+ *
+ * @param {function} callback The callback to invoke at the time.
+ * @param {*} value The value or values which should be passed to
+ * the callback function on invocation.
+ * @example
+ * var chord = new Tone.Note(function(time, chord){
+ * //the chord as well as the exact time of the event
+ * //are passed in as arguments to the callback function
+ * }, "Dm");
+ * //start the chord at the beginning of the transport timeline
+ * chord.start();
+ * //loop it every measure for 8 measures
+ * chord.loop = 8;
+ * chord.loopEnd = "1m";
+ */
+ Tone.Note = function () {
+ var options = this.optionsObject(arguments, [
+ 'callback',
+ 'value'
+ ], Tone.Note.defaults, true);
+ /**
+ * Loop value
+ * @type {Boolean|Positive}
+ * @private
+ */
+ this._loop = options.loop;
+ /**
+ * The callback to invoke.
+ * @type {Function}
+ */
+ this.callback = options.callback;
+ /**
+ * The value which is passed to the
+ * callback function.
+ * @type {*}
+ * @private
+ */
+ this.value = options.value;
+ /**
+ * When the note is scheduled to start.
+ * @type {Number}
+ * @private
+ */
+ this._loopStart = 0;
+ /**
+ * When the note is scheduled to start.
+ * @type {Number}
+ * @private
+ */
+ this._loopEnd = 0;
+ /**
+ * Tracks the scheduled events
+ * @type {Tone.TimelineState}
+ * @private
+ */
+ this._events = new Tone.TimelineState(Tone.State.Stopped);
+ /**
+ * The playback speed of the note. A speed of 1
+ * is no change.
+ * @private
+ * @type {Positive}
+ */
+ this._playbackRate = 1;
+ /**
+ * The probability that the callback will be invoked
+ * at the scheduled time.
+ * @type {NormalRange}
+ */
+ this.probability = options.probability;
+ /**
+ * Random variation +/-0.01s to the scheduled time.
+ * Or give it a time value which it will randomize by.
+ * @type {Boolean|Time}
+ */
+ this.humanize = options.humanize;
+ /**
+ * If the part is inactive and does
+ * not invoke the callback function.
+ * @type {Boolean}
+ */
+ this.mute = options.mute;
+ //set the initial values
+ this.loopStart = options.loopStart;
+ this.loopEnd = options.loopEnd;
+ this.playbackRate = options.playbackRate;
+ //if an object was used in the constructor, the value is all the extra parameters
+ if (arguments.length === 1 && typeof arguments[0] === 'object' && this.isUndef(this.value)) {
+ var valueObj = {};
+ for (var param in arguments[0]) {
+ if (!Tone.Note.defaults.hasOwnProperty(param)) {
+ valueObj[param] = arguments[0][param];
+ }
+ }
+ this.value = valueObj;
+ }
+ };
+ Tone.extend(Tone.Note);
+ /**
+ * The default values
+ * @type {Object}
+ * @const
+ */
+ Tone.Note.defaults = {
+ 'callback': Tone.noOp,
+ 'loop': false,
+ 'loopEnd': '1m',
+ 'loopStart': 0,
+ 'playbackRate': 1,
+ 'probability': 1,
+ 'mute': false,
+ 'humanize': false
+ };
+ /**
+ * Reschedule all of the events along the timeline
+ * with the updated values.
+ * @param {Time} after Only reschedules events after the given time.
+ * @return {Tone.Note} this
+ * @private
+ */
+ Tone.Note.prototype._rescheduleEvents = function (after) {
+ //if no argument is given, schedules all of the events
+ after = this.defaultArg(after, -1);
+ this._events.forEachFrom(after, function (event) {
+ var duration;
+ if (event.state === Tone.State.Started) {
+ if (!this.isUndef(event.id)) {
+ Tone.Transport.clear(event.id);
+ }
+ if (this._loop) {
+ duration = Infinity;
+ if (this.isNumber(this._loop)) {
+ duration = (this._loop - 1) * this._getLoopDuration();
+ }
+ var nextEvent = this._events.getEventAfter(event.time);
+ if (nextEvent !== null) {
+ duration = Math.min(duration, nextEvent.time - event.time);
+ }
+ //make it ticks
+ if (duration !== Infinity) {
+ duration += 'i';
+ }
+ event.id = Tone.Transport.scheduleRepeat(this._tick.bind(this), this._getLoopDuration().toString() + 'i', event.time + 'i', duration);
+ } else {
+ event.id = Tone.Transport.schedule(this._tick.bind(this), event.time + 'i');
+ }
+ }
+ }.bind(this));
+ return this;
+ };
+ /**
+ * Returns the playback state of the note, either "started" or "stopped".
+ * @type {String}
+ * @readOnly
+ * @memberOf Tone.Note#
+ * @name state
+ */
+ Object.defineProperty(Tone.Note.prototype, 'state', {
+ get: function () {
+ return this._events.getStateAtTime(Tone.Transport.ticks);
+ }
+ });
+ /**
+ * Start the note at the given time.
+ * @param {Time} time When the note should start.
+ * @return {Tone.Note} this
+ */
+ Tone.Note.prototype.start = function (time) {
+ time = this.toTicks(time);
+ if (this._events.getStateAtTime(time) === Tone.State.Stopped) {
+ this._events.addEvent({
+ 'state': Tone.State.Started,
+ 'time': time,
+ 'id': undefined
+ });
+ this._rescheduleEvents(time);
+ }
+ return this;
+ };
+ /**
+ * Stop the Note at the given time.
+ * @param {Time} time When the note should stop.
+ * @return {Tone.Note} this
+ */
+ Tone.Note.prototype.stop = function (time) {
+ time = this.toTicks(time);
+ if (this._events.getStateAtTime(time) === Tone.State.Started) {
+ this._events.setStateAtTime(Tone.State.Stopped, time);
+ var previousEvent = this._events.getEventBefore(time);
+ var reschedulTime = time;
+ if (previousEvent !== null) {
+ reschedulTime = previousEvent.time;
+ }
+ this._rescheduleEvents(reschedulTime);
+ }
+ return this;
+ };
+ /**
+ * Cancel all scheduled events greater than or equal to the given time
+ * @param {Time} [time=0] The time after which events will be cancel.
+ * @return {Tone.Note} this
+ */
+ Tone.Note.prototype.cancel = function (time) {
+ time = this.defaultArg(time, -Infinity);
+ time = this.toTicks(time);
+ this._events.forEachFrom(time, function (event) {
+ Tone.Transport.clear(event.id);
+ });
+ this._events.cancel(time);
+ return this;
+ };
+ /**
+ * The callback function invoker. Also
+ * checks if the Note is done playing
+ * @param {Number} time The time of the event in seconds
+ * @private
+ */
+ Tone.Note.prototype._tick = function (time) {
+ if (!this.mute && this._events.getStateAtTime(Tone.Transport.ticks) === Tone.State.Started) {
+ if (this.probability < 1 && Math.random() > this.probability) {
+ return;
+ }
+ if (this.humanize) {
+ var variation = 0.01;
+ if (!this.isBoolean(this.humanize)) {
+ variation = this.toSeconds(this.humanize);
+ }
+ time += (Math.random() * 2 - 1) * variation;
+ }
+ this.callback(time, this.value);
+ }
+ };
+ /**
+ * Get the duration of the loop.
+ * @return {Ticks}
+ * @private
+ */
+ Tone.Note.prototype._getLoopDuration = function () {
+ return Math.round((this._loopEnd - this._loopStart) / this._playbackRate);
+ };
+ /**
+ * If the note should loop or not
+ * between Tone.Note.loopStart and
+ * Tone.Note.loopEnd. An integer
+ * value corresponds to the number of
+ * loops the Note does after it starts.
+ * @memberOf Tone.Note#
+ * @type {Boolean|Positive}
+ * @name loop
+ */
+ Object.defineProperty(Tone.Note.prototype, 'loop', {
+ get: function () {
+ return this._loop;
+ },
+ set: function (loop) {
+ this._loop = loop;
+ this._rescheduleEvents();
+ }
+ });
+ /**
+ * The playback rate of the note. Defaults to 1.
+ * @memberOf Tone.Note#
+ * @type {Positive}
+ * @name playbackRate
+ * @example
+ * note.loop = true;
+ * //repeat the note twice as fast
+ * note.playbackRate = 2;
+ */
+ Object.defineProperty(Tone.Note.prototype, 'playbackRate', {
+ get: function () {
+ return this._playbackRate;
+ },
+ set: function (rate) {
+ this._playbackRate = rate;
+ if (this._loop) {
+ this._rescheduleEvents();
+ }
+ }
+ });
+ /**
+ * The loopEnd point determines when it will
+ * loop if Tone.Note.loop is true.
+ * @memberOf Tone.Note#
+ * @type {Boolean|Positive}
+ * @name loopEnd
+ */
+ Object.defineProperty(Tone.Note.prototype, 'loopEnd', {
+ get: function () {
+ return this.toNotation(this._loopEnd + 'i');
+ },
+ set: function (loopEnd) {
+ this._loopEnd = this.toTicks(loopEnd);
+ if (this._loop) {
+ this._rescheduleEvents();
+ }
+ }
+ });
+ /**
+ * The loopStart point determines when it will
+ * loop if Tone.Note.loop is true.
+ * @memberOf Tone.Note#
+ * @type {Boolean|Positive}
+ * @name loopStart
+ */
+ Object.defineProperty(Tone.Note.prototype, 'loopStart', {
+ get: function () {
+ return this.toNotation(this._loopStart + 'i');
+ },
+ set: function (loopStart) {
+ this._loopStart = this.toTicks(loopStart);
+ if (this._loop) {
+ this._rescheduleEvents();
+ }
+ }
+ });
+ /**
+ * The current progress of the loop interval.
+ * Returns 0 if the atom is not started yet or the
+ * atom is not set to loop.
+ * @memberOf Tone.Note#
+ * @type {NormalRange}
+ * @name progress
+ * @readOnly
+ */
+ Object.defineProperty(Tone.Note.prototype, 'progress', {
+ get: function () {
+ if (this._loop) {
+ var ticks = Tone.Transport.ticks;
+ var lastEvent = this._events.getEvent(ticks);
+ if (lastEvent !== null && lastEvent.state === Tone.State.Started) {
+ var loopDuration = this._getLoopDuration();
+ if (this.isNumber(this._loop)) {
+ var endTime = loopDuration * this._loop + lastEvent.time;
+ if (ticks > endTime) {
+ return 0;
+ }
+ }
+ var progress = (ticks - lastEvent.time) % loopDuration;
+ return progress / loopDuration;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ });
+ /**
+ * Clean up
+ * @return {Tone.Note} this
+ */
+ Tone.Note.prototype.dispose = function () {
+ this.cancel();
+ this._events.dispose();
+ this._events = null;
+ this.callback = null;
+ this.value = null;
+ };
+ return Tone.Note;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class Tone.Part is a collection Tone.Notes which can be
+ * started/stoped and looped as a single unit.
+ *
+ * @extends {Tone.Note}
+ * @example
+ * var part = new Tone.Part(function(time, note){
+ * synth.triggerAttackRelease(note, "8n", time);
+ * }, [[0, "C2"], ["0:2", "C3"], ["0:3:2", "G2"]]).start();
+ * @example
+ * //use JSON as long as the object has a "time" attribute
+ * var part = new Tone.Part(function(time, value){
+ * synth.triggerAttackRelease(value.note, "8n", time, value.velocity);
+ * }, [{"time" : 0, "note" : "C3", "velocity": 0.9},
+ * {"time" : "0:2", "note" : "C4", "velocity": 0.5}
+ * ]).start();
+ */
+ Tone.Part = function () {
+ var options = this.optionsObject(arguments, [
+ 'callback',
+ 'notes'
+ ], Tone.Part.defaults, true);
+ /**
+ * If the part is looping or not
+ * @type {Boolean|Positive}
+ * @private
+ */
+ this._loop = options.loop;
+ /**
+ * When the note is scheduled to start.
+ * @type {Number}
+ * @private
+ */
+ this._loopStart = 0;
+ /**
+ * When the note is scheduled to start.
+ * @type {Number}
+ * @private
+ */
+ this._loopEnd = 0;
+ /**
+ * The playback rate of the part
+ * @type {Positive}
+ * @private
+ */
+ this._playbackRate = 1;
+ /**
+ * Keeps track of the current state
+ * @type {Tone.TimelineState}
+ * @private
+ */
+ this._events = new Tone.TimelineState(Tone.State.Stopped);
+ /**
+ * An array of Objects. Each one
+ * contains a note object and the relative
+ * start time of the note.
+ * @type {Array}
+ * @private
+ */
+ this._notes = [];
+ /**
+ * The callback to invoke on every note
+ * @type {Function}
+ */
+ this.callback = options.callback;
+ /**
+ * If the part invokes the callback
+ * @type {Boolean}
+ */
+ this.mute = options.mute;
+ //setup
+ this.loopEnd = options.loopEnd;
+ this.loopStart = options.loopStart;
+ this.playbackRate = options.playbackRate;
+ this.mute = options.mute;
+ //add the notes
+ var notes = this.defaultArg(options.notes, []);
+ for (var i = 0; i < notes.length; i++) {
+ if (Array.isArray(notes[i])) {
+ this.add(notes[i][0], notes[i][1]);
+ } else {
+ this.add(notes[i]);
+ }
+ }
+ };
+ Tone.extend(Tone.Part, Tone.Note);
+ /**
+ * The default values
+ * @type {Object}
+ * @const
+ */
+ Tone.Part.defaults = {
+ 'callback': Tone.noOp,
+ 'loop': false,
+ 'loopEnd': '1m',
+ 'loopStart': 0,
+ 'playbackRate': 1,
+ 'mute': false
+ };
+ /**
+ * Start the part at the given time. Optionally
+ * set an offset time.
+ * @param {Time} time When to start the part.
+ * @param {Time=} offset The offset from the start of the part
+ * to begin playing at.
+ * @return {Tone.Part} this
+ */
+ Tone.Part.prototype.start = function (time, offset) {
+ var ticks = this.toTicks(time);
+ if (this._events.getStateAtTime(ticks) !== Tone.State.Started) {
+ this._events.setStateAtTime(Tone.State.Started, ticks);
+ offset = this.defaultArg(offset, 0);
+ offset = this.toTicks(offset);
+ this._forEach(function (event) {
+ var startTick;
+ if (this._loop) {
+ if (event.time >= this._loopStart && event.time < this._loopEnd) {
+ startTick = event.time - offset - this._loopStart;
+ event.note.start(Math.round(startTick / this.playbackRate + ticks) + 'i');
+ }
+ } else {
+ startTick = event.time - offset;
+ event.note.start(Math.round(startTick / this.playbackRate + ticks) + 'i');
+ }
+ }.bind(this));
+ }
+ return this;
+ };
+ /**
+ * Stop the part at the given time.
+ * @param {Time} time When to stop the part.
+ * @return {Tone.Part} this
+ */
+ Tone.Part.prototype.stop = function (time) {
+ var ticks = this.toTicks(time);
+ if (this._events.getStateAtTime(ticks) === Tone.State.Started) {
+ this._events.setStateAtTime(Tone.State.Stopped, ticks);
+ this._forEach(function (event) {
+ event.note.stop(time);
+ });
+ }
+ return this;
+ };
+ /**
+ * Get/Set a note by time. If there is no item
+ * at the given time, it will create one
+ * @return {*} the value at the given time
+ */
+ Tone.Part.prototype.at = function (time, value) {
+ time = this.toTicks(time);
+ for (var i = 0; i < this._notes.length; i++) {
+ var note = this._notes[i];
+ if (Math.abs(time - note.time) < 0.001) {
+ if (this.isUndef(value)) {
+ if (this.isUndef(note.note.value)) {
+ return note.note;
+ } else {
+ return note.note.value;
+ }
+ } else {
+ note.note.value = value;
+ return value;
+ }
+ }
+ }
+ if (!this.isUndef(value)) {
+ this._notes.push({
+ 'time': time,
+ 'note': new Tone.Note(this._tick.bind(this), value)
+ });
+ } else {
+ return null;
+ }
+ };
+ /**
+ * Add a note or part to the part.
+ * @param {Time} time The time the note should start.
+ * If an object is passed in, it should
+ * have a 'time' attribute and the rest
+ * of the object will be used as the 'value'.
+ * @param {Tone.Note|*} value
+ * @example
+ * part.add("1m", "C#+11");
+ */
+ Tone.Part.prototype.add = function (time, value) {
+ //extract the parameters
+ if (typeof time === 'object' && time.hasOwnProperty('time')) {
+ value = time;
+ time = value.time;
+ }
+ time = this.toTicks(time);
+ var note;
+ if (value instanceof Tone.Note || value instanceof Tone.Part) {
+ note = value;
+ note.callback = this._tick.bind(this);
+ } else {
+ note = new Tone.Note(this._tick.bind(this), value);
+ }
+ //initialize the stuff
+ note.playbackRate *= this._playbackRate;
+ note.loopStart = 0;
+ note.loopEnd = this.loopEnd;
+ note.loop = this.loop;
+ //and probability and humanize
+ //add it to the notes
+ this._notes.push({
+ 'time': time,
+ 'note': note
+ });
+ return this;
+ };
+ /**
+ * Remove a note from the part.
+ */
+ Tone.Part.prototype.remove = function (time, value) {
+ //extract the parameters
+ if (typeof time === 'object' && time.hasOwnProperty('time')) {
+ value = time;
+ time = value.time;
+ }
+ this._forEach(function (event, index) {
+ if (event.time === time) {
+ if (this.isUndef(value) || !this.isUndef && event.note.value === value) {
+ this._notes.splice(index, 1);
+ event.note.dispose();
+ }
+ }
+ });
+ return this;
+ };
+ /**
+ * Remove all of the notes from the group.
+ * @return {Tone.Part} this
+ */
+ Tone.Part.prototype.removeAll = function () {
+ this._forEach(function (event) {
+ event.note.dispose();
+ });
+ this._notes = [];
+ return this;
+ };
+ /**
+ * Cancel scheduled state change events: i.e. "start" and "stop".
+ * @param {Time} after The time after which to cancel the scheduled events.
+ * @return {Tone.Part} this
+ */
+ Tone.Part.prototype.cancel = function (after) {
+ this._forEach(function (event) {
+ event.note.cancel(after);
+ });
+ this._events.cancel(after);
+ return this;
+ };
+ /**
+ * Iterate over all of the notes
+ * @param {Function} callback
+ * @private
+ */
+ Tone.Part.prototype._forEach = function (callback) {
+ for (var i = this._notes.length - 1; i >= 0; i--) {
+ callback(this._notes[i], i);
+ }
+ return this;
+ };
+ /**
+ * Internal tick method
+ * @param {Number} time The time of the event in seconds
+ * @private
+ */
+ Tone.Part.prototype._tick = function (time, value) {
+ if (!this.mute && this._events.getStateAtTime(Tone.Transport.ticks) === Tone.State.Started) {
+ this.callback(time, value);
+ }
+ };
+ /**
+ * The probability of the notes being triggered.
+ * @memberOf Tone.Part#
+ * @type {NormalRange}
+ * @name probability
+ */
+ Object.defineProperty(Tone.Part.prototype, 'probability', {
+ get: function () {
+ return this._probability;
+ },
+ set: function (prob) {
+ this._probability = prob;
+ this._forEach(function (note) {
+ note.probability = prob;
+ });
+ }
+ });
+ /**
+ * If the note should loop or not
+ * between Tone.Part.loopStart and
+ * Tone.Part.loopEnd. An integer
+ * value corresponds to the number of
+ * loops the Part does after it starts.
+ * @memberOf Tone.Part#
+ * @type {Boolean|Positive}
+ * @name loop
+ */
+ Object.defineProperty(Tone.Part.prototype, 'loop', {
+ get: function () {
+ return this._loop;
+ },
+ set: function (loop) {
+ this._loop = loop;
+ this._forEach(function (event) {
+ event.note.loop = loop;
+ });
+ this.loopEnd = this._loopEnd + 'i';
+ this.loopStart = this._loopStart + 'i';
+ }
+ });
+ /**
+ * The loopEnd point determines when it will
+ * loop if Tone.Part.loop is true.
+ * @memberOf Tone.Part#
+ * @type {Boolean|Positive}
+ * @name loopEnd
+ */
+ Object.defineProperty(Tone.Part.prototype, 'loopEnd', {
+ get: function () {
+ return this.toNotation(this._loopEnd + 'i');
+ },
+ set: function (loopEnd) {
+ this._loopEnd = this.toTicks(loopEnd);
+ if (this._loop) {
+ this._forEach(function (event) {
+ event.note.loopEnd = this._loopEnd - this._loopStart + 'i';
+ if (event.note.time > this._loopEnd) {
+ event.note.cancel();
+ }
+ }.bind(this));
+ }
+ }
+ });
+ /**
+ * The loopStart point determines when it will
+ * loop if Tone.Part.loop is true.
+ * @memberOf Tone.Part#
+ * @type {Boolean|Positive}
+ * @name loopStart
+ */
+ Object.defineProperty(Tone.Part.prototype, 'loopStart', {
+ get: function () {
+ return this.toNotation(this._loopStart + 'i');
+ },
+ set: function (loopStart) {
+ this._loopStart = this.toTicks(loopStart);
+ if (this._loop) {
+ this._forEach(function (event) {
+ event.note.loopEnd = this._loopEnd - this._loopStart + 'i';
+ if (event.note.time <= this._loopStart) {
+ event.note.cancel();
+ }
+ }.bind(this));
+ }
+ }
+ });
+ /**
+ * The playback rate of the part
+ * @memberOf Tone.Part#
+ * @type {Positive}
+ * @name playbackRate
+ */
+ Object.defineProperty(Tone.Part.prototype, 'playbackRate', {
+ get: function () {
+ return this._playbackRate;
+ },
+ set: function (rate) {
+ this._forEach(function (event) {
+ var ratio = event.note.playbackRate / this._playbackRate;
+ event.note.playbackRate = rate * ratio;
+ }.bind(this));
+ this._playbackRate = rate;
+ }
+ });
+ /**
+ * The number of scheduled notes in the part.
+ * @memberOf Tone.Part#
+ * @type {Positive}
+ * @name length
+ * @readOnly
+ */
+ Object.defineProperty(Tone.Part.prototype, 'length', {
+ get: function () {
+ return this._notes.length;
+ }
+ });
+ /**
+ * Clean up
+ * @return {Tone.Part} this
+ */
+ Tone.Part.prototype.dispose = function () {
+ this.callback = null;
+ this.removeAll();
+ this._notes = null;
+ return this;
+ };
+ return Tone.Part;
+ });
+ Module(function (Tone) {
+ /**
+ * @class Tone.Pattern arpeggiates between the given notes
+ * in a number of patterns.
+ * @extends {Tone}
+ * @param {Function} callback The callback to invoke with the
+ * event.
+ * @param {Array} notes The notes to arpeggiate over.
+ */
+ Tone.Pattern = function (callback, notes) {
+ /**
+ * Called back with the current event
+ * @private
+ * @type {Function}
+ */
+ this._callback = callback;
+ /**
+ * The notes to arpeggiate
+ * @type {Array}
+ */
+ this.notes = notes;
+ /**
+ * The event index
+ * @type {Array}
+ * @private
+ */
+ this._eventIndex = -1;
+ /**
+ * The note which schedules the notes
+ * @type {Tone.Note}
+ * @private
+ */
+ this._note = new Tone.Note(this._tick.bind(this));
+ this._note.loop = true;
+ this._note.loopEnd = '4n';
+ /**
+ * The stepping direction of the notes
+ * @type {Number}
+ * @private
+ */
+ this._arpDirection = 1;
+ };
+ Tone.extend(Tone.Pattern);
+ /**
+ * Start the arpeggio at the given time.
+ * @param {Time=} time When to start the Arpeggio
+ * @return {Tone.Pattern} this
+ */
+ Tone.Pattern.prototype.start = function (time) {
+ this._note.start(time);
+ return this;
+ };
+ /**
+ * Stop the arpeggio at the given time.
+ * @param {Time=} time When to stop the Arpeggio
+ * @return {Tone.Pattern} this
+ */
+ Tone.Pattern.prototype.stop = function (time) {
+ this._note.stop(time);
+ return this;
+ };
+ /**
+ * Internal function called when the notes should be called
+ * @param {Number} time The time the event occurs
+ * @private
+ */
+ Tone.Pattern.prototype._tick = function (time) {
+ if (this._pattern === Tone.Pattern.Type.Random) {
+ this._eventIndex = Math.floor(Math.random() * this.notes.length);
+ } else {
+ this._eventIndex += this._arpDirection;
+ if (this._pattern === Tone.Pattern.Type.Alternate) {
+ if (this._eventIndex === 0) {
+ this._arpDirection = 1;
+ } else if (this._eventIndex === this.notes.length - 1) {
+ this._arpDirection = -1;
+ }
+ } else if (this._eventIndex < 0) {
+ this._eventIndex = this.notes.length - 1;
+ } else if (this._eventIndex >= this.notes.length) {
+ this._eventIndex = 0;
+ }
+ }
+ this._callback(time, this.notes[this._eventIndex]);
+ };
+ /**
+ * The interval of the notes
+ * @memberOf Tone.Pattern#
+ * @type {Time}
+ * @name interval
+ */
+ Object.defineProperty(Tone.Pattern.prototype, 'interval', {
+ get: function () {
+ return this._note.loopEnd;
+ },
+ set: function (interval) {
+ this._note.loopEnd = interval;
+ }
+ });
+ /**
+ * @memberOf Tone.Pattern#
+ * @type {Time}
+ * @name pattern
+ */
+ Object.defineProperty(Tone.Pattern.prototype, 'pattern', {
+ get: function () {
+ return this._pattern;
+ },
+ set: function (pattern) {
+ switch (pattern) {
+ case Tone.Pattern.Type.Forward:
+ this._arpDirection = 1;
+ break;
+ case Tone.Pattern.Type.Reverse:
+ this._arpDirection = -1;
+ break;
+ }
+ var hasType = false;
+ for (var pattr in Tone.Pattern.Type) {
+ if (pattern === Tone.Pattern.Type[pattr]) {
+ hasType = true;
+ break;
+ }
+ }
+ if (!hasType) {
+ throw new Error('Invalid pattern: ' + pattern);
+ }
+ this._pattern = pattern;
+ }
+ });
+ /**
+ * The arpeggiation patterns
+ * @type {Object}
+ * @enum {String}
+ */
+ Tone.Pattern.Type = {
+ Forward: 'forward',
+ Reverse: 'reverse',
+ Alternate: 'alternate',
+ Drunk: 'drunk',
+ Converge: 'converge',
+ Diverge: 'diverge',
+ RandomOnce: 'randomOnce',
+ Random: 'random'
+ };
+ return Tone.Pattern;
+ });
+ Module(function (Tone) {
+ /**
+ * @class Tone.Score allows you to start and stop multiple sections
+ * with precise timing and synchronization.
+ *
+ * @example
+ * var score = new Tone.Score({
+ * "keyboard" : [0, "0:1", "0:3"]
+ * }).on("keyboard", function(time){
+ * //play the keyboard note
+ * });
+ *
+ * score.solo("keyboard");
+ *
+ * score.unsolo();
+ */
+ Tone.Score = function (score) {
+ Tone.EventEmitter.call(this);
+ /**
+ * All of the parts by name.
+ * @type {Object}
+ */
+ this.parts = {};
+ this._readOnly(['parts']);
+ };
+ Tone.extend(Tone.Score, Tone.EventEmitter);
+ /**
+ * Mute all other parts except the given
+ * one.
+ * @param {String|Array} section The section name
+ * @return {Tone.Score} this
+ */
+ Tone.Score.prototype.solo = function (part) {
+ this.mute = true;
+ if (Array.isArray(part)) {
+ part.forEach(function (p) {
+ if (this.parts.hasOwnProperty(p)) {
+ this.parts[p].mute = false;
+ }
+ }.bind(this));
+ } else if (this.parts.hasOwnProperty(part)) {
+ this.parts[part].mute = false;
+ }
+ };
+ /**
+ * Unsolo the given part(s). If no arguments are passed
+ * in, will unsolo everything.
+ * @param {String|Array} section The section name
+ * @return {Tone.Score} this
+ */
+ Tone.Score.prototype.unsolo = function () {
+ };
+ /**
+ * Mute all of the parts in the score.
+ */
+ Object.defineProperty(Tone.Score.prototype, 'mute', {
+ get: function () {
+ },
+ set: function (mute) {
+ this._forEach(function (part) {
+ part.mute = mute;
+ });
+ }
+ });
+ return Tone.Score;
+ });
+ Module(function (Tone) {
+
+ /**
+ * @class A sequence is an alternate notation of a part. Instead
+ * of passing in an array of [time, event] pairs, pass
+ * in an array of events which will be parsed
+ * as quarter note events. Subdivisions are given
+ * as sub arrays. Sequence notation inspiration from [Tidal](http://yaxu.org/tidal/)
+ * @param {Function} callback The callback to invoke with every note
+ * @param {Array} sequence The sequence
+ * @extends {Tone.Part}
+ * @example
+ * //straight quater notes
+ * var seq = new Tone.Sequence(function(time, note){
+ * console.log(note);
+ * }, ["C4", "E4", "G4", "A4"]);
+ * @example
+ * //subdivisions are given as subarrays
+ * var seq = new Tone.Sequence(function(time, note){
+ * console.log(note);
+ * }, ["C4", "E4", "G4", ["A4", "G4"]]);
+ * @example
+ * //A sequence with objects which are converted into Atoms
+ * var seq = new Tone.Sequence(function(time, val){
+ *
+ * }, [{"note" : "C4", "probability" : 1},
+ * {"note" : "E4", "probability" : 0.8},
+ * {"note" : "G4", "probability" : 0.6},
+ * [{"note" : "A4", "probability" : 0.8},
+ * {"note" : "G4", "probability" : 0.1}
+ * ]
+ * ]);
+ */
+ Tone.Sequence = function (callback, sequence, subdivision) {
+ var options = this.optionsObject(arguments, [
+ 'callback',
+ 'sequence',
+ 'subdivision'
+ ], Tone.Sequence.defaults);
+ Tone.Part.call(this, callback);
+ /**
+ * The subdivison of each note
+ * @type {String}
+ */
+ this._subdivision = this.toTicks(subdivision);
+ if (Array.isArray(sequence)) {
+ for (var i = 0; i < sequence.length; i++) {
+ var subdivider = this._subdivision;
+ if (Array.isArray(sequence[i])) {
+ subdivider = sequence[i].length;
+ }
+ var subSeq = new Tone.Sequence(this._tick.bind(this), sequence[i], Math.floor(this._subdivision / subdivider) + 'i');
+ this.add(this._subdivision * i + 'i', subSeq);
+ }
+ } else if (sequence) {
+ this.add(0, sequence);
+ }
+ };
+ Tone.extend(Tone.Sequence, Tone.Part);
+ /**
+ * The default values.
+ * @type {Object}
+ */
+ Tone.Sequence.defaults = { 'subdivision': '4n' };
+ /**
+ * Parse an array into [time, value] pairs
+ * @param {Array} seq The sequence to parse
+ * @param {Ticks} subdiv The current subdivision at that tick level
+ * @param {Ticks} offset The offset from the
+ * @private
+ */
+ Tone.Sequence.prototype._parseSequence = function (seq, subdiv, offset) {
+ if (Array.isArray(seq)) {
+ for (var i = 0; i < seq.length; i++) {
+ var subSeq = new Tone.Sequence(this._tick.bind(this), seq[i], subdiv / 2 + 'i');
+ this.add(this._subdivision + ' * ' + i, subSeq);
+ }
+ } else if (seq) {
+ this.add(subdiv * offset + 'i', seq);
+ }
+ };
+ /**
+ * Get/Set an index of the sequence
+ * @example
+ * var sequence = new Tone.Sequence(playNote, ["E4", "C4", "F#4", "A4"])
+ * sequence.at(0)// => returns "E4"
+ * //set a value
+ * sequence.at(0, "G3");
+ */
+ Tone.Sequence.prototype.at = function (index, value) {
+ //call the parent's method
+ return Tone.Part.prototype.at.call(this, '4n * ' + index, value);
+ };
+ /**
+ * Clean up.
+ * @return {Tone.Sequence} this
+ */
+ Tone.Sequence.prototype.dispose = function () {
+ Tone.Part.prototype.dispose.call(this);
+ this._sequence = null;
+ return this;
+ };
+ return Tone.Sequence;
+ });
//UMD
if ( typeof define === "function" && define.amd ) {
define( "Tone", [], function() {
@@ -14692,4 +18311,40 @@
} else {
root.Tone = Tone;
}
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // P5 SHIM
+ ///////////////////////////////////////////////////////////////////////////
+
+ Tone.registeredPreload = function(callback){
+ return function(){
+ callback();
+ }
+ };
+
+ //overwrite load function
+ Tone.Buffer.load = function (url, callback) {
+ var handle = Tone.registeredPreload();
+ var request = new XMLHttpRequest();
+ request.open("GET", url, true);
+ request.responseType = "arraybuffer";
+ // decode asynchronously
+ request.onload = function () {
+ Tone.context.decodeAudioData(request.response, function (buff) {
+ if (!buff) {
+ throw new Error("could not decode audio data:" + url);
+ }
+ callback(buff);
+ handle();
+ });
+ };
+ //send the request
+ request.send();
+ return request;
+ };
+
+ p5.prototype.registerPreloadMethod("registeredPreload", Tone);
+
+
} (this));
\ No newline at end of file
diff --git a/build/p5.Tone.min.js b/build/p5.Tone.min.js
new file mode 100644
index 00000000..3dd50f4d
--- /dev/null
+++ b/build/p5.Tone.min.js
@@ -0,0 +1,13 @@
+!function(root){"use strict";function Main(t){Tone=t()}function Module(t){t(Tone)}var Tone;/**
+ * Tone.js
+ * @author Yotam Mann
+ * @license http://opensource.org/licenses/MIT MIT License
+ * @copyright 2014-2015 Yotam Mann
+ */
+Main(function(){function t(t){return void 0===t}function e(t){return"function"==typeof t}var i,n,s,o;if(t(window.AudioContext)&&(window.AudioContext=window.webkitAudioContext),t(window.OfflineAudioContext)&&(window.OfflineAudioContext=window.webkitOfflineAudioContext),t(AudioContext))throw new Error("Web Audio is not supported in this browser");return i=new AudioContext,e(AudioContext.prototype.createGain)||(AudioContext.prototype.createGain=AudioContext.prototype.createGainNode),e(AudioContext.prototype.createDelay)||(AudioContext.prototype.createDelay=AudioContext.prototype.createDelayNode),e(AudioContext.prototype.createPeriodicWave)||(AudioContext.prototype.createPeriodicWave=AudioContext.prototype.createWaveTable),e(AudioBufferSourceNode.prototype.start)||(AudioBufferSourceNode.prototype.start=AudioBufferSourceNode.prototype.noteGrainOn),e(AudioBufferSourceNode.prototype.stop)||(AudioBufferSourceNode.prototype.stop=AudioBufferSourceNode.prototype.noteOff),e(OscillatorNode.prototype.start)||(OscillatorNode.prototype.start=OscillatorNode.prototype.noteOn),e(OscillatorNode.prototype.stop)||(OscillatorNode.prototype.stop=OscillatorNode.prototype.noteOff),e(OscillatorNode.prototype.setPeriodicWave)||(OscillatorNode.prototype.setPeriodicWave=OscillatorNode.prototype.setWaveTable),AudioNode.prototype._nativeConnect=AudioNode.prototype.connect,AudioNode.prototype.connect=function(e,i,n){if(e.input)Array.isArray(e.input)?(t(n)&&(n=0),this.connect(e.input[n])):this.connect(e.input,i,n);else try{e instanceof AudioNode?this._nativeConnect(e,i,n):this._nativeConnect(e,i)}catch(s){throw new Error("error connecting to node: "+e)}},n=function(e,i){t(e)||1===e?this.input=this.context.createGain():e>1&&(this.input=new Array(e)),t(i)||1===i?this.output=this.context.createGain():i>1&&(this.output=new Array(e))},n.prototype.set=function(e,i,s){var o,r,a,l,h,u;"object"==typeof e?s=i:"string"==typeof e&&(o={},o[e]=i,e=o);for(r in e){if(i=e[r],a=this,-1!==r.indexOf(".")){for(l=r.split("."),h=0;h1)for(t=arguments[0],e=1;e1)for(t=1;t0)for(t=this,e=0;e0)for(var t=0;te;e++)n=e/i*2-1,this._curve[e]=t(n,e);return this._shaper.curve=this._curve,this},Object.defineProperty(t.WaveShaper.prototype,"curve",{get:function(){return this._shaper.curve},set:function(t){this._curve=new Float32Array(t),this._shaper.curve=this._curve}}),Object.defineProperty(t.WaveShaper.prototype,"oversample",{get:function(){return this._shaper.oversample},set:function(t){if(-1===["none","2x","4x"].indexOf(t))throw new Error("invalid oversampling: "+t);this._shaper.oversample=t}}),t.WaveShaper.prototype.dispose=function(){return t.prototype.dispose.call(this),this._shaper.disconnect(),this._shaper=null,this._curve=null,this},t.WaveShaper}),Module(function(Tone){function getTransportBpm(){return Tone.Transport&&Tone.Transport.bpm?Tone.Transport.bpm.value:120}function getTransportTimeSignature(){return Tone.Transport&&Tone.Transport.timeSignature?Tone.Transport.timeSignature:4}function toNotationHelper(t,e,i,n){var s,o,r,a,l=this.toSeconds(t),h=this.notationToSeconds(n[n.length-1],e,i),u="";for(s=0;s1-r%1&&(r+=a),r=Math.floor(r),r>0){if(u+=1===r?n[s]:r.toString()+"*"+n[s],l-=r*o,h>l)break;u+=" + "}return u}var noteToScaleIndex,scaleIndexToNote;return Tone.Type={Default:"number",Time:"time",Frequency:"frequency",Gain:"gain",NormalRange:"normalRange",AudioRange:"audioRange",Decibels:"db",Interval:"interval",BPM:"bpm",Positive:"positive",Cents:"cents",Degrees:"degrees",MIDI:"midi",TransportTime:"transportTime",Ticks:"tick",Note:"note",Milliseconds:"milliseconds",Notation:"notation"},Tone.prototype.isNowRelative=function(){var t=new RegExp(/^\W*\+(.)+/i);return function(e){return t.test(e)}}(),Tone.prototype.isTicks=function(){var t=new RegExp(/^\d+i$/i);return function(e){return t.test(e)}}(),Tone.prototype.isNotation=function(){var t=new RegExp(/^[0-9]+[mnt]$/i);return function(e){return t.test(e)}}(),Tone.prototype.isTransportTime=function(){var t=new RegExp(/^(\d+(\.\d+)?\:){1,2}(\d+(\.\d+)?)?$/i);return function(e){return t.test(e)}}(),Tone.prototype.isNote=function(){var t=new RegExp(/^[a-g]{1}(b|#|x|bb)?-?[0-9]+$/i);return function(e){return t.test(e)}}(),Tone.prototype.isFrequency=function(){var t=new RegExp(/^\d*\.?\d+hz$/i);return function(e){return t.test(e)}}(),Tone.prototype.notationToSeconds=function(t,e,i){var n,s,o,r;return e=this.defaultArg(e,getTransportBpm()),i=this.defaultArg(i,getTransportTimeSignature()),n=60/e,"1n"===t&&(t="1m"),s=parseInt(t,10),o=0,0===s&&(o=0),r=t.slice(-1),o="t"===r?4/s*2/3:"n"===r?4/s:"m"===r?s*i:0,n*o},Tone.prototype.transportTimeToSeconds=function(t,e,i){var n,s,o,r,a;return e=this.defaultArg(e,getTransportBpm()),i=this.defaultArg(i,getTransportTimeSignature()),n=0,s=0,o=0,r=t.split(":"),2===r.length?(n=parseFloat(r[0]),s=parseFloat(r[1])):1===r.length?s=parseFloat(r[0]):3===r.length&&(n=parseFloat(r[0]),s=parseFloat(r[1]),o=parseFloat(r[2])),a=n*i+s+o/4,a*this.notationToSeconds("4n",e,i)},Tone.prototype.ticksToSeconds=function(t,e,i){if(this.isUndef(Tone.Transport))return 0;t=parseInt(t);var n=this.notationToSeconds("4n",e,i);return n*t/Tone.Transport.PPQ},Tone.prototype.frequencyToSeconds=function(t){return 1/parseFloat(t)},Tone.prototype.samplesToSeconds=function(t){return t/this.context.sampleRate},Tone.prototype.secondsToSamples=function(t){return t*this.context.sampleRate},Tone.prototype.secondsToTransportTime=function(t,e,i){var n,s,o,r,a;return e=this.defaultArg(e,getTransportBpm()),i=this.defaultArg(i,getTransportTimeSignature()),n=this.notationToSeconds("4n",e,i),s=t/n,o=Math.floor(s/i),r=s%1*4,s=Math.floor(s)%i,a=[o,s,r],a.join(":")},Tone.prototype.secondsToFrequency=function(t){return 1/t},Tone.prototype.toTransportTime=function(t,e,i){var n=this.toSeconds(t,e,i);return this.secondsToTransportTime(n,e,i)},Tone.prototype.toFrequency=function(t,e){return this.isFrequency(t)?parseFloat(t):this.isNotation(t)||this.isTransportTime(t)?this.secondsToFrequency(this.toSeconds(t,e)):this.isNote(t)?this.noteToFrequency(t):t},Tone.prototype.toTicks=function(t,e,i){var n,s,o,r,a;if(this.isUndef(Tone.Transport))return 0;if(n=0,this.isNowRelative(t))t=t.replace(/^\W*/,""),n=Tone.Transport.ticks;else if(this.isUndef(t))return Tone.Transport.ticks;return s=this.toSeconds(t),o=this.notationToSeconds("4n",e,i),r=s/o,a=r*Tone.Transport.PPQ,Math.round(a)+n},Tone.prototype.toSamples=function(t){var e=this.toSeconds(t);return Math.round(e*this.context.sampleRate)},Tone.prototype.toSeconds=function(time,now){var plusTime,components,originalTime,i,symb,val;if(now=this.defaultArg(now,this.now()),"number"==typeof time)return time;if("string"==typeof time){if(plusTime=0,this.isNowRelative(time)&&(time=time.replace(/^\W*/,""),plusTime=now),components=time.split(/[\(\)\-\+\/\*]/),components.length>1){for(originalTime=time,i=0;is&&(n+=-12*s),e=scaleIndexToNote[n%12],e+s.toString()},Tone.prototype.intervalToFrequencyRatio=function(t){return Math.pow(2,t/12)},Tone.prototype.midiToNote=function(t){var e=Math.floor(t/12)-1,i=t%12;return scaleIndexToNote[i]+e},Tone.prototype.noteToMidi=function(t){var e,i,n=t.split(/(\d+)/);return 3===n.length?(e=noteToScaleIndex[n[0].toLowerCase()],i=n[1],e+12*(parseInt(i,10)+1)):0},Tone.prototype.midiToFrequency=function(t){return Tone.A4*Math.pow(2,(t-69)/12)},Tone}),Module(function(t){return t.Param=function(){var e=this.optionsObject(arguments,["param","units","convert"],t.Param.defaults);this._param=this.input=e.param,this.units=e.units,this.convert=e.convert,this.overridden=!1,this.isUndef(e.value)||(this.value=e.value)},t.extend(t.Param),t.Param.defaults={units:t.Type.Default,convert:!0,param:void 0},Object.defineProperty(t.Param.prototype,"value",{get:function(){return this._toUnits(this._param.value)},set:function(t){var e=this._fromUnits(t);this._param.value=e}}),t.Param.prototype._fromUnits=function(e){if(!this.convert&&!this.isUndef(this.convert))return e;switch(this.units){case t.Type.Time:return this.toSeconds(e);case t.Type.Frequency:return this.toFrequency(e);case t.Type.Decibels:return this.dbToGain(e);case t.Type.NormalRange:return Math.min(Math.max(e,0),1);case t.Type.AudioRange:return Math.min(Math.max(e,-1),1);case t.Type.Positive:return Math.max(e,0);default:return e}},t.Param.prototype._toUnits=function(e){if(!this.convert&&!this.isUndef(this.convert))return e;switch(this.units){case t.Type.Decibels:return this.gainToDb(e);default:return e}},t.Param.prototype._minOutput=1e-5,t.Param.prototype.setValueAtTime=function(t,e){return t=this._fromUnits(t),this._param.setValueAtTime(t,this.toSeconds(e)),this},t.Param.prototype.setRampPoint=function(t){t=this.defaultArg(t,this.now());var e=this._param.value;return this._param.setValueAtTime(e,t),this},t.Param.prototype.linearRampToValueAtTime=function(t,e){return t=this._fromUnits(t),this._param.linearRampToValueAtTime(t,this.toSeconds(e)),this},t.Param.prototype.exponentialRampToValueAtTime=function(t,e){return t=this._fromUnits(t),t=Math.max(this._minOutput,t),this._param.exponentialRampToValueAtTime(t,this.toSeconds(e)),this},t.Param.prototype.exponentialRampToValue=function(t,e){var i=this.now(),n=this.value;return this.setValueAtTime(Math.max(n,this._minOutput),i),this.exponentialRampToValueAtTime(t,i+this.toSeconds(e)),this},t.Param.prototype.linearRampToValue=function(t,e){var i=this.now();return this.setRampPoint(i),this.linearRampToValueAtTime(t,i+this.toSeconds(e)),this},t.Param.prototype.setTargetAtTime=function(t,e,i){return t=this._fromUnits(t),t=Math.max(this._minOutput,t),i=Math.max(this._minOutput,i),this._param.setTargetAtTime(t,this.toSeconds(e),i),this},t.Param.prototype.setValueCurveAtTime=function(t,e,i){for(var n=0;n=0?this._timeline[e-1]:null},t.Timeline.prototype.cancel=function(t){if(this._timeline.length){t=this.toSeconds(t);var e=this._search(t);this._timeline=e>=0?this._timeline.slice(0,e):[]}return this},t.Timeline.prototype.cancelBefore=function(t){if(this._timeline.length){t=this.toSeconds(t);var e=this._search(t);e>=0&&(this._timeline=this._timeline.slice(e+1))}return this},t.Timeline.prototype._search=function(t){for(var e,i,n,s,o=0,r=this._timeline.length,a=r;a>=o&&r>o;){if(e=Math.floor(o+(a-o)/2),i=this._timeline[e],i.time===t){for(n=e;nt?a=e-1:i.time=0;e--)t(this._timeline[e],e);return this},t.Timeline.prototype.forEachBefore=function(t,e){var i,n;if(t=this.toSeconds(t),i=this._search(t),-1!==i)for(n=i;n>=0;n--)e(this._timeline[n],n);return this},t.Timeline.prototype.forEachAfter=function(t,e){var i,n;for(t=this.toSeconds(t),i=this._search(t),n=this._timeline.length-1;n>i;n--)e(this._timeline[n],n);return this},t.Timeline.prototype.forEachFrom=function(t,e){var i,n;for(t=this.toSeconds(t),i=this._search(t);i>=0&&this._timeline[i].time>=t;)i--;for(n=this._timeline.length-1;n>i;n--)e(this._timeline[n],n);return this},t.Timeline.prototype.forEachAtTime=function(t,e){var i,n,s;if(t=this.toSeconds(t),i=this._search(t),-1!==i)for(n=i;n>=0&&(s=this._timeline[n],s.time===t);n--)e(s,n);return this},t.Timeline.prototype.dispose=function(){t.prototype.dispose.call(this),this._timeline=null},t.Timeline}),Module(function(t){return t.TimelineSignal=function(){var e=this.optionsObject(arguments,["value","units"],t.Signal.defaults);t.Signal.apply(this,e),e.param=this._param,t.Param.call(this,e),this._events=new t.Timeline,this._initial=this._fromUnits(this._param.value)},t.extend(t.TimelineSignal,t.Param),t.TimelineSignal.Type={Linear:"linear",Exponential:"exponential",Target:"target",Set:"set"},Object.defineProperty(t.TimelineSignal.prototype,"value",{get:function(){return this._toUnits(this._param.value)},set:function(t){var e=this._fromUnits(t);this._initial=e,this._param.value=e}}),t.TimelineSignal.prototype.setValueAtTime=function(e,i){return e=this._fromUnits(e),i=this.toSeconds(i),this._events.addEvent({type:t.TimelineSignal.Type.Set,value:e,time:i}),this._param.setValueAtTime(e,i),this},t.TimelineSignal.prototype.linearRampToValueAtTime=function(e,i){return e=this._fromUnits(e),i=this.toSeconds(i),this._events.addEvent({type:t.TimelineSignal.Type.Linear,value:e,time:i}),this._param.linearRampToValueAtTime(e,i),this},t.TimelineSignal.prototype.exponentialRampToValueAtTime=function(e,i){return e=this._fromUnits(e),e=Math.max(this._minOutput,e),i=this.toSeconds(i),this._events.addEvent({type:t.TimelineSignal.Type.Exponential,value:e,time:i}),this._param.exponentialRampToValueAtTime(e,i),this},t.TimelineSignal.prototype.setTargetAtTime=function(e,i,n){return e=this._fromUnits(e),e=Math.max(this._minOutput,e),i=this.toSeconds(i),this._events.addEvent({type:t.TimelineSignal.Type.Target,value:e,time:i,constant:n}),this._param.setTargetAtTime(e,i,n),this},t.TimelineSignal.prototype.cancelScheduledValues=function(t){return this._events.clear(t),this._param.cancelScheduledValues(this.toSeconds(t)),this},t.TimelineSignal.prototype.setRampPoint=function(t){t=this.toSeconds(t);var e=this.getValueAtTime(t);return this.setValueAtTime(e,t),this},t.TimelineSignal.prototype.linearRampToValueBetween=function(t,e,i){return this.setRampPoint(e),this.linearRampToValueAtTime(t,i),this},t.TimelineSignal.prototype.exponentialRampToValueBetween=function(t,e,i){return this.setRampPoint(e),this.exponentialRampToValueAtTime(t,i),this},t.TimelineSignal.prototype._searchBefore=function(t){return this._events.getEvent(t)},t.TimelineSignal.prototype._searchAfter=function(t){return this._events.getEventAfter(t)},t.TimelineSignal.prototype.getValueAtTime=function(e){var i,n,s=this._searchAfter(e),o=this._searchBefore(e);return null===o?this._initial:o.type===t.TimelineSignal.Type.Target?(i=this._searchBefore(o.time-1e-4),n=null===i?this._initial:i.value,this._exponentialApproach(o.time,n,o.value,o.constant,e)):null===s?o.value:s.type===t.TimelineSignal.Type.Linear?this._linearInterpolate(o.time,o.value,s.time,s.value,e):s.type===t.TimelineSignal.Type.Exponential?this._exponentialInterpolate(o.time,o.value,s.time,s.value,e):o.value},t.TimelineSignal.prototype.connect=t.SignalBase.prototype.connect,t.TimelineSignal.prototype._exponentialApproach=function(t,e,i,n,s){return i+(e-i)*Math.exp(-(s-t)/n)},t.TimelineSignal.prototype._linearInterpolate=function(t,e,i,n,s){return e+(n-e)*((s-t)/(i-t))},t.TimelineSignal.prototype._exponentialInterpolate=function(t,e,i,n,s){return e=Math.max(this._minOutput,e),e*Math.pow(n/e,(s-t)/(i-t))},t.TimelineSignal.prototype.dispose=function(){t.Signal.prototype.dispose.call(this),t.Param.prototype.dispose.call(this),this._events.dispose(),this._events=null},t.TimelineSignal}),Module(function(t){return t.Pow=function(e){this._exp=this.defaultArg(e,1),this._expScaler=this.input=this.output=new t.WaveShaper(this._expFunc(this._exp),8192)},t.extend(t.Pow,t.SignalBase),Object.defineProperty(t.Pow.prototype,"value",{get:function(){return this._exp},set:function(t){this._exp=t,this._expScaler.setMap(this._expFunc(this._exp))}}),t.Pow.prototype._expFunc=function(t){return function(e){return Math.pow(Math.abs(e),t)}},t.Pow.prototype.dispose=function(){return t.prototype.dispose.call(this),this._expScaler.dispose(),this._expScaler=null,this},t.Pow}),Module(function(t){return t.Envelope=function(){var e=this.optionsObject(arguments,["attack","decay","sustain","release"],t.Envelope.defaults);this.attack=e.attack,this.decay=e.decay,this.sustain=e.sustain,this.release=e.release,this._attackCurve=t.Envelope.Type.Linear,this._releaseCurve=t.Envelope.Type.Exponential,this._minOutput=1e-5,this._sig=this.output=new t.TimelineSignal,this._sig.setValueAtTime(0,0),this.attackCurve=e.attackCurve,this.releaseCurve=e.releaseCurve},t.extend(t.Envelope),t.Envelope.defaults={attack:.01,decay:.1,sustain:.5,release:1,attackCurve:"linear",releaseCurve:"exponential"},t.Envelope.prototype._timeMult=.25,Object.defineProperty(t.Envelope.prototype,"value",{get:function(){return this._sig.value}}),Object.defineProperty(t.Envelope.prototype,"attackCurve",{get:function(){return this._attackCurve},set:function(e){if(e!==t.Envelope.Type.Linear&&e!==t.Envelope.Type.Exponential)throw Error('attackCurve must be either "linear" or "exponential". Invalid type: ',e);this._attackCurve=e}}),Object.defineProperty(t.Envelope.prototype,"releaseCurve",{get:function(){return this._releaseCurve},set:function(e){if(e!==t.Envelope.Type.Linear&&e!==t.Envelope.Type.Exponential)throw Error('releaseCurve must be either "linear" or "exponential". Invalid type: ',e);this._releaseCurve=e}}),t.Envelope.prototype.triggerAttack=function(e,i){var n,s,o=this.now()+this.blockTime;return e=this.toSeconds(e,o),n=this.toSeconds(this.attack)+e,s=this.toSeconds(this.decay),i=this.defaultArg(i,1),this._attackCurve===t.Envelope.Type.Linear?this._sig.linearRampToValueBetween(i,e,n):this._sig.exponentialRampToValueBetween(i,e,n),this._sig.setTargetAtTime(this.sustain*i,n,s*this._timeMult),this},t.Envelope.prototype.triggerRelease=function(e){var i,n=this.now()+this.blockTime;return e=this.toSeconds(e,n),i=this.toSeconds(this.release),this._releaseCurve===t.Envelope.Type.Linear?this._sig.linearRampToValueBetween(this._minOutput,e,e+i):this._sig.setTargetAtTime(this._minOutput,e,i*this._timeMult),this},t.Envelope.prototype.triggerAttackRelease=function(t,e,i){return e=this.toSeconds(e),this.triggerAttack(e,i),this.triggerRelease(e+this.toSeconds(t)),this},t.Envelope.prototype.connect=t.Signal.prototype.connect,t.Envelope.prototype.dispose=function(){return t.prototype.dispose.call(this),this._sig.dispose(),this._sig=null,this},t.Envelope.Phase={Attack:"attack",Decay:"decay",Sustain:"sustain",Release:"release",Standby:"standby"},t.Envelope.Type={Linear:"linear",Exponential:"exponential"},t.Envelope}),Module(function(t){return t.AmplitudeEnvelope=function(){t.Envelope.apply(this,arguments),this.input=this.output=new t.Gain,this._sig.connect(this.output.gain)},t.extend(t.AmplitudeEnvelope,t.Envelope),t.AmplitudeEnvelope.prototype.dispose=function(){return this.input.dispose(),this.input=null,t.Envelope.prototype.dispose.call(this),this},t.AmplitudeEnvelope}),Module(function(t){return t.Analyser=function(){var e=this.optionsObject(arguments,["size","type"],t.Analyser.defaults);this._analyser=this.input=this.context.createAnalyser(),this._type=e.type,this._returnType=e.returnType,this._buffer=null,this.size=e.size,this.type=e.type,this.returnType=e.returnType,this.minDecibels=e.minDecibels,this.maxDecibels=e.maxDecibels},t.extend(t.Analyser),t.Analyser.defaults={size:2048,returnType:"byte",type:"fft",smoothing:.8,maxDecibels:-30,minDecibels:-100},t.Analyser.Type={Waveform:"waveform",FFT:"fft"},t.Analyser.ReturnType={Byte:"byte",Float:"float"},t.Analyser.prototype.analyse=function(){return this._type===t.Analyser.Type.FFT?this._returnType===t.Analyser.ReturnType.Byte?this._analyser.getByteFrequencyData(this._buffer):this._analyser.getFloatFrequencyData(this._buffer):this._type===t.Analyser.Type.Waveform&&(this._returnType===t.Analyser.ReturnType.Byte?this._analyser.getByteTimeDomainData(this._buffer):this._analyser.getFloatTimeDomainData(this._buffer)),this._buffer},Object.defineProperty(t.Analyser.prototype,"size",{get:function(){return this._analyser.frequencyBinCount},set:function(t){this._analyser.fftSize=2*t,this.type=this._type}}),Object.defineProperty(t.Analyser.prototype,"returnType",{get:function(){return this._returnType},set:function(e){if(e===t.Analyser.ReturnType.Byte)this._buffer=new Uint8Array(this._analyser.frequencyBinCount);else{if(e!==t.Analyser.ReturnType.Float)throw new Error("Invalid Return Type: "+e);this._buffer=new Float32Array(this._analyser.frequencyBinCount)}this._returnType=e}}),Object.defineProperty(t.Analyser.prototype,"type",{get:function(){return this._type},set:function(e){if(e!==t.Analyser.Type.Waveform&&e!==t.Analyser.Type.FFT)throw new Error("Invalid Type: "+e);this._type=e}}),Object.defineProperty(t.Analyser.prototype,"smoothing",{get:function(){return this._analyser.smoothingTimeConstant},set:function(t){this._analyser.smoothingTimeConstant=t}}),Object.defineProperty(t.Analyser.prototype,"minDecibels",{get:function(){return this._analyser.minDecibels},set:function(t){this._analyser.minDecibels=t}}),Object.defineProperty(t.Analyser.prototype,"maxDecibels",{get:function(){return this._analyser.maxDecibels},set:function(t){this._analyser.maxDecibels=t}}),t.Analyser.prototype.dispose=function(){t.prototype.dispose.call(this),this._analyser.disconnect(),this._analyser=null,this._buffer=null},t.Analyser}),Module(function(t){return t.Compressor=function(){var e=this.optionsObject(arguments,["threshold","ratio"],t.Compressor.defaults);this._compressor=this.input=this.output=this.context.createDynamicsCompressor(),this.threshold=this._compressor.threshold,this.attack=new t.Param(this._compressor.attack,t.Type.Time),this.release=new t.Param(this._compressor.release,t.Type.Time),this.knee=this._compressor.knee,this.ratio=this._compressor.ratio,this._readOnly(["knee","release","attack","ratio","threshold"]),this.set(e)},t.extend(t.Compressor),t.Compressor.defaults={ratio:12,threshold:-24,release:.25,attack:.003,knee:30},t.Compressor.prototype.dispose=function(){return t.prototype.dispose.call(this),this._writable(["knee","release","attack","ratio","threshold"]),this._compressor.disconnect(),this._compressor=null,this.attack.dispose(),this.attack=null,this.release.dispose(),this.release=null,this.threshold=null,this.ratio=null,this.knee=null,this},t.Compressor}),Module(function(t){return t.Add=function(e){t.call(this,2,0),this._sum=this.input[0]=this.input[1]=this.output=this.context.createGain(),this._param=this.input[1]=new t.Signal(e),this._param.connect(this._sum)},t.extend(t.Add,t.Signal),t.Add.prototype.dispose=function(){return t.prototype.dispose.call(this),
+this._sum.disconnect(),this._sum=null,this._param.dispose(),this._param=null,this},t.Add}),Module(function(t){return t.Multiply=function(e){t.call(this,2,0),this._mult=this.input[0]=this.output=this.context.createGain(),this._param=this.input[1]=this.output.gain,this._param.value=this.defaultArg(e,0)},t.extend(t.Multiply,t.Signal),t.Multiply.prototype.dispose=function(){return t.prototype.dispose.call(this),this._mult.disconnect(),this._mult=null,this._param=null,this},t.Multiply}),Module(function(t){return t.Negate=function(){this._multiply=this.input=this.output=new t.Multiply(-1)},t.extend(t.Negate,t.SignalBase),t.Negate.prototype.dispose=function(){return t.prototype.dispose.call(this),this._multiply.dispose(),this._multiply=null,this},t.Negate}),Module(function(t){return t.Subtract=function(e){t.call(this,2,0),this._sum=this.input[0]=this.output=this.context.createGain(),this._neg=new t.Negate,this._param=this.input[1]=new t.Signal(e),this._param.chain(this._neg,this._sum)},t.extend(t.Subtract,t.Signal),t.Subtract.prototype.dispose=function(){return t.prototype.dispose.call(this),this._neg.dispose(),this._neg=null,this._sum.disconnect(),this._sum=null,this._param.dispose(),this._param=null,this},t.Subtract}),Module(function(t){return t.GreaterThanZero=function(){this._thresh=this.output=new t.WaveShaper(function(t){return 0>=t?0:1}),this._scale=this.input=new t.Multiply(1e4),this._scale.connect(this._thresh)},t.extend(t.GreaterThanZero,t.SignalBase),t.GreaterThanZero.prototype.dispose=function(){return t.prototype.dispose.call(this),this._scale.dispose(),this._scale=null,this._thresh.dispose(),this._thresh=null,this},t.GreaterThanZero}),Module(function(t){return t.EqualZero=function(){this._scale=this.input=new t.Multiply(1e4),this._thresh=new t.WaveShaper(function(t){return 0===t?1:0},128),this._gtz=this.output=new t.GreaterThanZero,this._scale.chain(this._thresh,this._gtz)},t.extend(t.EqualZero,t.SignalBase),t.EqualZero.prototype.dispose=function(){return t.prototype.dispose.call(this),this._gtz.dispose(),this._gtz=null,this._scale.dispose(),this._scale=null,this._thresh.dispose(),this._thresh=null,this},t.EqualZero}),Module(function(t){return t.Equal=function(e){t.call(this,2,0),this._sub=this.input[0]=new t.Subtract(e),this._equals=this.output=new t.EqualZero,this._sub.connect(this._equals),this.input[1]=this._sub.input[1]},t.extend(t.Equal,t.SignalBase),Object.defineProperty(t.Equal.prototype,"value",{get:function(){return this._sub.value},set:function(t){this._sub.value=t}}),t.Equal.prototype.dispose=function(){return t.prototype.dispose.call(this),this._equals.dispose(),this._equals=null,this._sub.dispose(),this._sub=null,this},t.Equal}),Module(function(t){t.Select=function(i){var n,s;for(i=this.defaultArg(i,2),t.call(this,i,1),this.gate=new t.Signal(0),this._readOnly("gate"),n=0;i>n;n++)s=new e(n),this.input[n]=s,this.gate.connect(s.selecter),s.connect(this.output)},t.extend(t.Select,t.SignalBase),t.Select.prototype.select=function(t,e){return t=Math.floor(t),this.gate.setValueAtTime(t,this.toSeconds(e)),this},t.Select.prototype.dispose=function(){this._writable("gate"),this.gate.dispose(),this.gate=null;for(var e=0;ei;i++)this.input[i]=this._sum;this._sum.connect(this._gtz)},t.extend(t.OR,t.SignalBase),t.OR.prototype.dispose=function(){return t.prototype.dispose.call(this),this._gtz.dispose(),this._gtz=null,this._sum.disconnect(),this._sum=null,this},t.OR}),Module(function(t){return t.AND=function(e){e=this.defaultArg(e,2),t.call(this,e,0),this._equals=this.output=new t.Equal(e);for(var i=0;e>i;i++)this.input[i]=this._equals},t.extend(t.AND,t.SignalBase),t.AND.prototype.dispose=function(){return t.prototype.dispose.call(this),this._equals.dispose(),this._equals=null,this},t.AND}),Module(function(t){return t.NOT=t.EqualZero,t.NOT}),Module(function(t){return t.GreaterThan=function(e){t.call(this,2,0),this._param=this.input[0]=new t.Subtract(e),this.input[1]=this._param.input[1],this._gtz=this.output=new t.GreaterThanZero,this._param.connect(this._gtz)},t.extend(t.GreaterThan,t.Signal),t.GreaterThan.prototype.dispose=function(){return t.prototype.dispose.call(this),this._param.dispose(),this._param=null,this._gtz.dispose(),this._gtz=null,this},t.GreaterThan}),Module(function(t){return t.LessThan=function(e){t.call(this,2,0),this._neg=this.input[0]=new t.Negate,this._gt=this.output=new t.GreaterThan,this._rhNeg=new t.Negate,this._param=this.input[1]=new t.Signal(e),this._neg.connect(this._gt),this._param.connect(this._rhNeg),this._rhNeg.connect(this._gt,0,1)},t.extend(t.LessThan,t.Signal),t.LessThan.prototype.dispose=function(){return t.prototype.dispose.call(this),this._neg.dispose(),this._neg=null,this._gt.dispose(),this._gt=null,this._rhNeg.dispose(),this._rhNeg=null,this._param.dispose(),this._param=null,this},t.LessThan}),Module(function(t){return t.Abs=function(){t.call(this,1,0),this._ltz=new t.LessThan(0),this._switch=this.output=new t.Select(2),this._negate=new t.Negate,this.input.connect(this._switch,0,0),this.input.connect(this._negate),this._negate.connect(this._switch,0,1),this.input.chain(this._ltz,this._switch.gate)},t.extend(t.Abs,t.SignalBase),t.Abs.prototype.dispose=function(){return t.prototype.dispose.call(this),this._switch.dispose(),this._switch=null,this._ltz.dispose(),this._ltz=null,this._negate.dispose(),this._negate=null,this},t.Abs}),Module(function(t){return t.Max=function(e){t.call(this,2,0),this.input[0]=this.context.createGain(),this._param=this.input[1]=new t.Signal(e),this._ifThenElse=this.output=new t.IfThenElse,this._gt=new t.GreaterThan,this.input[0].chain(this._gt,this._ifThenElse["if"]),this.input[0].connect(this._ifThenElse.then),this._param.connect(this._ifThenElse["else"]),this._param.connect(this._gt,0,1)},t.extend(t.Max,t.Signal),t.Max.prototype.dispose=function(){return t.prototype.dispose.call(this),this._param.dispose(),this._ifThenElse.dispose(),this._gt.dispose(),this._param=null,this._ifThenElse=null,this._gt=null,this},t.Max}),Module(function(t){return t.Min=function(e){t.call(this,2,0),this.input[0]=this.context.createGain(),this._ifThenElse=this.output=new t.IfThenElse,this._lt=new t.LessThan,this._param=this.input[1]=new t.Signal(e),this.input[0].chain(this._lt,this._ifThenElse["if"]),this.input[0].connect(this._ifThenElse.then),this._param.connect(this._ifThenElse["else"]),this._param.connect(this._lt,0,1)},t.extend(t.Min,t.Signal),t.Min.prototype.dispose=function(){return t.prototype.dispose.call(this),this._param.dispose(),this._ifThenElse.dispose(),this._lt.dispose(),this._param=null,this._ifThenElse=null,this._lt=null,this},t.Min}),Module(function(t){return t.Modulo=function(e){t.call(this,1,1),this._shaper=new t.WaveShaper(Math.pow(2,16)),this._multiply=new t.Multiply,this._subtract=this.output=new t.Subtract,this._modSignal=new t.Signal(e),this.input.fan(this._shaper,this._subtract),this._modSignal.connect(this._multiply,0,0),this._shaper.connect(this._multiply,0,1),this._multiply.connect(this._subtract,0,1),this._setWaveShaper(e)},t.extend(t.Modulo,t.SignalBase),t.Modulo.prototype._setWaveShaper=function(t){this._shaper.setMap(function(e){var i=Math.floor((e+1e-4)/t);return i})},Object.defineProperty(t.Modulo.prototype,"value",{get:function(){return this._modSignal.value},set:function(t){this._modSignal.value=t,this._setWaveShaper(t)}}),t.Modulo.prototype.dispose=function(){return t.prototype.dispose.call(this),this._shaper.dispose(),this._shaper=null,this._multiply.dispose(),this._multiply=null,this._subtract.dispose(),this._subtract=null,this._modSignal.dispose(),this._modSignal=null,this},t.Modulo}),Module(function(t){return t.AudioToGain=function(){this._norm=this.input=this.output=new t.WaveShaper(function(t){return(t+1)/2})},t.extend(t.AudioToGain,t.SignalBase),t.AudioToGain.prototype.dispose=function(){return t.prototype.dispose.call(this),this._norm.dispose(),this._norm=null,this},t.AudioToGain}),Module(function(t){function e(t,e,i){var n=new t;return i._eval(e[0]).connect(n,0,0),i._eval(e[1]).connect(n,0,1),n}function i(t,e,i){var n=new t;return i._eval(e[0]).connect(n,0,0),n}function n(t){return t?parseFloat(t):void 0}function s(t){return t&&t.args?parseFloat(t.args):void 0}return t.Expr=function(){var t,e,i,n=this._replacements(Array.prototype.slice.call(arguments)),s=this._parseInputs(n);for(this._nodes=[],this.input=new Array(s),t=0;s>t;t++)this.input[t]=this.context.createGain();e=this._parseTree(n);try{i=this._eval(e)}catch(o){throw this._disposeNodes(),new Error("Could evaluate expression: "+n)}this.output=i},t.extend(t.Expr,t.SignalBase),t.Expr._Expressions={value:{signal:{regexp:/^\d+\.\d+|^\d+/,method:function(e){var i=new t.Signal(n(e));return i}},input:{regexp:/^\$\d/,method:function(t,e){return e.input[n(t.substr(1))]}}},glue:{"(":{regexp:/^\(/},")":{regexp:/^\)/},",":{regexp:/^,/}},func:{abs:{regexp:/^abs/,method:i.bind(this,t.Abs)},min:{regexp:/^min/,method:e.bind(this,t.Min)},max:{regexp:/^max/,method:e.bind(this,t.Max)},"if":{regexp:/^if/,method:function(e,i){var n=new t.IfThenElse;return i._eval(e[0]).connect(n["if"]),i._eval(e[1]).connect(n.then),i._eval(e[2]).connect(n["else"]),n}},gt0:{regexp:/^gt0/,method:i.bind(this,t.GreaterThanZero)},eq0:{regexp:/^eq0/,method:i.bind(this,t.EqualZero)},mod:{regexp:/^mod/,method:function(e,i){var n=s(e[1]),o=new t.Modulo(n);return i._eval(e[0]).connect(o),o}},pow:{regexp:/^pow/,method:function(e,i){var n=s(e[1]),o=new t.Pow(n);return i._eval(e[0]).connect(o),o}},a2g:{regexp:/^a2g/,method:function(e,i){var n=new t.AudioToGain;return i._eval(e[0]).connect(n),n}}},binary:{"+":{regexp:/^\+/,precedence:1,method:e.bind(this,t.Add)},"-":{regexp:/^\-/,precedence:1,method:function(n,s){return 1===n.length?i(t.Negate,n,s):e(t.Subtract,n,s)}},"*":{regexp:/^\*/,precedence:0,method:e.bind(this,t.Multiply)},">":{regexp:/^\>/,precedence:2,method:e.bind(this,t.GreaterThan)},"<":{regexp:/^,precedence:2,method:e.bind(this,t.LessThan)},"==":{regexp:/^==/,precedence:3,method:e.bind(this,t.Equal)},"&&":{regexp:/^&&/,precedence:4,method:e.bind(this,t.AND)},"||":{regexp:/^\|\|/,precedence:5,method:e.bind(this,t.OR)}},unary:{"-":{regexp:/^\-/,method:i.bind(this,t.Negate)},"!":{regexp:/^\!/,method:i.bind(this,t.NOT)}}},t.Expr.prototype._parseInputs=function(t){var e,i,n=t.match(/\$\d/g),s=0;if(null!==n)for(e=0;e0;)e=e.trim(),n=i(e),o.push(n),e=e.substr(n.value.length);return{next:function(){return o[++s]},peek:function(){return o[s+1]}}},t.Expr.prototype._parseTree=function(e){function i(t,e){return!u(t)&&"glue"===t.type&&t.value===e}function n(e,i,n){var s,o,r=!1,a=t.Expr._Expressions[i];if(!u(e))for(s in a)if(o=a[s],o.regexp.test(e.value)){if(u(n))return!0;if(o.precedence===n)return!0}return r}function s(t){var e,i;for(u(t)&&(t=5),e=0>t?o():s(t-1),i=h.peek();n(i,"binary",t);)i=h.next(),e={operator:i.value,method:i.method,args:[e,s(t)]},i=h.peek();return e}function o(){var t,e;return t=h.peek(),n(t,"unary")?(t=h.next(),e=o(),{operator:t.value,method:t.method,args:[e]}):r()}function r(){var t,e;if(t=h.peek(),u(t))throw new SyntaxError("Unexpected termination of expression");if("func"===t.type)return t=h.next(),a(t);if("value"===t.type)return t=h.next(),{method:t.method,args:t.value};if(i(t,"(")){if(h.next(),e=s(),t=h.next(),!i(t,")"))throw new SyntaxError("Expected )");return e}throw new SyntaxError("Parse error, cannot process token "+t.value)}function a(t){var e,n=[];if(e=h.next(),!i(e,"("))throw new SyntaxError('Expected ( in a function call "'+t.value+'"');if(e=h.peek(),i(e,")")||(n=l()),e=h.next(),!i(e,")"))throw new SyntaxError('Expected ) in a function call "'+t.value+'"');return{method:t.method,args:n,name:name}}function l(){for(var t,e,n=[];;){if(e=s(),u(e))break;if(n.push(e),t=h.peek(),!i(t,","))break;h.next()}return n}var h=this._tokenize(e),u=this.isUndef.bind(this);return s()},t.Expr.prototype._eval=function(t){if(!this.isUndef(t)){var e=t.method(t.args,this);return this._nodes.push(e),e}},t.Expr.prototype._disposeNodes=function(){var t,e;for(t=0;ts;s++)o=this.context.createBiquadFilter(),o.type=this._type,this.frequency.connect(o.frequency),this.detune.connect(o.detune),this.Q.connect(o.Q),this.gain.connect(o.gain),this._filters[s]=o;r=[this.input].concat(this._filters).concat([this.output]),this.connectSeries.apply(this,r)}}),t.Filter.prototype.dispose=function(){t.prototype.dispose.call(this);for(var e=0;e=i?t:e})},Object.defineProperty(t.Follower.prototype,"attack",{get:function(){return this._attack},set:function(t){this._attack=t,this._setAttackRelease(this._attack,this._release)}}),Object.defineProperty(t.Follower.prototype,"release",{get:function(){return this._release},set:function(t){this._release=t,this._setAttackRelease(this._attack,this._release)}}),t.Follower.prototype.connect=t.Signal.prototype.connect,t.Follower.prototype.dispose=function(){return t.prototype.dispose.call(this),this._filter.disconnect(),this._filter=null,this._frequencyValues.disconnect(),this._frequencyValues=null,this._delay.disconnect(),this._delay=null,this._sub.disconnect(),this._sub=null,this._abs.dispose(),this._abs=null,this._mult.dispose(),this._mult=null,this._curve=null,this},t.Follower}),Module(function(t){return t.Gate=function(){t.call(this);var e=this.optionsObject(arguments,["threshold","attack","release"],t.Gate.defaults);this._follower=new t.Follower(e.attack,e.release),this._gt=new t.GreaterThan(this.dbToGain(e.threshold)),this.input.connect(this.output),this.input.chain(this._gt,this._follower,this.output.gain)},t.extend(t.Gate),t.Gate.defaults={attack:.1,release:.1,threshold:-40},Object.defineProperty(t.Gate.prototype,"threshold",{get:function(){return this.gainToDb(this._gt.value)},set:function(t){this._gt.value=this.dbToGain(t)}}),Object.defineProperty(t.Gate.prototype,"attack",{get:function(){return this._follower.attack},set:function(t){this._follower.attack=t}}),Object.defineProperty(t.Gate.prototype,"release",{get:function(){return this._follower.release},set:function(t){this._follower.release=t}}),t.Gate.prototype.dispose=function(){return t.prototype.dispose.call(this),this._follower.dispose(),this._gt.dispose(),this._follower=null,this._gt=null,this},t.Gate}),Module(function(t){return t.TimelineState=function(e){t.Timeline.call(this),this._initial=e},t.extend(t.TimelineState,t.Timeline),t.TimelineState.prototype.getStateAtTime=function(t){var e=this.getEvent(t);return null!==e?e.state:this._initial},t.TimelineState.prototype.setStateAtTime=function(t,e){this.addEvent({state:t,time:this.toSeconds(e)})},t.TimelineState}),Module(function(t){return t.Clock=function(){var e=this.optionsObject(arguments,["callback","frequency"],t.Clock.defaults);this.callback=e.callback,this._lookAhead="auto",this._computedLookAhead=1/60,this._threshold=.5,this._nextTick=-1,this._lastUpdate=0,this._loopID=-1,this.frequency=new t.TimelineSignal(e.frequency,t.Type.Frequency),this.ticks=0,this._state=new t.TimelineState(t.State.Stopped),this._boundLoop=this._loop.bind(this),this._readOnly("frequency"),this._loop()},t.extend(t.Clock),t.Clock.defaults={callback:t.noOp,frequency:1,lookAhead:"auto"},Object.defineProperty(t.Clock.prototype,"state",{get:function(){return this._state.getStateAtTime(this.now())}}),Object.defineProperty(t.Clock.prototype,"lookAhead",{get:function(){return this._lookAhead},set:function(t){this._lookAhead="auto"===t?"auto":this.toSeconds(t)}}),t.Clock.prototype.start=function(e,i){return e=this.toSeconds(e),this._state.getStateAtTime(e)!==t.State.Started&&this._state.addEvent({state:t.State.Started,time:e,offset:i}),this},t.Clock.prototype.stop=function(e){return e=this.toSeconds(e),this._state.getStateAtTime(e)!==t.State.Stopped&&this._state.setStateAtTime(t.State.Stopped,e),this},t.Clock.prototype.pause=function(e){return e=this.toSeconds(e),this._state.getStateAtTime(e)===t.State.Started&&this._state.setStateAtTime(t.State.Paused,e),this},t.Clock.prototype._loop=function(e){var i,n,s,o,r,a;if(this._loopID=requestAnimationFrame(this._boundLoop),"auto"===this._lookAhead?this.isUndef(e)||(i=(e-this._lastUpdate)/1e3,this._lastUpdate=e,ithis._nextTick;)n>this._nextTick+this._threshold&&(this._nextTick=n),a=this._nextTick,this._nextTick+=1/this.frequency.getValueAtTime(this._nextTick),this.callback(a),this.ticks++;else r===t.State.Stopped&&(this._nextTick=-1,this.ticks=0)},t.Clock.prototype.getStateAtTime=function(t){return this._state.getStateAtTime(t)},t.Clock.prototype.dispose=function(){cancelAnimationFrame(this._loopID),t.TimelineState.prototype.dispose.call(this),this._writable("frequency"),this.frequency.dispose(),this.frequency=null,this._boundLoop=t.noOp,this._nextTick=1/0,this.callback=null,this._state.dispose(),this._state=null},t.Clock}),Module(function(t){return t.EventEmitter=function(){this._events={}},t.extend(t.EventEmitter),t.EventEmitter.prototype.on=function(t,e){var i,n,s=t.split(/\W+/);for(i=0;in;n++)i[n].apply(this,e);return this},t.EventEmitter.mixin=function(e){var i,n,s,o=["on","off","trigger"];for(e._events={},i=0;i0)if(null===t.left.right)i=t.left,i.right=t.right,n=i;else{for(i=t.left.right;null!==i.right;)i=i.right;i.parent.right=i.left,n=i.parent,i.left=t.left,i.right=t.right}else if(null===t.right.left)i=t.right,i.left=t.left,n=i;else{for(i=t.right.left;null!==i.left;)i=i.left;i.parent=i.parent,i.parent.left=i.right,n=i.parent,i.left=t.left,i.right=t.right}null!==t.parent?t.isLeftChild()?t.parent.left=i:t.parent.right=i:this._setRoot(i),this._rebalance(n)}t.dispose()},t.IntervalTimeline.prototype._rotateLeft=function(t){var e=t.parent,i=t.isLeftChild(),n=t.right;t.right=n.left,n.left=t,null!==e?i?e.left=n:e.right=n:this._setRoot(n)},t.IntervalTimeline.prototype._rotateRight=function(t){var e=t.parent,i=t.isLeftChild(),n=t.left;t.left=n.right,n.right=t,null!==e?i?e.left=n:e.right=n:this._setRoot(n)},t.IntervalTimeline.prototype._rebalance=function(t){var e=t.getBalance();e>1?t.left.getBalance()<0?this._rotateLeft(t.left):this._rotateRight(t):-1>e&&(t.right.getBalance()>0?this._rotateRight(t.right):this._rotateLeft(t))},t.IntervalTimeline.prototype.getEvent=function(t){var e,i,n;if(null!==this._root&&(e=[],this._root.search(t,e),e.length>0)){for(i=e[0],
+n=1;ni.low&&(i=e[n]);return i.event}return null},t.IntervalTimeline.prototype.forEach=function(t){var e,i;if(null!==this._root)for(e=[],null!==this._root&&this._root.traverse(function(t){e.push(t)}),i=0;i=0;n--)e(i[n].event);return this},t.IntervalTimeline.prototype.forEachAfter=function(t,e){var i,n;if(t=this.toSeconds(t),null!==this._root)for(i=[],this._root.searchAfter(t,i),n=i.length-1;n>=0;n--)e(i[n].event);return this},t.IntervalTimeline.prototype.dispose=function(){var t,e=[];for(null!==this._root&&this._root.traverse(function(t){e.push(t)}),t=0;tthis.max||(null!==this.left&&this.left.search(t,e),this.low<=t&&this.high>=t&&e.push(this),this.low>t||null!==this.right&&this.right.search(t,e))},e.prototype.searchAfter=function(t,e){this.low>=t&&(e.push(this),null!==this.left&&this.left.searchAfter(t,e)),null!==this.right&&this.right.searchAfter(t,e)},e.prototype.traverse=function(t){t(this),null!==this.left&&this.left.traverse(t),null!==this.right&&this.right.traverse(t)},e.prototype.updateHeight=function(){this.height=null!==this.left&&null!==this.right?Math.max(this.left.height,this.right.height)+1:null!==this.right?this.right.height+1:null!==this.left?this.left.height+1:0},e.prototype.updateMax=function(){this.max=this.high,null!==this.left&&(this.max=Math.max(this.max,this.left.max)),null!==this.right&&(this.max=Math.max(this.max,this.right.max))},e.prototype.getBalance=function(){var t=0;return null!==this.left&&null!==this.right?t=this.left.height-this.right.height:null!==this.left?t=this.left.height+1:null!==this.right&&(t=-(this.right.height+1)),t},e.prototype.isLeftChild=function(){return null!==this.parent&&this.parent.left===this},Object.defineProperty(e.prototype,"left",{get:function(){return this._left},set:function(t){this._left=t,null!==t&&(t.parent=this),this.updateHeight(),this.updateMax()}}),Object.defineProperty(e.prototype,"right",{get:function(){return this._right},set:function(t){this._right=t,null!==t&&(t.parent=this),this.updateHeight(),this.updateMax()}}),e.prototype.dispose=function(){this.parent=null,this._left=null,this._right=null,this.event=null},t.IntervalTimeline}),Module(function(t){t.Transport=function(){t.EventEmitter.call(this),this.loop=!1,this._loopStart=0,this._loopEnd=0,this._ppq=e.defaults.PPQ,this._clock=new t.Clock({callback:this._processTick.bind(this),frequency:0}),this.bpm=this._clock.frequency,this.bpm._toUnits=this._toUnits.bind(this),this.bpm._fromUnits=this._fromUnits.bind(this),this.bpm.units=t.Type.BPM,this.bpm.value=e.defaults.bpm,this._readOnly("bpm"),this._timeSignature=e.defaults.timeSignature,this._scheduledEvents={},this._eventID=0,this._timeline=new t.Timeline,this._repeatedEvents=new t.IntervalTimeline,this._onceEvents=new t.Timeline,this._syncedSignals=[],this._swingTicks=this.toTicks(e.defaults.swingSubdivision,e.defaults.bpm,e.defaults.timeSignature),this._swingAmount=0},t.extend(t.Transport,t.EventEmitter),t.Transport.defaults={bpm:120,swing:0,swingSubdivision:"16n",timeSignature:4,loopStart:0,loopEnd:"4m",PPQ:48},t.Transport.prototype._processTick=function(t){this._swingAmount>0&&this._clock.ticks%this._ppq!==0&&this._clock.ticks%this._swingTicks===0&&(t+=this.ticksToSeconds(this._swingTicks)*this._swingAmount),this.loop&&this._clock.ticks===this._loopEnd&&(this.ticks=this._loopStart,this.trigger("loop",t));var e=this._clock.ticks;this._timeline.forEachAtTime(e,function(e){e.callback(t)}),this._repeatedEvents.forEachOverlap(e,function(i){(e-i.time)%i.interval===0&&i.callback(t)}),this._onceEvents.forEachBefore(e,function(e){e.callback(t)}),this._onceEvents.cancelBefore(e)},t.Transport.prototype.schedule=function(t,e){var i={time:this.toTicks(e),callback:t},n=this._eventID++;return this._scheduledEvents[n.toString()]={event:i,timeline:this._timeline},this._timeline.addEvent(i),n},t.Transport.prototype.scheduleRepeat=function(t,e,i,n){var s,o;if(0>=e)throw new Error("repeat events must have an interval larger than 0");return s={time:this.toTicks(i),duration:this.toTicks(this.defaultArg(n,1/0)),interval:this.toTicks(e),callback:t},o=this._eventID++,this._scheduledEvents[o.toString()]={event:s,timeline:this._repeatedEvents},this._repeatedEvents.addEvent(s),o},t.Transport.prototype.scheduleOnce=function(t,e){var i={time:this.toTicks(e),callback:t},n=this._eventID++;return this._scheduledEvents[n.toString()]={event:i,timeline:this._onceEvents},this._onceEvents.addEvent(i),n},t.Transport.prototype.clear=function(t){if(this._scheduledEvents.hasOwnProperty(t)){var e=this._scheduledEvents[t.toString()];e.timeline.removeEvent(e.event),delete this._scheduledEvents[t.toString()]}return this},t.Transport.prototype.cancel=function(t){return t=this.defaultArg(t,0),t=this.toTicks(t),this._timeline.cancel(t),this._onceEvents.cancel(t),this._repeatedEvents.cancel(t),this},t.Transport.prototype.nextBeat=function(t){var e,i;t=this.defaultArg(t,"4n"),e=this.toTicks(t),i=transportTicks%e},Object.defineProperty(t.Transport.prototype,"state",{get:function(){return this._clock.getStateAtTime(this.now())}}),t.Transport.prototype.start=function(t,e){return t=this.toSeconds(t),e=this.isUndef(e)?this.defaultArg(e,this._clock.ticks):this.toTicks(e),this._clock.start(t,e),this.trigger("start",t,this.ticksToSeconds(e)),this},t.Transport.prototype.stop=function(t){return t=this.toSeconds(t),this._clock.stop(t),this.trigger("stop",t),this},t.Transport.prototype.pause=function(t){return t=this.toSeconds(t),this._clock.pause(t),this.trigger("pause",t),this},Object.defineProperty(t.Transport.prototype,"timeSignature",{get:function(){return this._timeSignature},set:function(t){Array.isArray(t)&&(t=t[0]/t[1]*4),this._timeSignature=t}}),Object.defineProperty(t.Transport.prototype,"loopStart",{get:function(){return this.ticksToSeconds(this._loopStart)},set:function(t){this._loopStart=this.toTicks(t)}}),Object.defineProperty(t.Transport.prototype,"loopEnd",{get:function(){return this.ticksToSeconds(this._loopEnd)},set:function(t){this._loopEnd=this.toTicks(t)}}),t.Transport.prototype.setLoopPoints=function(t,e){return this.loopStart=t,this.loopEnd=e,this},Object.defineProperty(t.Transport.prototype,"swing",{get:function(){return 2*this._swingAmount},set:function(t){this._swingAmount=.5*t}}),Object.defineProperty(t.Transport.prototype,"swingSubdivision",{get:function(){return this.toNotation(this._swingTicks+"i")},set:function(t){this._swingTicks=this.toTicks(t)}}),Object.defineProperty(t.Transport.prototype,"position",{get:function(){var t,e=this.ticks/this._ppq,i=Math.floor(e/this._timeSignature),n=e%1*4;return n%1>0&&(n=n.toFixed(3)),e=Math.floor(e)%this._timeSignature,t=[i,e,n],t.join(":")},set:function(t){var e=this.toTicks(t);this.ticks=e}}),Object.defineProperty(t.Transport.prototype,"progress",{get:function(){return this.loop?(this.ticks-this._loopStart)/(this._loopEnd-this._loopStart):0}}),Object.defineProperty(t.Transport.prototype,"ticks",{get:function(){return this._clock.ticks},set:function(t){this._clock.ticks=t}}),Object.defineProperty(t.Transport.prototype,"PPQ",{get:function(){return this._ppq},set:function(t){this._ppq=t,this.bpm.value=this.bpm.value}}),t.Transport.prototype._fromUnits=function(t){return 1/(60/t/this.PPQ)},t.Transport.prototype._toUnits=function(t){return t/this.PPQ*60},t.Transport.prototype.syncSignal=function(e,i){i||(i=0!==e._param.value?e._param.value/this.bpm._param.value:0);var n=new t.Gain(i);return this.bpm.chain(n,e._param),this._syncedSignals.push({ratio:n,signal:e,initial:e._param.value}),e._param.value=0,this},t.Transport.prototype.unsyncSignal=function(t){var e,i;for(e=this._syncedSignals.length-1;e>=0;e--)i=this._syncedSignals[e],i.signal===t&&(i.ratio.dispose(),i.signal._param.value=i.initial,this._syncedSignals.splice(e,1));return this},t.Transport.prototype.dispose=function(){return t.EventEmitter.prototype.dispose.call(this),this._clock.dispose(),this._clock=null,this._writable("bpm"),this.bpm=null,this._timeline.dispose(),this._timeline=null,this._onceEvents.dispose(),this._onceEvents=null,this._repeatedEvents.dispose(),this._repeatedEvents=null,this},t.Transport.prototype.setInterval=function(e,i){return console.warn("This method is deprecated. Use Tone.Transport.scheduleRepeat instead."),t.Transport.scheduleRepeat(e,i)},t.Transport.prototype.clearInterval=function(e){return console.warn("This method is deprecated. Use Tone.Transport.clear instead."),t.Transport.clear(e)},t.Transport.prototype.setTimeout=function(e,i){return console.warn("This method is deprecated. Use Tone.Transport.scheduleOnce instead."),t.Transport.scheduleOnce(e,i)},t.Transport.prototype.clearTimeout=function(e){return console.warn("This method is deprecated. Use Tone.Transport.clear instead."),t.Transport.clear(e)},t.Transport.prototype.setTimeline=function(e,i){return console.warn("This method is deprecated. Use Tone.Transport.schedule instead."),t.Transport.schedule(e,i)},t.Transport.prototype.clearTimeline=function(e){return console.warn("This method is deprecated. Use Tone.Transport.clear instead."),t.Transport.clear(e)};var e=t.Transport;return t._initAudioContext(function(){if("function"==typeof t.Transport)t.Transport=new t.Transport;else{t.Transport.stop();var i=t.Transport.get();t.Transport.dispose(),e.call(t.Transport),t.Transport.set(i)}}),t.Transport}),Module(function(t){return t.Volume=function(){var e=this.optionsObject(arguments,["value"],t.Volume.defaults);t.Gain.call(this,e.value,t.Type.Decibels)},t.extend(t.Volume,t.Gain),t.Volume.defaults={value:0},t.Volume}),Module(function(t){return t.Source=function(e){t.call(this),e=this.defaultArg(e,t.Source.defaults),this.volume=this.output=new t.Volume(e.volume),this._readOnly("volume"),this._state=new t.TimelineState(t.State.Stopped),this._syncStart=function(t,e){t=this.toSeconds(t),t+=this.toSeconds(this._startDelay),this.start(t,e)}.bind(this),this._syncStop=this.stop.bind(this),this._startDelay=0,this.output.channelCount=2,this.output.channelCountMode="explicit"},t.extend(t.Source),t.Source.defaults={volume:0},Object.defineProperty(t.Source.prototype,"state",{get:function(){return this._state.getStateAtTime(this.now())}}),t.Source.prototype.start=function(e){return e=this.toSeconds(e),(this._state.getStateAtTime(e)!==t.State.Started||this.retrigger)&&(this._state.setStateAtTime(t.State.Started,e),this._start&&this._start.apply(this,arguments)),this},t.Source.prototype.stop=function(e){return e=this.toSeconds(e),this._state.getStateAtTime(e)===t.State.Started&&(this._state.setStateAtTime(t.State.Stopped,e),this._stop&&this._stop.apply(this,arguments)),this},t.Source.prototype.sync=function(e){return this._startDelay=this.defaultArg(e,0),t.Transport.on("start",this._syncStart),t.Transport.on("stop pause",this._syncStop),this},t.Source.prototype.unsync=function(){return this._startDelay=0,t.Transport.off("start",this._syncStart),t.Transport.off("stop pause",this._syncStop),this},t.Source.prototype.dispose=function(){this.stop(),t.prototype.dispose.call(this),this.unsync(),this._writable("volume"),this.volume.dispose(),this.volume=null,this._state.dispose(),this._state=null,this._syncStart=null,this._syncStart=null},t.Source}),Module(function(t){return t.Oscillator=function(){var e=this.optionsObject(arguments,["frequency","type"],t.Oscillator.defaults);t.Source.call(this,e),this._oscillator=null,this.frequency=new t.Signal(e.frequency,t.Type.Frequency),this.detune=new t.Signal(e.detune,t.Type.Cents),this._wave=null,this._partials=this.defaultArg(e.partials,[1]),this._phase=e.phase,this._type=null,this.type=e.type,this.phase=this._phase,this._readOnly(["frequency","detune"])},t.extend(t.Oscillator,t.Source),t.Oscillator.defaults={type:"sine",frequency:440,detune:0,phase:0},t.Oscillator.Type={Sine:"sine",Triangle:"triangle",Sawtooth:"sawtooth",Square:"square",Custom:"custom"},t.Oscillator.prototype._start=function(t){this._oscillator=this.context.createOscillator(),this._oscillator.setPeriodicWave(this._wave),this._oscillator.connect(this.output),this.frequency.connect(this._oscillator.frequency),this.detune.connect(this._oscillator.detune),this._oscillator.start(this.toSeconds(t))},t.Oscillator.prototype._stop=function(t){return this._oscillator&&(this._oscillator.stop(this.toSeconds(t)),this._oscillator=null),this},t.Oscillator.prototype.syncFrequency=function(){return t.Transport.syncSignal(this.frequency),this},t.Oscillator.prototype.unsyncFrequency=function(){return t.Transport.unsyncSignal(this.frequency),this},Object.defineProperty(t.Oscillator.prototype,"type",{get:function(){return this._type},set:function(t){var e=this._getRealImaginary(t,this._phase),i=this.context.createPeriodicWave(e[0],e[1]);this._wave=i,null!==this._oscillator&&this._oscillator.setPeriodicWave(this._wave),this._type=t}}),t.Oscillator.prototype._getRealImaginary=function(e,i){var n,s,o,r,a=4096,l=a/2,h=new Float32Array(l),u=new Float32Array(l),c=1;for(e===t.Oscillator.Type.Custom?(c=this._partials.length+1,l=c):(n=/^(sine|triangle|square|sawtooth)(\d+)$/.exec(e),n&&(c=parseInt(n[2])+1,e=n[1],c=Math.max(c,2),l=c)),s=1;l>s;++s){switch(o=2/(s*Math.PI),e){case t.Oscillator.Type.Sine:r=c>=s?1:0;break;case t.Oscillator.Type.Square:r=1&s?2*o:0;break;case t.Oscillator.Type.Sawtooth:r=o*(1&s?1:-1);break;case t.Oscillator.Type.Triangle:r=1&s?2*o*o*(s-1>>1&1?-1:1):0;break;case t.Oscillator.Type.Custom:r=this._partials[s-1];break;default:throw new Error("invalid oscillator type: "+e)}0!==r?(h[s]=-r*Math.sin(i*s),u[s]=r*Math.cos(i*s)):(h[s]=0,u[s]=0)}return[h,u]},t.Oscillator.prototype._inverseFFT=function(t,e,i){var n,s=0,o=t.length;for(n=0;o>n;n++)s+=t[n]*Math.cos(n*i)+e[n]*Math.sin(n*i);return s},t.Oscillator.prototype._getInitialValue=function(){var t,e=this._getRealImaginary(this._type,0),i=e[0],n=e[1],s=0,o=2*Math.PI;for(t=0;8>t;t++)s=Math.max(this._inverseFFT(i,n,t/8*o),s);return-this._inverseFFT(i,n,this._phase)/s},Object.defineProperty(t.Oscillator.prototype,"partials",{get:function(){return this._type!==t.Oscillator.Type.Custom?[]:this._partials},set:function(e){this._partials=e,this.type=t.Oscillator.Type.Custom}}),Object.defineProperty(t.Oscillator.prototype,"phase",{get:function(){return this._phase*(180/Math.PI)},set:function(t){this._phase=t*Math.PI/180,this.type=this._type}}),t.Oscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),null!==this._oscillator&&(this._oscillator.disconnect(),this._oscillator=null),this._wave=null,this._writable(["frequency","detune"]),this.frequency.dispose(),this.frequency=null,this.detune.dispose(),this.detune=null,this._partials=null,this},t.Oscillator}),Module(function(t){return t.LFO=function(){var e=this.optionsObject(arguments,["frequency","min","max"],t.LFO.defaults);this._oscillator=new t.Oscillator({frequency:e.frequency,type:e.type}),this.frequency=this._oscillator.frequency,this.amplitude=this._oscillator.volume,this.amplitude.units=t.Type.NormalRange,this.amplitude.value=e.amplitude,this._stoppedSignal=new t.Signal(0,t.Type.AudioRange),this._stoppedValue=0,this._a2g=new t.AudioToGain,this._scaler=this.output=new t.Scale(e.min,e.max),this._units=t.Type.Default,this.units=e.units,this._oscillator.chain(this._a2g,this._scaler),this._stoppedSignal.connect(this._a2g),this._readOnly(["amplitude","frequency"]),this.phase=e.phase},t.extend(t.LFO,t.Oscillator),t.LFO.defaults={type:"sine",min:0,max:1,phase:0,frequency:"4n",amplitude:1,units:t.Type.Default},t.LFO.prototype.start=function(t){return t=this.toSeconds(t),this._stoppedSignal.setValueAtTime(0,t),this._oscillator.start(t),this},t.LFO.prototype.stop=function(t){return t=this.toSeconds(t),this._stoppedSignal.setValueAtTime(this._stoppedValue,t),this._oscillator.stop(t),this},t.LFO.prototype.sync=function(t){return this._oscillator.sync(t),this._oscillator.syncFrequency(),this},t.LFO.prototype.unsync=function(){return this._oscillator.unsync(),this._oscillator.unsyncFrequency(),this},Object.defineProperty(t.LFO.prototype,"min",{get:function(){return this._toUnits(this._scaler.min)},set:function(t){t=this._fromUnits(t),this._scaler.min=t}}),Object.defineProperty(t.LFO.prototype,"max",{get:function(){return this._toUnits(this._scaler.max)},set:function(t){t=this._fromUnits(t),this._scaler.max=t}}),Object.defineProperty(t.LFO.prototype,"type",{get:function(){return this._oscillator.type},set:function(t){this._oscillator.type=t,this._stoppedValue=this._oscillator._getInitialValue(),this._stoppedSignal.value=this._stoppedValue}}),Object.defineProperty(t.LFO.prototype,"phase",{get:function(){return this._oscillator.phase},set:function(t){this._oscillator.phase=t,this._stoppedValue=this._oscillator._getInitialValue(),this._stoppedSignal.value=this._stoppedValue}}),Object.defineProperty(t.LFO.prototype,"units",{get:function(){return this._units},set:function(t){var e=this.min,i=this.max;this._units=t,this.min=e,this.max=i}}),Object.defineProperty(t.LFO.prototype,"state",{get:function(){return this._oscillator.state}}),t.LFO.prototype.connect=function(e){return(e.constructor===t.Signal||e.constructor===t.Param||e.constructor===t.TimelineSignal)&&(this.convert=e.convert,this.units=e.units),t.Signal.prototype.connect.apply(this,arguments),this},t.LFO.prototype._fromUnits=t.Param.prototype._fromUnits,t.LFO.prototype._toUnits=t.Param.prototype._toUnits,t.LFO.prototype.dispose=function(){return t.prototype.dispose.call(this),this._writable(["amplitude","frequency"]),this._oscillator.dispose(),this._oscillator=null,this._stoppedSignal.dispose(),this._stoppedSignal=null,this._scaler.dispose(),this._scaler=null,this._a2g.dispose(),this._a2g=null,this.frequency=null,this.amplitude=null,this},t.LFO}),Module(function(t){return t.Limiter=function(){var e=this.optionsObject(arguments,["threshold"],t.Limiter.defaults);this._compressor=this.input=this.output=new t.Compressor({attack:.001,decay:.001,threshold:e.threshold}),this.threshold=this._compressor.threshold,this._readOnly("threshold")},t.extend(t.Limiter),t.Limiter.defaults={threshold:-12},t.Limiter.prototype.dispose=function(){return t.prototype.dispose.call(this),this._compressor.dispose(),this._compressor=null,this._writable("threshold"),this.threshold=null,this},t.Limiter}),Module(function(t){return t.LowpassCombFilter=function(){t.call(this);var e=this.optionsObject(arguments,["delayTime","resonance","dampening"],t.LowpassCombFilter.defaults);this._delay=this.input=this.context.createDelay(1),this.delayTime=new t.Signal(e.delayTime,t.Type.Time),this._lowpass=this.output=this.context.createBiquadFilter(),this._lowpass.Q.value=0,this._lowpass.type="lowpass",this.dampening=new t.Param({param:this._lowpass.frequency,units:t.Type.Frequency,value:e.dampening}),this._feedback=this.context.createGain(),this.resonance=new t.Param({param:this._feedback.gain,units:t.Type.NormalRange,value:e.resonance}),this._delay.chain(this._lowpass,this._feedback,this._delay),this.delayTime.connect(this._delay.delayTime),this._readOnly(["dampening","resonance","delayTime"])},t.extend(t.LowpassCombFilter),t.LowpassCombFilter.defaults={delayTime:.1,resonance:.5,dampening:3e3},t.LowpassCombFilter.prototype.dispose=function(){return t.prototype.dispose.call(this),this._writable(["dampening","resonance","delayTime"]),this.dampening.dispose(),this.dampening=null,this.resonance.dispose(),this.resonance=null,this._delay.disconnect(),this._delay=null,this._lowpass.disconnect(),this._lowpass=null,this._feedback.disconnect(),this._feedback=null,this.delayTime.dispose(),this.delayTime=null,this},t.LowpassCombFilter}),Module(function(t){return t.Merge=function(){t.call(this,2,0),this.left=this.input[0]=this.context.createGain(),this.right=this.input[1]=this.context.createGain(),this._merger=this.output=this.context.createChannelMerger(2),this.left.connect(this._merger,0,0),this.right.connect(this._merger,0,1)},t.extend(t.Merge),t.Merge.prototype.dispose=function(){return t.prototype.dispose.call(this),this.left.disconnect(),this.left=null,this.right.disconnect(),this.right=null,this._merger.disconnect(),this._merger=null,this},t.Merge}),Module(function(t){t.Master=function(){t.call(this),this._unmutedVolume=1,this._muted=!1,this.volume=this.output=new t.Volume,this._readOnly("volume"),this.input.chain(this.output,this.context.destination)},t.extend(t.Master),t.Master.defaults={volume:0,mute:!1},Object.defineProperty(t.Master.prototype,"mute",{get:function(){return this._muted},set:function(t){!this._muted&&t?(this._unmutedVolume=this.volume.value,this.volume.value=-(1/0)):this._muted&&!t&&(this.volume.value=this._unmutedVolume),this._muted=t}}),t.Master.prototype.chain=function(){this.input.disconnect(),this.input.chain.apply(this.input,arguments),arguments[arguments.length-1].connect(this.output)},t.Master.prototype.dispose=function(){t.prototype.dispose.call(this),this._writable("volume"),this.volume.dispose(),this.volume=null},t.prototype.toMaster=function(){return this.connect(t.Master),this},AudioNode.prototype.toMaster=function(){return this.connect(t.Master),this};var e=t.Master;return t._initAudioContext(function(){t.prototype.isUndef(t.Master)?(e.prototype.dispose.call(t.Master),e.call(t.Master)):t.Master=new e}),t.Master}),Module(function(t){return t.Meter=function(){var e,i,n=this.optionsObject(arguments,["channels","smoothing"],t.Meter.defaults);for(t.call(this),this._channels=n.channels,this.smoothing=n.smoothing,this.clipMemory=n.clipMemory,this.clipLevel=n.clipLevel,this._volume=new Array(this._channels),this._values=new Array(this._channels),e=0;er;r++)o=i[r],s+=o,n+=o*o;a=s/h,l=Math.sqrt(n/h),l>.9&&(this._lastClip[e]=Date.now()),this._volume[e]=Math.max(l,this._volume[e]*u),this._values[e]=a}},t.Meter.prototype.getLevel=function(t){t=this.defaultArg(t,0);var e=this._volume[t];return 1e-5>e?0:e},t.Meter.prototype.getValue=function(t){return t=this.defaultArg(t,0),this._values[t]},t.Meter.prototype.getDb=function(t){return this.gainToDb(this.getLevel(t))},t.Meter.prototype.isClipped=function(t){return t=this.defaultArg(t,0),Date.now()-this._lastClip[t]<1e3*this._clipMemory},t.Meter.prototype.dispose=function(){return t.prototype.dispose.call(this),this._jsNode.disconnect(),this._jsNode.onaudioprocess=null,this._jsNode=null,this._volume=null,this._values=null,this._lastClip=null,this},t.Meter}),Module(function(t){return t.Split=function(){t.call(this,0,2),this._splitter=this.input=this.context.createChannelSplitter(2),this.left=this.output[0]=this.context.createGain(),this.right=this.output[1]=this.context.createGain(),this._splitter.connect(this.left,0,0),this._splitter.connect(this.right,1,0)},t.extend(t.Split),t.Split.prototype.dispose=function(){return t.prototype.dispose.call(this),this._splitter.disconnect(),this.left.disconnect(),this.right.disconnect(),this.left=null,this.right=null,this._splitter=null,this},t.Split}),Module(function(t){t.MidSideSplit=function(){t.call(this,0,2),this._split=this.input=new t.Split,this.mid=this.output[0]=new t.Expr("($0 + $1) * $2"),this.side=this.output[1]=new t.Expr("($0 - $1) * $2"),this._split.connect(this.mid,0,0),this._split.connect(this.mid,1,1),this._split.connect(this.side,0,0),this._split.connect(this.side,1,1),e.connect(this.mid,0,2),e.connect(this.side,0,2)},t.extend(t.MidSideSplit);var e=null;return t._initAudioContext(function(){e=new t.Signal(1/Math.sqrt(2))}),t.MidSideSplit.prototype.dispose=function(){return t.prototype.dispose.call(this),this.mid.dispose(),this.mid=null,this.side.dispose(),this.side=null,this._split.dispose(),this._split=null,this},t.MidSideSplit}),Module(function(t){t.MidSideMerge=function(){t.call(this,2,0),this.mid=this.input[0]=this.context.createGain(),this._left=new t.Expr("($0 + $1) * $2"),this.side=this.input[1]=this.context.createGain(),this._right=new t.Expr("($0 - $1) * $2"),this._merge=this.output=new t.Merge,this.mid.connect(this._left,0,0),this.side.connect(this._left,0,1),this.mid.connect(this._right,0,0),this.side.connect(this._right,0,1),this._left.connect(this._merge,0,0),this._right.connect(this._merge,0,1),e.connect(this._left,0,2),e.connect(this._right,0,2)},t.extend(t.MidSideMerge);var e=null;return t._initAudioContext(function(){e=new t.Signal(1/Math.sqrt(2))}),t.MidSideMerge.prototype.dispose=function(){return t.prototype.dispose.call(this),this.mid.disconnect(),this.mid=null,this.side.disconnect(),this.side=null,this._left.dispose(),this._left=null,this._right.dispose(),this._right=null,this._merge.dispose(),this._merge=null,this},t.MidSideMerge}),Module(function(t){return t.MidSideCompressor=function(e){e=this.defaultArg(e,t.MidSideCompressor.defaults),this._midSideSplit=this.input=new t.MidSideSplit,this._midSideMerge=this.output=new t.MidSideMerge,this.mid=new t.Compressor(e.mid),this.side=new t.Compressor(e.side),this._midSideSplit.mid.chain(this.mid,this._midSideMerge.mid),this._midSideSplit.side.chain(this.side,this._midSideMerge.side),this._readOnly(["mid","side"])},t.extend(t.MidSideCompressor),t.MidSideCompressor.defaults={mid:{ratio:3,threshold:-24,release:.03,attack:.02,knee:16},side:{ratio:6,threshold:-30,release:.25,attack:.03,knee:10}},t.MidSideCompressor.prototype.dispose=function(){return t.prototype.dispose.call(this),this._writable(["mid","side"]),this.mid.dispose(),this.mid=null,this.side.dispose(),this.side=null,this._midSideSplit.dispose(),this._midSideSplit=null,this._midSideMerge.dispose(),this._midSideMerge=null,this},t.MidSideCompressor}),Module(function(t){return t.Mono=function(){t.call(this,1,0),this._merge=this.output=new t.Merge,this.input.connect(this._merge,0,0),this.input.connect(this._merge,0,1),this.input.gain.value=this.dbToGain(-10)},t.extend(t.Mono),t.Mono.prototype.dispose=function(){return t.prototype.dispose.call(this),this._merge.dispose(),this._merge=null,this},t.Mono}),Module(function(t){return t.MultibandCompressor=function(e){e=this.defaultArg(arguments,t.MultibandCompressor.defaults),this._splitter=this.input=new t.MultibandSplit({lowFrequency:e.lowFrequency,highFrequency:e.highFrequency}),this.lowFrequency=this._splitter.lowFrequency,this.highFrequency=this._splitter.highFrequency,this.output=this.context.createGain(),this.low=new t.Compressor(e.low),this.mid=new t.Compressor(e.mid),this.high=new t.Compressor(e.high),this._splitter.low.chain(this.low,this.output),this._splitter.mid.chain(this.mid,this.output),this._splitter.high.chain(this.high,this.output),this._readOnly(["high","mid","low","highFrequency","lowFrequency"])},t.extend(t.MultibandCompressor),t.MultibandCompressor.defaults={low:t.Compressor.defaults,mid:t.Compressor.defaults,high:t.Compressor.defaults,lowFrequency:250,highFrequency:2e3},t.MultibandCompressor.prototype.dispose=function(){return t.prototype.dispose.call(this),this._splitter.dispose(),this._writable(["high","mid","low","highFrequency","lowFrequency"]),this.low.dispose(),this.mid.dispose(),this.high.dispose(),this._splitter=null,this.low=null,this.mid=null,this.high=null,this.lowFrequency=null,this.highFrequency=null,this},t.MultibandCompressor}),Module(function(t){return t.GainToAudio=function(){this._norm=this.input=this.output=new t.WaveShaper(function(t){return 2*Math.abs(t)-1})},t.extend(t.GainToAudio,t.SignalBase),t.GainToAudio.prototype.dispose=function(){return t.prototype.dispose.call(this),this._norm.dispose(),this._norm=null,this},t.GainToAudio}),Module(function(t){return t.Panner=function(e){t.call(this),this._hasStereoPanner=this.isFunction(this.context.createStereoPanner),this._hasStereoPanner?(this._panner=this.input=this.output=this.context.createStereoPanner(),this.pan=new t.Signal(0,t.Type.NormalRange),this._scalePan=new t.GainToAudio,this.pan.chain(this._scalePan,this._panner.pan)):(this._crossFade=new t.CrossFade,this._merger=this.output=new t.Merge,this._splitter=this.input=new t.Split,this.pan=this._crossFade.fade,this._splitter.connect(this._crossFade,0,0),this._splitter.connect(this._crossFade,1,1),this._crossFade.a.connect(this._merger,0,0),this._crossFade.b.connect(this._merger,0,1)),this.pan.value=this.defaultArg(e,.5),this._readOnly("pan")},t.extend(t.Panner),t.Panner.prototype.dispose=function(){return t.prototype.dispose.call(this),this._writable("pan"),this._hasStereoPanner?(this._panner.disconnect(),this._panner=null,this.pan.dispose(),this.pan=null,this._scalePan.dispose(),this._scalePan=null):(this._crossFade.dispose(),this._crossFade=null,this._splitter.dispose(),this._splitter=null,this._merger.dispose(),this._merger=null,this.pan=null),this},t.Panner}),Module(function(t){return t.PanVol=function(){var e=this.optionsObject(arguments,["pan","volume"],t.PanVol.defaults);this._panner=this.input=new t.Panner(e.pan),this.pan=this._panner.pan,this.volume=this.output=new t.Volume(e.volume),this._panner.connect(this.volume),this._readOnly(["pan","volume"])},t.extend(t.PanVol),t.PanVol.defaults={pan:.5,volume:0},t.PanVol.prototype.dispose=function(){return t.prototype.dispose.call(this),this._writable(["pan","volume"]),this._panner.dispose(),this._panner=null,this.pan=null,this.volume.dispose(),this.volume=null,this},t.PanVol}),Module(function(t){return t.ScaledEnvelope=function(){var e=this.optionsObject(arguments,["attack","decay","sustain","release"],t.Envelope.defaults);t.Envelope.call(this,e),e=this.defaultArg(e,t.ScaledEnvelope.defaults),this._exp=this.output=new t.Pow(e.exponent),this._scale=this.output=new t.Scale(e.min,e.max),this._sig.chain(this._exp,this._scale)},t.extend(t.ScaledEnvelope,t.Envelope),t.ScaledEnvelope.defaults={min:0,max:1,exponent:1},Object.defineProperty(t.ScaledEnvelope.prototype,"min",{get:function(){return this._scale.min},set:function(t){this._scale.min=t}}),Object.defineProperty(t.ScaledEnvelope.prototype,"max",{get:function(){return this._scale.max},set:function(t){this._scale.max=t}}),Object.defineProperty(t.ScaledEnvelope.prototype,"exponent",{get:function(){return this._exp.value},set:function(t){this._exp.value=t}}),t.ScaledEnvelope.prototype.dispose=function(){return t.Envelope.prototype.dispose.call(this),this._scale.dispose(),this._scale=null,this._exp.dispose(),this._exp=null,this},t.ScaledEnvelope}),Module(function(t){return t.PulseOscillator=function(){var e=this.optionsObject(arguments,["frequency","width"],t.Oscillator.defaults);t.Source.call(this,e),this.width=new t.Signal(e.width,t.Type.NormalRange),this._widthGate=this.context.createGain(),this._sawtooth=new t.Oscillator({frequency:e.frequency,detune:e.detune,type:"sawtooth",phase:e.phase}),this.frequency=this._sawtooth.frequency,this.detune=this._sawtooth.detune,this._thresh=new t.WaveShaper(function(t){return 0>t?-1:1}),this._sawtooth.chain(this._thresh,this.output),this.width.chain(this._widthGate,this._thresh),this._readOnly(["width","frequency","detune"])},t.extend(t.PulseOscillator,t.Oscillator),t.PulseOscillator.defaults={
+frequency:440,detune:0,phase:0,width:.2},t.PulseOscillator.prototype._start=function(t){t=this.toSeconds(t),this._sawtooth.start(t),this._widthGate.gain.setValueAtTime(1,t)},t.PulseOscillator.prototype._stop=function(t){t=this.toSeconds(t),this._sawtooth.stop(t),this._widthGate.gain.setValueAtTime(0,t)},Object.defineProperty(t.PulseOscillator.prototype,"phase",{get:function(){return this._sawtooth.phase},set:function(t){this._sawtooth.phase=t}}),Object.defineProperty(t.PulseOscillator.prototype,"type",{get:function(){return"pulse"}}),t.PulseOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._sawtooth.dispose(),this._sawtooth=null,this._writable(["width","frequency","detune"]),this.width.dispose(),this.width=null,this._widthGate.disconnect(),this._widthGate=null,this._widthGate=null,this._thresh.disconnect(),this._thresh=null,this.frequency=null,this.detune=null,this},t.PulseOscillator}),Module(function(t){return t.PWMOscillator=function(){var e=this.optionsObject(arguments,["frequency","modulationFrequency"],t.PWMOscillator.defaults);t.Source.call(this,e),this._pulse=new t.PulseOscillator(e.modulationFrequency),this._pulse._sawtooth.type="sine",this._modulator=new t.Oscillator({frequency:e.frequency,detune:e.detune,phase:e.phase}),this._scale=new t.Multiply(1.01),this.frequency=this._modulator.frequency,this.detune=this._modulator.detune,this.modulationFrequency=this._pulse.frequency,this._modulator.chain(this._scale,this._pulse.width),this._pulse.connect(this.output),this._readOnly(["modulationFrequency","frequency","detune"])},t.extend(t.PWMOscillator,t.Oscillator),t.PWMOscillator.defaults={frequency:440,detune:0,phase:0,modulationFrequency:.4},t.PWMOscillator.prototype._start=function(t){t=this.toSeconds(t),this._modulator.start(t),this._pulse.start(t)},t.PWMOscillator.prototype._stop=function(t){t=this.toSeconds(t),this._modulator.stop(t),this._pulse.stop(t)},Object.defineProperty(t.PWMOscillator.prototype,"type",{get:function(){return"pwm"}}),Object.defineProperty(t.PWMOscillator.prototype,"phase",{get:function(){return this._modulator.phase},set:function(t){this._modulator.phase=t}}),t.PWMOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._pulse.dispose(),this._pulse=null,this._scale.dispose(),this._scale=null,this._modulator.dispose(),this._modulator=null,this._writable(["modulationFrequency","frequency","detune"]),this.frequency=null,this.detune=null,this.modulationFrequency=null,this},t.PWMOscillator}),Module(function(t){t.OmniOscillator=function(){var e=this.optionsObject(arguments,["frequency","type"],t.OmniOscillator.defaults);t.Source.call(this,e),this.frequency=new t.Signal(e.frequency,t.Type.Frequency),this.detune=new t.Signal(e.detune,t.Type.Cents),this._sourceType=void 0,this._oscillator=null,this.type=e.type,this.phase=e.phase,this._readOnly(["frequency","detune"])},t.extend(t.OmniOscillator,t.Oscillator),t.OmniOscillator.defaults={frequency:440,detune:0,type:"sine",phase:0,width:.4,modulationFrequency:.4};var e={PulseOscillator:"PulseOscillator",PWMOscillator:"PWMOscillator",Oscillator:"Oscillator"};return t.OmniOscillator.prototype._start=function(t){this._oscillator.start(t)},t.OmniOscillator.prototype._stop=function(t){this._oscillator.stop(t)},Object.defineProperty(t.OmniOscillator.prototype,"type",{get:function(){return this._oscillator.type},set:function(i){if(0===i.indexOf("sine")||0===i.indexOf("square")||0===i.indexOf("triangle")||0===i.indexOf("sawtooth"))this._sourceType!==e.Oscillator&&(this._sourceType=e.Oscillator,this._createNewOscillator(t.Oscillator)),this._oscillator.type=i;else if("pwm"===i)this._sourceType!==e.PWMOscillator&&(this._sourceType=e.PWMOscillator,this._createNewOscillator(t.PWMOscillator));else{if("pulse"!==i)throw new Error("Tone.OmniOscillator does not support type "+i);this._sourceType!==e.PulseOscillator&&(this._sourceType=e.PulseOscillator,this._createNewOscillator(t.PulseOscillator))}}}),t.OmniOscillator.prototype._createNewOscillator=function(e){var i,n=this.now()+this.blockTime;null!==this._oscillator&&(i=this._oscillator,i.stop(n),setTimeout(function(){i.dispose(),i=null},1e3*this.blockTime)),this._oscillator=new e,this.frequency.connect(this._oscillator.frequency),this.detune.connect(this._oscillator.detune),this._oscillator.connect(this.output),this.state===t.State.Started&&this._oscillator.start(n)},Object.defineProperty(t.OmniOscillator.prototype,"phase",{get:function(){return this._oscillator.phase},set:function(t){this._oscillator.phase=t}}),Object.defineProperty(t.OmniOscillator.prototype,"width",{get:function(){return this._sourceType===e.PulseOscillator?this._oscillator.width:void 0}}),Object.defineProperty(t.OmniOscillator.prototype,"modulationFrequency",{get:function(){return this._sourceType===e.PWMOscillator?this._oscillator.modulationFrequency:void 0}}),t.OmniOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._writable(["frequency","detune"]),this.detune.dispose(),this.detune=null,this.frequency.dispose(),this.frequency=null,this._oscillator.dispose(),this._oscillator=null,this._sourceType=null,this},t.OmniOscillator}),Module(function(t){return t.Instrument=function(e){e=this.defaultArg(e,t.Instrument.defaults),this.volume=this.output=new t.Volume(e.volume),this._readOnly("volume")},t.extend(t.Instrument),t.Instrument.defaults={volume:0},t.Instrument.prototype.triggerAttack=t.noOp,t.Instrument.prototype.triggerRelease=t.noOp,t.Instrument.prototype.triggerAttackRelease=function(t,e,i,n){return i=this.toSeconds(i),e=this.toSeconds(e),this.triggerAttack(t,i,n),this.triggerRelease(i+e),this},t.Instrument.prototype.dispose=function(){return t.prototype.dispose.call(this),this._writable(["volume"]),this.volume.dispose(),this.volume=null,this},t.Instrument}),Module(function(t){return t.Monophonic=function(e){e=this.defaultArg(e,t.Monophonic.defaults),t.Instrument.call(this,e),this.portamento=e.portamento},t.extend(t.Monophonic,t.Instrument),t.Monophonic.defaults={portamento:0},t.Monophonic.prototype.triggerAttack=function(t,e,i){return e=this.toSeconds(e),this._triggerEnvelopeAttack(e,i),this.setNote(t,e),this},t.Monophonic.prototype.triggerRelease=function(t){return this._triggerEnvelopeRelease(t),this},t.Monophonic.prototype._triggerEnvelopeAttack=function(){},t.Monophonic.prototype._triggerEnvelopeRelease=function(){},t.Monophonic.prototype.setNote=function(t,e){var i,n;return e=this.toSeconds(e),this.portamento>0?(i=this.frequency.value,this.frequency.setValueAtTime(i,e),n=this.toSeconds(this.portamento),this.frequency.exponentialRampToValueAtTime(t,e+n)):this.frequency.setValueAtTime(t,e),this},t.Monophonic}),Module(function(t){return t.MonoSynth=function(e){e=this.defaultArg(e,t.MonoSynth.defaults),t.Monophonic.call(this,e),this.oscillator=new t.OmniOscillator(e.oscillator),this.frequency=this.oscillator.frequency,this.detune=this.oscillator.detune,this.filter=new t.Filter(e.filter),this.filterEnvelope=new t.ScaledEnvelope(e.filterEnvelope),this.envelope=new t.AmplitudeEnvelope(e.envelope),this.oscillator.chain(this.filter,this.envelope,this.output),this.oscillator.start(),this.filterEnvelope.connect(this.filter.frequency),this._readOnly(["oscillator","frequency","detune","filter","filterEnvelope","envelope"])},t.extend(t.MonoSynth,t.Monophonic),t.MonoSynth.defaults={frequency:"C4",detune:0,oscillator:{type:"square"},filter:{Q:6,type:"lowpass",rolloff:-24},envelope:{attack:.005,decay:.1,sustain:.9,release:1},filterEnvelope:{attack:.06,decay:.2,sustain:.5,release:2,min:20,max:4e3,exponent:2}},t.MonoSynth.prototype._triggerEnvelopeAttack=function(t,e){return this.envelope.triggerAttack(t,e),this.filterEnvelope.triggerAttack(t),this},t.MonoSynth.prototype._triggerEnvelopeRelease=function(t){return this.envelope.triggerRelease(t),this.filterEnvelope.triggerRelease(t),this},t.MonoSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["oscillator","frequency","detune","filter","filterEnvelope","envelope"]),this.oscillator.dispose(),this.oscillator=null,this.envelope.dispose(),this.envelope=null,this.filterEnvelope.dispose(),this.filterEnvelope=null,this.filter.dispose(),this.filter=null,this.frequency=null,this.detune=null,this},t.MonoSynth}),Module(function(t){return t.AMSynth=function(e){e=this.defaultArg(e,t.AMSynth.defaults),t.Monophonic.call(this,e),this.carrier=new t.MonoSynth(e.carrier),this.carrier.volume.value=-10,this.modulator=new t.MonoSynth(e.modulator),this.modulator.volume.value=-10,this.frequency=new t.Signal(440,t.Type.Frequency),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this._modulationScale=new t.AudioToGain,this._modulationNode=this.context.createGain(),this.frequency.connect(this.carrier.frequency),this.frequency.chain(this.harmonicity,this.modulator.frequency),this.modulator.chain(this._modulationScale,this._modulationNode.gain),this.carrier.chain(this._modulationNode,this.output),this._readOnly(["carrier","modulator","frequency","harmonicity"])},t.extend(t.AMSynth,t.Monophonic),t.AMSynth.defaults={harmonicity:3,carrier:{volume:-10,oscillator:{type:"sine"},envelope:{attack:.01,decay:.01,sustain:1,release:.5},filterEnvelope:{attack:.01,decay:0,sustain:1,release:.5,min:2e4,max:2e4},filter:{Q:6,type:"lowpass",rolloff:-24}},modulator:{volume:-10,oscillator:{type:"square"},envelope:{attack:2,decay:0,sustain:1,release:.5},filterEnvelope:{attack:4,decay:.2,sustain:.5,release:.5,min:20,max:1500},filter:{Q:6,type:"lowpass",rolloff:-24}}},t.AMSynth.prototype._triggerEnvelopeAttack=function(t,e){return t=this.toSeconds(t),this.carrier.envelope.triggerAttack(t,e),this.modulator.envelope.triggerAttack(t),this.carrier.filterEnvelope.triggerAttack(t),this.modulator.filterEnvelope.triggerAttack(t),this},t.AMSynth.prototype._triggerEnvelopeRelease=function(t){return this.carrier.triggerRelease(t),this.modulator.triggerRelease(t),this},t.AMSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["carrier","modulator","frequency","harmonicity"]),this.carrier.dispose(),this.carrier=null,this.modulator.dispose(),this.modulator=null,this.frequency.dispose(),this.frequency=null,this.harmonicity.dispose(),this.harmonicity=null,this._modulationScale.dispose(),this._modulationScale=null,this._modulationNode.disconnect(),this._modulationNode=null,this},t.AMSynth}),Module(function(t){return t.DrumSynth=function(e){e=this.defaultArg(e,t.DrumSynth.defaults),t.Instrument.call(this,e),this.oscillator=new t.Oscillator(e.oscillator).start(),this.envelope=new t.AmplitudeEnvelope(e.envelope),this.octaves=e.octaves,this.pitchDecay=e.pitchDecay,this.oscillator.chain(this.envelope,this.output),this._readOnly(["oscillator","envelope"])},t.extend(t.DrumSynth,t.Instrument),t.DrumSynth.defaults={pitchDecay:.05,octaves:10,oscillator:{type:"sine"},envelope:{attack:.001,decay:.4,sustain:.01,release:1.4,attackCurve:"exponential"}},t.DrumSynth.prototype.triggerAttack=function(t,e,i){e=this.toSeconds(e),t=this.toFrequency(t);var n=t*this.octaves;return this.oscillator.frequency.setValueAtTime(n,e),this.oscillator.frequency.exponentialRampToValueAtTime(t,e+this.toSeconds(this.pitchDecay)),this.envelope.triggerAttack(e,i),this},t.DrumSynth.prototype.triggerRelease=function(t){return this.envelope.triggerRelease(t),this},t.DrumSynth.prototype.dispose=function(){return t.Instrument.prototype.dispose.call(this),this._writable(["oscillator","envelope"]),this.oscillator.dispose(),this.oscillator=null,this.envelope.dispose(),this.envelope=null,this},t.DrumSynth}),Module(function(t){return t.DuoSynth=function(e){e=this.defaultArg(e,t.DuoSynth.defaults),t.Monophonic.call(this,e),this.voice0=new t.MonoSynth(e.voice0),this.voice0.volume.value=-10,this.voice1=new t.MonoSynth(e.voice1),this.voice1.volume.value=-10,this._vibrato=new t.LFO(e.vibratoRate,-50,50),this._vibrato.start(),this.vibratoRate=this._vibrato.frequency,this._vibratoGain=this.context.createGain(),this.vibratoAmount=new t.Param({param:this._vibratoGain.gain,units:t.Type.Gain,value:e.vibratoAmount}),this._vibratoDelay=this.toSeconds(e.vibratoDelay),this.frequency=new t.Signal(440,t.Type.Frequency),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this.frequency.connect(this.voice0.frequency),this.frequency.chain(this.harmonicity,this.voice1.frequency),this._vibrato.connect(this._vibratoGain),this._vibratoGain.fan(this.voice0.detune,this.voice1.detune),this.voice0.connect(this.output),this.voice1.connect(this.output),this._readOnly(["voice0","voice1","frequency","vibratoAmount","vibratoRate"])},t.extend(t.DuoSynth,t.Monophonic),t.DuoSynth.defaults={vibratoAmount:.5,vibratoRate:5,vibratoDelay:1,harmonicity:1.5,voice0:{volume:-10,portamento:0,oscillator:{type:"sine"},filterEnvelope:{attack:.01,decay:0,sustain:1,release:.5},envelope:{attack:.01,decay:0,sustain:1,release:.5}},voice1:{volume:-10,portamento:0,oscillator:{type:"sine"},filterEnvelope:{attack:.01,decay:0,sustain:1,release:.5},envelope:{attack:.01,decay:0,sustain:1,release:.5}}},t.DuoSynth.prototype._triggerEnvelopeAttack=function(t,e){return t=this.toSeconds(t),this.voice0.envelope.triggerAttack(t,e),this.voice1.envelope.triggerAttack(t,e),this.voice0.filterEnvelope.triggerAttack(t),this.voice1.filterEnvelope.triggerAttack(t),this},t.DuoSynth.prototype._triggerEnvelopeRelease=function(t){return this.voice0.triggerRelease(t),this.voice1.triggerRelease(t),this},t.DuoSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["voice0","voice1","frequency","vibratoAmount","vibratoRate"]),this.voice0.dispose(),this.voice0=null,this.voice1.dispose(),this.voice1=null,this.frequency.dispose(),this.frequency=null,this._vibrato.dispose(),this._vibrato=null,this._vibratoGain.disconnect(),this._vibratoGain=null,this.harmonicity.dispose(),this.harmonicity=null,this.vibratoAmount.dispose(),this.vibratoAmount=null,this.vibratoRate=null,this},t.DuoSynth}),Module(function(t){return t.FMSynth=function(e){e=this.defaultArg(e,t.FMSynth.defaults),t.Monophonic.call(this,e),this.carrier=new t.MonoSynth(e.carrier),this.carrier.volume.value=-10,this.modulator=new t.MonoSynth(e.modulator),this.modulator.volume.value=-10,this.frequency=new t.Signal(440,t.Type.Frequency),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this.modulationIndex=new t.Multiply(e.modulationIndex),this.modulationIndex.units=t.Type.Positive,this._modulationNode=this.context.createGain(),this.frequency.connect(this.carrier.frequency),this.frequency.chain(this.harmonicity,this.modulator.frequency),this.frequency.chain(this.modulationIndex,this._modulationNode),this.modulator.connect(this._modulationNode.gain),this._modulationNode.gain.value=0,this._modulationNode.connect(this.carrier.frequency),this.carrier.connect(this.output),this._readOnly(["carrier","modulator","frequency","harmonicity","modulationIndex"])},t.extend(t.FMSynth,t.Monophonic),t.FMSynth.defaults={harmonicity:3,modulationIndex:10,carrier:{volume:-10,portamento:0,oscillator:{type:"sine"},envelope:{attack:.01,decay:0,sustain:1,release:.5},filterEnvelope:{attack:.01,decay:0,sustain:1,release:.5,min:2e4,max:2e4}},modulator:{volume:-10,portamento:0,oscillator:{type:"triangle"},envelope:{attack:.01,decay:0,sustain:1,release:.5},filterEnvelope:{attack:.01,decay:0,sustain:1,release:.5,min:2e4,max:2e4}}},t.FMSynth.prototype._triggerEnvelopeAttack=function(t,e){return t=this.toSeconds(t),this.carrier.envelope.triggerAttack(t,e),this.modulator.envelope.triggerAttack(t),this.carrier.filterEnvelope.triggerAttack(t),this.modulator.filterEnvelope.triggerAttack(t),this},t.FMSynth.prototype._triggerEnvelopeRelease=function(t){return this.carrier.triggerRelease(t),this.modulator.triggerRelease(t),this},t.FMSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["carrier","modulator","frequency","harmonicity","modulationIndex"]),this.carrier.dispose(),this.carrier=null,this.modulator.dispose(),this.modulator=null,this.frequency.dispose(),this.frequency=null,this.modulationIndex.dispose(),this.modulationIndex=null,this.harmonicity.dispose(),this.harmonicity=null,this._modulationNode.disconnect(),this._modulationNode=null,this},t.FMSynth}),Module(function(t){t.Noise=function(){var e=this.optionsObject(arguments,["type"],t.Noise.defaults);t.Source.call(this,e),this._source=null,this._buffer=null,this.type=e.type},t.extend(t.Noise,t.Source),t.Noise.defaults={type:"white"},Object.defineProperty(t.Noise.prototype,"type",{get:function(){return this._buffer===n?"white":this._buffer===i?"brown":this._buffer===e?"pink":void 0},set:function(s){if(this.type!==s){switch(s){case"white":this._buffer=n;break;case"pink":this._buffer=e;break;case"brown":this._buffer=i;break;default:throw new Error("invalid noise type: "+s)}if(this.state===t.State.Started){var o=this.now()+this.blockTime;this._source.onended=void 0,this._stop(o),this._start(o)}}}}),t.Noise.prototype._start=function(t){this._source=this.context.createBufferSource(),this._source.buffer=this._buffer,this._source.loop=!0,this._source.connect(this.output),this._source.start(this.toSeconds(t)),this._source.onended=this.onended},t.Noise.prototype._stop=function(t){this._source&&this._source.stop(this.toSeconds(t))},t.Noise.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),null!==this._source&&(this._source.disconnect(),this._source=null),this._buffer=null,this};var e=null,i=null,n=null;return t._initAudioContext(function(t){var s=t.sampleRate,o=4*s;e=function(){var e,i,n,r,a,l,h,u,c,p,d,f=t.createBuffer(2,o,s);for(e=0;ep;p++)d=2*Math.random()-1,n=.99886*n+.0555179*d,r=.99332*r+.0750759*d,a=.969*a+.153852*d,l=.8665*l+.3104856*d,h=.55*h+.5329522*d,u=-.7616*u-.016898*d,i[p]=n+r+a+l+h+u+c+.5362*d,i[p]*=.11,c=.115926*d;return f}(),i=function(){var e,i,n,r,a,l=t.createBuffer(2,o,s);for(e=0;er;r++)a=2*Math.random()-1,i[r]=(n+.02*a)/1.02,n=i[r],i[r]*=3.5;return l}(),n=function(){var e,i,n,r=t.createBuffer(2,o,s);for(e=0;en;n++)i[n]=2*Math.random()-1;return r}()}),t.Noise}),Module(function(t){return t.NoiseSynth=function(e){e=this.defaultArg(e,t.NoiseSynth.defaults),t.Instrument.call(this,e),this.noise=new t.Noise,this.filter=new t.Filter(e.filter),this.filterEnvelope=new t.ScaledEnvelope(e.filterEnvelope),this.envelope=new t.AmplitudeEnvelope(e.envelope),this.noise.chain(this.filter,this.envelope,this.output),this.noise.start(),this.filterEnvelope.connect(this.filter.frequency),this._readOnly(["noise","filter","filterEnvelope","envelope"])},t.extend(t.NoiseSynth,t.Instrument),t.NoiseSynth.defaults={noise:{type:"white"},filter:{Q:6,type:"highpass",rolloff:-24},envelope:{attack:.005,decay:.1,sustain:0},filterEnvelope:{attack:.06,decay:.2,sustain:0,release:2,min:20,max:4e3,exponent:2}},t.NoiseSynth.prototype.triggerAttack=function(t,e){return this.envelope.triggerAttack(t,e),this.filterEnvelope.triggerAttack(t),this},t.NoiseSynth.prototype.triggerRelease=function(t){return this.envelope.triggerRelease(t),this.filterEnvelope.triggerRelease(t),this},t.NoiseSynth.prototype.triggerAttackRelease=function(t,e,i){return e=this.toSeconds(e),t=this.toSeconds(t),this.triggerAttack(e,i),this.triggerRelease(e+t),this},t.NoiseSynth.prototype.dispose=function(){return t.Instrument.prototype.dispose.call(this),this._writable(["noise","filter","filterEnvelope","envelope"]),this.noise.dispose(),this.noise=null,this.envelope.dispose(),this.envelope=null,this.filterEnvelope.dispose(),this.filterEnvelope=null,this.filter.dispose(),this.filter=null,this},t.NoiseSynth}),Module(function(t){return t.PluckSynth=function(e){e=this.defaultArg(e,t.PluckSynth.defaults),t.Instrument.call(this,e),this._noise=new t.Noise("pink"),this.attackNoise=1,this._lfcf=new t.LowpassCombFilter({resonance:e.resonance,dampening:e.dampening}),this.resonance=this._lfcf.resonance,this.dampening=this._lfcf.dampening,this._noise.connect(this._lfcf),this._lfcf.connect(this.output),this._readOnly(["resonance","dampening"])},t.extend(t.PluckSynth,t.Instrument),t.PluckSynth.defaults={attackNoise:1,dampening:4e3,resonance:.9},t.PluckSynth.prototype.triggerAttack=function(t,e){t=this.toFrequency(t),e=this.toSeconds(e);var i=1/t;return this._lfcf.delayTime.setValueAtTime(i,e),this._noise.start(e),this._noise.stop(e+i*this.attackNoise),this},t.PluckSynth.prototype.dispose=function(){return t.Instrument.prototype.dispose.call(this),this._noise.dispose(),this._lfcf.dispose(),this._noise=null,this._lfcf=null,this._writable(["resonance","dampening"]),this.dampening=null,this.resonance=null,this},t.PluckSynth}),Module(function(t){return t.PolySynth=function(){var e,i,n;for(t.Instrument.call(this),e=this.optionsObject(arguments,["polyphony","voice"],t.PolySynth.defaults),this.voices=new Array(e.polyphony),this.stealVoices=!0,this._freeVoices=[],this._activeVoices={},i=0;i0)r=this._freeVoices.shift(),r.triggerAttack(s,e,i),this._activeVoices[o]=r;else if(this.stealVoices)for(a in this._activeVoices){this._activeVoices[a].triggerAttack(s,e,i);break}return this},t.PolySynth.prototype.triggerAttackRelease=function(t,e,i,n){return i=this.toSeconds(i),this.triggerAttack(t,i,n),this.triggerRelease(t,i+this.toSeconds(e)),this},t.PolySynth.prototype.triggerRelease=function(t,e){var i,n,s;for(Array.isArray(t)||(t=[t]),i=0;i0){if(t.Buffer._currentDownloads.length0){for(e=0;r>e;e++)i=t.Buffer._currentDownloads[e],o+=i.progress;a=o}n=r-a,s=t.Buffer._totalDownloads-t.Buffer._queue.length-n,t.Buffer.onprogress(s/t.Buffer._totalDownloads)},t.Buffer.load=function(e,i){var n=new XMLHttpRequest;return n.open("GET",e,!0),n.responseType="arraybuffer",n.onload=function(){t.context.decodeAudioData(n.response,function(t){if(!t)throw new Error("could not decode audio data:"+e);i(t)})},n.send(),n},t.Buffer.onload=t.noOp,t.Buffer.onprogress=t.noOp,t.Buffer.onerror=t.noOp,t.Buffer}),Module(function(t){return t.Player=function(e){var i;e instanceof t.Buffer?(e=e.get(),i=t.Player.defaults):i=this.optionsObject(arguments,["url","onload"],t.Player.defaults),t.Source.call(this,i),this._source=null,this.autostart=i.autostart,this._buffer=new t.Buffer({url:i.url,onload:this._onload.bind(this,i.onload),reverse:i.reverse}),e instanceof AudioBuffer&&this._buffer.set(e),this._loop=i.loop,this._loopStart=i.loopStart,this._loopEnd=i.loopEnd,this._playbackRate=i.playbackRate,this.retrigger=i.retrigger},t.extend(t.Player,t.Source),t.Player.defaults={onload:t.noOp,playbackRate:1,loop:!1,autostart:!1,loopStart:0,loopEnd:0,retrigger:!1,reverse:!1},t.Player.prototype.load=function(t,e){return this._buffer.load(t,this._onload.bind(this,e)),this},t.Player.prototype._onload=function(t){t(this),this.autostart&&this.start()},t.Player.prototype._start=function(e,i,n){if(!this._buffer.loaded)throw Error("tried to start Player before the buffer was loaded");return i=this._loop?this.defaultArg(i,this._loopStart):this.defaultArg(i,0),i=this.toSeconds(i),n=this.defaultArg(n,this._buffer.duration-i),e=this.toSeconds(e),n=this.toSeconds(n),this._source=this.context.createBufferSource(),this._source.buffer=this._buffer.get(),this._loop?(this._source.loop=this._loop,this._source.loopStart=this.toSeconds(this._loopStart),this._source.loopEnd=this.toSeconds(this._loopEnd)):this._state.setStateAtTime(t.State.Stopped,e+n),this._source.playbackRate.value=this._playbackRate,this._source.connect(this.output),this._loop?this._source.start(e,i):this._source.start(e,i,n),this},t.Player.prototype._stop=function(t){return this._source&&(this._source.stop(this.toSeconds(t)),this._source=null),this},t.Player.prototype.setLoopPoints=function(t,e){return this.loopStart=t,this.loopEnd=e,this},Object.defineProperty(t.Player.prototype,"loopStart",{get:function(){return this._loopStart},set:function(t){this._loopStart=t,this._source&&(this._source.loopStart=this.toSeconds(t))}}),Object.defineProperty(t.Player.prototype,"loopEnd",{get:function(){return this._loopEnd},set:function(t){this._loopEnd=t,this._source&&(this._source.loopEnd=this.toSeconds(t))}}),Object.defineProperty(t.Player.prototype,"buffer",{get:function(){return this._buffer},set:function(t){this._buffer.set(t)}}),Object.defineProperty(t.Player.prototype,"loop",{get:function(){return this._loop},set:function(t){this._loop=t,this._source&&(this._source.loop=t)}}),Object.defineProperty(t.Player.prototype,"playbackRate",{get:function(){return this._playbackRate},set:function(t){this._playbackRate=t,this._source&&(this._source.playbackRate.value=t)}}),Object.defineProperty(t.Player.prototype,"reverse",{get:function(){return this._buffer.reverse},set:function(t){this._buffer.reverse=t}}),t.Player.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),null!==this._source&&(this._source.disconnect(),this._source=null),this._buffer.dispose(),this._buffer=null,this},t.Player}),Module(function(t){return t.Sampler=function(e,i){i=this.defaultArg(i,t.Sampler.defaults),t.Instrument.call(this,i),this.player=new t.Player(i.player),this.player.retrigger=!0,this._buffers={},this.envelope=new t.AmplitudeEnvelope(i.envelope),this.filterEnvelope=new t.ScaledEnvelope(i.filterEnvelope),this._sample=i.sample,this._pitch=i.pitch,this.filter=new t.Filter(i.filter),this._loadBuffers(e),this.pitch=i.pitch,this.player.chain(this.filter,this.envelope,this.output),this.filterEnvelope.connect(this.filter.frequency),this._readOnly(["player","filterEnvelope","envelope","filter"])},t.extend(t.Sampler,t.Instrument),t.Sampler.defaults={sample:0,pitch:0,player:{loop:!1},envelope:{attack:.001,decay:0,sustain:1,release:.1},filterEnvelope:{attack:.001,decay:.001,sustain:1,release:.5,min:20,max:2e4,exponent:2},filter:{type:"lowpass"}},t.Sampler.prototype._loadBuffers=function(e){var i,n;if("string"==typeof e)this._buffers[0]=new t.Buffer(e,function(){this.sample="0"}.bind(this));else{e=this._flattenUrls(e);for(i in e)this._sample=i,n=e[i],this._buffers[i]=new t.Buffer(n)}},t.Sampler.prototype._flattenUrls=function(t){var e,i,n,s={};for(e in t)if(t.hasOwnProperty(e))if("object"==typeof t[e]){i=this._flattenUrls(t[e]);for(n in i)i.hasOwnProperty(n)&&(s[e+"."+n]=i[n])}else s[e]=t[e];return s},t.Sampler.prototype.triggerAttack=function(t,e,i){return e=this.toSeconds(e),t&&(this.sample=t),this.player.start(e),this.envelope.triggerAttack(e,i),this.filterEnvelope.triggerAttack(e),this},t.Sampler.prototype.triggerRelease=function(t){return t=this.toSeconds(t),this.filterEnvelope.triggerRelease(t),this.envelope.triggerRelease(t),this.player.stop(this.toSeconds(this.envelope.release)+t),this},Object.defineProperty(t.Sampler.prototype,"sample",{get:function(){return this._sample},set:function(t){if(!this._buffers.hasOwnProperty(t))throw new Error("Sampler does not have a sample named "+t);this._sample=t,this.player.buffer=this._buffers[t]}}),Object.defineProperty(t.Sampler.prototype,"reverse",{get:function(){for(var t in this._buffers)return this._buffers[t].reverse},set:function(t){for(var e in this._buffers)this._buffers[e].reverse=t}}),Object.defineProperty(t.Sampler.prototype,"pitch",{get:function(){return this._pitch},set:function(t){this._pitch=t,this.player.playbackRate=this.intervalToFrequencyRatio(t)}}),t.Sampler.prototype.dispose=function(){t.Instrument.prototype.dispose.call(this),this._writable(["player","filterEnvelope","envelope","filter"]),this.player.dispose(),this.filterEnvelope.dispose(),this.envelope.dispose(),this.filter.dispose(),this.player=null,this.filterEnvelope=null,this.envelope=null,this.filter=null;for(var e in this._buffers)this._buffers[e].dispose(),this._buffers[e]=null;return this._buffers=null,this},t.Sampler}),Module(function(t){return t.SimpleSynth=function(e){
+e=this.defaultArg(e,t.SimpleSynth.defaults),t.Monophonic.call(this,e),this.oscillator=new t.OmniOscillator(e.oscillator),this.frequency=this.oscillator.frequency,this.detune=this.oscillator.detune,this.envelope=new t.AmplitudeEnvelope(e.envelope),this.oscillator.chain(this.envelope,this.output),this.oscillator.start(),this._readOnly(["oscillator","frequency","detune","envelope"])},t.extend(t.SimpleSynth,t.Monophonic),t.SimpleSynth.defaults={oscillator:{type:"triangle"},envelope:{attack:.005,decay:.1,sustain:.3,release:1}},t.SimpleSynth.prototype._triggerEnvelopeAttack=function(t,e){return this.envelope.triggerAttack(t,e),this},t.SimpleSynth.prototype._triggerEnvelopeRelease=function(t){return this.envelope.triggerRelease(t),this},t.SimpleSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["oscillator","frequency","detune","envelope"]),this.oscillator.dispose(),this.oscillator=null,this.envelope.dispose(),this.envelope=null,this.frequency=null,this.detune=null,this},t.SimpleSynth}),Module(function(t){return t.SimpleAM=function(e){e=this.defaultArg(e,t.SimpleAM.defaults),t.Monophonic.call(this,e),this.carrier=new t.SimpleSynth(e.carrier),this.modulator=new t.SimpleSynth(e.modulator),this.frequency=new t.Signal(440,t.Type.Frequency),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this._modulationScale=new t.AudioToGain,this._modulationNode=this.context.createGain(),this.frequency.connect(this.carrier.frequency),this.frequency.chain(this.harmonicity,this.modulator.frequency),this.modulator.chain(this._modulationScale,this._modulationNode.gain),this.carrier.chain(this._modulationNode,this.output),this._readOnly(["carrier","modulator","frequency","harmonicity"])},t.extend(t.SimpleAM,t.Monophonic),t.SimpleAM.defaults={harmonicity:3,carrier:{volume:-10,portamento:0,oscillator:{type:"sine"},envelope:{attack:.01,decay:.01,sustain:1,release:.5}},modulator:{volume:-10,portamento:0,oscillator:{type:"sine"},envelope:{attack:.5,decay:.1,sustain:1,release:.5}}},t.SimpleAM.prototype._triggerEnvelopeAttack=function(t,e){return t=this.toSeconds(t),this.carrier.envelope.triggerAttack(t,e),this.modulator.envelope.triggerAttack(t),this},t.SimpleAM.prototype._triggerEnvelopeRelease=function(t){return this.carrier.triggerRelease(t),this.modulator.triggerRelease(t),this},t.SimpleAM.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["carrier","modulator","frequency","harmonicity"]),this.carrier.dispose(),this.carrier=null,this.modulator.dispose(),this.modulator=null,this.frequency.dispose(),this.frequency=null,this.harmonicity.dispose(),this.harmonicity=null,this._modulationScale.dispose(),this._modulationScale=null,this._modulationNode.disconnect(),this._modulationNode=null,this},t.SimpleAM}),Module(function(t){return t.SimpleFM=function(e){e=this.defaultArg(e,t.SimpleFM.defaults),t.Monophonic.call(this,e),this.carrier=new t.SimpleSynth(e.carrier),this.carrier.volume.value=-10,this.modulator=new t.SimpleSynth(e.modulator),this.modulator.volume.value=-10,this.frequency=new t.Signal(440,t.Type.Frequency),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this.modulationIndex=new t.Multiply(e.modulationIndex),this.modulationIndex.units=t.Type.Positive,this._modulationNode=this.context.createGain(),this.frequency.connect(this.carrier.frequency),this.frequency.chain(this.harmonicity,this.modulator.frequency),this.frequency.chain(this.modulationIndex,this._modulationNode),this.modulator.connect(this._modulationNode.gain),this._modulationNode.gain.value=0,this._modulationNode.connect(this.carrier.frequency),this.carrier.connect(this.output),this._readOnly(["carrier","modulator","frequency","harmonicity","modulationIndex"])},t.extend(t.SimpleFM,t.Monophonic),t.SimpleFM.defaults={harmonicity:3,modulationIndex:10,carrier:{volume:-10,portamento:0,oscillator:{type:"sine"},envelope:{attack:.01,decay:0,sustain:1,release:.5}},modulator:{volume:-10,portamento:0,oscillator:{type:"triangle"},envelope:{attack:.01,decay:0,sustain:1,release:.5}}},t.SimpleFM.prototype._triggerEnvelopeAttack=function(t,e){return t=this.toSeconds(t),this.carrier.envelope.triggerAttack(t,e),this.modulator.envelope.triggerAttack(t),this},t.SimpleFM.prototype._triggerEnvelopeRelease=function(t){return this.carrier.triggerRelease(t),this.modulator.triggerRelease(t),this},t.SimpleFM.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["carrier","modulator","frequency","harmonicity","modulationIndex"]),this.carrier.dispose(),this.carrier=null,this.modulator.dispose(),this.modulator=null,this.frequency.dispose(),this.frequency=null,this.modulationIndex.dispose(),this.modulationIndex=null,this.harmonicity.dispose(),this.harmonicity=null,this._modulationNode.disconnect(),this._modulationNode=null,this},t.SimpleFM}),Module(function(t){var e={};return t.prototype.send=function(t,i){e.hasOwnProperty(t)||(e[t]=this.context.createGain());var n=this.context.createGain();return n.gain.value=this.dbToGain(this.defaultArg(i,1)),this.output.chain(n,e[t]),n},t.prototype.receive=function(t,i){return e.hasOwnProperty(t)||(e[t]=this.context.createGain()),this.isUndef(i)&&(i=this.input),e[t].connect(i),this},t}),Module(function(t){return t.Delay=function(){var e=this.optionsObject(arguments,["value","maxDelay"],t.Delay.defaults);this._delayNode=this.context.createDelay(this.toSeconds(e.maxDelay)),t.Param.call(this,{param:this._delayNode.delayTime,units:t.Type.Time,value:e.value}),this.input=this.output=this._delayNode,this.delayTime=this._param,this._readOnly("delayTime")},t.extend(t.Delay,t.Param),t.Delay.defaults={maxDelay:1,value:0},t.Delay.prototype.dispose=function(){return t.Param.prototype.dispose.call(this),this._delayNode.disconnect(),this._delayNode=null,this._writable("delayTime"),this.delayTime=null,this},t.Delay}),Module(function(t){function e(t,e,n){var s,o,r,a;if(i.hasOwnProperty(t))for(s=i[t],o=0,r=s.length;r>o;o++)a=s[o],Array.isArray(n)?a.apply(window,[e].concat(n)):a(e,n)}t.Note=function(e,i,n){this.value=n,this._channel=e,this._timelineID=t.Transport.setTimeline(this._trigger.bind(this),i)},t.Note.prototype._trigger=function(t){e(this._channel,t,this.value)},t.Note.prototype.dispose=function(){return t.Transport.clearTimeline(this._timelineID),this.value=null,this};var i={};return t.Note.route=function(t,e){i.hasOwnProperty(t)?i[t].push(e):i[t]=[e]},t.Note.unroute=function(t,e){var n,s;i.hasOwnProperty(t)&&(n=i[t],s=n.indexOf(e),-1!==s&&i[t].splice(s,1))},t.Note.parseScore=function(e){var i,n,s,o,r,a,l,h=[];for(i in e)if(n=e[i],"tempo"===i)t.Transport.bpm.value=n;else if("timeSignature"===i)t.Transport.timeSignature=n[0]/(n[1]/4);else{if(!Array.isArray(n))throw new TypeError("score parts must be Arrays");for(s=0;sn;++n)s=2*n/i-1,e[n]=0===s?0:this._getCoefficient(s,t,{});this._shaper.curve=e}}),Object.defineProperty(t.Chebyshev.prototype,"oversample",{get:function(){return this._shaper.oversample},set:function(t){this._shaper.oversample=t}}),t.Chebyshev.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._shaper.dispose(),this._shaper=null,this},t.Chebyshev}),Module(function(t){return t.StereoEffect=function(){t.call(this);var e=this.optionsObject(arguments,["wet"],t.Effect.defaults);this._dryWet=new t.CrossFade(e.wet),this.wet=this._dryWet.fade,this._split=new t.Split,this.effectSendL=this._split.left,this.effectSendR=this._split.right,this._merge=new t.Merge,this.effectReturnL=this._merge.left,this.effectReturnR=this._merge.right,this.input.connect(this._split),this.input.connect(this._dryWet,0,0),this._merge.connect(this._dryWet,0,1),this._dryWet.connect(this.output),this._readOnly(["wet"])},t.extend(t.StereoEffect,t.Effect),t.StereoEffect.prototype.dispose=function(){return t.prototype.dispose.call(this),this._dryWet.dispose(),this._dryWet=null,this._split.dispose(),this._split=null,this._merge.dispose(),this._merge=null,this.effectSendL=null,this.effectSendR=null,this.effectReturnL=null,this.effectReturnR=null,this._writable(["wet"]),this.wet=null,this},t.StereoEffect}),Module(function(t){return t.FeedbackEffect=function(){var e=this.optionsObject(arguments,["feedback"]);e=this.defaultArg(e,t.FeedbackEffect.defaults),t.Effect.call(this,e),this.feedback=new t.Signal(e.feedback,t.Type.NormalRange),this._feedbackGain=this.context.createGain(),this.effectReturn.chain(this._feedbackGain,this.effectSend),this.feedback.connect(this._feedbackGain.gain),this._readOnly(["feedback"])},t.extend(t.FeedbackEffect,t.Effect),t.FeedbackEffect.defaults={feedback:.125},t.FeedbackEffect.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._writable(["feedback"]),this.feedback.dispose(),this.feedback=null,this._feedbackGain.disconnect(),this._feedbackGain=null,this},t.FeedbackEffect}),Module(function(t){return t.StereoXFeedbackEffect=function(){var e=this.optionsObject(arguments,["feedback"],t.FeedbackEffect.defaults);t.StereoEffect.call(this,e),this.feedback=new t.Signal(e.feedback,t.Type.NormalRange),this._feedbackLR=this.context.createGain(),this._feedbackRL=this.context.createGain(),this.effectReturnL.chain(this._feedbackLR,this.effectSendR),this.effectReturnR.chain(this._feedbackRL,this.effectSendL),this.feedback.fan(this._feedbackLR.gain,this._feedbackRL.gain),this._readOnly(["feedback"])},t.extend(t.StereoXFeedbackEffect,t.FeedbackEffect),t.StereoXFeedbackEffect.prototype.dispose=function(){return t.StereoEffect.prototype.dispose.call(this),this._writable(["feedback"]),this.feedback.dispose(),this.feedback=null,this._feedbackLR.disconnect(),this._feedbackLR=null,this._feedbackRL.disconnect(),this._feedbackRL=null,this},t.StereoXFeedbackEffect}),Module(function(t){return t.Chorus=function(){var e=this.optionsObject(arguments,["frequency","delayTime","depth"],t.Chorus.defaults);t.StereoXFeedbackEffect.call(this,e),this._depth=e.depth,this._delayTime=e.delayTime/1e3,this._lfoL=new t.LFO({frequency:e.frequency,min:0,max:1}),this._lfoR=new t.LFO({frequency:e.frequency,min:0,max:1,phase:180}),this._delayNodeL=this.context.createDelay(),this._delayNodeR=this.context.createDelay(),this.frequency=this._lfoL.frequency,this.effectSendL.chain(this._delayNodeL,this.effectReturnL),this.effectSendR.chain(this._delayNodeR,this.effectReturnR),this.effectSendL.connect(this.effectReturnL),this.effectSendR.connect(this.effectReturnR),this._lfoL.connect(this._delayNodeL.delayTime),this._lfoR.connect(this._delayNodeR.delayTime),this._lfoL.start(),this._lfoR.start(),this._lfoL.frequency.connect(this._lfoR.frequency),this.depth=this._depth,this.frequency.value=e.frequency,this.type=e.type,this._readOnly(["frequency"])},t.extend(t.Chorus,t.StereoXFeedbackEffect),t.Chorus.defaults={frequency:1.5,delayTime:3.5,depth:.7,feedback:.1,type:"sine"},Object.defineProperty(t.Chorus.prototype,"depth",{get:function(){return this._depth},set:function(t){this._depth=t;var e=this._delayTime*t;this._lfoL.min=Math.max(this._delayTime-e,0),this._lfoL.max=this._delayTime+e,this._lfoR.min=Math.max(this._delayTime-e,0),this._lfoR.max=this._delayTime+e}}),Object.defineProperty(t.Chorus.prototype,"delayTime",{get:function(){return 1e3*this._delayTime},set:function(t){this._delayTime=t/1e3,this.depth=this._depth}}),Object.defineProperty(t.Chorus.prototype,"type",{get:function(){return this._lfoL.type},set:function(t){this._lfoL.type=t,this._lfoR.type=t}}),t.Chorus.prototype.dispose=function(){return t.StereoXFeedbackEffect.prototype.dispose.call(this),this._lfoL.dispose(),this._lfoL=null,this._lfoR.dispose(),this._lfoR=null,this._delayNodeL.disconnect(),this._delayNodeL=null,this._delayNodeR.disconnect(),this._delayNodeR=null,this._writable("frequency"),this.frequency=null,this},t.Chorus}),Module(function(t){return t.Convolver=function(){var e=this.optionsObject(arguments,["url"],t.Convolver.defaults);t.Effect.call(this,e),this._convolver=this.context.createConvolver(),this._buffer=new t.Buffer(e.url,function(t){this.buffer=t,e.onload()}.bind(this)),this.connectEffect(this._convolver)},t.extend(t.Convolver,t.Effect),t.Convolver.defaults={url:"",onload:t.noOp},Object.defineProperty(t.Convolver.prototype,"buffer",{get:function(){return this._buffer.get()},set:function(t){this._buffer.set(t),this._convolver.buffer=this._buffer.get()}}),t.Convolver.prototype.load=function(t,e){return this._buffer.load(t,function(t){this.buffer=t,e&&e()}.bind(this)),this},t.Convolver.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._convolver.disconnect(),this._convolver=null,this._buffer.dispose(),this._buffer=null,this},t.Convolver}),Module(function(t){return t.Distortion=function(){var e=this.optionsObject(arguments,["distortion"],t.Distortion.defaults);t.Effect.call(this,e),this._shaper=new t.WaveShaper(4096),this._distortion=e.distortion,this.connectEffect(this._shaper),this.distortion=e.distortion,this.oversample=e.oversample},t.extend(t.Distortion,t.Effect),t.Distortion.defaults={distortion:.4,oversample:"none"},Object.defineProperty(t.Distortion.prototype,"distortion",{get:function(){return this._distortion},set:function(t){var e,i;this._distortion=t,e=100*t,i=Math.PI/180,this._shaper.setMap(function(t){return Math.abs(t)<.001?0:(3+e)*t*20*i/(Math.PI+e*Math.abs(t))})}}),Object.defineProperty(t.Distortion.prototype,"oversample",{get:function(){return this._shaper.oversample},set:function(t){this._shaper.oversample=t}}),t.Distortion.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._shaper.dispose(),this._shaper=null,this},t.Distortion}),Module(function(t){return t.FeedbackDelay=function(){var e=this.optionsObject(arguments,["delayTime","feedback"],t.FeedbackDelay.defaults);t.FeedbackEffect.call(this,e),this.delayTime=new t.Signal(e.delayTime,t.Type.Time),this._delayNode=this.context.createDelay(4),this.connectEffect(this._delayNode),this.delayTime.connect(this._delayNode.delayTime),this._readOnly(["delayTime"])},t.extend(t.FeedbackDelay,t.FeedbackEffect),t.FeedbackDelay.defaults={delayTime:.25},t.FeedbackDelay.prototype.dispose=function(){return t.FeedbackEffect.prototype.dispose.call(this),this.delayTime.dispose(),this._delayNode.disconnect(),this._delayNode=null,this._writable(["delayTime"]),this.delayTime=null,this},t.FeedbackDelay}),Module(function(t){var e=[1557/44100,1617/44100,1491/44100,1422/44100,1277/44100,1356/44100,1188/44100,1116/44100],i=[225,556,441,341];return t.Freeverb=function(){var n,s,o,r,a,l,h=this.optionsObject(arguments,["roomSize","dampening"],t.Freeverb.defaults);for(t.StereoEffect.call(this,h),this.roomSize=new t.Signal(h.roomSize,t.Type.NormalRange),this.dampening=new t.Signal(h.dampening,t.Type.Frequency),this._combFilters=[],this._allpassFiltersL=[],this._allpassFiltersR=[],n=0;nn;n++)s=this.context.createBiquadFilter(),s.type="allpass",i.connect(s.Q),e.connect(s.frequency),o[n]=s;return this.connectSeries.apply(this,o),o},Object.defineProperty(t.Phaser.prototype,"depth",{get:function(){return this._depth},set:function(t){this._depth=t;var e=this._baseFrequency+this._baseFrequency*t;this._lfoL.max=e,this._lfoR.max=e}}),Object.defineProperty(t.Phaser.prototype,"baseFrequency",{get:function(){return this._baseFrequency},set:function(t){this._baseFrequency=t,this._lfoL.min=t,this._lfoR.min=t,this.depth=this._depth}}),t.Phaser.prototype.dispose=function(){var e,i;for(t.StereoEffect.prototype.dispose.call(this),this._writable(["frequency","Q"]),this.Q.dispose(),this.Q=null,this._lfoL.dispose(),this._lfoL=null,this._lfoR.dispose(),this._lfoR=null,e=0;et?(this._lfoA.min=0,this._lfoA.max=this._windowSize,this._lfoB.min=0,this._lfoB.max=this._windowSize,e=this.intervalToFrequencyRatio(t-1)+1):(this._lfoA.min=this._windowSize,this._lfoA.max=0,this._lfoB.min=this._windowSize,this._lfoB.max=0,e=this.intervalToFrequencyRatio(t)-1),this._frequency.value=e*(1.2/this._windowSize)}}),Object.defineProperty(t.PitchShift.prototype,"windowSize",{get:function(){return this._windowSize},set:function(t){this._windowSize=this.toSeconds(t),this.pitch=this._pitch}}),t.PitchShift.prototype.dispose=function(){return t.FeedbackEffect.prototype.dispose.call(this),this._frequency.dispose(),this._frequency=null,this._delayA.disconnect(),this._delayA=null,this._delayB.disconnect(),this._delayB=null,this._lfoA.dispose(),this._lfoA=null,this._lfoB.dispose(),this._lfoB=null,this._crossFade.dispose(),this._crossFade=null,this._crossFadeLFO.dispose(),this._crossFadeLFO=null,this._writable("delayTime"),this.delayTime.dispose(),this.delayTime=null,this},t.PitchShift}),Module(function(t){return t.StereoFeedbackEffect=function(){var e=this.optionsObject(arguments,["feedback"],t.FeedbackEffect.defaults);
+
+t.StereoEffect.call(this,e),this.feedback=new t.Signal(e.feedback,t.Type.NormalRange),this._feedbackL=this.context.createGain(),this._feedbackR=this.context.createGain(),this.effectReturnL.chain(this._feedbackL,this.effectSendL),this.effectReturnR.chain(this._feedbackR,this.effectSendR),this.feedback.fan(this._feedbackL.gain,this._feedbackR.gain),this._readOnly(["feedback"])},t.extend(t.StereoFeedbackEffect,t.FeedbackEffect),t.StereoFeedbackEffect.prototype.dispose=function(){return t.StereoEffect.prototype.dispose.call(this),this._writable(["feedback"]),this.feedback.dispose(),this.feedback=null,this._feedbackL.disconnect(),this._feedbackL=null,this._feedbackR.disconnect(),this._feedbackR=null,this},t.StereoFeedbackEffect}),Module(function(t){return t.StereoWidener=function(){var e=this.optionsObject(arguments,["width"],t.StereoWidener.defaults);t.MidSideEffect.call(this,e),this.width=new t.Signal(e.width,t.Type.NormalRange),this._midMult=new t.Expr("$0 * ($1 * (1 - $2))"),this._sideMult=new t.Expr("$0 * ($1 * $2)"),this._two=new t.Signal(2),this._two.connect(this._midMult,0,1),this.width.connect(this._midMult,0,2),this._two.connect(this._sideMult,0,1),this.width.connect(this._sideMult,0,2),this.midSend.chain(this._midMult,this.midReturn),this.sideSend.chain(this._sideMult,this.sideReturn),this._readOnly(["width"])},t.extend(t.StereoWidener,t.MidSideEffect),t.StereoWidener.defaults={width:.5},t.StereoWidener.prototype.dispose=function(){return t.MidSideEffect.prototype.dispose.call(this),this._writable(["width"]),this.width.dispose(),this.width=null,this._midMult.dispose(),this._midMult=null,this._sideMult.dispose(),this._sideMult=null,this._two.dispose(),this._two=null,this},t.StereoWidener}),Module(function(t){return t.Tremolo=function(){var e=this.optionsObject(arguments,["frequency","depth"],t.Tremolo.defaults);t.Effect.call(this,e),this._lfo=new t.LFO({frequency:e.frequency,amplitude:e.depth,min:1,max:0}),this._amplitude=this.context.createGain(),this.frequency=this._lfo.frequency,this.depth=this._lfo.amplitude,this._readOnly(["frequency","depth"]),this.connectEffect(this._amplitude),this._lfo.connect(this._amplitude.gain),this.type=e.type},t.extend(t.Tremolo,t.Effect),t.Tremolo.defaults={frequency:10,type:"sine",depth:.5},t.Tremolo.prototype.start=function(t){return this._lfo.start(t),this},t.Tremolo.prototype.stop=function(t){return this._lfo.stop(t),this},t.Tremolo.prototype.sync=function(t){return this._lfo.sync(t),this},t.Tremolo.prototype.unsync=function(){return this._lfo.unsync(),this},Object.defineProperty(t.Tremolo.prototype,"type",{get:function(){return this._lfo.type},set:function(t){this._lfo.type=t}}),t.Tremolo.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._writable(["frequency","depth"]),this._lfo.dispose(),this._lfo=null,this._amplitude.disconnect(),this._amplitude=null,this.frequency=null,this.depth=null,this},t.Tremolo}),Module(function(t){return t.Vibrato=function(){var e=this.optionsObject(arguments,["frequency","depth"],t.Vibrato.defaults);t.Effect.call(this,e),this._delayNode=new t.Delay(e.maxDelay),this._lfo=new t.LFO({type:e.type,min:0,max:e.maxDelay,frequency:e.frequency,phase:-90}).start().connect(this._delayNode.delayTime),this.frequency=this._lfo.frequency,this.depth=this._lfo.amplitude,this.depth.value=e.depth,this._readOnly(["frequency","depth"]),this.effectSend.chain(this._delayNode,this.effectReturn)},t.extend(t.Vibrato,t.Effect),t.Vibrato.defaults={maxDelay:.005,frequency:5,depth:.1,type:"sine"},Object.defineProperty(t.Vibrato.prototype,"type",{get:function(){return this._lfo.type},set:function(t){this._lfo.type=t}}),t.Vibrato.prototype.dispose=function(){t.Effect.prototype.dispose.call(this),this._delayNode.dispose(),this._delayNode=null,this._lfo.dispose(),this._lfo=null,this._writable(["frequency","depth"]),this.frequency=null,this.depth=null},t.Vibrato}),Module(function(t){return t.Clip=function(e,i){if(e>i){var n=e;e=i,i=n}this.min=this.input=new t.Min(i),this._readOnly("min"),this.max=this.output=new t.Max(e),this._readOnly("max"),this.min.connect(this.max)},t.extend(t.Clip,t.SignalBase),t.Clip.prototype.dispose=function(){return t.prototype.dispose.call(this),this._writable("min"),this.min.dispose(),this.min=null,this._writable("max"),this.max.dispose(),this.max=null,this},t.Clip}),Module(function(t){return t.Normalize=function(e,i){this._inputMin=this.defaultArg(e,0),this._inputMax=this.defaultArg(i,1),this._sub=this.input=new t.Add(0),this._div=this.output=new t.Multiply(1),this._sub.connect(this._div),this._setRange()},t.extend(t.Normalize,t.SignalBase),Object.defineProperty(t.Normalize.prototype,"min",{get:function(){return this._inputMin},set:function(t){this._inputMin=t,this._setRange()}}),Object.defineProperty(t.Normalize.prototype,"max",{get:function(){return this._inputMax},set:function(t){this._inputMax=t,this._setRange()}}),t.Normalize.prototype._setRange=function(){this._sub.value=-this._inputMin,this._div.value=1/(this._inputMax-this._inputMin)},t.Normalize.prototype.dispose=function(){return t.prototype.dispose.call(this),this._sub.dispose(),this._sub=null,this._div.dispose(),this._div=null,this},t.Normalize}),Module(function(t){t.Route=function(i){var n,s;for(i=this.defaultArg(i,2),t.call(this,1,i),this.gate=new t.Signal(0),this._readOnly("gate"),n=0;i>n;n++)s=new e(n),this.output[n]=s,this.gate.connect(s.selecter),this.input.connect(s)},t.extend(t.Route,t.SignalBase),t.Route.prototype.select=function(t,e){return t=Math.floor(t),this.gate.setValueAtTime(t,this.toSeconds(e)),this},t.Route.prototype.dispose=function(){this._writable("gate"),this.gate.dispose(),this.gate=null;for(var e=0;ethis.probability)return;if(this.humanize){var i=.01;this.isBoolean(this.humanize)||(i=this.toSeconds(this.humanize)),e+=(2*Math.random()-1)*i}this.callback(e,this.value)}},t.Note.prototype._getLoopDuration=function(){return Math.round((this._loopEnd-this._loopStart)/this._playbackRate)},Object.defineProperty(t.Note.prototype,"loop",{get:function(){return this._loop},set:function(t){this._loop=t,this._rescheduleEvents()}}),Object.defineProperty(t.Note.prototype,"playbackRate",{get:function(){return this._playbackRate},set:function(t){this._playbackRate=t,this._loop&&this._rescheduleEvents()}}),Object.defineProperty(t.Note.prototype,"loopEnd",{get:function(){return this.toNotation(this._loopEnd+"i")},set:function(t){this._loopEnd=this.toTicks(t),this._loop&&this._rescheduleEvents()}}),Object.defineProperty(t.Note.prototype,"loopStart",{get:function(){return this.toNotation(this._loopStart+"i")},set:function(t){this._loopStart=this.toTicks(t),this._loop&&this._rescheduleEvents()}}),Object.defineProperty(t.Note.prototype,"progress",{get:function(){var e,i,n,s,o;return this._loop?(e=t.Transport.ticks,i=this._events.getEvent(e),null!==i&&i.state===t.State.Started?(n=this._getLoopDuration(),this.isNumber(this._loop)&&(s=n*this._loop+i.time,e>s)?0:(o=(e-i.time)%n,o/n)):0):0}}),t.Note.prototype.dispose=function(){this.cancel(),this._events.dispose(),this._events=null,this.callback=null,this.value=null},t.Note}),Module(function(t){return t.Part=function(){var e,i,n=this.optionsObject(arguments,["callback","notes"],t.Part.defaults,!0);for(this._loop=n.loop,this._loopStart=0,this._loopEnd=0,this._playbackRate=1,this._events=new t.TimelineState(t.State.Stopped),this._notes=[],this.callback=n.callback,this.mute=n.mute,this.loopEnd=n.loopEnd,this.loopStart=n.loopStart,this.playbackRate=n.playbackRate,this.mute=n.mute,e=this.defaultArg(n.notes,[]),i=0;i=this._loopStart&&t.time=0;e--)t(this._notes[e],e);return this},t.Part.prototype._tick=function(e,i){this.mute||this._events.getStateAtTime(t.Transport.ticks)!==t.State.Started||this.callback(e,i)},Object.defineProperty(t.Part.prototype,"probability",{get:function(){return this._probability},set:function(t){this._probability=t,this._forEach(function(e){e.probability=t})}}),Object.defineProperty(t.Part.prototype,"loop",{get:function(){return this._loop},set:function(t){this._loop=t,this._forEach(function(e){e.note.loop=t}),this.loopEnd=this._loopEnd+"i",this.loopStart=this._loopStart+"i"}}),Object.defineProperty(t.Part.prototype,"loopEnd",{get:function(){return this.toNotation(this._loopEnd+"i")},set:function(t){this._loopEnd=this.toTicks(t),this._loop&&this._forEach(function(t){t.note.loopEnd=this._loopEnd-this._loopStart+"i",t.note.time>this._loopEnd&&t.note.cancel()}.bind(this))}}),Object.defineProperty(t.Part.prototype,"loopStart",{get:function(){return this.toNotation(this._loopStart+"i")},set:function(t){this._loopStart=this.toTicks(t),this._loop&&this._forEach(function(t){t.note.loopEnd=this._loopEnd-this._loopStart+"i",t.note.time<=this._loopStart&&t.note.cancel()}.bind(this))}}),Object.defineProperty(t.Part.prototype,"playbackRate",{get:function(){return this._playbackRate},set:function(t){this._forEach(function(e){var i=e.note.playbackRate/this._playbackRate;e.note.playbackRate=t*i}.bind(this)),this._playbackRate=t}}),Object.defineProperty(t.Part.prototype,"length",{get:function(){return this._notes.length}}),t.Part.prototype.dispose=function(){return this.callback=null,this.removeAll(),this._notes=null,this},t.Part}),Module(function(t){return t.Pattern=function(e,i){this._callback=e,this.notes=i,this._eventIndex=-1,this._note=new t.Note(this._tick.bind(this)),this._note.loop=!0,this._note.loopEnd="4n",this._arpDirection=1},t.extend(t.Pattern),t.Pattern.prototype.start=function(t){return this._note.start(t),this},t.Pattern.prototype.stop=function(t){return this._note.stop(t),this},t.Pattern.prototype._tick=function(e){this._pattern===t.Pattern.Type.Random?this._eventIndex=Math.floor(Math.random()*this.notes.length):(this._eventIndex+=this._arpDirection,this._pattern===t.Pattern.Type.Alternate?0===this._eventIndex?this._arpDirection=1:this._eventIndex===this.notes.length-1&&(this._arpDirection=-1):this._eventIndex<0?this._eventIndex=this.notes.length-1:this._eventIndex>=this.notes.length&&(this._eventIndex=0)),this._callback(e,this.notes[this._eventIndex])},Object.defineProperty(t.Pattern.prototype,"interval",{get:function(){return this._note.loopEnd},set:function(t){this._note.loopEnd=t}}),Object.defineProperty(t.Pattern.prototype,"pattern",{get:function(){return this._pattern},set:function(e){var i,n;switch(e){case t.Pattern.Type.Forward:this._arpDirection=1;break;case t.Pattern.Type.Reverse:this._arpDirection=-1}i=!1;for(n in t.Pattern.Type)if(e===t.Pattern.Type[n]){i=!0;break}if(!i)throw new Error("Invalid pattern: "+e);this._pattern=e}}),t.Pattern.Type={Forward:"forward",Reverse:"reverse",Alternate:"alternate",Drunk:"drunk",Converge:"converge",Diverge:"diverge",RandomOnce:"randomOnce",Random:"random"},t.Pattern}),Module(function(t){return t.Score=function(e){t.EventEmitter.call(this),this.parts={},this._readOnly(["parts"])},t.extend(t.Score,t.EventEmitter),t.Score.prototype.solo=function(t){this.mute=!0,Array.isArray(t)?t.forEach(function(t){this.parts.hasOwnProperty(t)&&(this.parts[t].mute=!1)}.bind(this)):this.parts.hasOwnProperty(t)&&(this.parts[t].mute=!1)},t.Score.prototype.unsolo=function(){},Object.defineProperty(t.Score.prototype,"mute",{get:function(){},set:function(t){this._forEach(function(e){e.mute=t})}}),t.Score}),Module(function(t){return t.Sequence=function(e,i,n){{var s,o,r;this.optionsObject(arguments,["callback","sequence","subdivision"],t.Sequence.defaults)}if(t.Part.call(this,e),this._subdivision=this.toTicks(n),Array.isArray(i))for(s=0;s