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). - * - * - * - * @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). + * + * + * + * @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. + * + * @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.: + * + * + * @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:/^0;)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