2014-07-02 19:51:55 +00:00
|
|
|
/**
|
|
|
|
* Tone.js
|
|
|
|
* @author Yotam Mann
|
2015-01-06 03:45:24 +00:00
|
|
|
* @license http://opensource.org/licenses/MIT MIT License
|
2017-01-08 22:53:23 +00:00
|
|
|
* @copyright 2014-2017 Yotam Mann
|
2014-07-02 19:51:55 +00:00
|
|
|
*/
|
2014-10-02 22:55:24 +00:00
|
|
|
define(function(){
|
2014-04-05 22:05:42 +00:00
|
|
|
|
2014-09-04 04:41:40 +00:00
|
|
|
"use strict";
|
|
|
|
|
2014-03-12 20:29:43 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2014-03-16 17:33:56 +00:00
|
|
|
// TONE
|
2014-03-12 20:29:43 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2014-06-16 05:44:00 +00:00
|
|
|
/**
|
2017-04-25 02:08:59 +00:00
|
|
|
* @class Tone is the base class of all other classes.
|
2014-06-16 05:44:00 +00:00
|
|
|
* @constructor
|
|
|
|
*/
|
2017-11-29 21:28:04 +00:00
|
|
|
var Tone = function(){
|
|
|
|
if (!(this instanceof Tone)){
|
|
|
|
throw new Error("constructor needs to be called with the 'new' keyword");
|
|
|
|
}
|
|
|
|
};
|
2014-03-12 20:29:43 +00:00
|
|
|
|
2017-05-01 18:06:36 +00:00
|
|
|
/**
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone#
|
2017-05-01 18:06:36 +00:00
|
|
|
* @returns {string} returns the name of the class as a string
|
|
|
|
*/
|
|
|
|
Tone.prototype.toString = function(){
|
|
|
|
for (var className in Tone){
|
|
|
|
var isLetter = className[0].match(/^[A-Z]$/);
|
2017-10-26 19:26:04 +00:00
|
|
|
var sameConstructor = Tone[className] === this.constructor;
|
2017-05-01 18:06:36 +00:00
|
|
|
if (Tone.isFunction(Tone[className]) && isLetter && sameConstructor){
|
|
|
|
return className;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "Tone";
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone#
|
2017-05-01 18:06:36 +00:00
|
|
|
* disconnect and dispose
|
|
|
|
* @returns {Tone} this
|
|
|
|
*/
|
|
|
|
Tone.prototype.dispose = function(){
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GET/SET
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2015-02-02 01:02:13 +00:00
|
|
|
/**
|
2015-02-23 05:32:33 +00:00
|
|
|
* Set the parameters at once. Either pass in an
|
|
|
|
* object mapping parameters to values, or to set a
|
2015-02-23 19:07:52 +00:00
|
|
|
* single parameter, by passing in a string and value.
|
2017-08-26 16:47:44 +00:00
|
|
|
* The last argument is an optional ramp time which
|
2015-05-24 13:34:17 +00:00
|
|
|
* will ramp any signal values to their destination value
|
|
|
|
* over the duration of the rampTime.
|
2015-05-13 03:46:12 +00:00
|
|
|
* @param {Object|string} params
|
|
|
|
* @param {number=} value
|
2015-06-14 00:20:36 +00:00
|
|
|
* @param {Time=} rampTime
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone} this
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone#
|
2015-02-25 21:20:12 +00:00
|
|
|
* @example
|
2015-06-14 02:03:58 +00:00
|
|
|
* //set values using an object
|
|
|
|
* filter.set({
|
|
|
|
* "frequency" : 300,
|
|
|
|
* "type" : highpass
|
|
|
|
* });
|
|
|
|
* @example
|
|
|
|
* filter.set("type", "highpass");
|
|
|
|
* @example
|
2017-08-26 16:47:44 +00:00
|
|
|
* //ramp to the value 220 over 3 seconds.
|
2015-06-14 02:03:58 +00:00
|
|
|
* oscillator.set({
|
|
|
|
* "frequency" : 220
|
|
|
|
* }, 3);
|
2015-02-02 01:02:13 +00:00
|
|
|
*/
|
2015-02-23 19:07:52 +00:00
|
|
|
Tone.prototype.set = function(params, value, rampTime){
|
2017-04-26 04:24:19 +00:00
|
|
|
if (Tone.isObject(params)){
|
2015-02-23 19:07:52 +00:00
|
|
|
rampTime = value;
|
2017-04-26 04:24:19 +00:00
|
|
|
} else if (Tone.isString(params)){
|
2015-02-23 05:32:33 +00:00
|
|
|
var tmpObj = {};
|
|
|
|
tmpObj[params] = value;
|
|
|
|
params = tmpObj;
|
|
|
|
}
|
2016-10-20 06:57:13 +00:00
|
|
|
|
|
|
|
paramLoop:
|
2015-02-02 01:02:13 +00:00
|
|
|
for (var attr in params){
|
2015-05-24 13:34:17 +00:00
|
|
|
value = params[attr];
|
|
|
|
var parent = this;
|
|
|
|
if (attr.indexOf(".") !== -1){
|
|
|
|
var attrSplit = attr.split(".");
|
|
|
|
for (var i = 0; i < attrSplit.length - 1; i++){
|
|
|
|
parent = parent[attrSplit[i]];
|
2016-10-20 06:57:13 +00:00
|
|
|
if (parent instanceof Tone) {
|
2018-01-03 17:07:06 +00:00
|
|
|
attrSplit.splice(0, i+1);
|
2016-10-20 06:57:13 +00:00
|
|
|
var innerParam = attrSplit.join(".");
|
|
|
|
parent.set(innerParam, value);
|
|
|
|
continue paramLoop;
|
|
|
|
}
|
2015-05-24 13:34:17 +00:00
|
|
|
}
|
|
|
|
attr = attrSplit[attrSplit.length - 1];
|
|
|
|
}
|
|
|
|
var param = parent[attr];
|
2017-04-26 04:24:19 +00:00
|
|
|
if (Tone.isUndef(param)){
|
2015-02-17 16:07:34 +00:00
|
|
|
continue;
|
|
|
|
}
|
2017-08-26 16:47:44 +00:00
|
|
|
if ((Tone.Signal && param instanceof Tone.Signal) ||
|
2015-10-21 14:29:04 +00:00
|
|
|
(Tone.Param && param instanceof Tone.Param)){
|
2015-02-17 16:07:34 +00:00
|
|
|
if (param.value !== value){
|
2017-04-26 04:24:19 +00:00
|
|
|
if (Tone.isUndef(rampTime)){
|
2015-02-23 19:07:52 +00:00
|
|
|
param.value = value;
|
|
|
|
} else {
|
|
|
|
param.rampTo(value, rampTime);
|
|
|
|
}
|
2015-02-17 16:07:34 +00:00
|
|
|
}
|
2015-02-23 19:07:52 +00:00
|
|
|
} else if (param instanceof AudioParam){
|
|
|
|
if (param.value !== value){
|
|
|
|
param.value = value;
|
2017-08-26 16:47:44 +00:00
|
|
|
}
|
2017-12-16 19:23:08 +00:00
|
|
|
} else if (Tone.TimeBase && param instanceof Tone.TimeBase){
|
|
|
|
parent[attr] = value;
|
2015-02-04 15:16:33 +00:00
|
|
|
} else if (param instanceof Tone){
|
|
|
|
param.set(value);
|
2015-02-17 16:07:34 +00:00
|
|
|
} else if (param !== value){
|
2015-05-24 13:34:17 +00:00
|
|
|
parent[attr] = value;
|
2015-02-02 01:02:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2015-06-14 05:21:10 +00:00
|
|
|
* Get the object's attributes. Given no arguments get
|
2015-05-24 13:34:17 +00:00
|
|
|
* will return all available object properties and their corresponding
|
|
|
|
* values. Pass in a single attribute to retrieve or an array
|
|
|
|
* of attributes. The attribute strings can also include a "."
|
|
|
|
* to access deeper properties.
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone#
|
2015-02-26 16:26:49 +00:00
|
|
|
* @example
|
2015-05-24 13:34:17 +00:00
|
|
|
* osc.get();
|
|
|
|
* //returns {"type" : "sine", "frequency" : 440, ...etc}
|
|
|
|
* @example
|
|
|
|
* osc.get("type");
|
|
|
|
* //returns { "type" : "sine"}
|
|
|
|
* @example
|
|
|
|
* //use dot notation to access deep properties
|
|
|
|
* synth.get(["envelope.attack", "envelope.release"]);
|
|
|
|
* //returns {"envelope" : {"attack" : 0.2, "release" : 0.4}}
|
2017-08-26 16:47:44 +00:00
|
|
|
* @param {Array=|string|undefined} params the parameters to get, otherwise will return
|
2015-05-13 03:46:12 +00:00
|
|
|
* all available.
|
2015-05-24 13:34:17 +00:00
|
|
|
* @returns {Object}
|
2015-02-02 01:02:13 +00:00
|
|
|
*/
|
|
|
|
Tone.prototype.get = function(params){
|
2017-04-26 04:24:19 +00:00
|
|
|
if (Tone.isUndef(params)){
|
2015-02-02 01:02:13 +00:00
|
|
|
params = this._collectDefaults(this.constructor);
|
2017-04-26 04:24:19 +00:00
|
|
|
} else if (Tone.isString(params)){
|
2015-05-13 16:13:00 +00:00
|
|
|
params = [params];
|
2017-08-26 16:47:44 +00:00
|
|
|
}
|
2015-02-02 01:02:13 +00:00
|
|
|
var ret = {};
|
2015-05-13 16:13:00 +00:00
|
|
|
for (var i = 0; i < params.length; i++){
|
|
|
|
var attr = params[i];
|
2015-05-24 13:34:17 +00:00
|
|
|
var parent = this;
|
|
|
|
var subRet = ret;
|
|
|
|
if (attr.indexOf(".") !== -1){
|
|
|
|
var attrSplit = attr.split(".");
|
|
|
|
for (var j = 0; j < attrSplit.length - 1; j++){
|
|
|
|
var subAttr = attrSplit[j];
|
|
|
|
subRet[subAttr] = subRet[subAttr] || {};
|
|
|
|
subRet = subRet[subAttr];
|
|
|
|
parent = parent[subAttr];
|
|
|
|
}
|
|
|
|
attr = attrSplit[attrSplit.length - 1];
|
|
|
|
}
|
|
|
|
var param = parent[attr];
|
2017-04-26 04:24:19 +00:00
|
|
|
if (Tone.isObject(params[attr])){
|
2015-05-24 13:34:17 +00:00
|
|
|
subRet[attr] = param.get();
|
2015-08-16 18:23:30 +00:00
|
|
|
} else if (Tone.Signal && param instanceof Tone.Signal){
|
2015-05-24 13:34:17 +00:00
|
|
|
subRet[attr] = param.value;
|
2015-10-21 14:29:04 +00:00
|
|
|
} else if (Tone.Param && param instanceof Tone.Param){
|
|
|
|
subRet[attr] = param.value;
|
2015-02-10 16:41:14 +00:00
|
|
|
} else if (param instanceof AudioParam){
|
2015-05-24 13:34:17 +00:00
|
|
|
subRet[attr] = param.value;
|
2015-02-10 16:41:14 +00:00
|
|
|
} else if (param instanceof Tone){
|
2015-05-24 13:34:17 +00:00
|
|
|
subRet[attr] = param.get();
|
2017-04-26 04:24:19 +00:00
|
|
|
} else if (!Tone.isFunction(param) && !Tone.isUndef(param)){
|
2015-05-24 13:34:17 +00:00
|
|
|
subRet[attr] = param;
|
2017-08-26 16:47:44 +00:00
|
|
|
}
|
2015-02-02 01:02:13 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* collect all of the default attributes in one
|
|
|
|
* @private
|
|
|
|
* @param {function} constr the constructor to find the defaults from
|
2015-05-13 16:13:00 +00:00
|
|
|
* @return {Array} all of the attributes which belong to the class
|
2015-02-02 01:02:13 +00:00
|
|
|
*/
|
|
|
|
Tone.prototype._collectDefaults = function(constr){
|
2015-05-13 16:13:00 +00:00
|
|
|
var ret = [];
|
2017-04-26 04:24:19 +00:00
|
|
|
if (!Tone.isUndef(constr.defaults)){
|
2015-05-13 16:13:00 +00:00
|
|
|
ret = Object.keys(constr.defaults);
|
2015-02-02 01:02:13 +00:00
|
|
|
}
|
2017-04-26 04:24:19 +00:00
|
|
|
if (!Tone.isUndef(constr._super)){
|
2015-05-13 03:46:12 +00:00
|
|
|
var superDefs = this._collectDefaults(constr._super);
|
2015-05-23 22:57:05 +00:00
|
|
|
//filter out repeats
|
|
|
|
for (var i = 0; i < superDefs.length; i++){
|
|
|
|
if (ret.indexOf(superDefs[i]) === -1){
|
|
|
|
ret.push(superDefs[i]);
|
|
|
|
}
|
|
|
|
}
|
2015-02-02 01:02:13 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2017-05-01 18:06:36 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// DEFAULTS
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2015-05-13 03:46:12 +00:00
|
|
|
|
2017-04-25 02:08:59 +00:00
|
|
|
/**
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-04-25 02:08:59 +00:00
|
|
|
* @param {Array} values The arguments array
|
|
|
|
* @param {Array} keys The names of the arguments
|
2017-08-26 16:47:44 +00:00
|
|
|
* @param {Function|Object} constr The class constructor
|
2017-04-25 02:08:59 +00:00
|
|
|
* @return {Object} An object composed of the defaults between the class' defaults
|
|
|
|
* and the passed in arguments.
|
|
|
|
*/
|
2017-04-26 02:22:29 +00:00
|
|
|
Tone.defaults = function(values, keys, constr){
|
2017-04-25 02:08:59 +00:00
|
|
|
var options = {};
|
2017-04-26 02:22:29 +00:00
|
|
|
if (values.length === 1 && Tone.isObject(values[0])){
|
2017-04-25 02:08:59 +00:00
|
|
|
options = values[0];
|
|
|
|
} else {
|
|
|
|
for (var i = 0; i < keys.length; i++){
|
|
|
|
options[keys[i]] = values[i];
|
|
|
|
}
|
|
|
|
}
|
2017-04-26 02:22:29 +00:00
|
|
|
if (!Tone.isUndef(constr.defaults)){
|
|
|
|
return Tone.defaultArg(options, constr.defaults);
|
2017-08-26 16:47:44 +00:00
|
|
|
} else if (Tone.isObject(constr)){
|
|
|
|
return Tone.defaultArg(options, constr);
|
2017-04-25 02:08:59 +00:00
|
|
|
} else {
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-12-19 01:57:00 +00:00
|
|
|
/**
|
2017-08-26 16:47:44 +00:00
|
|
|
* If the `given` parameter is undefined, use the `fallback`.
|
2017-05-01 18:06:36 +00:00
|
|
|
* If both `given` and `fallback` are object literals, it will
|
2017-08-26 16:47:44 +00:00
|
|
|
* return a deep copy which includes all of the parameters from both
|
2017-05-01 18:06:36 +00:00
|
|
|
* objects. If a parameter is undefined in given, it will return
|
2017-08-26 16:47:44 +00:00
|
|
|
* the fallback property.
|
2017-05-01 18:06:36 +00:00
|
|
|
* <br><br>
|
2017-08-26 16:47:44 +00:00
|
|
|
* WARNING: if object is self referential, it will go into an an
|
2017-05-01 18:06:36 +00:00
|
|
|
* infinite recursive loop.
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-08-26 16:47:44 +00:00
|
|
|
* @param {*} given
|
|
|
|
* @param {*} fallback
|
|
|
|
* @return {*}
|
2016-12-19 01:57:00 +00:00
|
|
|
*/
|
2017-05-01 18:06:36 +00:00
|
|
|
Tone.defaultArg = function(given, fallback){
|
|
|
|
if (Tone.isObject(given) && Tone.isObject(fallback)){
|
|
|
|
var ret = {};
|
|
|
|
//make a deep copy of the given object
|
|
|
|
for (var givenProp in given) {
|
|
|
|
ret[givenProp] = Tone.defaultArg(fallback[givenProp], given[givenProp]);
|
2016-12-19 01:57:00 +00:00
|
|
|
}
|
2017-05-01 18:06:36 +00:00
|
|
|
for (var fallbackProp in fallback) {
|
|
|
|
ret[fallbackProp] = Tone.defaultArg(given[fallbackProp], fallback[fallbackProp]);
|
2016-12-19 01:57:00 +00:00
|
|
|
}
|
2017-05-01 18:06:36 +00:00
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
return Tone.isUndef(given) ? fallback : given;
|
2016-12-19 01:57:00 +00:00
|
|
|
}
|
2017-05-01 18:06:36 +00:00
|
|
|
};
|
|
|
|
|
2014-06-16 05:44:00 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// CONNECTIONS
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2014-03-12 20:29:43 +00:00
|
|
|
|
2014-06-16 05:44:00 +00:00
|
|
|
/**
|
2017-05-01 18:06:36 +00:00
|
|
|
* connect together all of the arguments in series
|
|
|
|
* @param {...AudioParam|Tone|AudioNode} nodes
|
|
|
|
* @returns {Tone}
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2014-06-16 05:44:00 +00:00
|
|
|
*/
|
2017-05-01 18:06:36 +00:00
|
|
|
Tone.connectSeries = function(){
|
2017-05-01 18:26:11 +00:00
|
|
|
var currentUnit = arguments[0];
|
|
|
|
for (var i = 1; i < arguments.length; i++){
|
|
|
|
var toUnit = arguments[i];
|
|
|
|
currentUnit.connect(toUnit);
|
|
|
|
currentUnit = toUnit;
|
2017-04-26 02:22:29 +00:00
|
|
|
}
|
2017-05-01 18:06:36 +00:00
|
|
|
return Tone;
|
2017-04-26 02:22:29 +00:00
|
|
|
};
|
|
|
|
|
2015-10-21 17:12:51 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// TYPE CHECKING
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2017-04-26 02:22:29 +00:00
|
|
|
/**
|
|
|
|
* test if the arg is undefined
|
|
|
|
* @param {*} arg the argument to test
|
|
|
|
* @returns {boolean} true if the arg is undefined
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-04-26 02:22:29 +00:00
|
|
|
*/
|
|
|
|
Tone.isUndef = function(val){
|
|
|
|
return typeof val === "undefined";
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* test if the arg is a function
|
|
|
|
* @param {*} arg the argument to test
|
|
|
|
* @returns {boolean} true if the arg is a function
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-04-26 02:22:29 +00:00
|
|
|
*/
|
|
|
|
Tone.isFunction = function(val){
|
|
|
|
return typeof val === "function";
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test if the argument is a number.
|
|
|
|
* @param {*} arg the argument to test
|
|
|
|
* @returns {boolean} true if the arg is a number
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-04-26 02:22:29 +00:00
|
|
|
*/
|
|
|
|
Tone.isNumber = function(arg){
|
|
|
|
return (typeof arg === "number");
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test if the given argument is an object literal (i.e. `{}`);
|
|
|
|
* @param {*} arg the argument to test
|
|
|
|
* @returns {boolean} true if the arg is an object literal.
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-04-26 02:22:29 +00:00
|
|
|
*/
|
|
|
|
Tone.isObject = function(arg){
|
|
|
|
return (Object.prototype.toString.call(arg) === "[object Object]" && arg.constructor === Object);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test if the argument is a boolean.
|
|
|
|
* @param {*} arg the argument to test
|
|
|
|
* @returns {boolean} true if the arg is a boolean
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-04-26 02:22:29 +00:00
|
|
|
*/
|
|
|
|
Tone.isBoolean = function(arg){
|
|
|
|
return (typeof arg === "boolean");
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test if the argument is an Array
|
|
|
|
* @param {*} arg the argument to test
|
|
|
|
* @returns {boolean} true if the arg is an array
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-04-26 02:22:29 +00:00
|
|
|
*/
|
|
|
|
Tone.isArray = function(arg){
|
|
|
|
return (Array.isArray(arg));
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test if the argument is a string.
|
|
|
|
* @param {*} arg the argument to test
|
|
|
|
* @returns {boolean} true if the arg is a string
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-04-26 02:22:29 +00:00
|
|
|
*/
|
|
|
|
Tone.isString = function(arg){
|
|
|
|
return (typeof arg === "string");
|
|
|
|
};
|
|
|
|
|
2017-05-01 15:11:11 +00:00
|
|
|
/**
|
|
|
|
* Test if the argument is in the form of a note in scientific pitch notation.
|
|
|
|
* e.g. "C4"
|
|
|
|
* @param {*} arg the argument to test
|
|
|
|
* @returns {boolean} true if the arg is a string
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2017-05-01 15:11:11 +00:00
|
|
|
*/
|
|
|
|
Tone.isNote = function(arg){
|
|
|
|
return Tone.isString(arg) && /^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i.test(arg);
|
|
|
|
};
|
|
|
|
|
2017-10-21 23:02:46 +00:00
|
|
|
/**
|
2017-04-26 04:27:28 +00:00
|
|
|
* An empty function.
|
|
|
|
* @static
|
|
|
|
*/
|
|
|
|
Tone.noOp = function(){};
|
|
|
|
|
2015-01-06 04:33:05 +00:00
|
|
|
/**
|
2017-08-26 16:47:44 +00:00
|
|
|
* Make the property not writable. Internal use only.
|
2015-04-05 18:00:52 +00:00
|
|
|
* @private
|
2015-04-05 18:42:32 +00:00
|
|
|
* @param {string} property the property to make not writable
|
2015-01-06 04:33:05 +00:00
|
|
|
*/
|
2015-04-05 18:00:52 +00:00
|
|
|
Tone.prototype._readOnly = function(property){
|
2015-04-05 18:53:27 +00:00
|
|
|
if (Array.isArray(property)){
|
|
|
|
for (var i = 0; i < property.length; i++){
|
|
|
|
this._readOnly(property[i]);
|
|
|
|
}
|
|
|
|
} else {
|
2017-08-26 16:47:44 +00:00
|
|
|
Object.defineProperty(this, property, {
|
2017-10-25 21:57:52 +00:00
|
|
|
writable : false,
|
2015-04-05 18:53:27 +00:00
|
|
|
enumerable : true,
|
|
|
|
});
|
|
|
|
}
|
2015-01-06 04:33:05 +00:00
|
|
|
};
|
2015-01-06 03:45:24 +00:00
|
|
|
|
2015-04-05 18:42:32 +00:00
|
|
|
/**
|
2017-08-26 16:47:44 +00:00
|
|
|
* Make an attribute writeable. Interal use only.
|
2015-04-05 18:42:32 +00:00
|
|
|
* @private
|
|
|
|
* @param {string} property the property to make writable
|
|
|
|
*/
|
|
|
|
Tone.prototype._writable = function(property){
|
2015-04-05 18:53:27 +00:00
|
|
|
if (Array.isArray(property)){
|
|
|
|
for (var i = 0; i < property.length; i++){
|
|
|
|
this._writable(property[i]);
|
|
|
|
}
|
|
|
|
} else {
|
2017-08-26 16:47:44 +00:00
|
|
|
Object.defineProperty(this, property, {
|
2017-10-25 21:57:52 +00:00
|
|
|
writable : true,
|
2015-04-05 18:53:27 +00:00
|
|
|
});
|
|
|
|
}
|
2015-04-05 18:42:32 +00:00
|
|
|
};
|
|
|
|
|
2015-07-18 18:59:18 +00:00
|
|
|
/**
|
2017-08-26 16:47:44 +00:00
|
|
|
* Possible play states.
|
2015-07-18 18:59:18 +00:00
|
|
|
* @enum {string}
|
|
|
|
*/
|
|
|
|
Tone.State = {
|
|
|
|
Started : "started",
|
|
|
|
Stopped : "stopped",
|
|
|
|
Paused : "paused",
|
2017-10-21 23:02:46 +00:00
|
|
|
};
|
2015-07-18 18:59:18 +00:00
|
|
|
|
2015-01-06 03:45:24 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2017-02-11 18:56:51 +00:00
|
|
|
// CONVERSIONS
|
2015-01-06 03:45:24 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2014-06-16 05:44:00 +00:00
|
|
|
/**
|
2015-08-21 21:05:09 +00:00
|
|
|
* Equal power gain scale. Good for cross-fading.
|
|
|
|
* @param {NormalRange} percent (0-1)
|
2015-11-18 03:52:22 +00:00
|
|
|
* @return {Number} output gain (0-1)
|
2017-05-08 02:15:35 +00:00
|
|
|
* @static
|
|
|
|
* @memberOf Tone
|
2014-06-16 05:44:00 +00:00
|
|
|
*/
|
2017-04-27 03:21:26 +00:00
|
|
|
Tone.equalPowerScale = function(percent){
|
2014-06-16 05:44:00 +00:00
|
|
|
var piFactor = 0.5 * Math.PI;
|
|
|
|
return Math.sin(percent * piFactor);
|
2014-06-15 21:38:36 +00:00
|
|
|
};
|
2014-03-12 20:29:43 +00:00
|
|
|
|
2014-06-16 05:44:00 +00:00
|
|
|
/**
|
2015-08-21 21:05:09 +00:00
|
|
|
* Convert decibels into gain.
|
|
|
|
* @param {Decibels} db
|
2017-08-26 16:47:44 +00:00
|
|
|
* @return {Number}
|
|
|
|
* @static
|
|
|
|
* @memberOf Tone
|
2014-06-16 05:44:00 +00:00
|
|
|
*/
|
2017-04-27 03:21:26 +00:00
|
|
|
Tone.dbToGain = function(db) {
|
2017-12-16 05:04:50 +00:00
|
|
|
return Math.pow(10, db / 20);
|
2014-06-15 21:38:36 +00:00
|
|
|
};
|
2014-03-12 20:29:43 +00:00
|
|
|
|
2014-06-16 05:44:00 +00:00
|
|
|
/**
|
2015-08-21 21:05:09 +00:00
|
|
|
* Convert gain to decibels.
|
2015-11-18 03:52:22 +00:00
|
|
|
* @param {Number} gain (0-1)
|
2017-08-26 16:47:44 +00:00
|
|
|
* @return {Decibels}
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2014-06-16 05:44:00 +00:00
|
|
|
*/
|
2017-04-27 03:21:26 +00:00
|
|
|
Tone.gainToDb = function(gain) {
|
2017-10-26 19:26:04 +00:00
|
|
|
return 20 * (Math.log(gain) / Math.LN10);
|
2014-06-15 21:38:36 +00:00
|
|
|
};
|
2014-03-12 20:29:43 +00:00
|
|
|
|
2016-04-13 00:30:18 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2017-05-08 02:15:35 +00:00
|
|
|
* @static
|
|
|
|
* @memberOf Tone
|
2016-04-13 00:30:18 +00:00
|
|
|
* @example
|
|
|
|
* tone.intervalToFrequencyRatio(0); // 1
|
|
|
|
* tone.intervalToFrequencyRatio(12); // 2
|
|
|
|
* tone.intervalToFrequencyRatio(-12); // 0.5
|
|
|
|
*/
|
2017-04-27 03:21:26 +00:00
|
|
|
Tone.intervalToFrequencyRatio = function(interval){
|
2018-01-03 17:07:06 +00:00
|
|
|
return Math.pow(2, (interval/12));
|
2016-04-13 00:30:18 +00:00
|
|
|
};
|
|
|
|
|
2014-03-16 17:33:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2014-04-11 23:17:01 +00:00
|
|
|
// TIMING
|
2014-03-16 17:33:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2014-06-16 05:44:00 +00:00
|
|
|
/**
|
2016-02-25 22:58:26 +00:00
|
|
|
* Return the current time of the AudioContext clock.
|
|
|
|
* @return {Number} the currentTime from the AudioContext
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone#
|
2014-06-16 05:44:00 +00:00
|
|
|
*/
|
|
|
|
Tone.prototype.now = function(){
|
2017-02-19 00:49:06 +00:00
|
|
|
return Tone.context.now();
|
2014-06-16 05:44:00 +00:00
|
|
|
};
|
|
|
|
|
2016-02-25 22:58:26 +00:00
|
|
|
/**
|
|
|
|
* Return the current time of the AudioContext clock.
|
|
|
|
* @return {Number} the currentTime from the AudioContext
|
|
|
|
* @static
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2016-02-25 22:58:26 +00:00
|
|
|
*/
|
|
|
|
Tone.now = function(){
|
2017-02-19 00:49:06 +00:00
|
|
|
return Tone.context.now();
|
2016-02-25 22:58:26 +00:00
|
|
|
};
|
|
|
|
|
2017-09-15 21:45:57 +00:00
|
|
|
/**
|
|
|
|
* Adds warning in the console if the scheduled time has passed.
|
|
|
|
* @type {Time}
|
|
|
|
*/
|
|
|
|
Tone.isPast = function(time){
|
|
|
|
if (time < Tone.context.currentTime){
|
2017-10-21 23:02:46 +00:00
|
|
|
// eslint-disable-next-line no-console
|
2017-09-15 21:45:57 +00:00
|
|
|
console.warn("Time '" + time + "' is in the past. Scheduled time must be ≥ AudioContext.currentTime");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-02-01 20:48:20 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2015-02-02 01:02:13 +00:00
|
|
|
// INHERITANCE
|
2015-02-01 20:48:20 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2014-06-15 21:38:36 +00:00
|
|
|
/**
|
|
|
|
* have a child inherit all of Tone's (or a parent's) prototype
|
2017-08-26 16:47:44 +00:00
|
|
|
* to inherit the parent's properties, make sure to call
|
2014-06-15 21:38:36 +00:00
|
|
|
* Parent.call(this) in the child's constructor
|
|
|
|
*
|
|
|
|
* based on closure library's inherit function
|
2014-07-22 15:30:18 +00:00
|
|
|
*
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
2014-07-22 15:30:18 +00:00
|
|
|
* @static
|
2017-08-26 16:47:44 +00:00
|
|
|
* @param {function} child
|
2014-06-15 21:38:36 +00:00
|
|
|
* @param {function=} parent (optional) parent to inherit from
|
|
|
|
* if no parent is supplied, the child
|
|
|
|
* will inherit from Tone
|
|
|
|
*/
|
2014-03-16 17:33:56 +00:00
|
|
|
Tone.extend = function(child, parent){
|
2017-04-26 04:27:28 +00:00
|
|
|
if (Tone.isUndef(parent)){
|
2014-04-04 17:07:16 +00:00
|
|
|
parent = Tone;
|
|
|
|
}
|
2014-09-10 17:51:37 +00:00
|
|
|
function TempConstructor(){}
|
|
|
|
TempConstructor.prototype = parent.prototype;
|
|
|
|
child.prototype = new TempConstructor();
|
2014-03-15 05:02:33 +00:00
|
|
|
/** @override */
|
|
|
|
child.prototype.constructor = child;
|
2015-02-02 01:02:13 +00:00
|
|
|
child._super = parent;
|
|
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// CONTEXT
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/**
|
2017-08-26 16:47:44 +00:00
|
|
|
* A static pointer to the audio context accessible as Tone.context.
|
2017-02-19 00:49:06 +00:00
|
|
|
* @type {Tone.Context}
|
2017-02-11 18:56:51 +00:00
|
|
|
* @name context
|
|
|
|
* @memberOf Tone
|
2015-02-02 01:02:13 +00:00
|
|
|
*/
|
2017-02-11 18:56:51 +00:00
|
|
|
Object.defineProperty(Tone, "context", {
|
|
|
|
get : function(){
|
2017-09-29 03:44:07 +00:00
|
|
|
return window.TONE_AUDIO_CONTEXT;
|
2017-02-11 18:56:51 +00:00
|
|
|
},
|
|
|
|
set : function(context){
|
2017-02-19 00:49:06 +00:00
|
|
|
if (Tone.Context && context instanceof Tone.Context){
|
2017-09-29 03:44:07 +00:00
|
|
|
window.TONE_AUDIO_CONTEXT = context;
|
2017-02-19 00:49:06 +00:00
|
|
|
} else {
|
2017-09-29 03:44:07 +00:00
|
|
|
window.TONE_AUDIO_CONTEXT = new Tone.Context(context);
|
2017-02-19 00:49:06 +00:00
|
|
|
}
|
|
|
|
//initialize the new audio context
|
2017-09-29 03:44:07 +00:00
|
|
|
Tone.Context.emit("init", window.TONE_AUDIO_CONTEXT);
|
2017-02-11 18:56:51 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
2017-02-19 00:49:06 +00:00
|
|
|
* The AudioContext
|
|
|
|
* @type {Tone.Context}
|
2017-02-11 18:56:51 +00:00
|
|
|
* @name context
|
|
|
|
* @memberOf Tone#
|
|
|
|
* @readOnly
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.prototype, "context", {
|
|
|
|
get : function(){
|
2017-02-19 00:49:06 +00:00
|
|
|
return Tone.context;
|
2017-02-11 18:56:51 +00:00
|
|
|
}
|
|
|
|
});
|
2015-02-02 01:02:13 +00:00
|
|
|
|
|
|
|
/**
|
2015-06-15 15:27:13 +00:00
|
|
|
* Tone automatically creates a context on init, but if you are working
|
2015-02-25 21:20:12 +00:00
|
|
|
* with other libraries which also create an AudioContext, it can be
|
2017-08-26 16:47:44 +00:00
|
|
|
* useful to set your own. If you are going to set your own context,
|
2015-02-25 21:20:12 +00:00
|
|
|
* be sure to do it at the start of your code, before creating any objects.
|
2015-02-02 01:02:13 +00:00
|
|
|
* @static
|
2015-02-25 21:20:12 +00:00
|
|
|
* @param {AudioContext} ctx The new audio context to set
|
2015-02-02 01:02:13 +00:00
|
|
|
*/
|
|
|
|
Tone.setContext = function(ctx){
|
|
|
|
Tone.context = ctx;
|
2014-06-15 21:38:36 +00:00
|
|
|
};
|
2014-03-12 20:29:43 +00:00
|
|
|
|
2017-05-01 18:06:36 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// ATTRIBUTES
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2017-02-11 18:56:51 +00:00
|
|
|
/**
|
|
|
|
* The number of seconds of 1 processing block (128 samples)
|
|
|
|
* @type {Number}
|
|
|
|
* @name blockTime
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
|
|
|
* @static
|
2017-02-11 18:56:51 +00:00
|
|
|
* @readOnly
|
|
|
|
*/
|
2017-07-27 21:54:57 +00:00
|
|
|
Object.defineProperty(Tone.prototype, "blockTime", {
|
2017-02-11 18:56:51 +00:00
|
|
|
get : function(){
|
2017-07-27 21:54:57 +00:00
|
|
|
return 128 / this.context.sampleRate;
|
2017-02-11 18:56:51 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The duration in seconds of one sample.
|
|
|
|
* @type {Number}
|
|
|
|
* @name sampleTime
|
2017-05-08 02:15:35 +00:00
|
|
|
* @memberOf Tone
|
|
|
|
* @static
|
2017-02-11 18:56:51 +00:00
|
|
|
* @readOnly
|
|
|
|
*/
|
2017-07-27 21:54:57 +00:00
|
|
|
Object.defineProperty(Tone.prototype, "sampleTime", {
|
2017-02-11 18:56:51 +00:00
|
|
|
get : function(){
|
2017-07-27 21:54:57 +00:00
|
|
|
return 1 / this.context.sampleRate;
|
2017-02-11 18:56:51 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
2017-08-26 16:47:44 +00:00
|
|
|
* Whether or not all the technologies that Tone.js relies on are supported by the current browser.
|
2017-02-11 18:56:51 +00:00
|
|
|
* @type {Boolean}
|
|
|
|
* @name supported
|
|
|
|
* @memberOf Tone
|
|
|
|
* @readOnly
|
2017-05-01 18:06:36 +00:00
|
|
|
* @static
|
2017-02-11 18:56:51 +00:00
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone, "supported", {
|
|
|
|
get : function(){
|
|
|
|
var hasAudioContext = window.hasOwnProperty("AudioContext") || window.hasOwnProperty("webkitAudioContext");
|
|
|
|
var hasPromises = window.hasOwnProperty("Promise");
|
2017-03-13 01:03:17 +00:00
|
|
|
var hasWorkers = window.hasOwnProperty("Worker");
|
|
|
|
return hasAudioContext && hasPromises && hasWorkers;
|
2017-02-11 18:56:51 +00:00
|
|
|
}
|
2014-07-30 17:55:59 +00:00
|
|
|
});
|
|
|
|
|
2017-08-14 01:36:21 +00:00
|
|
|
/**
|
|
|
|
* Boolean value if the audio context has been initialized.
|
|
|
|
* @type {Boolean}
|
|
|
|
* @memberOf Tone
|
|
|
|
* @static
|
|
|
|
* @name initialized
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone, "initialized", {
|
|
|
|
get : function(){
|
2017-09-29 03:44:07 +00:00
|
|
|
return !Tone.isUndef(window.TONE_AUDIO_CONTEXT);
|
2017-08-14 01:36:21 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the context when it becomes available
|
|
|
|
* @param {Function} resolve Callback when the context is initialized
|
|
|
|
* @return {Tone}
|
|
|
|
*/
|
|
|
|
Tone.getContext = function(resolve){
|
|
|
|
if (Tone.initialized){
|
|
|
|
resolve(Tone.context);
|
|
|
|
} else {
|
|
|
|
var resCallback = function(){
|
|
|
|
resolve(Tone.context);
|
|
|
|
Tone.Context.off("init", resCallback);
|
2017-08-14 01:46:43 +00:00
|
|
|
};
|
2017-08-14 01:36:21 +00:00
|
|
|
Tone.Context.on("init", resCallback);
|
|
|
|
}
|
|
|
|
return Tone;
|
|
|
|
};
|
|
|
|
|
2017-05-01 18:06:36 +00:00
|
|
|
/**
|
|
|
|
* The version number
|
|
|
|
* @type {String}
|
|
|
|
* @static
|
|
|
|
*/
|
2017-09-17 16:19:39 +00:00
|
|
|
Tone.version = "r12-dev";
|
2015-05-23 22:57:05 +00:00
|
|
|
|
2014-08-25 22:43:37 +00:00
|
|
|
return Tone;
|
2014-06-19 17:38:21 +00:00
|
|
|
});
|