Last active
December 19, 2015 18:38
-
-
Save therewasaguy/0ad4bf964551aaf0bab7 to your computer and use it in GitHub Desktop.
update
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// shims | |
var requestAnimationFrame = window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame; | |
var cancelAnimationFrame = window.cancelAnimationFrame || | |
window.webkitCancelRequestAnimationFrame || | |
window.webkitCancelAnimationFrame || | |
window.mozCancelRequestAnimationFrame || window.mozCancelAnimationFrame || | |
window.oCancelRequestAnimationFrame || window.oCancelAnimationFrame || | |
window.msCancelRequestAnimationFrame || window.msCancelAnimationFrame; | |
navigator.getUserMedia = navigator.getUserMedia || | |
navigator.webkitGetUserMedia || navigator.mozGetUserMedia || | |
navigator.msGetUserMedia; | |
// two canvases / contexts | |
var cnv1, ctx1, cnv2, ctx2; | |
var video; | |
var numRows = 8; | |
var numCols = 2; | |
var counter = 0; | |
var curMatrixIndex, prevMatrixIndex; | |
var curColor = [0, 0, 0]; | |
var w, h; | |
var pixelScaling = 12; | |
// event listeners | |
window.addEventListener('load', init); | |
function init() { | |
w = window.innerWidth/pixelScaling; | |
h = window.innerHeight/pixelScaling; | |
cnv1 = document.createElement('canvas'); | |
cnv1.width = w; | |
cnv1.height = h; | |
ctx1 = cnv1.getContext('2d'); | |
// flip canvas | |
ctx1.translate(cnv1.width, 0); | |
ctx1.scale(-1, 1); | |
cnv2 = document.createElement('canvas'); | |
cnv2.width = w*pixelScaling; | |
cnv2.height = h*pixelScaling; | |
ctx2 = cnv2.getContext('2d'); | |
ctx2.scale(pixelScaling,pixelScaling); | |
var container = document.getElementById('container'); | |
container.appendChild(cnv2); | |
video = document.createElement('video'); | |
video.width = w; | |
video.height = h; | |
video.hidden = true; | |
document.body.appendChild(video); | |
startCam(); | |
draw(); | |
} | |
function startCam() { | |
if (navigator.getUserMedia){ | |
navigator.getUserMedia({video: true}, function(stream) { | |
video.src = window.URL.createObjectURL(stream) || stream; | |
video.play(); | |
}, function(error) {alert("Your browser doesnt allow access to webcam. Try Chrome ");}); | |
} | |
}; | |
function draw() { | |
var rowWidth = Math.round(w/numRows); | |
var colHeight = Math.round(h/numCols); | |
// draw video to graphics context 1 (which is hidden) | |
ctx1.drawImage(video, 0, 0, cnv1.width, cnv1.height); | |
// keep track of which box we're in | |
var indexCounter = 0; | |
// split into rows and colums | |
for (var col = 0; col < (h - colHeight); col+= colHeight) { | |
for (var row = 0; row < (w - rowWidth); row+= rowWidth) { | |
ctx2.globalAlpha=0.9; | |
// get a rectangle of image data (pixels) | |
var imgData = ctx1.getImageData(row, col, rowWidth, colHeight); | |
// find average rgb value in this rectangle | |
var avgRGB = getAverageRGB(imgData.data); | |
// draw a rectangle on graphics context 2 (visible) | |
var c = rgbToHex(avgRGB); | |
ctx2.fillStyle=c; | |
ctx2.fillRect(row, col, rowWidth, colHeight); | |
// show which item we're on. curMatrixIndex is set by tone.loop in app-audio.js | |
if (curMatrixIndex === indexCounter) { | |
ctx2.globalAlpha=0.07; | |
ctx2.fillStyle='#fffff0'; | |
ctx2.fillRect(row, col, rowWidth, colHeight); | |
curColor = rgbToHsl(avgRGB); | |
} | |
indexCounter++; | |
} | |
} | |
// call this function again in 50 ms | |
setTimeout(draw,50); | |
} | |
// via https://jsfiddle.net/xLF38/ | |
function getAverageRGB(pxls) { | |
var blockSize = 5, // only visit every 5 pixels | |
rgb = {r: 0, g: 0, b: 0}, | |
i = -4, | |
length, | |
count = 0; | |
var length = pxls.length; | |
while ( (i += blockSize * 4) < length ) { | |
++count; | |
rgb.r += pxls[i]; | |
rgb.g += pxls[i+1]; | |
rgb.b += pxls[i+2]; | |
} | |
// ~~ used to floor values | |
rgb.r = ~~(rgb.r/count); | |
rgb.g = ~~(rgb.g/count); | |
rgb.b = ~~(rgb.b/count); | |
return rgb; | |
} | |
// via http://stackoverflow.com/a/5624139 | |
function componentToHex(c) { | |
var hex = c.toString(16); | |
return hex.length == 1 ? "0" + hex : hex; | |
} | |
function rgbToHex(c) { | |
return "#" + componentToHex(c.r) + componentToHex(c.g) + componentToHex(c.b); | |
} | |
/** | |
* via http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c | |
* Converts an RGB color value to HSL. Conversion formula | |
* adapted from http://en.wikipedia.org/wiki/HSL_color_space. | |
* Assumes r, g, and b are contained in the set [0, 255] and | |
* returns h, s, and l in the set [0, 1]. | |
* | |
* @param Number r The red color value | |
* @param Number g The green color value | |
* @param Number b The blue color value | |
* @return Array The HSL representation | |
*/ | |
function rgbToHsl(c){ | |
var r = c.r/255, g = c.g/255, b = c.b/= 255; | |
var max = Math.max(r, g, b), min = Math.min(r, g, b); | |
var h, s, l = (max + min) / 2; | |
if(max == min){ | |
h = s = 0; // achromatic | |
}else{ | |
var d = max - min; | |
s = l > 0.5 ? d / (2 - max - min) : d / (max + min); | |
switch(max){ | |
case r: h = (g - b) / d + (g < b ? 6 : 0); break; | |
case g: h = (b - r) / d + 2; break; | |
case b: h = (r - g) / d + 4; break; | |
} | |
h /= 6; | |
} | |
return [h, s, l]; | |
} | |
function map(value, low1, high1, low2, high2) { | |
return low2 + (high2 - low2) * (value - low1) / (high1 - low1); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<head> | |
<script src="tone.js"></script> | |
<script src="cam.js"></script> | |
<script src="sketch.js"></script> | |
<style> | |
body { | |
background:#222; | |
} | |
#container { | |
width:100%; | |
height:100%; | |
} | |
canvas { | |
position: absolute; | |
top: 0px; | |
left: 0px; | |
right:0px; | |
bottom:0px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="container"> | |
</div> | |
</body> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! p5.dom.js v0.2.2 May 30, 2015 */ | |
/** | |
* <p>The web is much more than just canvas and p5.dom makes it easy to interact | |
* with other HTML5 objects, including text, hyperlink, image, input, video, | |
* audio, and webcam.</p> | |
* <p>There is a set of creation methods, DOM manipulation methods, and | |
* an extended p5.Element that supports a range of HTML elements. See the | |
* <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas"> | |
* beyond the canvas tutorial</a> for a full overview of how this addon works. | |
* | |
* <p>Methods and properties shown in black are part of the p5.js core, items in | |
* blue are part of the p5.dom library. You will need to include an extra file | |
* in order to access the blue functions. See the | |
* <a href="http://p5js.org/libraries/#using-a-library">using a library</a> | |
* section for information on how to include this library. p5.dom comes with | |
* <a href="http://p5js.org/download">p5 complete</a> or you can download the single file | |
* <a href="https://raw.githubusercontent.com/lmccart/p5.js/master/lib/addons/p5.dom.js"> | |
* here</a>.</p> | |
* <p>See <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">tutorial: beyond the canvas]</a> | |
* for more info on how to use this libary.</a> | |
* | |
* @module p5.dom | |
* @submodule p5.dom | |
* @for p5.dom | |
* @main | |
*/ | |
(function (root, factory) { | |
if (typeof define === 'function' && define.amd) | |
define('p5.dom', ['p5'], function (p5) { (factory(p5));}); | |
else if (typeof exports === 'object') | |
factory(require('../p5')); | |
else | |
factory(root['p5']); | |
}(this, function (p5) { | |
// ============================================================================= | |
// p5 additions | |
// ============================================================================= | |
/** | |
* Searches the page for an element with the given ID, class, or tag name (using the '#' or '.' | |
* prefixes to specify an ID or class respectively, and none for a tag) and returns it as | |
* a p5.Element. If a class or tag name is given with more than 1 element, | |
* only the first element will be returned. | |
* The DOM node itself can be accessed with .elt. | |
* Returns null if none found. | |
* | |
* @method select | |
* @param {String} name id, class, or tag name of element to search for | |
* @return {Object/p5.Element|Null} p5.Element containing node found | |
*/ | |
p5.prototype.select = function (e) { | |
var res; | |
var str; | |
if (e[0] === '.'){ | |
str = e.slice(1); | |
res = document.getElementsByClassName(str); | |
if (res) { | |
return wrapElement(res[0]); | |
}else { | |
return null; | |
} | |
}else if (e[0] === '#'){ | |
str = e.slice(1); | |
res = document.getElementById(str); | |
if (res) { | |
return wrapElement(res); | |
}else { | |
return null; | |
} | |
}else{ | |
res = document.getElementsByTagName(e); | |
if (res) { | |
return wrapElement(res[0]); | |
}else { | |
return null; | |
} | |
} | |
}; | |
/** | |
* Searches the page for elements with the given class or tag name (using the '.' prefix | |
* to specify a class and no prefix for a tag) and returns them as p5.Elements | |
* in an array. | |
* The DOM node itself can be accessed with .elt. | |
* Returns null if none found. | |
* | |
* @method selectAll | |
* @param {String} name class or tag name of elements to search for | |
* @return {Array} Array of p5.Elements containing nodes found | |
*/ | |
p5.prototype.selectAll = function (e) { | |
var arr = []; | |
var res; | |
var str; | |
if (e[0] === '.'){ | |
str = e.slice(1); | |
res = document.getElementsByClassName(str); | |
}else { | |
res = document.getElementsByTagName(e); | |
} | |
if (res) { | |
for (var j = 0; j < res.length; j++) { | |
var obj = wrapElement(res[j]); | |
arr.push(obj); | |
} | |
} | |
return arr; | |
}; | |
/** | |
* Helper function for getElement and getElements. | |
*/ | |
function wrapElement(elt) { | |
if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") { | |
return new p5.MediaElement(elt); | |
} else { | |
return new p5.Element(elt); | |
} | |
} | |
/** | |
* Removes all elements created by p5, except any canvas / graphics | |
* elements created by createCanvas or createGraphics. | |
* Event handlers are removed, and element is removed from the DOM. | |
* @method removeElements | |
* @example | |
* <div class='norender'><code> | |
* function setup() { | |
* createCanvas(100, 100); | |
* createDiv('this is some text'); | |
* createP('this is a paragraph'); | |
* } | |
* function mousePressed() { | |
* removeElements(); // this will remove the div and p, not canvas | |
* } | |
* </code></div> | |
* | |
*/ | |
p5.prototype.removeElements = function (e) { | |
for (var i=0; i<this._elements.length; i++) { | |
if (!(this._elements[i].elt instanceof HTMLCanvasElement)) { | |
this._elements[i].remove(); | |
} | |
} | |
}; | |
/** | |
* Helpers for create methods. | |
*/ | |
function addElement(elt, pInst, media) { | |
var node = pInst._userNode ? pInst._userNode : document.body; | |
node.appendChild(elt); | |
var c = media ? new p5.MediaElement(elt) : new p5.Element(elt); | |
pInst._elements.push(c); | |
return c; | |
} | |
/** | |
* Creates a <div></div> element in the DOM with given inner HTML. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createDiv | |
* @param {String} html inner HTML for element created | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
/** | |
* Creates a <p></p> element in the DOM with given inner HTML. Used | |
* for paragraph length text. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createP | |
* @param {String} html inner HTML for element created | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
/** | |
* Creates a <span></span> element in the DOM with given inner HTML. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createSpan | |
* @param {String} html inner HTML for element created | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
var tags = ['div', 'p', 'span']; | |
tags.forEach(function(tag) { | |
var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); | |
p5.prototype[method] = function(html) { | |
var elt = document.createElement(tag); | |
elt.innerHTML = typeof html === undefined ? "" : html; | |
return addElement(elt, this); | |
} | |
}); | |
/** | |
* Creates an <img /> element in the DOM with given src and | |
* alternate text. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createImg | |
* @param {String} src src path or url for image | |
* @param {String} [alt] alternate text to be used if image does not load | |
* @param {Function} [successCallback] callback to be called once image data is loaded | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
p5.prototype.createImg = function() { | |
var elt = document.createElement('img'); | |
var args = arguments; | |
var self = {}; | |
var setAttrs = function(){ | |
self.width = elt.width; | |
self.height = elt.height; | |
if (args.length === 3 && typeof args[2] === 'function'){ | |
self.fn = args[2]; | |
self.fn(); | |
} | |
}; | |
elt.src = args[0]; | |
if (args.length > 1 && typeof args[1] === 'string'){ | |
elt.alt = args[1]; | |
} | |
if (elt.complete){ | |
setAttrs(); | |
}else{ | |
elt.onload = function(){ | |
setAttrs(); | |
} | |
} | |
self = addElement(elt, this); | |
return self; | |
}; | |
/** | |
* Creates an <a></a> element in the DOM for including a hyperlink. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createA | |
* @param {String} href url of page to link to | |
* @param {String} html inner html of link element to display | |
* @param {String} [target] target where new link should open, | |
* could be _blank, _self, _parent, _top. | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
p5.prototype.createA = function(href, html, target) { | |
var elt = document.createElement('a'); | |
elt.href = href; | |
elt.innerHTML = html; | |
if (target) elt.target = target; | |
return addElement(elt, this); | |
}; | |
/** INPUT **/ | |
/** | |
* Creates a slider <input></input> element in the DOM. | |
* Use .size() to set the display length of the slider. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createSlider | |
* @param {Number} min minimum value of the slider | |
* @param {Number} max maximum value of the slider | |
* @param {Number} [value] default value of the slider | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
p5.prototype.createSlider = function(min, max, value, step) { | |
var elt = document.createElement('input'); | |
elt.type = 'range'; | |
elt.min = min; | |
elt.max = max; | |
if (step) elt.step = step; | |
if (value) elt.value = value; | |
return addElement(elt, this); | |
}; | |
/** | |
* Creates a <button></button> element in the DOM. | |
* Use .size() to set the display size of the button. | |
* Use .mousePressed() to specify behavior on press. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createButton | |
* @param {String} label label displayed on the button | |
* @param {String} [value] value of the button | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
p5.prototype.createButton = function(label, value) { | |
var elt = document.createElement('button'); | |
elt.innerHTML = label; | |
elt.value = value; | |
if (value) elt.value = value; | |
return addElement(elt, this); | |
}; | |
/** | |
* Creates a checkbox <input></input> element in the DOM. | |
* | |
* @method createCheckbox | |
* @param {String} [label] label displayed after checkbox | |
* @param {boolean} [value] value of the checkbox; checked is true, unchecked is false. Unchecked if no value given | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
p5.prototype.createCheckbox = function() { | |
var elt = document.createElement('input'); | |
elt.type = 'checkbox'; | |
//checkbox must be wrapped in p5.Element before label so that label appears after | |
var self = addElement(elt, this); | |
self.checked = function(){ | |
if (arguments.length === 0){ | |
return self.elt.checked; | |
}else if(arguments[0]){ | |
self.elt.checked = true; | |
}else{ | |
self.elt.checked = false; | |
} | |
return self; | |
}; | |
this.value = function(val){ | |
self.value = val; | |
return this; | |
}; | |
if (arguments[0]){ | |
var ran = Math.random().toString(36).slice(2); | |
var label = document.createElement('label'); | |
elt.setAttribute('id', ran); | |
label.htmlFor = ran; | |
self.value(arguments[0]); | |
label.appendChild(document.createTextNode(arguments[0])); | |
addElement(label, this); | |
} | |
if (arguments[1]){ | |
elt.checked = true; | |
} | |
return self; | |
}; | |
/** | |
* Creates a dropdown menu <select></select> element in the DOM. | |
* @method createSelect | |
* @param {boolean} [multiple] [true if dropdown should support multiple selections] | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
p5.prototype.createSelect = function(mult) { | |
var elt = document.createElement('select'); | |
if (mult){ | |
elt.setAttribute('multiple', 'true'); | |
} | |
var self = addElement(elt, this); | |
self.option = function(name, value){ | |
var opt = document.createElement('option'); | |
opt.innerHTML = name; | |
if (arguments.length > 1) | |
opt.value = value; | |
else | |
opt.value = name; | |
elt.appendChild(opt); | |
}; | |
self.selected = function(value){ | |
var arr = []; | |
if (arguments.length > 0){ | |
for (var i = 0; i < this.elt.length; i++){ | |
if (value.toString() === this.elt[i].value){ | |
this.elt.selectedIndex = i; | |
} | |
} | |
return this; | |
}else{ | |
if (mult){ | |
for (var i = 0; i < this.elt.selectedOptions.length; i++){ | |
arr.push(this.elt.selectedOptions[i].value); | |
} | |
return arr; | |
}else{ | |
return this.elt.value; | |
} | |
} | |
}; | |
return self; | |
}; | |
/** | |
* Creates an <input></input> element in the DOM for text input. | |
* Use .size() to set the display length of the box. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createInput | |
* @param {Number} [value] default value of the input box | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
p5.prototype.createInput = function(value) { | |
var elt = document.createElement('input'); | |
elt.type = 'text'; | |
if (value) elt.value = value; | |
return addElement(elt, this); | |
}; | |
/** | |
* Creates an <input></input> element in the DOM of type 'file'. | |
* This allows users to select local files for use in a sketch. | |
* | |
* @method createFileInput | |
* @param {Function} [callback] callback function for when a file loaded | |
* @param {String} [multiple] optional to allow multiple files selected | |
* @return {Object/p5.Element} pointer to p5.Element holding created DOM element | |
*/ | |
p5.prototype.createFileInput = function(callback, multiple) { | |
// Is the file stuff supported? | |
if (window.File && window.FileReader && window.FileList && window.Blob) { | |
// Yup, we're ok and make an input file selector | |
var elt = document.createElement('input'); | |
elt.type = 'file'; | |
// If we get a second argument that evaluates to true | |
// then we are looking for multiple files | |
if (multiple) { | |
// Anything gets the job done | |
elt.multiple = 'multiple'; | |
} | |
// Now let's handle when a file was selected | |
elt.addEventListener('change', handleFileSelect, false); | |
// Function to handle when a file is selected | |
// We're simplifying life and assuming that we always | |
// want to load every selected file | |
function handleFileSelect(evt) { | |
// These are the files | |
var files = evt.target.files; | |
// Load each one and trigger a callback | |
for (var i = 0; i < files.length; i++) { | |
var f = files[i]; | |
var reader = new FileReader(); | |
reader.onload = makeLoader(f); | |
function makeLoader(theFile) { | |
// Making a p5.File object | |
var p5file = new p5.File(theFile); | |
return function(e) { | |
p5file.data = e.target.result; | |
callback(p5file); | |
}; | |
}; | |
// Text of data? | |
// This should likely be improved | |
if (f.type === 'text') { | |
reader.readAsText(f); | |
} else { | |
reader.readAsDataURL(f); | |
} | |
} | |
} | |
return addElement(elt, this); | |
} else { | |
console.log('The File APIs are not fully supported in this browser. Cannot create element.'); | |
} | |
}; | |
/** VIDEO STUFF **/ | |
function createMedia(pInst, type, src, callback) { | |
var elt = document.createElement(type); | |
if (typeof src === 'string') { | |
src = [src]; | |
} | |
for (var i=0; i<src.length; i++) { | |
var source = document.createElement('source'); | |
source.src = src[i]; | |
elt.appendChild(source); | |
} | |
if (typeof callback !== 'undefined') { | |
elt.addEventListener('canplaythrough', function() { | |
callback(); | |
}); | |
} | |
var c = addElement(elt, pInst, true); | |
c.loadedmetadata = false; | |
// set width and height onload metadata | |
elt.addEventListener('loadedmetadata', function() { | |
c.width = elt.videoWidth; | |
c.height = elt.videoHeight; | |
c.loadedmetadata = true; | |
}); | |
return c; | |
} | |
/** | |
* Creates an HTML5 <video> element in the DOM for simple playback | |
* of audio/video. Shown by default, can be hidden with .hide() | |
* and drawn into canvas using video(). Appends to the container | |
* node if one is specified, otherwise appends to body. The first parameter | |
* can be either a single string path to a video file, or an array of string | |
* paths to different formats of the same video. This is useful for ensuring | |
* that your video can play across different browsers, as each supports | |
* different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this | |
* page for further information about supported formats. | |
* | |
* @method createVideo | |
* @param {String|Array} src path to a video file, or array of paths for | |
* supporting different browsers | |
* @param {Object} [callback] callback function to be called upon | |
* 'canplaythrough' event fire, that is, when the | |
* browser can play the media, and estimates that | |
* enough data has been loaded to play the media | |
* up to its end without having to stop for | |
* further buffering of content | |
* @return {Object/p5.Element} pointer to video p5.Element | |
*/ | |
p5.prototype.createVideo = function(src, callback) { | |
return createMedia(this, 'video', src, callback); | |
}; | |
/** AUDIO STUFF **/ | |
/** | |
* Creates a hidden HTML5 <audio> element in the DOM for simple audio | |
* playback. Appends to the container node if one is specified, | |
* otherwise appends to body. The first parameter | |
* can be either a single string path to a audio file, or an array of string | |
* paths to different formats of the same audio. This is useful for ensuring | |
* that your audio can play across different browsers, as each supports | |
* different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this | |
* page for further information about supported formats. | |
* | |
* @method createAudio | |
* @param {String|Array} src path to an audio file, or array of paths for | |
* supporting different browsers | |
* @param {Object} [callback] callback function to be called upon | |
* 'canplaythrough' event fire, that is, when the | |
* browser can play the media, and estimates that | |
* enough data has been loaded to play the media | |
* up to its end without having to stop for | |
* further buffering of content | |
* @return {Object/p5.Element} pointer to audio p5.Element | |
*/ | |
p5.prototype.createAudio = function(src, callback) { | |
return createMedia(this, 'audio', src, callback); | |
}; | |
/** CAMERA STUFF **/ | |
p5.prototype.VIDEO = 'video'; | |
p5.prototype.AUDIO = 'audio'; | |
navigator.getUserMedia = navigator.getUserMedia || | |
navigator.webkitGetUserMedia || | |
navigator.mozGetUserMedia || | |
navigator.msGetUserMedia; | |
/** | |
* Creates a new <video> element that contains the audio/video feed | |
* from a webcam. This can be drawn onto the canvas using video(). More | |
* specific properties of the stream can be passing in a Constraints object. | |
* See the | |
* <a href="http://w3c.github.io/mediacapture-main/getusermedia.html">W3C | |
* spec</a> for possible properties. Note that not all of these are supported | |
* by all browsers. | |
* | |
* @method createCapture | |
* @param {String|Constant|Object} type type of capture, either VIDEO or | |
* AUDIO if none specified, default both, | |
* or a Constraints boject | |
* @param {Function} callback function to be called once | |
* stream has loaded | |
* @return {Object/p5.Element} capture video p5.Element | |
* @example | |
* <div><class='norender'><code> | |
* var capture; | |
* | |
* function setup() { | |
* createCanvas(480, 120); | |
* capture = createCapture(VIDEO); | |
* } | |
* | |
* function draw() { | |
* image(capture, 0, 0, width, width*capture.height/capture.width); | |
* filter(INVERT); | |
* } | |
* </code></div> | |
* <div><class='norender'><code> | |
* function setup() { | |
* createCanvas(480, 120); | |
* var constraints = { | |
* video: { | |
* mandatory: { | |
* minWidth: 1280, | |
* minHeight: 720 | |
* }, | |
* optional: [ | |
* { maxFrameRate: 10 } | |
* ] | |
* }, | |
* audio: true | |
* }; | |
* createCapture(constraints, function(stream) { | |
* console.log(stream); | |
* }); | |
* } | |
* </code></div> | |
*/ | |
p5.prototype.createCapture = function() { | |
var useVideo = true; | |
var useAudio = true; | |
var constraints; | |
var cb; | |
for (var i=0; i<arguments.length; i++) { | |
if (arguments[i] === p5.prototype.VIDEO) { | |
useAudio = false; | |
} else if (arguments[i] === p5.prototype.AUDIO) { | |
useVideo = false; | |
} else if (typeof arguments[i] === 'object') { | |
constraints = arguments[i]; | |
} else if (typeof arguments[i] === 'function') { | |
cb = arguments[i]; | |
} | |
} | |
if (navigator.getUserMedia) { | |
var elt = document.createElement('video'); | |
if (!constraints) { | |
constraints = {video: useVideo, audio: useAudio}; | |
} | |
navigator.getUserMedia(constraints, function(stream) { | |
elt.src = window.URL.createObjectURL(stream); | |
elt.play(); | |
if (cb) { | |
cb(stream); | |
} | |
}, function(e) { console.log(e); }); | |
} else { | |
throw 'getUserMedia not supported in this browser'; | |
} | |
var c = addElement(elt, this, true); | |
c.loadedmetadata = false; | |
// set width and height onload metadata | |
elt.addEventListener('loadedmetadata', function() { | |
c.width = elt.videoWidth; | |
c.height = elt.videoHeight; | |
c.loadedmetadata = true; | |
}); | |
return c; | |
}; | |
/** | |
* Creates element with given tag in the DOM with given content. | |
* Appends to the container node if one is specified, otherwise | |
* appends to body. | |
* | |
* @method createElement | |
* @param {String} tag tag for the new element | |
* @param {String} [content] html content to be inserted into the element | |
* @return {Object/p5.Element} pointer to p5.Element holding created node | |
*/ | |
p5.prototype.createElement = function(tag, content) { | |
var elt = document.createElement(tag); | |
if (typeof content !== 'undefined') { | |
elt.innerHTML = content; | |
} | |
return addElement(elt, this); | |
}; | |
// ============================================================================= | |
// p5.Element additions | |
// ============================================================================= | |
/** | |
* | |
* Adds specified class to the element. | |
* | |
* @for p5.Element | |
* @method addClass | |
* @param {String} class name of class to add | |
* @return {Object/p5.Element} | |
*/ | |
p5.Element.prototype.addClass = function(c) { | |
if (this.elt.className) { | |
// PEND don't add class more than once | |
//var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?'); | |
//if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) { | |
this.elt.className = this.elt.className+' '+c; | |
//} | |
} else { | |
this.elt.className = c; | |
} | |
return this; | |
} | |
/** | |
* | |
* Removes specified class from the element. | |
* | |
* @method removeClass | |
* @param {String} class name of class to remove | |
* @return {Object/p5.Element} | |
*/ | |
p5.Element.prototype.removeClass = function(c) { | |
var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)'); | |
this.elt.className = this.elt.className.replace(regex, ''); | |
this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional) | |
return this; | |
} | |
/** | |
* | |
* Attaches the element as a child to the parent specified. | |
* Accepts either a string ID, DOM node, or p5.Element | |
* | |
* @method child | |
* @param {String|Object/p5.Element} child the ID, DOM node, or p5.Element | |
* to add to the current element | |
* @return {p5.Element} | |
* @example | |
* <div class='norender'><code> | |
* var div0 = createDiv('this is the parent'); | |
* var div1 = createDiv('this is the child'); | |
* div0.child(div1); // use p5.Element | |
* </code></div> | |
* <div class='norender'><code> | |
* var div0 = createDiv('this is the parent'); | |
* var div1 = createDiv('this is the child'); | |
* div1.id('apples'); | |
* div0.child('apples'); // use id | |
* </code></div> | |
* <div class='norender'><code> | |
* var div0 = createDiv('this is the parent'); | |
* var elt = document.getElementById('myChildDiv'); | |
* div0.child(elt); // use element from page | |
* </code></div> | |
*/ | |
p5.Element.prototype.child = function(c) { | |
if (typeof c === 'string') { | |
c = document.getElementById(c); | |
} else if (c instanceof p5.Element) { | |
c = c.elt; | |
} | |
this.elt.appendChild(c); | |
return this; | |
}; | |
/** | |
* | |
* If an argument is given, sets the inner HTML of the element, | |
* replacing any existing html. If no arguments are given, returns | |
* the inner HTML of the element. | |
* | |
* @for p5.Element | |
* @method html | |
* @param {String} [html] the HTML to be placed inside the element | |
* @return {Object/p5.Element|String} | |
*/ | |
p5.Element.prototype.html = function(html) { | |
if (typeof html !== 'undefined') { | |
this.elt.innerHTML = html; | |
return this; | |
} else { | |
return this.elt.innerHTML; | |
} | |
}; | |
/** | |
* | |
* Sets the position of the element relative to (0, 0) of the | |
* window. Essentially, sets position:absolute and left and top | |
* properties of style. If no arguments given returns the x and y position | |
* of the element in an object. | |
* | |
* @method position | |
* @param {Number} [x] x-position relative to upper left of window | |
* @param {Number} [y] y-position relative to upper left of window | |
* @return {Object/p5.Element} | |
* @example | |
* <div><code class='norender'> | |
* function setup() { | |
* var cnv = createCanvas(100, 100); | |
* // positions canvas 50px to the right and 100px | |
* // below upper left corner of the window | |
* cnv.position(50, 100); | |
* } | |
* </code></div> | |
*/ | |
p5.Element.prototype.position = function() { | |
if (arguments.length === 0){ | |
return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop }; | |
}else{ | |
this.elt.style.position = 'absolute'; | |
this.elt.style.left = arguments[0]+'px'; | |
this.elt.style.top = arguments[1]+'px'; | |
this.x = arguments[0]; | |
this.y = arguments[1]; | |
return this; | |
} | |
}; | |
/** | |
* Translates an element with css transforms in either 2d (if 2 arguments given) | |
* or 3d (if 3 arguments given) space. | |
* @method translate | |
* @param {Number} x x-position in px | |
* @param {Number} y y-position in px | |
* @param {Number} [z] z-position in px | |
* @param {Number} [perspective] sets the perspective of the parent element in px, | |
* default value set to 1000px | |
* @return {Object/p5.Element} | |
* @example | |
* <div><code> | |
* function setup() { | |
* createCanvas(100,100); | |
* //translates canvas 50px down | |
* select('canvas').translate(0,50); | |
* } | |
* </code></div> | |
*/ | |
p5.Element.prototype.translate = function(){ | |
this.elt.style.position = 'absolute'; | |
if (arguments.length === 2){ | |
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, ''); | |
style = style.replace(/translate[X-Z]?\(.*\)/g, ''); | |
this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)'; | |
this.elt.style.transform += style; | |
}else if (arguments.length === 3){ | |
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, ''); | |
style = style.replace(/translate[X-Z]?\(.*\)/g, ''); | |
this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)'; | |
this.elt.style.transform += style; | |
this.elt.parentElement.style.perspective = '1000px'; | |
}else if (arguments.length === 4){ | |
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, ''); | |
style = style.replace(/translate[X-Z]?\(.*\)/g, ''); | |
this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)'; | |
this.elt.style.transform += style; | |
this.elt.parentElement.style.perspective = arguments[3]+'px'; | |
} | |
return this; | |
}; | |
/** | |
* Rotates an element with css transforms in either 2d (if 2 arguments given) | |
* or 3d (if 3 arguments given) space. | |
* @method rotate | |
* @param {Number} x amount of degrees to rotate the element along the x-axis in deg | |
* @param {Number} [y] amount of degrees to rotate the element along the y-axis in deg | |
* @param {Number} [z] amount of degrees to rotate the element along the z-axis in deg | |
* @return {Object/p5.Element} | |
* @example | |
* <div><code> | |
* var x = 0, | |
* y = 0, | |
* z = 0; | |
* function setup(){ | |
* createCanvas(100,100); | |
* } | |
* function draw(){ | |
* x+=.5 % 360; | |
* y+=.5 % 360; | |
* z+=.5 % 360; | |
* //rotates the canvas .5deg (degrees) on every axis each frame. | |
* select('canvas').rotate(x,y,z); | |
* } | |
* </code></div> | |
*/ | |
p5.Element.prototype.rotate = function(){ | |
if (arguments.length === 1){ | |
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, ''); | |
style = style.replace(/rotate[X-Z]?\(.*\)/g, ''); | |
this.elt.style.transform = 'rotate('+arguments[0]+'deg)'; | |
this.elt.style.transform += style; | |
}else if (arguments.length === 2){ | |
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, ''); | |
style = style.replace(/rotate[X-Z]?\(.*\)/g, ''); | |
this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)'; | |
this.elt.style.transform += style; | |
}else if (arguments.length === 3){ | |
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, ''); | |
style = style.replace(/rotate[X-Z]?\(.*\)/g, ''); | |
this.elt.style.transform = 'rotateX('+arguments[0]+'deg)'; | |
this.elt.style.transform += 'rotateY('+arguments[1]+'deg)'; | |
this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)'; | |
this.elt.style.transform += style; | |
} | |
return this; | |
}; | |
/** | |
* Sets the given style (css) property of the element with the given value. | |
* If no value is specified, returns the value of the given property, | |
* or undefined if the property is not. | |
* | |
* @method style | |
* @param {String} property property to be set | |
* @param {String} [value] value to assign to property | |
* @return {String|Object/p5.Element} value of property, if no value is specified | |
* or p5.Element | |
* @example | |
* <div><code class="norender"> | |
* var myDiv = createDiv("I like pandas."); | |
* myDiv.style("color", "#ff0000"); | |
* myDiv.style("font-size", "18px"); | |
* </code></div> | |
*/ | |
p5.Element.prototype.style = function(prop, val) { | |
if (typeof val === 'undefined') { | |
var attrs = prop.split(';'); | |
for (var i=0; i<attrs.length; i++) { | |
var parts = attrs[i].split(':'); | |
if (parts[0] && parts[1]) { | |
this.elt.style[parts[0].trim()] = parts[1].trim(); | |
} | |
} | |
} else { | |
this.elt.style[prop] = val; | |
if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top'){ | |
var numVal = val.replace(/\D+/g,''); | |
this[prop] = parseInt(numVal); | |
} | |
} | |
return this; | |
}; | |
/** | |
* | |
* Adds a new attribute or changes the value of an existing attribute | |
* on the specified element. If no value is specified, returns the | |
* value of the given attribute, or null if attribute is not set. | |
* | |
* @method attribute | |
* @param {String} attr attribute to set | |
* @param {String} [value] value to assign to attribute | |
* @return {String|Object/p5.Element} value of attribute, if no value is | |
* specified or p5.Element | |
* @example | |
* <div class="norender"><code> | |
* var myDiv = createDiv("I like pandas."); | |
*myDiv.attribute("align", "center"); | |
* </code></div> | |
*/ | |
p5.Element.prototype.attribute = function(attr, value) { | |
if (typeof value === 'undefined') { | |
return this.elt.getAttribute(attr); | |
} else { | |
this.elt.setAttribute(attr, value); | |
return this; | |
} | |
}; | |
/** | |
* Either returns the value of the element if no arguments | |
* given, or sets the value of the element. | |
* | |
* @method value | |
* @param {String|Number} [value] | |
* @return {String|Object/p5.Element} value of element if no value is specified or p5.Element | |
*/ | |
p5.Element.prototype.value = function() { | |
if (arguments.length > 0) { | |
this.elt.value = arguments[0]; | |
return this; | |
} else { | |
if (this.elt.type === 'range') { | |
return parseFloat(this.elt.value); | |
} | |
else return this.elt.value; | |
} | |
}; | |
/** | |
* | |
* Shows the current element. Essentially, setting display:block for the style. | |
* | |
* @method show | |
* @return {Object/p5.Element} | |
*/ | |
p5.Element.prototype.show = function() { | |
this.elt.style.display = 'block'; | |
return this; | |
}; | |
/** | |
* Hides the current element. Essentially, setting display:none for the style. | |
* | |
* @method hide | |
* @return {Object/p5.Element} | |
*/ | |
p5.Element.prototype.hide = function() { | |
this.elt.style.display = 'none'; | |
return this; | |
}; | |
/** | |
* | |
* Sets the width and height of the element. AUTO can be used to | |
* only adjust one dimension. If no arguments given returns the width and height | |
* of the element in an object. | |
* | |
* @method size | |
* @param {Number} [w] width of the element | |
* @param {Number} [h] height of the element | |
* @return {Object/p5.Element} | |
*/ | |
p5.Element.prototype.size = function(w, h) { | |
if (arguments.length === 0){ | |
return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight }; | |
}else{ | |
var aW = w; | |
var aH = h; | |
var AUTO = p5.prototype.AUTO; | |
if (aW !== AUTO || aH !== AUTO) { | |
if (aW === AUTO) { | |
aW = h * this.width / this.height; | |
} else if (aH === AUTO) { | |
aH = w * this.height / this.width; | |
} | |
// set diff for cnv vs normal div | |
if (this.elt instanceof HTMLCanvasElement) { | |
var j = {}; | |
var k = this.elt.getContext('2d'); | |
for (var prop in k) { | |
j[prop] = k[prop]; | |
} | |
this.elt.setAttribute('width', aW * this._pInst._pixelDensity); | |
this.elt.setAttribute('height', aH * this._pInst._pixelDensity); | |
this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px'); | |
this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity); | |
for (var prop in j) { | |
this.elt.getContext('2d')[prop] = j[prop]; | |
} | |
} else { | |
this.elt.style.width = aW+'px'; | |
this.elt.style.height = aH+'px'; | |
this.elt.width = aW; | |
this.elt.height = aH; | |
this.width = aW; | |
this.height = aH; | |
} | |
this.elt.style.overflow = 'hidden'; | |
this.width = this.elt.offsetWidth; | |
this.height = this.elt.offsetHeight; | |
if (this._pInst) { // main canvas associated with p5 instance | |
if (this._pInst._curElement.elt === this.elt) { | |
this._pInst._setProperty('width', this.elt.offsetWidth); | |
this._pInst._setProperty('height', this.elt.offsetHeight); | |
} | |
} | |
} | |
return this; | |
} | |
}; | |
/** | |
* Removes the element and deregisters all listeners. | |
* @method remove | |
* @example | |
* <div class='norender'><code> | |
* var myDiv = createDiv('this is some text'); | |
* myDiv.remove(); | |
* </code></div> | |
*/ | |
p5.Element.prototype.remove = function() { | |
// deregister events | |
for (var ev in this._events) { | |
this.elt.removeEventListener(ev, this._events[ev]); | |
} | |
if (this.elt.parentNode) { | |
this.elt.parentNode.removeChild(this.elt); | |
} | |
delete(this); | |
}; | |
// ============================================================================= | |
// p5.MediaElement additions | |
// ============================================================================= | |
/** | |
* Extends p5.Element to handle audio and video. In addition to the methods | |
* of p5.Element, it also contains methods for controlling media. It is not | |
* called directly, but p5.MediaElements are created by calling createVideo, | |
* createAudio, and createCapture. | |
* | |
* @class p5.MediaElement | |
* @constructor | |
* @param {String} elt DOM node that is wrapped | |
* @param {Object} [pInst] pointer to p5 instance | |
*/ | |
p5.MediaElement = function(elt, pInst) { | |
p5.Element.call(this, elt, pInst); | |
this._prevTime = 0; | |
this._cueIDCounter = 0; | |
this._cues = []; | |
this.pixelDensity = 1; | |
}; | |
p5.MediaElement.prototype = Object.create(p5.Element.prototype); | |
/** | |
* Play an HTML5 media element. | |
* | |
* @method play | |
* @return {Object/p5.Element} | |
*/ | |
p5.MediaElement.prototype.play = function() { | |
if (this.elt.currentTime === this.elt.duration) { | |
this.elt.currentTime = 0; | |
} | |
if (this.elt.readyState > 1) { | |
this.elt.play(); | |
} else { | |
// in Chrome, playback cannot resume after being stopped and must reload | |
this.elt.load(); | |
this.elt.play(); | |
} | |
return this; | |
}; | |
/** | |
* Stops an HTML5 media element (sets current time to zero). | |
* | |
* @method stop | |
* @return {Object/p5.Element} | |
*/ | |
p5.MediaElement.prototype.stop = function() { | |
this.elt.pause(); | |
this.elt.currentTime = 0; | |
return this; | |
}; | |
/** | |
* Pauses an HTML5 media element. | |
* | |
* @method pause | |
* @return {Object/p5.Element} | |
*/ | |
p5.MediaElement.prototype.pause = function() { | |
this.elt.pause(); | |
return this; | |
}; | |
/** | |
* Set 'loop' to true for an HTML5 media element, and starts playing. | |
* | |
* @method loop | |
* @return {Object/p5.Element} | |
*/ | |
p5.MediaElement.prototype.loop = function() { | |
this.elt.setAttribute('loop', true); | |
this.play(); | |
return this; | |
}; | |
/** | |
* Set 'loop' to false for an HTML5 media element. Element will stop | |
* when it reaches the end. | |
* | |
* @method noLoop | |
* @return {Object/p5.Element} | |
*/ | |
p5.MediaElement.prototype.noLoop = function() { | |
this.elt.setAttribute('loop', false); | |
return this; | |
}; | |
/** | |
* Set HTML5 media element to autoplay or not. | |
* | |
* @method autoplay | |
* @param {Boolean} autoplay whether the element should autoplay | |
* @return {Object/p5.Element} | |
*/ | |
p5.MediaElement.prototype.autoplay = function(val) { | |
this.elt.setAttribute('autoplay', val); | |
return this; | |
}; | |
/** | |
* Sets volume for this HTML5 media element. If no argument is given, | |
* returns the current volume. | |
* | |
* @param {Number} [val] volume between 0.0 and 1.0 | |
* @return {Number|p5.MediaElement} current volume or p5.MediaElement | |
* @method volume | |
*/ | |
p5.MediaElement.prototype.volume = function(val) { | |
if (typeof val === 'undefined') { | |
return this.elt.volume; | |
} else { | |
this.elt.volume = val; | |
} | |
}; | |
/** | |
* If no arguments are given, returns the current time of the elmeent. | |
* If an argument is given the current time of the element is set to it. | |
* | |
* @method time | |
* @param {Number} [time] time to jump to (in seconds) | |
* @return {Number|Object/p5.MediaElement} current time (in seconds) | |
* or p5.MediaElement | |
*/ | |
p5.MediaElement.prototype.time = function(val) { | |
if (typeof val === 'undefined') { | |
return this.elt.currentTime; | |
} else { | |
this.elt.currentTime = val; | |
} | |
}; | |
/** | |
* Returns the duration of the HTML5 media element. | |
* | |
* @method duration | |
* @return {Number} duration | |
*/ | |
p5.MediaElement.prototype.duration = function() { | |
return this.elt.duration; | |
}; | |
p5.MediaElement.prototype.pixels = []; | |
p5.MediaElement.prototype.loadPixels = function() { | |
if (this.loadedmetadata) { // wait for metadata for w/h | |
if (!this.canvas) { | |
this.canvas = document.createElement('canvas'); | |
this.canvas.width = this.width; | |
this.canvas.height = this.height; | |
this.drawingContext = this.canvas.getContext('2d'); | |
} | |
this.drawingContext.drawImage(this.elt, 0, 0, this.width, this.height); | |
p5.Renderer2D.prototype.loadPixels.call(this); | |
} | |
return this; | |
} | |
p5.MediaElement.prototype.updatePixels = function(x, y, w, h){ | |
if (this.loadedmetadata) { // wait for metadata | |
p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h); | |
} | |
return this; | |
} | |
p5.MediaElement.prototype.get = function(x, y, w, h){ | |
if (this.loadedmetadata) { // wait for metadata | |
return p5.Renderer2D.prototype.get.call(this, x, y, w, h); | |
} else return [0, 0, 0, 255]; | |
}; | |
p5.MediaElement.prototype.set = function(x, y, imgOrCol){ | |
if (this.loadedmetadata) { // wait for metadata | |
p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol); | |
} | |
}; | |
/*** CONNECT TO WEB AUDIO API / p5.sound.js ***/ | |
/** | |
* Send the audio output of this element to a specified audioNode or | |
* p5.sound object. If no element is provided, connects to p5's master | |
* output. That connection is established when this method is first called. | |
* All connections are removed by the .disconnect() method. | |
* | |
* This method is meant to be used with the p5.sound.js addon library. | |
* | |
* @method connect | |
* @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API, | |
* or an object from the p5.sound library | |
*/ | |
p5.MediaElement.prototype.connect = function(obj) { | |
var audioContext, masterOutput; | |
// if p5.sound exists, same audio context | |
if (typeof p5.prototype.getAudioContext === 'function') { | |
audioContext = p5.prototype.getAudioContext(); | |
masterOutput = p5.soundOut.input; | |
} else { | |
try { | |
audioContext = obj.context; | |
masterOutput = audioContext.destination | |
} catch(e) { | |
throw 'connect() is meant to be used with Web Audio API or p5.sound.js' | |
} | |
} | |
// create a Web Audio MediaElementAudioSourceNode if none already exists | |
if (!this.audioSourceNode) { | |
this.audioSourceNode = audioContext.createMediaElementSource(this.elt); | |
// connect to master output when this method is first called | |
this.audioSourceNode.connect(masterOutput); | |
} | |
// connect to object if provided | |
if (obj) { | |
if (obj.input) { | |
this.audioSourceNode.connect(obj.input); | |
} else { | |
this.audioSourceNode.connect(obj); | |
} | |
} | |
// otherwise connect to master output of p5.sound / AudioContext | |
else { | |
this.audioSourceNode.connect(masterOutput); | |
} | |
}; | |
/** | |
* Disconnect all Web Audio routing, including to master output. | |
* This is useful if you want to re-route the output through | |
* audio effects, for example. | |
* | |
* @method disconnect | |
*/ | |
p5.MediaElement.prototype.disconnect = function() { | |
if (this.audioSourceNode) { | |
this.audioSourceNode.disconnect(); | |
} else { | |
throw 'nothing to disconnect'; | |
} | |
}; | |
/*** SHOW / HIDE CONTROLS ***/ | |
/** | |
* Show the default MediaElement controls, as determined by the web browser. | |
* | |
* @method showControls | |
*/ | |
p5.MediaElement.prototype.showControls = function() { | |
// must set style for the element to show on the page | |
this.elt.style['text-align'] = 'inherit'; | |
this.elt.controls = true; | |
}; | |
/** | |
* Hide the default mediaElement controls. | |
* | |
* @method hideControls | |
*/ | |
p5.MediaElement.prototype.hideControls = function() { | |
this.elt.controls = false; | |
}; | |
/*** SCHEDULE EVENTS ***/ | |
/** | |
* Schedule events to trigger every time a MediaElement | |
* (audio/video) reaches a playback cue point. | |
* | |
* Accepts a callback function, a time (in seconds) at which to trigger | |
* the callback, and an optional parameter for the callback. | |
* | |
* Time will be passed as the first parameter to the callback function, | |
* and param will be the second parameter. | |
* | |
* | |
* @method addCue | |
* @param {Number} time Time in seconds, relative to this media | |
* element's playback. For example, to trigger | |
* an event every time playback reaches two | |
* seconds, pass in the number 2. This will be | |
* passed as the first parameter to | |
* the callback function. | |
* @param {Function} callback Name of a function that will be | |
* called at the given time. The callback will | |
* receive time and (optionally) param as its | |
* two parameters. | |
* @param {Object} [value] An object to be passed as the | |
* second parameter to the | |
* callback function. | |
* @return {Number} id ID of this cue, | |
* useful for removeCue(id) | |
* @example | |
* <div><code> | |
* function setup() { | |
* background(255,255,255); | |
* | |
* audioEl = createAudio('assets/beat.mp3'); | |
* audioEl.showControls(); | |
* | |
* // schedule three calls to changeBackground | |
* audioEl.addCue(0.5, changeBackground, color(255,0,0) ); | |
* audioEl.addCue(1.0, changeBackground, color(0,255,0) ); | |
* audioEl.addCue(2.5, changeBackground, color(0,0,255) ); | |
* audioEl.addCue(3.0, changeBackground, color(0,255,255) ); | |
* audioEl.addCue(4.2, changeBackground, color(255,255,0) ); | |
* audioEl.addCue(5.0, changeBackground, color(255,255,0) ); | |
* } | |
* | |
* function changeBackground(val) { | |
* background(val); | |
* } | |
* </code></div> | |
*/ | |
p5.MediaElement.prototype.addCue = function(time, callback, val) { | |
var id = this._cueIDCounter++; | |
var cue = new Cue(callback, time, id, val); | |
this._cues.push(cue); | |
if (!this.elt.ontimeupdate) { | |
this.elt.ontimeupdate = this._onTimeUpdate.bind(this); | |
} | |
return id; | |
}; | |
/** | |
* Remove a callback based on its ID. The ID is returned by the | |
* addCue method. | |
* | |
* @method removeCue | |
* @param {Number} id ID of the cue, as returned by addCue | |
*/ | |
p5.MediaElement.prototype.removeCue = function(id) { | |
for (var i = 0; i < this._cues.length; i++) { | |
var cue = this._cues[i]; | |
if (cue.id === id) { | |
this.cues.splice(i, 1); | |
} | |
} | |
if (this._cues.length === 0) { | |
this.elt.ontimeupdate = null | |
} | |
}; | |
/** | |
* Remove all of the callbacks that had originally been scheduled | |
* via the addCue method. | |
* | |
* @method clearCues | |
*/ | |
p5.MediaElement.prototype.clearCues = function() { | |
this._cues = []; | |
this.elt.ontimeupdate = null; | |
}; | |
// private method that checks for cues to be fired if events | |
// have been scheduled using addCue(callback, time). | |
p5.MediaElement.prototype._onTimeUpdate = function() { | |
var playbackTime = this.time(); | |
for (var i = 0 ; i < this._cues.length; i++) { | |
var callbackTime = this._cues[i].time; | |
var val = this._cues[i].val; | |
if (this._prevTime < callbackTime && callbackTime <= playbackTime) { | |
// pass the scheduled callbackTime as parameter to the callback | |
this._cues[i].callback(val); | |
} | |
} | |
this._prevTime = playbackTime; | |
}; | |
// Cue inspired by JavaScript setTimeout, and the | |
// Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org | |
var Cue = function(callback, time, id, val) { | |
this.callback = callback; | |
this.time = time; | |
this.id = id; | |
this.val = val; | |
}; | |
// ============================================================================= | |
// p5.File | |
// ============================================================================= | |
/** | |
* Base class for a file | |
* Using this for createFileInput | |
* | |
* @class p5.File | |
* @constructor | |
* @param {File} file File that is wrapped | |
* @param {Object} [pInst] pointer to p5 instance | |
*/ | |
p5.File = function(file, pInst) { | |
/** | |
* Underlying File object. All normal File methods can be called on this. | |
* | |
* @property file | |
*/ | |
this.file = file; | |
this._pInst = pInst; | |
// Splitting out the file type into two components | |
// This makes determining if image or text etc simpler | |
var typeList = file.type.split('/'); | |
/** | |
* File type (image, text, etc.) | |
* | |
* @property type | |
*/ | |
this.type = typeList[0]; | |
/** | |
* File subtype (usually the file extension jpg, png, xml, etc.) | |
* | |
* @property subtype | |
*/ | |
this.subtype = typeList[1]; | |
/** | |
* File name | |
* | |
* @property name | |
*/ | |
this.name = file.name; | |
/** | |
* File size | |
* | |
* @property size | |
*/ | |
this.size = file.size; | |
// Data not loaded yet | |
this.data = undefined; | |
}; | |
})); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! p5.js v0.4.9 September 03, 2015 */ !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.p5=a()}}(function(){var define,module,exports;return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){},{}],2:[function(a,b,c){"use strict";c.argument=function(a,b){if(!a)throw new Error(b)},c.assert=c.argument},{}],3:[function(a,b,c){"use strict";function d(a,b,c,d,e){a.beginPath(),a.moveTo(b,c),a.lineTo(d,e),a.stroke()}c.line=d},{}],4:[function(a,b,c){"use strict";function d(a){this.font=a}function e(a){this.cmap=a}function f(a,b){this.encoding=a,this.charset=b}function g(a){var b;switch(a.version){case 1:this.names=c.standardNames.slice();break;case 2:for(this.names=new Array(a.numberOfGlyphs),b=0;b<a.numberOfGlyphs;b++)a.glyphNameIndex[b]<c.standardNames.length?this.names[b]=c.standardNames[a.glyphNameIndex[b]]:this.names[b]=a.names[a.glyphNameIndex[b]-c.standardNames.length];break;case 2.5:for(this.names=new Array(a.numberOfGlyphs),b=0;b<a.numberOfGlyphs;b++)this.names[b]=c.standardNames[b+a.glyphNameIndex[b]];break;case 3:this.names=[]}}function h(a){for(var b,c=a.tables.cmap.glyphIndexMap,d=Object.keys(c),e=0;e<d.length;e+=1){var f=d[e],g=c[f];b=a.glyphs.get(g),b.addUnicode(parseInt(f))}for(e=0;e<a.glyphs.length;e+=1)b=a.glyphs.get(e),a.cffEncoding?b.name=a.cffEncoding.charset[e]:b.name=a.glyphNames.glyphIndexToName(e)}var i=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","266 ff","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"],j=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","","endash","dagger","daggerdbl","periodcentered","","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","","questiondown","","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","","ring","cedilla","","hungarumlaut","ogonek","caron","emdash","","","","","","","","","","","","","","","","","AE","","ordfeminine","","","","","Lslash","Oslash","OE","ordmasculine","","","","","","ae","","","","dotlessi","","","lslash","oslash","oe","germandbls"],k=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","","asuperior","bsuperior","centsuperior","dsuperior","esuperior","","","isuperior","","","lsuperior","msuperior","nsuperior","osuperior","","","rsuperior","ssuperior","tsuperior","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdownsmall","centoldstyle","Lslashsmall","","","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","","Dotaccentsmall","","","Macronsmall","","","figuredash","hypheninferior","","","Ogoneksmall","Ringsmall","Cedillasmall","","","","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],l=[".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior","twosuperior","threesuperior","onehalf","onequarter","threequarters","franc","Gbreve","gbreve","Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron","dcroat"];d.prototype.charToGlyphIndex=function(a){var b=a.charCodeAt(0),c=this.font.glyphs;if(!c)return null;for(var d=0;d<c.length;d+=1)for(var e=c.get(d),f=0;f<e.unicodes.length;f+=1)if(e.unicodes[f]===b)return d},e.prototype.charToGlyphIndex=function(a){return this.cmap.glyphIndexMap[a.charCodeAt(0)]||0},f.prototype.charToGlyphIndex=function(a){var b=a.charCodeAt(0),c=this.encoding[b];return this.charset.indexOf(c)},g.prototype.nameToGlyphIndex=function(a){return this.names.indexOf(a)},g.prototype.glyphIndexToName=function(a){return this.names[a]},c.cffStandardStrings=i,c.cffStandardEncoding=j,c.cffExpertEncoding=k,c.standardNames=l,c.DefaultEncoding=d,c.CmapEncoding=e,c.CffEncoding=f,c.GlyphNames=g,c.addGlyphNames=h},{}],5:[function(a,b,c){"use strict";function d(a){a=a||{},this.names={fontFamily:{en:a.familyName||" "},fontSubfamily:{en:a.styleName||" "},designer:{en:a.designer||" "},designerURL:{en:a.designerURL||" "},manufacturer:{en:a.manufacturer||" "},manufacturerURL:{en:a.manufacturerURL||" "},license:{en:a.license||" "},licenseURL:{en:a.licenseURL||" "},version:{en:a.version||"Version 0.1"},description:{en:a.description||" "},copyright:{en:a.copyright||" "},trademark:{en:a.trademark||" "}},this.unitsPerEm=a.unitsPerEm||1e3,this.ascender=a.ascender,this.descender=a.descender,this.supported=!0,this.glyphs=new h.GlyphSet(this,a.glyphs||[]),this.encoding=new g.DefaultEncoding(this),this.tables={}}var e=a("./path"),f=a("./tables/sfnt"),g=a("./encoding"),h=a("./glyphset");d.prototype.hasChar=function(a){return null!==this.encoding.charToGlyphIndex(a)},d.prototype.charToGlyphIndex=function(a){return this.encoding.charToGlyphIndex(a)},d.prototype.charToGlyph=function(a){var b=this.charToGlyphIndex(a),c=this.glyphs.get(b);return c||(c=this.glyphs.get(0)),c},d.prototype.stringToGlyphs=function(a){for(var b=[],c=0;c<a.length;c+=1){var d=a[c];b.push(this.charToGlyph(d))}return b},d.prototype.nameToGlyphIndex=function(a){return this.glyphNames.nameToGlyphIndex(a)},d.prototype.nameToGlyph=function(a){var b=this.nametoGlyphIndex(a),c=this.glyphs.get(b);return c||(c=this.glyphs.get(0)),c},d.prototype.glyphIndexToName=function(a){return this.glyphNames.glyphIndexToName?this.glyphNames.glyphIndexToName(a):""},d.prototype.getKerningValue=function(a,b){a=a.index||a,b=b.index||b;var c=this.getGposKerningValue;return c?c(a,b):this.kerningPairs[a+","+b]||0},d.prototype.forEachGlyph=function(a,b,c,d,e,f){b=void 0!==b?b:0,c=void 0!==c?c:0,d=void 0!==d?d:72,e=e||{};for(var g=void 0===e.kerning?!0:e.kerning,h=1/this.unitsPerEm*d,i=this.stringToGlyphs(a),j=0;j<i.length;j+=1){var k=i[j];if(f(k,b,c,d,e),k.advanceWidth&&(b+=k.advanceWidth*h),g&&j<i.length-1){var l=this.getKerningValue(k,i[j+1]);b+=l*h}}},d.prototype.getPath=function(a,b,c,d,f){var g=new e.Path;return this.forEachGlyph(a,b,c,d,f,function(a,b,c,d){var e=a.getPath(b,c,d);g.extend(e)}),g},d.prototype.draw=function(a,b,c,d,e,f){this.getPath(b,c,d,e,f).draw(a)},d.prototype.drawPoints=function(a,b,c,d,e,f){this.forEachGlyph(b,c,d,e,f,function(b,c,d,e){b.drawPoints(a,c,d,e)})},d.prototype.drawMetrics=function(a,b,c,d,e,f){this.forEachGlyph(b,c,d,e,f,function(b,c,d,e){b.drawMetrics(a,c,d,e)})},d.prototype.getEnglishName=function(a){var b=this.names[a];return b?b.en:void 0},d.prototype.validate=function(){function a(a,b){a||c.push(b)}function b(b){var c=d.getEnglishName(b);a(c&&c.trim().length>0,"No English "+b+" specified.")}var c=[],d=this;b("fontFamily"),b("weightName"),b("manufacturer"),b("copyright"),b("version"),a(this.unitsPerEm>0,"No unitsPerEm specified.")},d.prototype.toTables=function(){return f.fontToTable(this)},d.prototype.toBuffer=function(){for(var a=this.toTables(),b=a.encode(),c=new ArrayBuffer(b.length),d=new Uint8Array(c),e=0;e<b.length;e++)d[e]=b[e];return c},d.prototype.download=function(){var a=this.getEnglishName("fontFamily"),b=this.getEnglishName("fontSubfamily"),c=a.replace(/\s/g,"")+"-"+b+".otf",d=this.toBuffer();window.requestFileSystem=window.requestFileSystem||window.webkitRequestFileSystem,window.requestFileSystem(window.TEMPORARY,d.byteLength,function(a){a.root.getFile(c,{create:!0},function(a){a.createWriter(function(b){var c=new DataView(d),e=new Blob([c],{type:"font/opentype"});b.write(e),b.addEventListener("writeend",function(){location.href=a.toURL()},!1)})})},function(a){throw a})},c.Font=d},{"./encoding":4,"./glyphset":7,"./path":10,"./tables/sfnt":27}],6:[function(a,b,c){"use strict";function d(a,b){var c=b||{commands:[]};return{configurable:!0,get:function(){return"function"==typeof c&&(c=c()),c},set:function(a){c=a}}}function e(a){this.bindConstructorValues(a)}var f=a("./check"),g=a("./draw"),h=a("./path");e.prototype.bindConstructorValues=function(a){this.index=a.index||0,this.name=a.name||null,this.unicode=a.unicode||void 0,this.unicodes=a.unicodes||void 0!==a.unicode?[a.unicode]:[],a.xMin&&(this.xMin=a.xMin),a.yMin&&(this.yMin=a.yMin),a.xMax&&(this.xMax=a.xMax),a.yMax&&(this.yMax=a.yMax),a.advanceWidth&&(this.advanceWidth=a.advanceWidth),Object.defineProperty(this,"path",d(this,a.path))},e.prototype.addUnicode=function(a){0===this.unicodes.length&&(this.unicode=a),this.unicodes.push(a)},e.prototype.getPath=function(a,b,c){a=void 0!==a?a:0,b=void 0!==b?b:0,c=void 0!==c?c:72;for(var d=1/this.path.unitsPerEm*c,e=new h.Path,f=this.path.commands,g=0;g<f.length;g+=1){var i=f[g];"M"===i.type?e.moveTo(a+i.x*d,b+-i.y*d):"L"===i.type?e.lineTo(a+i.x*d,b+-i.y*d):"Q"===i.type?e.quadraticCurveTo(a+i.x1*d,b+-i.y1*d,a+i.x*d,b+-i.y*d):"C"===i.type?e.curveTo(a+i.x1*d,b+-i.y1*d,a+i.x2*d,b+-i.y2*d,a+i.x*d,b+-i.y*d):"Z"===i.type&&e.closePath()}return e},e.prototype.getContours=function(){if(void 0===this.points)return[];for(var a=[],b=[],c=0;c<this.points.length;c+=1){var d=this.points[c];b.push(d),d.lastPointOfContour&&(a.push(b),b=[])}return f.argument(0===b.length,"There are still points left in the current contour."),a},e.prototype.getMetrics=function(){for(var a=this.path.commands,b=[],c=[],d=0;d<a.length;d+=1){var e=a[d];"Z"!==e.type&&(b.push(e.x),c.push(e.y)),("Q"===e.type||"C"===e.type)&&(b.push(e.x1),c.push(e.y1)),"C"===e.type&&(b.push(e.x2),c.push(e.y2))}var f={xMin:Math.min.apply(null,b),yMin:Math.min.apply(null,c),xMax:Math.max.apply(null,b),yMax:Math.max.apply(null,c),leftSideBearing:0};return f.rightSideBearing=this.advanceWidth-f.leftSideBearing-(f.xMax-f.xMin),f},e.prototype.draw=function(a,b,c,d){this.getPath(b,c,d).draw(a)},e.prototype.drawPoints=function(a,b,c,d){function e(b,c,d,e){var f=2*Math.PI;a.beginPath();for(var g=0;g<b.length;g+=1)a.moveTo(c+b[g].x*e,d+b[g].y*e),a.arc(c+b[g].x*e,d+b[g].y*e,2,0,f,!1);a.closePath(),a.fill()}b=void 0!==b?b:0,c=void 0!==c?c:0,d=void 0!==d?d:24;for(var f=1/this.path.unitsPerEm*d,g=[],h=[],i=this.path,j=0;j<i.commands.length;j+=1){var k=i.commands[j];void 0!==k.x&&g.push({x:k.x,y:-k.y}),void 0!==k.x1&&h.push({x:k.x1,y:-k.y1}),void 0!==k.x2&&h.push({x:k.x2,y:-k.y2})}a.fillStyle="blue",e(g,b,c,f),a.fillStyle="red",e(h,b,c,f)},e.prototype.drawMetrics=function(a,b,c,d){var e;b=void 0!==b?b:0,c=void 0!==c?c:0,d=void 0!==d?d:24,e=1/this.path.unitsPerEm*d,a.lineWidth=1,a.strokeStyle="black",g.line(a,b,-1e4,b,1e4),g.line(a,-1e4,c,1e4,c);var f=this.xMin||0,h=this.yMin||0,i=this.xMax||0,j=this.yMax||0,k=this.advanceWidth||0;a.strokeStyle="blue",g.line(a,b+f*e,-1e4,b+f*e,1e4),g.line(a,b+i*e,-1e4,b+i*e,1e4),g.line(a,-1e4,c+-h*e,1e4,c+-h*e),g.line(a,-1e4,c+-j*e,1e4,c+-j*e),a.strokeStyle="green",g.line(a,b+k*e,-1e4,b+k*e,1e4)},c.Glyph=e},{"./check":2,"./draw":3,"./path":10}],7:[function(a,b,c){"use strict";function d(a,b){if(this.font=a,this.glyphs={},Array.isArray(b))for(var c=0;c<b.length;c++)this.glyphs[c]=b[c];this.length=b&&b.length||0}function e(a,b){return new h.Glyph({index:b,font:a})}function f(a,b,c,d,e,f){return function(){var g=new h.Glyph({index:b,font:a});return g.path=function(){c(g,d,e);var b=f(a.glyphs,g);return b.unitsPerEm=a.unitsPerEm,b},g}}function g(a,b,c,d){return function(){var e=new h.Glyph({index:b,font:a});return e.path=function(){var b=c(a,e,d);return b.unitsPerEm=a.unitsPerEm,b},e}}var h=a("./glyph");d.prototype.get=function(a){return"function"==typeof this.glyphs[a]&&(this.glyphs[a]=this.glyphs[a]()),this.glyphs[a]},d.prototype.push=function(a,b){this.glyphs[a]=b,this.length++},c.GlyphSet=d,c.glyphLoader=e,c.ttfGlyphLoader=f,c.cffGlyphLoader=g},{"./glyph":6}],8:[function(a,b,c){"use strict";function d(a){for(var b=new ArrayBuffer(a.length),c=new Uint8Array(b),d=0;d<a.length;d+=1)c[d]=a[d];return b}function e(b,c){var e=a("fs");e.readFile(b,function(a,b){return a?c(a.message):void c(null,d(b))})}function f(a,b){var c=new XMLHttpRequest;c.open("get",a,!0),c.responseType="arraybuffer",c.onload=function(){return 200!==c.status?b("Font could not be loaded: "+c.statusText):b(null,c.response)},c.send()}function g(a){var b,c,d,e,f,g,h,i,l,n,D=new k.Font,E=new DataView(a,0),F=m.getFixed(E,0);if(1===F)D.outlinesFormat="truetype";else{if(F=m.getTag(E,0),"OTTO"!==F)throw new Error("Unsupported OpenType version "+F);D.outlinesFormat="cff"}for(var G=m.getUShort(E,4),H=12,I=0;G>I;I+=1){var J=m.getTag(E,H),K=m.getULong(E,H+8);switch(J){case"cmap":D.tables.cmap=o.parse(E,K),D.encoding=new j.CmapEncoding(D.tables.cmap);break;case"fvar":e=K;break;case"head":D.tables.head=t.parse(E,K),D.unitsPerEm=D.tables.head.unitsPerEm,b=D.tables.head.indexToLocFormat;break;case"hhea":D.tables.hhea=u.parse(E,K),D.ascender=D.tables.hhea.ascender,D.descender=D.tables.hhea.descender,D.numberOfHMetrics=D.tables.hhea.numberOfHMetrics;break;case"hmtx":h=K;break;case"ltag":c=x.parse(E,K);break;case"maxp":D.tables.maxp=z.parse(E,K),D.numGlyphs=D.tables.maxp.numGlyphs;break;case"name":n=K;break;case"OS/2":D.tables.os2=B.parse(E,K);break;case"post":D.tables.post=C.parse(E,K),D.glyphNames=new j.GlyphNames(D.tables.post);break;case"glyf":f=K;break;case"loca":l=K;break;case"CFF ":d=K;break;case"kern":i=K;break;case"GPOS":g=K}H+=16}if(D.tables.name=A.parse(E,n,c),D.names=D.tables.name,f&&l){var L=0===b,M=y.parse(E,l,D.numGlyphs,L);D.glyphs=r.parse(E,f,M,D),v.parse(E,h,D.numberOfHMetrics,D.numGlyphs,D.glyphs),j.addGlyphNames(D)}else{if(!d)throw new Error("Font doesn't contain TrueType or CFF outlines.");p.parse(E,d,D),j.addGlyphNames(D)}return i?D.kerningPairs=w.parse(E,i):D.kerningPairs={},g&&s.parse(E,g,D),e&&(D.tables.fvar=q.parse(E,e,D.names)),D}function h(a,b){var c="undefined"==typeof window,d=c?e:f;d(a,function(a,c){if(a)return b(a);var d=g(c);return b(null,d)})}function i(b){var c=a("fs"),e=c.readFileSync(b);return g(d(e))}var j=a("./encoding"),k=a("./font"),l=a("./glyph"),m=a("./parse"),n=a("./path"),o=a("./tables/cmap"),p=a("./tables/cff"),q=a("./tables/fvar"),r=a("./tables/glyf"),s=a("./tables/gpos"),t=a("./tables/head"),u=a("./tables/hhea"),v=a("./tables/hmtx"),w=a("./tables/kern"),x=a("./tables/ltag"),y=a("./tables/loca"),z=a("./tables/maxp"),A=a("./tables/name"),B=a("./tables/os2"),C=a("./tables/post");c._parse=m,c.Font=k.Font,c.Glyph=l.Glyph,c.Path=n.Path,c.parse=g,c.load=h,c.loadSync=i},{"./encoding":4,"./font":5,"./glyph":6,"./parse":9,"./path":10,"./tables/cff":12,"./tables/cmap":13,"./tables/fvar":14,"./tables/glyf":15,"./tables/gpos":16,"./tables/head":17,"./tables/hhea":18,"./tables/hmtx":19,"./tables/kern":20,"./tables/loca":21,"./tables/ltag":22,"./tables/maxp":23,"./tables/name":24,"./tables/os2":25,"./tables/post":26,fs:1}],9:[function(a,b,c){"use strict";function d(a,b){this.data=a,this.offset=b,this.relativeOffset=0}c.getByte=function(a,b){return a.getUint8(b)},c.getCard8=c.getByte,c.getUShort=function(a,b){return a.getUint16(b,!1)},c.getCard16=c.getUShort,c.getShort=function(a,b){return a.getInt16(b,!1)},c.getULong=function(a,b){return a.getUint32(b,!1)},c.getFixed=function(a,b){var c=a.getInt16(b,!1),d=a.getUint16(b+2,!1);return c+d/65535},c.getTag=function(a,b){for(var c="",d=b;b+4>d;d+=1)c+=String.fromCharCode(a.getInt8(d));return c},c.getOffset=function(a,b,c){for(var d=0,e=0;c>e;e+=1)d<<=8,d+=a.getUint8(b+e);return d},c.getBytes=function(a,b,c){for(var d=[],e=b;c>e;e+=1)d.push(a.getUint8(e));return d},c.bytesToString=function(a){for(var b="",c=0;c<a.length;c+=1)b+=String.fromCharCode(a[c]);return b};var e={"byte":1,uShort:2,"short":2,uLong:4,fixed:4,longDateTime:8,tag:4};d.prototype.parseByte=function(){var a=this.data.getUint8(this.offset+this.relativeOffset);return this.relativeOffset+=1,a},d.prototype.parseChar=function(){var a=this.data.getInt8(this.offset+this.relativeOffset);return this.relativeOffset+=1,a},d.prototype.parseCard8=d.prototype.parseByte,d.prototype.parseUShort=function(){var a=this.data.getUint16(this.offset+this.relativeOffset);return this.relativeOffset+=2,a},d.prototype.parseCard16=d.prototype.parseUShort,d.prototype.parseSID=d.prototype.parseUShort,d.prototype.parseOffset16=d.prototype.parseUShort,d.prototype.parseShort=function(){var a=this.data.getInt16(this.offset+this.relativeOffset);return this.relativeOffset+=2,a},d.prototype.parseF2Dot14=function(){var a=this.data.getInt16(this.offset+this.relativeOffset)/16384;return this.relativeOffset+=2,a},d.prototype.parseULong=function(){var a=c.getULong(this.data,this.offset+this.relativeOffset);return this.relativeOffset+=4,a},d.prototype.parseFixed=function(){var a=c.getFixed(this.data,this.offset+this.relativeOffset);return this.relativeOffset+=4,a},d.prototype.parseOffset16List=d.prototype.parseUShortList=function(a){for(var b=new Array(a),d=this.data,e=this.offset+this.relativeOffset,f=0;a>f;f++)b[f]=c.getUShort(d,e),e+=2;return this.relativeOffset+=2*a,b},d.prototype.parseString=function(a){var b=this.data,c=this.offset+this.relativeOffset,d="";this.relativeOffset+=a;for(var e=0;a>e;e++)d+=String.fromCharCode(b.getUint8(c+e));return d},d.prototype.parseTag=function(){return this.parseString(4)},d.prototype.parseLongDateTime=function(){var a=c.getULong(this.data,this.offset+this.relativeOffset+4);return this.relativeOffset+=8,a},d.prototype.parseFixed=function(){var a=c.getULong(this.data,this.offset+this.relativeOffset);return this.relativeOffset+=4,a/65536},d.prototype.parseVersion=function(){var a=c.getUShort(this.data,this.offset+this.relativeOffset),b=c.getUShort(this.data,this.offset+this.relativeOffset+2);return this.relativeOffset+=4,a+b/4096/10},d.prototype.skip=function(a,b){void 0===b&&(b=1),this.relativeOffset+=e[a]*b},c.Parser=d},{}],10:[function(a,b,c){"use strict";function d(){this.commands=[],this.fill="black",this.stroke=null,this.strokeWidth=1}d.prototype.moveTo=function(a,b){this.commands.push({type:"M",x:a,y:b})},d.prototype.lineTo=function(a,b){this.commands.push({type:"L",x:a,y:b})},d.prototype.curveTo=d.prototype.bezierCurveTo=function(a,b,c,d,e,f){this.commands.push({type:"C",x1:a,y1:b,x2:c,y2:d,x:e,y:f})},d.prototype.quadTo=d.prototype.quadraticCurveTo=function(a,b,c,d){this.commands.push({type:"Q",x1:a,y1:b,x:c,y:d})},d.prototype.close=d.prototype.closePath=function(){this.commands.push({type:"Z"})},d.prototype.extend=function(a){a.commands&&(a=a.commands),Array.prototype.push.apply(this.commands,a)},d.prototype.draw=function(a){a.beginPath();for(var b=0;b<this.commands.length;b+=1){var c=this.commands[b];"M"===c.type?a.moveTo(c.x,c.y):"L"===c.type?a.lineTo(c.x,c.y):"C"===c.type?a.bezierCurveTo(c.x1,c.y1,c.x2,c.y2,c.x,c.y):"Q"===c.type?a.quadraticCurveTo(c.x1,c.y1,c.x,c.y):"Z"===c.type&&a.closePath()}this.fill&&(a.fillStyle=this.fill,a.fill()),this.stroke&&(a.strokeStyle=this.stroke,a.lineWidth=this.strokeWidth,a.stroke())},d.prototype.toPathData=function(a){function b(b){return Math.round(b)===b?""+Math.round(b):b.toFixed(a)}function c(){for(var a="",c=0;c<arguments.length;c+=1){var d=arguments[c];d>=0&&c>0&&(a+=" "),a+=b(d)}return a}a=void 0!==a?a:2;for(var d="",e=0;e<this.commands.length;e+=1){var f=this.commands[e];"M"===f.type?d+="M"+c(f.x,f.y):"L"===f.type?d+="L"+c(f.x,f.y):"C"===f.type?d+="C"+c(f.x1,f.y1,f.x2,f.y2,f.x,f.y):"Q"===f.type?d+="Q"+c(f.x1,f.y1,f.x,f.y):"Z"===f.type&&(d+="Z")}return d},d.prototype.toSVG=function(a){var b='<path d="';return b+=this.toPathData(a),b+='"',this.fill&"black"!==this.fill&&(b+=null===this.fill?' fill="none"':' fill="'+this.fill+'"'),this.stroke&&(b+=' stroke="'+this.stroke+'" stroke-width="'+this.strokeWidth+'"'),b+="/>"},c.Path=d},{}],11:[function(a,b,c){"use strict";function d(a,b,c){var d;for(d=0;d<b.length;d+=1){var e=b[d];this[e.name]=e.value}if(this.tableName=a,this.fields=b,c){var f=Object.keys(c);for(d=0;d<f.length;d+=1){var g=f[d],h=c[g];void 0!==this[g]&&(this[g]=h)}}}var e=a("./check"),f=a("./types").encode,g=a("./types").sizeOf;d.prototype.sizeOf=function(){for(var a=0,b=0;b<this.fields.length;b+=1){var c=this.fields[b],d=this[c.name];if(void 0===d&&(d=c.value),"function"==typeof d.sizeOf)a+=d.sizeOf();else{var f=g[c.type];e.assert("function"==typeof f,"Could not find sizeOf function for field"+c.name),a+=f(d)}}return a},d.prototype.encode=function(){return f.TABLE(this)},c.Table=d},{"./check":2,"./types":28}],12:[function(a,b,c){"use strict";function d(a,b){if(a===b)return!0;if(Array.isArray(a)&&Array.isArray(b)){if(a.length!==b.length)return!1;for(var c=0;c<a.length;c+=1)if(!d(a[c],b[c]))return!1;return!0}return!1}function e(a,b,c){var d,e,f,g=[],h=[],i=J.getCard16(a,b);if(0!==i){var j=J.getByte(a,b+2);e=b+(i+1)*j+2;var k=b+3;for(d=0;i+1>d;d+=1)g.push(J.getOffset(a,k,j)),k+=j;f=e+g[i]}else f=b+2;for(d=0;d<g.length-1;d+=1){var l=J.getBytes(a,e+g[d],e+g[d+1]);c&&(l=c(l)),h.push(l)}return{objects:h,startOffset:b,endOffset:f}}function f(a){for(var b="",c=15,d=["0","1","2","3","4","5","6","7","8","9",".","E","E-",null,"-"];;){var e=a.parseByte(),f=e>>4,g=15&e;if(f===c)break;if(b+=d[f],g===c)break;b+=d[g]}return parseFloat(b)}function g(a,b){var c,d,e,g;if(28===b)return c=a.parseByte(),d=a.parseByte(),c<<8|d;if(29===b)return c=a.parseByte(),d=a.parseByte(),e=a.parseByte(),g=a.parseByte(),c<<24|d<<16|e<<8|g;if(30===b)return f(a);if(b>=32&&246>=b)return b-139;if(b>=247&&250>=b)return c=a.parseByte(),256*(b-247)+c+108;if(b>=251&&254>=b)return c=a.parseByte(),256*-(b-251)-c-108;throw new Error("Invalid b0 "+b)}function h(a){for(var b={},c=0;c<a.length;c+=1){var d,e=a[c][0],f=a[c][1];if(d=1===f.length?f[0]:f,b.hasOwnProperty(e))throw new Error("Object "+b+" already has key "+e);b[e]=d}return b}function i(a,b,c){b=void 0!==b?b:0;var d=new J.Parser(a,b),e=[],f=[];for(c=void 0!==c?c:a.length;d.relativeOffset<c;){var i=d.parseByte();21>=i?(12===i&&(i=1200+d.parseByte()),e.push([i,f]),f=[]):f.push(g(d,i))}return h(e)}function j(a,b){return b=390>=b?H.cffStandardStrings[b]:a[b-391]}function k(a,b,c){for(var d={},e=0;e<b.length;e+=1){var f=b[e],g=a[f.op];void 0===g&&(g=void 0!==f.value?f.value:null),"SID"===f.type&&(g=j(c,g)),d[f.name]=g}return d}function l(a,b){var c={};return c.formatMajor=J.getCard8(a,b),c.formatMinor=J.getCard8(a,b+1),c.size=J.getCard8(a,b+2),c.offsetSize=J.getCard8(a,b+3),c.startOffset=b,c.endOffset=b+4,c}function m(a,b){var c=i(a,0,a.byteLength);return k(c,M,b)}function n(a,b,c,d){var e=i(a,b,c);return k(e,N,d)}function o(a,b,c,d){var e,f,g,h=new J.Parser(a,b);c-=1;var i=[".notdef"],k=h.parseCard8();if(0===k)for(e=0;c>e;e+=1)f=h.parseSID(),i.push(j(d,f));else if(1===k)for(;i.length<=c;)for(f=h.parseSID(),g=h.parseCard8(),e=0;g>=e;e+=1)i.push(j(d,f)),f+=1;else{if(2!==k)throw new Error("Unknown charset format "+k);for(;i.length<=c;)for(f=h.parseSID(),g=h.parseCard16(),e=0;g>=e;e+=1)i.push(j(d,f)),f+=1}return i}function p(a,b,c){var d,e,f={},g=new J.Parser(a,b),h=g.parseCard8();if(0===h){var i=g.parseCard8();for(d=0;i>d;d+=1)e=g.parseCard8(), | |
f[e]=d}else{if(1!==h)throw new Error("Unknown encoding format "+h);var j=g.parseCard8();for(e=1,d=0;j>d;d+=1)for(var k=g.parseCard8(),l=g.parseCard8(),m=k;k+l>=m;m+=1)f[m]=e,e+=1}return new H.CffEncoding(f,c)}function q(a,b,c){function d(a,b){p&&k.closePath(),k.moveTo(a,b),p=!0}function e(){var b;b=l.length%2!==0,b&&!n&&(o=l.shift()+a.nominalWidthX),m+=l.length>>1,l.length=0,n=!0}function f(c){for(var s,t,u,v,w,x,y,z,A,B,C,D,E=0;E<c.length;){var F=c[E];switch(E+=1,F){case 1:e();break;case 3:e();break;case 4:l.length>1&&!n&&(o=l.shift()+a.nominalWidthX,n=!0),r+=l.pop(),d(q,r);break;case 5:for(;l.length>0;)q+=l.shift(),r+=l.shift(),k.lineTo(q,r);break;case 6:for(;l.length>0&&(q+=l.shift(),k.lineTo(q,r),0!==l.length);)r+=l.shift(),k.lineTo(q,r);break;case 7:for(;l.length>0&&(r+=l.shift(),k.lineTo(q,r),0!==l.length);)q+=l.shift(),k.lineTo(q,r);break;case 8:for(;l.length>0;)g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+l.shift(),k.curveTo(g,h,i,j,q,r);break;case 10:w=l.pop()+a.subrsBias,x=a.subrs[w],x&&f(x);break;case 11:return;case 12:switch(F=c[E],E+=1,F){case 35:g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),y=i+l.shift(),z=j+l.shift(),A=y+l.shift(),B=z+l.shift(),C=A+l.shift(),D=B+l.shift(),q=C+l.shift(),r=D+l.shift(),l.shift(),k.curveTo(g,h,i,j,y,z),k.curveTo(A,B,C,D,q,r);break;case 34:g=q+l.shift(),h=r,i=g+l.shift(),j=h+l.shift(),y=i+l.shift(),z=j,A=y+l.shift(),B=j,C=A+l.shift(),D=r,q=C+l.shift(),k.curveTo(g,h,i,j,y,z),k.curveTo(A,B,C,D,q,r);break;case 36:g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),y=i+l.shift(),z=j,A=y+l.shift(),B=j,C=A+l.shift(),D=B+l.shift(),q=C+l.shift(),k.curveTo(g,h,i,j,y,z),k.curveTo(A,B,C,D,q,r);break;case 37:g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),y=i+l.shift(),z=j+l.shift(),A=y+l.shift(),B=z+l.shift(),C=A+l.shift(),D=B+l.shift(),Math.abs(C-q)>Math.abs(D-r)?q=C+l.shift():r=D+l.shift(),k.curveTo(g,h,i,j,y,z),k.curveTo(A,B,C,D,q,r);break;default:console.log("Glyph "+b.index+": unknown operator 1200"+F),l.length=0}break;case 14:l.length>0&&!n&&(o=l.shift()+a.nominalWidthX,n=!0),p&&(k.closePath(),p=!1);break;case 18:e();break;case 19:case 20:e(),E+=m+7>>3;break;case 21:l.length>2&&!n&&(o=l.shift()+a.nominalWidthX,n=!0),r+=l.pop(),q+=l.pop(),d(q,r);break;case 22:l.length>1&&!n&&(o=l.shift()+a.nominalWidthX,n=!0),q+=l.pop(),d(q,r);break;case 23:e();break;case 24:for(;l.length>2;)g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+l.shift(),k.curveTo(g,h,i,j,q,r);q+=l.shift(),r+=l.shift(),k.lineTo(q,r);break;case 25:for(;l.length>6;)q+=l.shift(),r+=l.shift(),k.lineTo(q,r);g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+l.shift(),k.curveTo(g,h,i,j,q,r);break;case 26:for(l.length%2&&(q+=l.shift());l.length>0;)g=q,h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i,r=j+l.shift(),k.curveTo(g,h,i,j,q,r);break;case 27:for(l.length%2&&(r+=l.shift());l.length>0;)g=q+l.shift(),h=r,i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j,k.curveTo(g,h,i,j,q,r);break;case 28:s=c[E],t=c[E+1],l.push((s<<24|t<<16)>>16),E+=2;break;case 29:w=l.pop()+a.gsubrsBias,x=a.gsubrs[w],x&&f(x);break;case 30:for(;l.length>0&&(g=q,h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+(1===l.length?l.shift():0),k.curveTo(g,h,i,j,q,r),0!==l.length);)g=q+l.shift(),h=r,i=g+l.shift(),j=h+l.shift(),r=j+l.shift(),q=i+(1===l.length?l.shift():0),k.curveTo(g,h,i,j,q,r);break;case 31:for(;l.length>0&&(g=q+l.shift(),h=r,i=g+l.shift(),j=h+l.shift(),r=j+l.shift(),q=i+(1===l.length?l.shift():0),k.curveTo(g,h,i,j,q,r),0!==l.length);)g=q,h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+(1===l.length?l.shift():0),k.curveTo(g,h,i,j,q,r);break;default:32>F?console.log("Glyph "+b.index+": unknown operator "+F):247>F?l.push(F-139):251>F?(s=c[E],E+=1,l.push(256*(F-247)+s+108)):255>F?(s=c[E],E+=1,l.push(256*-(F-251)-s-108)):(s=c[E],t=c[E+1],u=c[E+2],v=c[E+3],E+=4,l.push((s<<24|t<<16|u<<8|v)/65536))}}}var g,h,i,j,k=new K.Path,l=[],m=0,n=!1,o=a.defaultWidthX,p=!1,q=0,r=0;return f(c),b.advanceWidth=o,k}function r(a){var b;return b=a.length<1240?107:a.length<33900?1131:32768}function s(a,b,c){c.tables.cff={};var d=l(a,b),f=e(a,d.endOffset,J.bytesToString),g=e(a,f.endOffset),h=e(a,g.endOffset,J.bytesToString),i=e(a,h.endOffset);c.gsubrs=i.objects,c.gsubrsBias=r(c.gsubrs);var j=new DataView(new Uint8Array(g.objects[0]).buffer),k=m(j,h.objects);c.tables.cff.topDict=k;var s=b+k["private"][1],t=n(a,s,k["private"][0],h.objects);if(c.defaultWidthX=t.defaultWidthX,c.nominalWidthX=t.nominalWidthX,0!==t.subrs){var u=s+t.subrs,v=e(a,u);c.subrs=v.objects,c.subrsBias=r(c.subrs)}else c.subrs=[],c.subrsBias=0;var w=e(a,b+k.charStrings);c.nGlyphs=w.objects.length;var x=o(a,b+k.charset,c.nGlyphs,h.objects);0===k.encoding?c.cffEncoding=new H.CffEncoding(H.cffStandardEncoding,x):1===k.encoding?c.cffEncoding=new H.CffEncoding(H.cffExpertEncoding,x):c.cffEncoding=p(a,b+k.encoding,x),c.encoding=c.encoding||c.cffEncoding,c.glyphs=new I.GlyphSet(c);for(var y=0;y<c.nGlyphs;y+=1){var z=w.objects[y];c.glyphs.push(y,I.cffGlyphLoader(c,y,q,z))}}function t(a,b){var c,d=H.cffStandardStrings.indexOf(a);return d>=0&&(c=d),d=b.indexOf(a),d>=0?c=d+H.cffStandardStrings.length:(c=H.cffStandardStrings.length+b.length,b.push(a)),c}function u(){return new L.Table("Header",[{name:"major",type:"Card8",value:1},{name:"minor",type:"Card8",value:0},{name:"hdrSize",type:"Card8",value:4},{name:"major",type:"Card8",value:1}])}function v(a){var b=new L.Table("Name INDEX",[{name:"names",type:"INDEX",value:[]}]);b.names=[];for(var c=0;c<a.length;c+=1)b.names.push({name:"name_"+c,type:"NAME",value:a[c]});return b}function w(a,b,c){for(var e={},f=0;f<a.length;f+=1){var g=a[f],h=b[g.name];void 0===h||d(h,g.value)||("SID"===g.type&&(h=t(h,c)),e[g.op]={name:g.name,type:g.type,value:h})}return e}function x(a,b){var c=new L.Table("Top DICT",[{name:"dict",type:"DICT",value:{}}]);return c.dict=w(M,a,b),c}function y(a){var b=new L.Table("Top DICT INDEX",[{name:"topDicts",type:"INDEX",value:[]}]);return b.topDicts=[{name:"topDict_0",type:"TABLE",value:a}],b}function z(a){var b=new L.Table("String INDEX",[{name:"strings",type:"INDEX",value:[]}]);b.strings=[];for(var c=0;c<a.length;c+=1)b.strings.push({name:"string_"+c,type:"STRING",value:a[c]});return b}function A(){return new L.Table("Global Subr INDEX",[{name:"subrs",type:"INDEX",value:[]}])}function B(a,b){for(var c=new L.Table("Charsets",[{name:"format",type:"Card8",value:0}]),d=0;d<a.length;d+=1){var e=a[d],f=t(e,b);c.fields.push({name:"glyph_"+d,type:"SID",value:f})}return c}function C(a){var b=[],c=a.path;b.push({name:"width",type:"NUMBER",value:a.advanceWidth});for(var d=0,e=0,f=0;f<c.commands.length;f+=1){var g,h,i=c.commands[f];if("Q"===i.type){var j=1/3,k=2/3;i={type:"C",x:i.x,y:i.y,x1:j*d+k*i.x1,y1:j*e+k*i.y1,x2:j*i.x+k*i.x1,y2:j*i.y+k*i.y1}}if("M"===i.type)g=Math.round(i.x-d),h=Math.round(i.y-e),b.push({name:"dx",type:"NUMBER",value:g}),b.push({name:"dy",type:"NUMBER",value:h}),b.push({name:"rmoveto",type:"OP",value:21}),d=Math.round(i.x),e=Math.round(i.y);else if("L"===i.type)g=Math.round(i.x-d),h=Math.round(i.y-e),b.push({name:"dx",type:"NUMBER",value:g}),b.push({name:"dy",type:"NUMBER",value:h}),b.push({name:"rlineto",type:"OP",value:5}),d=Math.round(i.x),e=Math.round(i.y);else if("C"===i.type){var l=Math.round(i.x1-d),m=Math.round(i.y1-e),n=Math.round(i.x2-i.x1),o=Math.round(i.y2-i.y1);g=Math.round(i.x-i.x2),h=Math.round(i.y-i.y2),b.push({name:"dx1",type:"NUMBER",value:l}),b.push({name:"dy1",type:"NUMBER",value:m}),b.push({name:"dx2",type:"NUMBER",value:n}),b.push({name:"dy2",type:"NUMBER",value:o}),b.push({name:"dx",type:"NUMBER",value:g}),b.push({name:"dy",type:"NUMBER",value:h}),b.push({name:"rrcurveto",type:"OP",value:8}),d=Math.round(i.x),e=Math.round(i.y)}}return b.push({name:"endchar",type:"OP",value:14}),b}function D(a){for(var b=new L.Table("CharStrings INDEX",[{name:"charStrings",type:"INDEX",value:[]}]),c=0;c<a.length;c+=1){var d=a.get(c),e=C(d);b.charStrings.push({name:d.name,type:"CHARSTRING",value:e})}return b}function E(a,b){var c=new L.Table("Private DICT",[{name:"dict",type:"DICT",value:{}}]);return c.dict=w(N,a,b),c}function F(a){var b=new L.Table("Private DICT INDEX",[{name:"privateDicts",type:"INDEX",value:[]}]);return b.privateDicts=[{name:"privateDict_0",type:"TABLE",value:a}],b}function G(a,b){for(var c,d=new L.Table("CFF ",[{name:"header",type:"TABLE"},{name:"nameIndex",type:"TABLE"},{name:"topDictIndex",type:"TABLE"},{name:"stringIndex",type:"TABLE"},{name:"globalSubrIndex",type:"TABLE"},{name:"charsets",type:"TABLE"},{name:"charStringsIndex",type:"TABLE"},{name:"privateDictIndex",type:"TABLE"}]),e=1/b.unitsPerEm,f={version:b.version,fullName:b.fullName,familyName:b.familyName,weight:b.weightName,fontMatrix:[e,0,0,e,0,0],charset:999,encoding:0,charStrings:999,"private":[0,999]},g={},h=[],i=1;i<a.length;i+=1)c=a.get(i),h.push(c.name);var j=[];d.header=u(),d.nameIndex=v([b.postScriptName]);var k=x(f,j);d.topDictIndex=y(k),d.globalSubrIndex=A(),d.charsets=B(h,j),d.charStringsIndex=D(a);var l=E(g,j);d.privateDictIndex=F(l),d.stringIndex=z(j);var m=d.header.sizeOf()+d.nameIndex.sizeOf()+d.topDictIndex.sizeOf()+d.stringIndex.sizeOf()+d.globalSubrIndex.sizeOf();return f.charset=m,f.encoding=0,f.charStrings=f.charset+d.charsets.sizeOf(),f["private"][1]=f.charStrings+d.charStringsIndex.sizeOf(),k=x(f,j),d.topDictIndex=y(k),d}var H=a("../encoding"),I=a("../glyphset"),J=a("../parse"),K=a("../path"),L=a("../table"),M=[{name:"version",op:0,type:"SID"},{name:"notice",op:1,type:"SID"},{name:"copyright",op:1200,type:"SID"},{name:"fullName",op:2,type:"SID"},{name:"familyName",op:3,type:"SID"},{name:"weight",op:4,type:"SID"},{name:"isFixedPitch",op:1201,type:"number",value:0},{name:"italicAngle",op:1202,type:"number",value:0},{name:"underlinePosition",op:1203,type:"number",value:-100},{name:"underlineThickness",op:1204,type:"number",value:50},{name:"paintType",op:1205,type:"number",value:0},{name:"charstringType",op:1206,type:"number",value:2},{name:"fontMatrix",op:1207,type:["real","real","real","real","real","real"],value:[.001,0,0,.001,0,0]},{name:"uniqueId",op:13,type:"number"},{name:"fontBBox",op:5,type:["number","number","number","number"],value:[0,0,0,0]},{name:"strokeWidth",op:1208,type:"number",value:0},{name:"xuid",op:14,type:[],value:null},{name:"charset",op:15,type:"offset",value:0},{name:"encoding",op:16,type:"offset",value:0},{name:"charStrings",op:17,type:"offset",value:0},{name:"private",op:18,type:["number","offset"],value:[0,0]}],N=[{name:"subrs",op:19,type:"offset",value:0},{name:"defaultWidthX",op:20,type:"number",value:0},{name:"nominalWidthX",op:21,type:"number",value:0}];c.parse=s,c.make=G},{"../encoding":4,"../glyphset":7,"../parse":9,"../path":10,"../table":11}],13:[function(a,b,c){"use strict";function d(a,b){var c,d={};d.version=i.getUShort(a,b),h.argument(0===d.version,"cmap table version should be 0."),d.numTables=i.getUShort(a,b+2);var e=-1;for(c=0;c<d.numTables;c+=1){var f=i.getUShort(a,b+4+8*c),g=i.getUShort(a,b+4+8*c+2);if(3===f&&(1===g||0===g)){e=i.getULong(a,b+4+8*c+4);break}}if(-1===e)return null;var j=new i.Parser(a,b+e);d.format=j.parseUShort(),h.argument(4===d.format,"Only format 4 cmap tables are supported."),d.length=j.parseUShort(),d.language=j.parseUShort();var k;d.segCount=k=j.parseUShort()>>1,j.skip("uShort",3),d.glyphIndexMap={};var l=new i.Parser(a,b+e+14),m=new i.Parser(a,b+e+16+2*k),n=new i.Parser(a,b+e+16+4*k),o=new i.Parser(a,b+e+16+6*k),p=b+e+16+8*k;for(c=0;k-1>c;c+=1)for(var q,r=l.parseUShort(),s=m.parseUShort(),t=n.parseShort(),u=o.parseUShort(),v=s;r>=v;v+=1)0!==u?(p=o.offset+o.relativeOffset-2,p+=u,p+=2*(v-s),q=i.getUShort(a,p),0!==q&&(q=q+t&65535)):q=v+t&65535,d.glyphIndexMap[v]=q;return d}function e(a,b,c){a.segments.push({end:b,start:b,delta:-(b-c),offset:0})}function f(a){a.segments.push({end:65535,start:65535,delta:1,offset:0})}function g(a){var b,c=new j.Table("cmap",[{name:"version",type:"USHORT",value:0},{name:"numTables",type:"USHORT",value:1},{name:"platformID",type:"USHORT",value:3},{name:"encodingID",type:"USHORT",value:1},{name:"offset",type:"ULONG",value:12},{name:"format",type:"USHORT",value:4},{name:"length",type:"USHORT",value:0},{name:"language",type:"USHORT",value:0},{name:"segCountX2",type:"USHORT",value:0},{name:"searchRange",type:"USHORT",value:0},{name:"entrySelector",type:"USHORT",value:0},{name:"rangeShift",type:"USHORT",value:0}]);for(c.segments=[],b=0;b<a.length;b+=1){for(var d=a.get(b),g=0;g<d.unicodes.length;g+=1)e(c,d.unicodes[g],b);c.segments=c.segments.sort(function(a,b){return a.start-b.start})}f(c);var h;h=c.segments.length,c.segCountX2=2*h,c.searchRange=2*Math.pow(2,Math.floor(Math.log(h)/Math.log(2))),c.entrySelector=Math.log(c.searchRange/2)/Math.log(2),c.rangeShift=c.segCountX2-c.searchRange;var i=[],k=[],l=[],m=[],n=[];for(b=0;h>b;b+=1){var o=c.segments[b];i=i.concat({name:"end_"+b,type:"USHORT",value:o.end}),k=k.concat({name:"start_"+b,type:"USHORT",value:o.start}),l=l.concat({name:"idDelta_"+b,type:"SHORT",value:o.delta}),m=m.concat({name:"idRangeOffset_"+b,type:"USHORT",value:o.offset}),void 0!==o.glyphId&&(n=n.concat({name:"glyph_"+b,type:"USHORT",value:o.glyphId}))}return c.fields=c.fields.concat(i),c.fields.push({name:"reservedPad",type:"USHORT",value:0}),c.fields=c.fields.concat(k),c.fields=c.fields.concat(l),c.fields=c.fields.concat(m),c.fields=c.fields.concat(n),c.length=14+2*i.length+2+2*k.length+2*l.length+2*m.length+2*n.length,c}var h=a("../check"),i=a("../parse"),j=a("../table");c.parse=d,c.make=g},{"../check":2,"../parse":9,"../table":11}],14:[function(a,b,c){"use strict";function d(a,b){var c=JSON.stringify(a),d=256;for(var e in b){var f=parseInt(e);if(f&&!(256>f)){if(JSON.stringify(b[e])===c)return f;f>=d&&(d=f+1)}}return b[d]=a,d}function e(a,b){var c=d(a.name,b);return new m.Table("fvarAxis",[{name:"tag",type:"TAG",value:a.tag},{name:"minValue",type:"FIXED",value:a.minValue<<16},{name:"defaultValue",type:"FIXED",value:a.defaultValue<<16},{name:"maxValue",type:"FIXED",value:a.maxValue<<16},{name:"flags",type:"USHORT",value:0},{name:"nameID",type:"USHORT",value:c}])}function f(a,b,c){var d={},e=new l.Parser(a,b);return d.tag=e.parseTag(),d.minValue=e.parseFixed(),d.defaultValue=e.parseFixed(),d.maxValue=e.parseFixed(),e.skip("uShort",1),d.name=c[e.parseUShort()]||{},d}function g(a,b,c){for(var e=d(a.name,c),f=[{name:"nameID",type:"USHORT",value:e},{name:"flags",type:"USHORT",value:0}],g=0;g<b.length;++g){var h=b[g].tag;f.push({name:"axis "+h,type:"FIXED",value:a.coordinates[h]<<16})}return new m.Table("fvarInstance",f)}function h(a,b,c,d){var e={},f=new l.Parser(a,b);e.name=d[f.parseUShort()]||{},f.skip("uShort",1),e.coordinates={};for(var g=0;g<c.length;++g)e.coordinates[c[g].tag]=f.parseFixed();return e}function i(a,b){var c=new m.Table("fvar",[{name:"version",type:"ULONG",value:65536},{name:"offsetToData",type:"USHORT",value:0},{name:"countSizePairs",type:"USHORT",value:2},{name:"axisCount",type:"USHORT",value:a.axes.length},{name:"axisSize",type:"USHORT",value:20},{name:"instanceCount",type:"USHORT",value:a.instances.length},{name:"instanceSize",type:"USHORT",value:4+4*a.axes.length}]);c.offsetToData=c.sizeOf();for(var d=0;d<a.axes.length;d++)c.fields.push({name:"axis "+d,type:"TABLE",value:e(a.axes[d],b)});for(var f=0;f<a.instances.length;f++)c.fields.push({name:"instance "+f,type:"TABLE",value:g(a.instances[f],a.axes,b)});return c}function j(a,b,c){var d=new l.Parser(a,b),e=d.parseULong();k.argument(65536===e,"Unsupported fvar table version.");var g=d.parseOffset16();d.skip("uShort",1);for(var i=d.parseUShort(),j=d.parseUShort(),m=d.parseUShort(),n=d.parseUShort(),o=[],p=0;i>p;p++)o.push(f(a,b+g+p*j,c));for(var q=[],r=b+g+i*j,s=0;m>s;s++)q.push(h(a,r+s*n,o,c));return{axes:o,instances:q}}var k=a("../check"),l=a("../parse"),m=a("../table");c.make=i,c.parse=j},{"../check":2,"../parse":9,"../table":11}],15:[function(a,b,c){"use strict";function d(a,b,c,d,e){var f;return(b&d)>0?(f=a.parseByte(),0===(b&e)&&(f=-f),f=c+f):f=(b&e)>0?c:c+a.parseShort(),f}function e(a,b,c){var e=new m.Parser(b,c);a.numberOfContours=e.parseShort(),a.xMin=e.parseShort(),a.yMin=e.parseShort(),a.xMax=e.parseShort(),a.yMax=e.parseShort();var f,g;if(a.numberOfContours>0){var h,i=a.endPointIndices=[];for(h=0;h<a.numberOfContours;h+=1)i.push(e.parseUShort());for(a.instructionLength=e.parseUShort(),a.instructions=[],h=0;h<a.instructionLength;h+=1)a.instructions.push(e.parseByte());var j=i[i.length-1]+1;for(f=[],h=0;j>h;h+=1)if(g=e.parseByte(),f.push(g),(8&g)>0)for(var l=e.parseByte(),n=0;l>n;n+=1)f.push(g),h+=1;if(k.argument(f.length===j,"Bad flags."),i.length>0){var o,p=[];if(j>0){for(h=0;j>h;h+=1)g=f[h],o={},o.onCurve=!!(1&g),o.lastPointOfContour=i.indexOf(h)>=0,p.push(o);var q=0;for(h=0;j>h;h+=1)g=f[h],o=p[h],o.x=d(e,g,q,2,16),q=o.x;var r=0;for(h=0;j>h;h+=1)g=f[h],o=p[h],o.y=d(e,g,r,4,32),r=o.y}a.points=p}else a.points=[]}else if(0===a.numberOfContours)a.points=[];else{a.isComposite=!0,a.points=[],a.components=[];for(var s=!0;s;){f=e.parseUShort();var t={glyphIndex:e.parseUShort(),xScale:1,scale01:0,scale10:0,yScale:1,dx:0,dy:0};(1&f)>0?(t.dx=e.parseShort(),t.dy=e.parseShort()):(t.dx=e.parseChar(),t.dy=e.parseChar()),(8&f)>0?t.xScale=t.yScale=e.parseF2Dot14():(64&f)>0?(t.xScale=e.parseF2Dot14(),t.yScale=e.parseF2Dot14()):(128&f)>0&&(t.xScale=e.parseF2Dot14(),t.scale01=e.parseF2Dot14(),t.scale10=e.parseF2Dot14(),t.yScale=e.parseF2Dot14()),a.components.push(t),s=!!(32&f)}}}function f(a,b){for(var c=[],d=0;d<a.length;d+=1){var e=a[d],f={x:b.xScale*e.x+b.scale01*e.y+b.dx,y:b.scale10*e.x+b.yScale*e.y+b.dy,onCurve:e.onCurve,lastPointOfContour:e.lastPointOfContour};c.push(f)}return c}function g(a){for(var b=[],c=[],d=0;d<a.length;d+=1){var e=a[d];c.push(e),e.lastPointOfContour&&(b.push(c),c=[])}return k.argument(0===c.length,"There are still points left in the current contour."),b}function h(a){var b=new n.Path;if(!a)return b;for(var c=g(a),d=0;d<c.length;d+=1){var e,f,h=c[d],i=h[0],j=h[h.length-1];i.onCurve?(e=null,f=!0):(i=j.onCurve?j:{x:(i.x+j.x)/2,y:(i.y+j.y)/2},e=i,f=!1),b.moveTo(i.x,i.y);for(var k=f?1:0;k<h.length;k+=1){var l=h[k],m=0===k?i:h[k-1];if(m.onCurve&&l.onCurve)b.lineTo(l.x,l.y);else if(m.onCurve&&!l.onCurve)e=l;else if(m.onCurve||l.onCurve){if(m.onCurve||!l.onCurve)throw new Error("Invalid state.");b.quadraticCurveTo(e.x,e.y,l.x,l.y),e=null}else{var o={x:(m.x+l.x)/2,y:(m.y+l.y)/2};b.quadraticCurveTo(m.x,m.y,o.x,o.y),e=l}}i!==j&&(e?b.quadraticCurveTo(e.x,e.y,i.x,i.y):b.lineTo(i.x,i.y))}return b.closePath(),b}function i(a,b){if(b.isComposite)for(var c=0;c<b.components.length;c+=1){var d=b.components[c],e=a.get(d.glyphIndex);if(e.points){var g=f(e.points,d);b.points=b.points.concat(g)}}return h(b.points)}function j(a,b,c,d){var f,g=new l.GlyphSet(d);for(f=0;f<c.length-1;f+=1){var h=c[f],j=c[f+1];h!==j?g.push(f,l.ttfGlyphLoader(d,f,e,a,b+h,i)):g.push(f,l.glyphLoader(d,f))}return g}var k=a("../check"),l=a("../glyphset"),m=a("../parse"),n=a("../path");c.parse=j},{"../check":2,"../glyphset":7,"../parse":9,"../path":10}],16:[function(a,b,c){"use strict";function d(a,b){for(var c=new k.Parser(a,b),d=c.parseUShort(),e=[],f=0;d>f;f++)e[c.parseTag()]={offset:c.parseUShort()};return e}function e(a,b){var c=new k.Parser(a,b),d=c.parseUShort(),e=c.parseUShort();if(1===d)return c.parseUShortList(e);if(2===d){for(var f=[];e--;)for(var g=c.parseUShort(),h=c.parseUShort(),i=c.parseUShort(),j=g;h>=j;j++)f[i++]=j;return f}}function f(a,b){var c=new k.Parser(a,b),d=c.parseUShort();if(1===d){var e=c.parseUShort(),f=c.parseUShort(),g=c.parseUShortList(f);return function(a){return g[a-e]||0}}if(2===d){for(var h=c.parseUShort(),i=[],j=[],l=[],m=0;h>m;m++)i[m]=c.parseUShort(),j[m]=c.parseUShort(),l[m]=c.parseUShort();return function(a){for(var b=0,c=i.length-1;c>b;){var d=b+c+1>>1;a<i[d]?c=d-1:b=d}return i[b]<=a&&a<=j[b]?l[b]||0:0}}}function g(a,b){var c,d,g=new k.Parser(a,b),h=g.parseUShort(),i=g.parseUShort(),j=e(a,b+i),l=g.parseUShort(),m=g.parseUShort();if(4===l&&0===m){var n={};if(1===h){for(var o=g.parseUShort(),p=[],q=g.parseOffset16List(o),r=0;o>r;r++){var s=q[r],t=n[s];if(!t){t={},g.relativeOffset=s;for(var u=g.parseUShort();u--;){var v=g.parseUShort();l&&(c=g.parseShort()),m&&(d=g.parseShort()),t[v]=c}}p[j[r]]=t}return function(a,b){var c=p[a];return c?c[b]:void 0}}if(2===h){for(var w=g.parseUShort(),x=g.parseUShort(),y=g.parseUShort(),z=g.parseUShort(),A=f(a,b+w),B=f(a,b+x),C=[],D=0;y>D;D++)for(var E=C[D]=[],F=0;z>F;F++)l&&(c=g.parseShort()),m&&(d=g.parseShort()),E[F]=c;var G={};for(D=0;D<j.length;D++)G[j[D]]=1;return function(a,b){if(G[a]){var c=A(a),d=B(b),e=C[c];return e?e[d]:void 0}}}}}function h(a,b){var c=new k.Parser(a,b),d=c.parseUShort(),e=c.parseUShort(),f=16&e,h=c.parseUShort(),i=c.parseOffset16List(h),j={lookupType:d,lookupFlag:e,markFilteringSet:f?c.parseUShort():-1};if(2===d){for(var l=[],m=0;h>m;m++)l.push(g(a,b+i[m]));j.getKerningValue=function(a,b){for(var c=l.length;c--;){var d=l[c](a,b);if(void 0!==d)return d}return 0}}return j}function i(a,b,c){var e=new k.Parser(a,b),f=e.parseFixed();j.argument(1===f,"Unsupported GPOS table version."),d(a,b+e.parseUShort()),d(a,b+e.parseUShort());var g=e.parseUShort();e.relativeOffset=g;for(var i=e.parseUShort(),l=e.parseOffset16List(i),m=b+g,n=0;i>n;n++){var o=h(a,m+l[n]);2!==o.lookupType||c.getGposKerningValue||(c.getGposKerningValue=o.getKerningValue)}}var j=a("../check"),k=a("../parse");c.parse=i},{"../check":2,"../parse":9}],17:[function(a,b,c){"use strict";function d(a,b){var c={},d=new g.Parser(a,b);return c.version=d.parseVersion(),c.fontRevision=Math.round(1e3*d.parseFixed())/1e3,c.checkSumAdjustment=d.parseULong(),c.magicNumber=d.parseULong(),f.argument(1594834165===c.magicNumber,"Font header has wrong magic number."),c.flags=d.parseUShort(),c.unitsPerEm=d.parseUShort(),c.created=d.parseLongDateTime(),c.modified=d.parseLongDateTime(),c.xMin=d.parseShort(),c.yMin=d.parseShort(),c.xMax=d.parseShort(),c.yMax=d.parseShort(),c.macStyle=d.parseUShort(),c.lowestRecPPEM=d.parseUShort(),c.fontDirectionHint=d.parseShort(),c.indexToLocFormat=d.parseShort(),c.glyphDataFormat=d.parseShort(),c}function e(a){return new h.Table("head",[{name:"version",type:"FIXED",value:65536},{name:"fontRevision",type:"FIXED",value:65536},{name:"checkSumAdjustment",type:"ULONG",value:0},{name:"magicNumber",type:"ULONG",value:1594834165},{name:"flags",type:"USHORT",value:0},{name:"unitsPerEm",type:"USHORT",value:1e3},{name:"created",type:"LONGDATETIME",value:0},{name:"modified",type:"LONGDATETIME",value:0},{name:"xMin",type:"SHORT",value:0},{name:"yMin",type:"SHORT",value:0},{name:"xMax",type:"SHORT",value:0},{name:"yMax",type:"SHORT",value:0},{name:"macStyle",type:"USHORT",value:0},{name:"lowestRecPPEM",type:"USHORT",value:0},{name:"fontDirectionHint",type:"SHORT",value:2},{name:"indexToLocFormat",type:"SHORT",value:0},{name:"glyphDataFormat",type:"SHORT",value:0}],a)}var f=a("../check"),g=a("../parse"),h=a("../table");c.parse=d,c.make=e},{"../check":2,"../parse":9,"../table":11}],18:[function(a,b,c){"use strict";function d(a,b){var c={},d=new f.Parser(a,b);return c.version=d.parseVersion(),c.ascender=d.parseShort(),c.descender=d.parseShort(),c.lineGap=d.parseShort(),c.advanceWidthMax=d.parseUShort(),c.minLeftSideBearing=d.parseShort(),c.minRightSideBearing=d.parseShort(),c.xMaxExtent=d.parseShort(),c.caretSlopeRise=d.parseShort(),c.caretSlopeRun=d.parseShort(),c.caretOffset=d.parseShort(),d.relativeOffset+=8,c.metricDataFormat=d.parseShort(),c.numberOfHMetrics=d.parseUShort(),c}function e(a){return new g.Table("hhea",[{name:"version",type:"FIXED",value:65536},{name:"ascender",type:"FWORD",value:0},{name:"descender",type:"FWORD",value:0},{name:"lineGap",type:"FWORD",value:0},{name:"advanceWidthMax",type:"UFWORD",value:0},{name:"minLeftSideBearing",type:"FWORD",value:0},{name:"minRightSideBearing",type:"FWORD",value:0},{name:"xMaxExtent",type:"FWORD",value:0},{name:"caretSlopeRise",type:"SHORT",value:1},{name:"caretSlopeRun",type:"SHORT",value:0},{name:"caretOffset",type:"SHORT",value:0},{name:"reserved1",type:"SHORT",value:0},{name:"reserved2",type:"SHORT",value:0},{name:"reserved3",type:"SHORT",value:0},{name:"reserved4",type:"SHORT",value:0},{name:"metricDataFormat",type:"SHORT",value:0},{name:"numberOfHMetrics",type:"USHORT",value:0}],a)}var f=a("../parse"),g=a("../table");c.parse=d,c.make=e},{"../parse":9,"../table":11}],19:[function(a,b,c){"use strict";function d(a,b,c,d,e){for(var g,h,i=new f.Parser(a,b),j=0;d>j;j+=1){c>j&&(g=i.parseUShort(),h=i.parseShort());var k=e.get(j);k.advanceWidth=g,k.leftSideBearing=h}}function e(a){for(var b=new g.Table("hmtx",[]),c=0;c<a.length;c+=1){var d=a.get(c),e=d.advanceWidth||0,f=d.leftSideBearing||0;b.fields.push({name:"advanceWidth_"+c,type:"USHORT",value:e}),b.fields.push({name:"leftSideBearing_"+c,type:"SHORT",value:f})}return b}var f=a("../parse"),g=a("../table");c.parse=d,c.make=e},{"../parse":9,"../table":11}],20:[function(a,b,c){"use strict";function d(a,b){var c={},d=new f.Parser(a,b),g=d.parseUShort();e.argument(0===g,"Unsupported kern table version."),d.skip("uShort",1);var h=d.parseUShort();e.argument(0===h,"Unsupported kern sub-table version."),d.skip("uShort",2);var i=d.parseUShort();d.skip("uShort",3);for(var j=0;i>j;j+=1){var k=d.parseUShort(),l=d.parseUShort(),m=d.parseShort();c[k+","+l]=m}return c}var e=a("../check"),f=a("../parse");c.parse=d},{"../check":2,"../parse":9}],21:[function(a,b,c){"use strict";function d(a,b,c,d){for(var f=new e.Parser(a,b),g=d?f.parseUShort:f.parseULong,h=[],i=0;c+1>i;i+=1){var j=g.call(f);d&&(j*=2),h.push(j)}return h}var e=a("../parse");c.parse=d},{"../parse":9}],22:[function(a,b,c){"use strict";function d(a){for(var b=new h.Table("ltag",[{name:"version",type:"ULONG",value:1},{name:"flags",type:"ULONG",value:0},{name:"numTags",type:"ULONG",value:a.length}]),c="",d=12+4*a.length,e=0;e<a.length;++e){var f=c.indexOf(a[e]);0>f&&(f=c.length,c+=a[e]),b.fields.push({name:"offset "+e,type:"USHORT",value:d+f}),b.fields.push({name:"length "+e,type:"USHORT",value:a[e].length})}return b.fields.push({name:"stringPool",type:"CHARARRAY",value:c}),b}function e(a,b){var c=new g.Parser(a,b),d=c.parseULong();f.argument(1===d,"Unsupported ltag table version."),c.skip("uLong",1);for(var e=c.parseULong(),h=[],i=0;e>i;i++){for(var j="",k=b+c.parseUShort(),l=c.parseUShort(),m=k;k+l>m;++m)j+=String.fromCharCode(a.getInt8(m));h.push(j)}return h}var f=a("../check"),g=a("../parse"),h=a("../table");c.make=d,c.parse=e},{"../check":2,"../parse":9,"../table":11}],23:[function(a,b,c){"use strict";function d(a,b){var c={},d=new f.Parser(a,b);return c.version=d.parseVersion(),c.numGlyphs=d.parseUShort(),1===c.version&&(c.maxPoints=d.parseUShort(),c.maxContours=d.parseUShort(),c.maxCompositePoints=d.parseUShort(),c.maxCompositeContours=d.parseUShort(),c.maxZones=d.parseUShort(),c.maxTwilightPoints=d.parseUShort(),c.maxStorage=d.parseUShort(),c.maxFunctionDefs=d.parseUShort(),c.maxInstructionDefs=d.parseUShort(),c.maxStackElements=d.parseUShort(),c.maxSizeOfInstructions=d.parseUShort(),c.maxComponentElements=d.parseUShort(),c.maxComponentDepth=d.parseUShort()),c}function e(a){return new g.Table("maxp",[{name:"version",type:"FIXED",value:20480},{name:"numGlyphs",type:"USHORT",value:a}])}var f=a("../parse"),g=a("../table");c.parse=d,c.make=e},{"../parse":9,"../table":11}],24:[function(a,b,c){"use strict";function d(a,b,c){switch(a){case 0:if(65535===b)return"und";if(c)return c[b];break;case 1:return r[b];case 3:return t[b]}return void 0}function e(a,b,c){switch(a){case 0:return u;case 1:return w[c]||v[b];case 3:if(1===b||10===b)return u}return void 0}function f(a,b,c){for(var f={},g=new o.Parser(a,b),h=g.parseUShort(),i=g.parseUShort(),j=g.offset+g.parseUShort(),k=0;i>k;k++){var l=g.parseUShort(),n=g.parseUShort(),p=g.parseUShort(),r=g.parseUShort(),s=q[r]||r,t=g.parseUShort(),v=g.parseUShort(),w=d(l,p,c),x=e(l,n,p);if(void 0!==x&&void 0!==w){var y;if(y=x===u?m.UTF16(a,j+v,t):m.MACSTRING(a,j+v,t,x)){var z=f[s];void 0===z&&(z=f[s]={}),z[w]=y}}}var A=0;return 1===h&&(A=g.parseUShort()),f}function g(a){var b={};for(var c in a)b[a[c]]=parseInt(c);return b}function h(a,b,c,d,e,f){return new p.Table("NameRecord",[{name:"platformID",type:"USHORT",value:a},{name:"encodingID",type:"USHORT",value:b},{name:"languageID",type:"USHORT",value:c},{name:"nameID",type:"USHORT",value:d},{name:"length",type:"USHORT",value:e},{name:"offset",type:"USHORT",value:f}])}function i(a,b){var c=a.length,d=b.length-c+1;a:for(var e=0;d>e;e++)for(;d>e;e++){for(var f=0;c>f;f++)if(b[e+f]!==a[f])continue a;return e}return-1}function j(a,b){var c=i(a,b);if(0>c){c=b.length;for(var d=0,e=a.length;e>d;++d)b.push(a[d])}return c}function k(a,b){var c,d=[],f={},i=g(q);for(var k in a){var l=i[k];void 0===l&&(l=k),c=parseInt(l),f[c]=a[k],d.push(c)}for(var m=g(r),o=g(t),u=[],v=[],w=0;w<d.length;w++){c=d[w];var x=f[c];for(var y in x){var z=x[y],A=1,B=m[y],C=s[B],D=e(A,C,B),E=n.MACSTRING(z,D);void 0===E&&(A=0,B=b.indexOf(y),0>B&&(B=b.length,b.push(y)),C=4,E=n.UTF16(z));var F=j(E,v);u.push(h(A,C,B,c,E.length,F));var G=o[y];if(void 0!==G){var H=n.UTF16(z),I=j(H,v);u.push(h(3,1,G,c,H.length,I))}}}u.sort(function(a,b){return a.platformID-b.platformID||a.encodingID-b.encodingID||a.languageID-b.languageID||a.nameID-b.nameID});for(var J=new p.Table("name",[{name:"format",type:"USHORT",value:0},{name:"count",type:"USHORT",value:u.length},{name:"stringOffset",type:"USHORT",value:6+12*u.length}]),K=0;K<u.length;K++)J.fields.push({name:"record_"+K,type:"TABLE",value:u[K]});return J.fields.push({name:"strings",type:"LITERAL",value:v}),J}var l=a("../types"),m=l.decode,n=l.encode,o=a("../parse"),p=a("../table"),q=["copyright","fontFamily","fontSubfamily","uniqueID","fullName","version","postScriptName","trademark","manufacturer","designer","description","manufacturerURL","designerURL","licence","licenceURL","reserved","preferredFamily","preferredSubfamily","compatibleFullName","sampleText","postScriptFindFontName","wwsFamily","wwsSubfamily"],r={0:"en",1:"fr",2:"de",3:"it",4:"nl",5:"sv",6:"es",7:"da",8:"pt",9:"no",10:"he",11:"ja",12:"ar",13:"fi",14:"el",15:"is",16:"mt",17:"tr",18:"hr",19:"zh-Hant",20:"ur",21:"hi",22:"th",23:"ko",24:"lt",25:"pl",26:"hu",27:"es",28:"lv",29:"se",30:"fo",31:"fa",32:"ru",33:"zh",34:"nl-BE",35:"ga",36:"sq",37:"ro",38:"cz",39:"sk",40:"si",41:"yi",42:"sr",43:"mk",44:"bg",45:"uk",46:"be",47:"uz",48:"kk",49:"az-Cyrl",50:"az-Arab",51:"hy",52:"ka",53:"mo",54:"ky",55:"tg",56:"tk",57:"mn-CN",58:"mn",59:"ps",60:"ks",61:"ku",62:"sd",63:"bo",64:"ne",65:"sa",66:"mr",67:"bn",68:"as",69:"gu",70:"pa",71:"or",72:"ml",73:"kn",74:"ta",75:"te",76:"si",77:"my",78:"km",79:"lo",80:"vi",81:"id",82:"tl",83:"ms",84:"ms-Arab",85:"am",86:"ti",87:"om",88:"so",89:"sw",90:"rw",91:"rn",92:"ny",93:"mg",94:"eo",128:"cy",129:"eu",130:"ca",131:"la",132:"qu",133:"gn",134:"ay",135:"tt",136:"ug",137:"dz",138:"jv",139:"su",140:"gl",141:"af",142:"br",143:"iu",144:"gd",145:"gv",146:"ga",147:"to",148:"el-polyton",149:"kl",150:"az",151:"nn"},s={0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0,10:5,11:1,12:4,13:0,14:6,15:0,16:0,17:0,18:0,19:2,20:4,21:9,22:21,23:3,24:29,25:29,26:29,27:29,28:29,29:0,30:0,31:4,32:7,33:25,34:0,35:0,36:0,37:0,38:29,39:29,40:0,41:5,42:7,43:7,44:7,45:7,46:7,47:7,48:7,49:7,50:4,51:24,52:23,53:7,54:7,55:7,56:7,57:27,58:7,59:4,60:4,61:4,62:4,63:26,64:9,65:9,66:9,67:13,68:13,69:11,70:10,71:12,72:17,73:16,74:14,75:15,76:18,77:19,78:20,79:22,80:30,81:0,82:0,83:0,84:4,85:28,86:28,87:28,88:0,89:0,90:0,91:0,92:0,93:0,94:0,128:0,129:0,130:0,131:0,132:0,133:0,134:0,135:7,136:4,137:26,138:0,139:0,140:0,141:0,142:0,143:28,144:0,145:0,146:0,147:0,148:6,149:0,150:0,151:0},t={1078:"af",1052:"sq",1156:"gsw",1118:"am",5121:"ar-DZ",15361:"ar-BH",3073:"ar",2049:"ar-IQ",11265:"ar-JO",13313:"ar-KW",12289:"ar-LB",4097:"ar-LY",6145:"ary",8193:"ar-OM",16385:"ar-QA",1025:"ar-SA",10241:"ar-SY",7169:"aeb",14337:"ar-AE",9217:"ar-YE",1067:"hy",1101:"as",2092:"az-Cyrl",1068:"az",1133:"ba",1069:"eu",1059:"be",2117:"bn",1093:"bn-IN", | |
8218:"bs-Cyrl",5146:"bs",1150:"br",1026:"bg",1027:"ca",3076:"zh-HK",5124:"zh-MO",2052:"zh",4100:"zh-SG",1028:"zh-TW",1155:"co",1050:"hr",4122:"hr-BA",1029:"cs",1030:"da",1164:"prs",1125:"dv",2067:"nl-BE",1043:"nl",3081:"en-AU",10249:"en-BZ",4105:"en-CA",9225:"en-029",16393:"en-IN",6153:"en-IE",8201:"en-JM",17417:"en-MY",5129:"en-NZ",13321:"en-PH",18441:"en-SG",7177:"en-ZA",11273:"en-TT",2057:"en-GB",1033:"en",12297:"en-ZW",1061:"et",1080:"fo",1124:"fil",1035:"fi",2060:"fr-BE",3084:"fr-CA",1036:"fr",5132:"fr-LU",6156:"fr-MC",4108:"fr-CH",1122:"fy",1110:"gl",1079:"ka",3079:"de-AT",1031:"de",5127:"de-LI",4103:"de-LU",2055:"de-CH",1032:"el",1135:"kl",1095:"gu",1128:"ha",1037:"he",1081:"hi",1038:"hu",1039:"is",1136:"ig",1057:"id",1117:"iu",2141:"iu-Latn",2108:"ga",1076:"xh",1077:"zu",1040:"it",2064:"it-CH",1041:"ja",1099:"kn",1087:"kk",1107:"km",1158:"quc",1159:"rw",1089:"sw",1111:"kok",1042:"ko",1088:"ky",1108:"lo",1062:"lv",1063:"lt",2094:"dsb",1134:"lb",1071:"mk",2110:"ms-BN",1086:"ms",1100:"ml",1082:"mt",1153:"mi",1146:"arn",1102:"mr",1148:"moh",1104:"mn",2128:"mn-CN",1121:"ne",1044:"nb",2068:"nn",1154:"oc",1096:"or",1123:"ps",1045:"pl",1046:"pt",2070:"pt-PT",1094:"pa",1131:"qu-BO",2155:"qu-EC",3179:"qu",1048:"ro",1047:"rm",1049:"ru",9275:"smn",4155:"smj-NO",5179:"smj",3131:"se-FI",1083:"se",2107:"se-SE",8251:"sms",6203:"sma-NO",7227:"sms",1103:"sa",7194:"sr-Cyrl-BA",3098:"sr",6170:"sr-Latn-BA",2074:"sr-Latn",1132:"nso",1074:"tn",1115:"si",1051:"sk",1060:"sl",11274:"es-AR",16394:"es-BO",13322:"es-CL",9226:"es-CO",5130:"es-CR",7178:"es-DO",12298:"es-EC",17418:"es-SV",4106:"es-GT",18442:"es-HN",2058:"es-MX",19466:"es-NI",6154:"es-PA",15370:"es-PY",10250:"es-PE",20490:"es-PR",3082:"es",1034:"es",21514:"es-US",14346:"es-UY",8202:"es-VE",2077:"sv-FI",1053:"sv",1114:"syr",1064:"tg",2143:"tzm",1097:"ta",1092:"tt",1098:"te",1054:"th",1105:"bo",1055:"tr",1090:"tk",1152:"ug",1058:"uk",1070:"hsb",1056:"ur",2115:"uz-Cyrl",1091:"uz",1066:"vi",1106:"cy",1160:"wo",1157:"sah",1144:"ii",1130:"yo"},u="utf-16",v={0:"macintosh",1:"x-mac-japanese",2:"x-mac-chinesetrad",3:"x-mac-korean",6:"x-mac-greek",7:"x-mac-cyrillic",9:"x-mac-devanagai",10:"x-mac-gurmukhi",11:"x-mac-gujarati",12:"x-mac-oriya",13:"x-mac-bengali",14:"x-mac-tamil",15:"x-mac-telugu",16:"x-mac-kannada",17:"x-mac-malayalam",18:"x-mac-sinhalese",19:"x-mac-burmese",20:"x-mac-khmer",21:"x-mac-thai",22:"x-mac-lao",23:"x-mac-georgian",24:"x-mac-armenian",25:"x-mac-chinesesimp",26:"x-mac-tibetan",27:"x-mac-mongolian",28:"x-mac-ethiopic",29:"x-mac-ce",30:"x-mac-vietnamese",31:"x-mac-extarabic"},w={15:"x-mac-icelandic",17:"x-mac-turkish",18:"x-mac-croatian",24:"x-mac-ce",25:"x-mac-ce",26:"x-mac-ce",27:"x-mac-ce",28:"x-mac-ce",30:"x-mac-icelandic",37:"x-mac-romanian",38:"x-mac-ce",39:"x-mac-ce",40:"x-mac-ce",143:"x-mac-inuit",146:"x-mac-gaelic"};c.parse=f,c.make=k},{"../parse":9,"../table":11,"../types":28}],25:[function(a,b,c){"use strict";function d(a){for(var b=0;b<i.length;b+=1){var c=i[b];if(a>=c.begin&&a<c.end)return b}return-1}function e(a,b){var c={},d=new g.Parser(a,b);c.version=d.parseUShort(),c.xAvgCharWidth=d.parseShort(),c.usWeightClass=d.parseUShort(),c.usWidthClass=d.parseUShort(),c.fsType=d.parseUShort(),c.ySubscriptXSize=d.parseShort(),c.ySubscriptYSize=d.parseShort(),c.ySubscriptXOffset=d.parseShort(),c.ySubscriptYOffset=d.parseShort(),c.ySuperscriptXSize=d.parseShort(),c.ySuperscriptYSize=d.parseShort(),c.ySuperscriptXOffset=d.parseShort(),c.ySuperscriptYOffset=d.parseShort(),c.yStrikeoutSize=d.parseShort(),c.yStrikeoutPosition=d.parseShort(),c.sFamilyClass=d.parseShort(),c.panose=[];for(var e=0;10>e;e++)c.panose[e]=d.parseByte();return c.ulUnicodeRange1=d.parseULong(),c.ulUnicodeRange2=d.parseULong(),c.ulUnicodeRange3=d.parseULong(),c.ulUnicodeRange4=d.parseULong(),c.achVendID=String.fromCharCode(d.parseByte(),d.parseByte(),d.parseByte(),d.parseByte()),c.fsSelection=d.parseUShort(),c.usFirstCharIndex=d.parseUShort(),c.usLastCharIndex=d.parseUShort(),c.sTypoAscender=d.parseShort(),c.sTypoDescender=d.parseShort(),c.sTypoLineGap=d.parseShort(),c.usWinAscent=d.parseUShort(),c.usWinDescent=d.parseUShort(),c.version>=1&&(c.ulCodePageRange1=d.parseULong(),c.ulCodePageRange2=d.parseULong()),c.version>=2&&(c.sxHeight=d.parseShort(),c.sCapHeight=d.parseShort(),c.usDefaultChar=d.parseUShort(),c.usBreakChar=d.parseUShort(),c.usMaxContent=d.parseUShort()),c}function f(a){return new h.Table("OS/2",[{name:"version",type:"USHORT",value:3},{name:"xAvgCharWidth",type:"SHORT",value:0},{name:"usWeightClass",type:"USHORT",value:0},{name:"usWidthClass",type:"USHORT",value:0},{name:"fsType",type:"USHORT",value:0},{name:"ySubscriptXSize",type:"SHORT",value:650},{name:"ySubscriptYSize",type:"SHORT",value:699},{name:"ySubscriptXOffset",type:"SHORT",value:0},{name:"ySubscriptYOffset",type:"SHORT",value:140},{name:"ySuperscriptXSize",type:"SHORT",value:650},{name:"ySuperscriptYSize",type:"SHORT",value:699},{name:"ySuperscriptXOffset",type:"SHORT",value:0},{name:"ySuperscriptYOffset",type:"SHORT",value:479},{name:"yStrikeoutSize",type:"SHORT",value:49},{name:"yStrikeoutPosition",type:"SHORT",value:258},{name:"sFamilyClass",type:"SHORT",value:0},{name:"bFamilyType",type:"BYTE",value:0},{name:"bSerifStyle",type:"BYTE",value:0},{name:"bWeight",type:"BYTE",value:0},{name:"bProportion",type:"BYTE",value:0},{name:"bContrast",type:"BYTE",value:0},{name:"bStrokeVariation",type:"BYTE",value:0},{name:"bArmStyle",type:"BYTE",value:0},{name:"bLetterform",type:"BYTE",value:0},{name:"bMidline",type:"BYTE",value:0},{name:"bXHeight",type:"BYTE",value:0},{name:"ulUnicodeRange1",type:"ULONG",value:0},{name:"ulUnicodeRange2",type:"ULONG",value:0},{name:"ulUnicodeRange3",type:"ULONG",value:0},{name:"ulUnicodeRange4",type:"ULONG",value:0},{name:"achVendID",type:"CHARARRAY",value:"XXXX"},{name:"fsSelection",type:"USHORT",value:0},{name:"usFirstCharIndex",type:"USHORT",value:0},{name:"usLastCharIndex",type:"USHORT",value:0},{name:"sTypoAscender",type:"SHORT",value:0},{name:"sTypoDescender",type:"SHORT",value:0},{name:"sTypoLineGap",type:"SHORT",value:0},{name:"usWinAscent",type:"USHORT",value:0},{name:"usWinDescent",type:"USHORT",value:0},{name:"ulCodePageRange1",type:"ULONG",value:0},{name:"ulCodePageRange2",type:"ULONG",value:0},{name:"sxHeight",type:"SHORT",value:0},{name:"sCapHeight",type:"SHORT",value:0},{name:"usDefaultChar",type:"USHORT",value:0},{name:"usBreakChar",type:"USHORT",value:0},{name:"usMaxContext",type:"USHORT",value:0}],a)}var g=a("../parse"),h=a("../table"),i=[{begin:0,end:127},{begin:128,end:255},{begin:256,end:383},{begin:384,end:591},{begin:592,end:687},{begin:688,end:767},{begin:768,end:879},{begin:880,end:1023},{begin:11392,end:11519},{begin:1024,end:1279},{begin:1328,end:1423},{begin:1424,end:1535},{begin:42240,end:42559},{begin:1536,end:1791},{begin:1984,end:2047},{begin:2304,end:2431},{begin:2432,end:2559},{begin:2560,end:2687},{begin:2688,end:2815},{begin:2816,end:2943},{begin:2944,end:3071},{begin:3072,end:3199},{begin:3200,end:3327},{begin:3328,end:3455},{begin:3584,end:3711},{begin:3712,end:3839},{begin:4256,end:4351},{begin:6912,end:7039},{begin:4352,end:4607},{begin:7680,end:7935},{begin:7936,end:8191},{begin:8192,end:8303},{begin:8304,end:8351},{begin:8352,end:8399},{begin:8400,end:8447},{begin:8448,end:8527},{begin:8528,end:8591},{begin:8592,end:8703},{begin:8704,end:8959},{begin:8960,end:9215},{begin:9216,end:9279},{begin:9280,end:9311},{begin:9312,end:9471},{begin:9472,end:9599},{begin:9600,end:9631},{begin:9632,end:9727},{begin:9728,end:9983},{begin:9984,end:10175},{begin:12288,end:12351},{begin:12352,end:12447},{begin:12448,end:12543},{begin:12544,end:12591},{begin:12592,end:12687},{begin:43072,end:43135},{begin:12800,end:13055},{begin:13056,end:13311},{begin:44032,end:55215},{begin:55296,end:57343},{begin:67840,end:67871},{begin:19968,end:40959},{begin:57344,end:63743},{begin:12736,end:12783},{begin:64256,end:64335},{begin:64336,end:65023},{begin:65056,end:65071},{begin:65040,end:65055},{begin:65104,end:65135},{begin:65136,end:65279},{begin:65280,end:65519},{begin:65520,end:65535},{begin:3840,end:4095},{begin:1792,end:1871},{begin:1920,end:1983},{begin:3456,end:3583},{begin:4096,end:4255},{begin:4608,end:4991},{begin:5024,end:5119},{begin:5120,end:5759},{begin:5760,end:5791},{begin:5792,end:5887},{begin:6016,end:6143},{begin:6144,end:6319},{begin:10240,end:10495},{begin:40960,end:42127},{begin:5888,end:5919},{begin:66304,end:66351},{begin:66352,end:66383},{begin:66560,end:66639},{begin:118784,end:119039},{begin:119808,end:120831},{begin:1044480,end:1048573},{begin:65024,end:65039},{begin:917504,end:917631},{begin:6400,end:6479},{begin:6480,end:6527},{begin:6528,end:6623},{begin:6656,end:6687},{begin:11264,end:11359},{begin:11568,end:11647},{begin:19904,end:19967},{begin:43008,end:43055},{begin:65536,end:65663},{begin:65856,end:65935},{begin:66432,end:66463},{begin:66464,end:66527},{begin:66640,end:66687},{begin:66688,end:66735},{begin:67584,end:67647},{begin:68096,end:68191},{begin:119552,end:119647},{begin:73728,end:74751},{begin:119648,end:119679},{begin:7040,end:7103},{begin:7168,end:7247},{begin:7248,end:7295},{begin:43136,end:43231},{begin:43264,end:43311},{begin:43312,end:43359},{begin:43520,end:43615},{begin:65936,end:65999},{begin:66e3,end:66047},{begin:66208,end:66271},{begin:127024,end:127135}];c.unicodeRanges=i,c.getUnicodeRange=d,c.parse=e,c.make=f},{"../parse":9,"../table":11}],26:[function(a,b,c){"use strict";function d(a,b){var c,d={},e=new g.Parser(a,b);switch(d.version=e.parseVersion(),d.italicAngle=e.parseFixed(),d.underlinePosition=e.parseShort(),d.underlineThickness=e.parseShort(),d.isFixedPitch=e.parseULong(),d.minMemType42=e.parseULong(),d.maxMemType42=e.parseULong(),d.minMemType1=e.parseULong(),d.maxMemType1=e.parseULong(),d.version){case 1:d.names=f.standardNames.slice();break;case 2:for(d.numberOfGlyphs=e.parseUShort(),d.glyphNameIndex=new Array(d.numberOfGlyphs),c=0;c<d.numberOfGlyphs;c++)d.glyphNameIndex[c]=e.parseUShort();for(d.names=[],c=0;c<d.numberOfGlyphs;c++)if(d.glyphNameIndex[c]>=f.standardNames.length){var h=e.parseChar();d.names.push(e.parseString(h))}break;case 2.5:for(d.numberOfGlyphs=e.parseUShort(),d.offset=new Array(d.numberOfGlyphs),c=0;c<d.numberOfGlyphs;c++)d.offset[c]=e.parseChar()}return d}function e(){return new h.Table("post",[{name:"version",type:"FIXED",value:196608},{name:"italicAngle",type:"FIXED",value:0},{name:"underlinePosition",type:"FWORD",value:0},{name:"underlineThickness",type:"FWORD",value:0},{name:"isFixedPitch",type:"ULONG",value:0},{name:"minMemType42",type:"ULONG",value:0},{name:"maxMemType42",type:"ULONG",value:0},{name:"minMemType1",type:"ULONG",value:0},{name:"maxMemType1",type:"ULONG",value:0}])}var f=a("../encoding"),g=a("../parse"),h=a("../table");c.parse=d,c.make=e},{"../encoding":4,"../parse":9,"../table":11}],27:[function(a,b,c){"use strict";function d(a){return Math.log(a)/Math.log(2)|0}function e(a){for(;a.length%4!==0;)a.push(0);for(var b=0,c=0;c<a.length;c+=4)b+=(a[c]<<24)+(a[c+1]<<16)+(a[c+2]<<8)+a[c+3];return b%=Math.pow(2,32)}function f(a,b,c,d){return new l.Table("Table Record",[{name:"tag",type:"TAG",value:void 0!==a?a:""},{name:"checkSum",type:"ULONG",value:void 0!==b?b:0},{name:"offset",type:"ULONG",value:void 0!==c?c:0},{name:"length",type:"ULONG",value:void 0!==d?d:0}])}function g(a){var b=new l.Table("sfnt",[{name:"version",type:"TAG",value:"OTTO"},{name:"numTables",type:"USHORT",value:0},{name:"searchRange",type:"USHORT",value:0},{name:"entrySelector",type:"USHORT",value:0},{name:"rangeShift",type:"USHORT",value:0}]);b.tables=a,b.numTables=a.length;var c=Math.pow(2,d(b.numTables));b.searchRange=16*c,b.entrySelector=d(c),b.rangeShift=16*b.numTables-b.searchRange;for(var g=[],h=[],i=b.sizeOf()+f().sizeOf()*b.numTables;i%4!==0;)i+=1,h.push({name:"padding",type:"BYTE",value:0});for(var j=0;j<a.length;j+=1){var m=a[j];k.argument(4===m.tableName.length,"Table name"+m.tableName+" is invalid.");var n=m.sizeOf(),o=f(m.tableName,e(m.encode()),i,n);for(g.push({name:o.tag+" Table Record",type:"TABLE",value:o}),h.push({name:m.tableName+" table",type:"TABLE",value:m}),i+=n,k.argument(!isNaN(i),"Something went wrong calculating the offset.");i%4!==0;)i+=1,h.push({name:"padding",type:"BYTE",value:0})}return g.sort(function(a,b){return a.value.tag>b.value.tag?1:-1}),b.fields=b.fields.concat(g),b.fields=b.fields.concat(h),b}function h(a,b,c){for(var d=0;d<b.length;d+=1){var e=a.charToGlyphIndex(b[d]);if(e>0){var f=a.glyphs.get(e);return f.getMetrics()}}return c}function i(a){for(var b=0,c=0;c<a.length;c+=1)b+=a[c];return b/a.length}function j(a){for(var b,c=[],d=[],f=[],j=[],k=[],l=[],w=[],x=0,y=0,z=0,A=0,B=0,C=0;C<a.glyphs.length;C+=1){var D=a.glyphs.get(C),E=0|D.unicode;(b>E||null===b)&&(b=E),E>x&&(x=E);var F=u.getUnicodeRange(E);if(32>F)y|=1<<F;else if(64>F)z|=1<<F-32;else if(96>F)A|=1<<F-64;else{if(!(123>F))throw new Error("Unicode ranges bits > 123 are reserved for internal usage");B|=1<<F-96}if(".notdef"!==D.name){var G=D.getMetrics();c.push(G.xMin),d.push(G.yMin),f.push(G.xMax),j.push(G.yMax),l.push(G.leftSideBearing),w.push(G.rightSideBearing),k.push(D.advanceWidth)}}var H={xMin:Math.min.apply(null,c),yMin:Math.min.apply(null,d),xMax:Math.max.apply(null,f),yMax:Math.max.apply(null,j),advanceWidthMax:Math.max.apply(null,k),advanceWidthAvg:i(k),minLeftSideBearing:Math.min.apply(null,l),maxLeftSideBearing:Math.max.apply(null,l),minRightSideBearing:Math.min.apply(null,w)};H.ascender=void 0!==a.ascender?a.ascender:H.yMax,H.descender=void 0!==a.descender?a.descender:H.yMin;var I=o.make({unitsPerEm:a.unitsPerEm,xMin:H.xMin,yMin:H.yMin,xMax:H.xMax,yMax:H.yMax}),J=p.make({ascender:H.ascender,descender:H.descender,advanceWidthMax:H.advanceWidthMax,minLeftSideBearing:H.minLeftSideBearing,minRightSideBearing:H.minRightSideBearing,xMaxExtent:H.maxLeftSideBearing+(H.xMax-H.xMin),numberOfHMetrics:a.glyphs.length}),K=s.make(a.glyphs.length),L=u.make({xAvgCharWidth:Math.round(H.advanceWidthAvg),usWeightClass:500,usWidthClass:5,usFirstCharIndex:b,usLastCharIndex:x,ulUnicodeRange1:y,ulUnicodeRange2:z,ulUnicodeRange3:A,ulUnicodeRange4:B,sTypoAscender:H.ascender,sTypoDescender:H.descender,sTypoLineGap:0,usWinAscent:H.ascender,usWinDescent:-H.descender,sxHeight:h(a,"xyvw",{yMax:0}).yMax,sCapHeight:h(a,"HIKLEFJMNTZBDPRAGOQSUVWXY",H).yMax,usBreakChar:a.hasChar(" ")?32:0}),M=q.make(a.glyphs),N=m.make(a.glyphs),O=a.getEnglishName("fontFamily"),P=a.getEnglishName("fontSubfamily"),Q=O+" "+P,R=a.getEnglishName("postScriptName");R||(R=O.replace(/\s/g,"")+"-"+P);var S={};for(var T in a.names)S[T]=a.names[T];S.uniqueID||(S.uniqueID={en:a.getEnglishName("manufacturer")+":"+Q}),S.postScriptName||(S.postScriptName={en:R}),S.preferredFamily||(S.preferredFamily=a.names.fontFamily),S.preferredSubfamily||(S.preferredSubfamily=a.names.fontSubfamily);var U=[],V=t.make(S,U),W=U.length>0?r.make(U):void 0,X=v.make(),Y=n.make(a.glyphs,{version:a.getEnglishName("version"),fullName:Q,familyName:O,weightName:P,postScriptName:R,unitsPerEm:a.unitsPerEm}),Z=[I,J,K,L,V,N,X,Y,M];W&&Z.push(W);var $=g(Z),_=$.encode(),aa=e(_),ba=$.fields,ca=!1;for(C=0;C<ba.length;C+=1)if("head table"===ba[C].name){ba[C].value.checkSumAdjustment=2981146554-aa,ca=!0;break}if(!ca)throw new Error("Could not find head table with checkSum to adjust.");return $}var k=a("../check"),l=a("../table"),m=a("./cmap"),n=a("./cff"),o=a("./head"),p=a("./hhea"),q=a("./hmtx"),r=a("./ltag"),s=a("./maxp"),t=a("./name"),u=a("./os2"),v=a("./post");c.computeCheckSum=e,c.make=g,c.fontToTable=j},{"../check":2,"../table":11,"./cff":12,"./cmap":13,"./head":17,"./hhea":18,"./hmtx":19,"./ltag":22,"./maxp":23,"./name":24,"./os2":25,"./post":26}],28:[function(a,b,c){"use strict";function d(a){return function(){return a}}var e=a("./check"),f=32768,g=2147483648,h={},i={},j={};i.BYTE=function(a){return e.argument(a>=0&&255>=a,"Byte value should be between 0 and 255."),[a]},j.BYTE=d(1),i.CHAR=function(a){return[a.charCodeAt(0)]},j.CHAR=d(1),i.CHARARRAY=function(a){for(var b=[],c=0;c<a.length;c+=1)b.push(a.charCodeAt(c));return b},j.CHARARRAY=function(a){return a.length},i.USHORT=function(a){return[a>>8&255,255&a]},j.USHORT=d(2),i.SHORT=function(a){return a>=f&&(a=-(2*f-a)),[a>>8&255,255&a]},j.SHORT=d(2),i.UINT24=function(a){return[a>>16&255,a>>8&255,255&a]},j.UINT24=d(3),i.ULONG=function(a){return[a>>24&255,a>>16&255,a>>8&255,255&a]},j.ULONG=d(4),i.LONG=function(a){return a>=g&&(a=-(2*g-a)),[a>>24&255,a>>16&255,a>>8&255,255&a]},j.LONG=d(4),i.FIXED=i.ULONG,j.FIXED=j.ULONG,i.FWORD=i.SHORT,j.FWORD=j.SHORT,i.UFWORD=i.USHORT,j.UFWORD=j.USHORT,i.LONGDATETIME=function(){return[0,0,0,0,0,0,0,0]},j.LONGDATETIME=d(8),i.TAG=function(a){return e.argument(4===a.length,"Tag should be exactly 4 ASCII characters."),[a.charCodeAt(0),a.charCodeAt(1),a.charCodeAt(2),a.charCodeAt(3)]},j.TAG=d(4),i.Card8=i.BYTE,j.Card8=j.BYTE,i.Card16=i.USHORT,j.Card16=j.USHORT,i.OffSize=i.BYTE,j.OffSize=j.BYTE,i.SID=i.USHORT,j.SID=j.USHORT,i.NUMBER=function(a){return a>=-107&&107>=a?[a+139]:a>=108&&1131>=a?(a-=108,[(a>>8)+247,255&a]):a>=-1131&&-108>=a?(a=-a-108,[(a>>8)+251,255&a]):a>=-32768&&32767>=a?i.NUMBER16(a):i.NUMBER32(a)},j.NUMBER=function(a){return i.NUMBER(a).length},i.NUMBER16=function(a){return[28,a>>8&255,255&a]},j.NUMBER16=d(3),i.NUMBER32=function(a){return[29,a>>24&255,a>>16&255,a>>8&255,255&a]},j.NUMBER32=d(5),i.REAL=function(a){var b=a.toString(),c=/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(b);if(c){var d=parseFloat("1e"+((c[2]?+c[2]:0)+c[1].length));b=(Math.round(a*d)/d).toString()}var e,f,g="";for(e=0,f=b.length;f>e;e+=1){var h=b[e];g+="e"===h?"-"===b[++e]?"c":"b":"."===h?"a":"-"===h?"e":h}g+=1&g.length?"f":"ff";var i=[30];for(e=0,f=g.length;f>e;e+=2)i.push(parseInt(g.substr(e,2),16));return i},j.REAL=function(a){return i.REAL(a).length},i.NAME=i.CHARARRAY,j.NAME=j.CHARARRAY,i.STRING=i.CHARARRAY,j.STRING=j.CHARARRAY,h.UTF16=function(a,b,c){for(var d=[],e=c/2,f=0;e>f;f++,b+=2)d[f]=a.getUint16(b);return String.fromCharCode.apply(null,d)},i.UTF16=function(a){for(var b=[],c=0;c<a.length;c+=1){var d=a.charCodeAt(c);b.push(d>>8&255),b.push(255&d)}return b},j.UTF16=function(a){return 2*a.length};var k={"x-mac-croatian":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®Š™´¨≠ŽØ∞±≤≥∆µ∂∑∏š∫ªºΩžø¿¡¬√ƒ≈ƫȅ ÀÃÕŒœĐ—“”‘’÷◊©⁄€‹›Æ»–·‚„‰ÂćÁčÈÍÎÏÌÓÔđÒÚÛÙıˆ˜¯πË˚¸Êæˇ","x-mac-cyrillic":"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ†°Ґ£§•¶І®©™Ђђ≠Ѓѓ∞±≤≥іµґЈЄєЇїЉљЊњјЅ¬√ƒ≈∆«»… ЋћЌќѕ–—“”‘’÷„ЎўЏџ№Ёёяабвгдежзийклмнопрстуфхцчшщъыьэю","x-mac-gaelic":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØḂ±≤≥ḃĊċḊḋḞḟĠġṀæøṁṖṗɼƒſṠ«»… ÀÃÕŒœ–—“”‘’ṡẛÿŸṪ€‹›Ŷŷṫ·Ỳỳ⁊ÂÊÁËÈÍÎÏÌÓÔ♣ÒÚÛÙıÝýŴŵẄẅẀẁẂẃ","x-mac-greek":"Ĺ²É³ÖÜ΅àâä΄¨çéèê룙î‰ôö¦€ùûü†ΓΔΘΛΞΠß®©ΣΪ§≠°·Α±≤≥¥ΒΕΖΗΙΚΜΦΫΨΩάΝ¬ΟΡ≈Τ«»… ΥΧΆΈœ–―“”‘’÷ΉΊΌΎέήίόΏύαβψδεφγηιξκλμνοπώρστθωςχυζϊϋΐΰ","x-mac-icelandic":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûüݰ¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€ÐðÞþý·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-inuit":"ᐃᐄᐅᐆᐊᐋᐱᐲᐳᐴᐸᐹᑉᑎᑏᑐᑑᑕᑖᑦᑭᑮᑯᑰᑲᑳᒃᒋᒌᒍᒎᒐᒑ°ᒡᒥᒦ•¶ᒧ®©™ᒨᒪᒫᒻᓂᓃᓄᓅᓇᓈᓐᓯᓰᓱᓲᓴᓵᔅᓕᓖᓗᓘᓚᓛᓪᔨᔩᔪᔫᔭ… ᔮᔾᕕᕖᕗ–—“”‘’ᕘᕙᕚᕝᕆᕇᕈᕉᕋᕌᕐᕿᖀᖁᖂᖃᖄᖅᖏᖐᖑᖒᖓᖔᖕᙱᙲᙳᙴᙵᙶᖖᖠᖡᖢᖣᖤᖥᖦᕼŁł","x-mac-ce":"ÄĀāÉĄÖÜáąČäčĆć鏟ĎíďĒēĖóėôöõúĚěü†°Ę£§•¶ß®©™ę¨≠ģĮįĪ≤≥īĶ∂∑łĻļĽľĹĺŅņѬ√ńŇ∆«»… ňŐÕőŌ–—“”‘’÷◊ōŔŕŘ‹›řŖŗŠ‚„šŚśÁŤťÍŽžŪÓÔūŮÚůŰűŲųÝýķŻŁżĢˇ",macintosh:"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›fifl‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-romanian":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ĂȘ∞±≤≥¥µ∂∑∏π∫ªºΩăș¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›Țț‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ","x-mac-turkish":"ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸĞğİıŞş‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙˆ˜¯˘˙˚¸˝˛ˇ"};h.MACSTRING=function(a,b,c,d){var e=k[d];if(void 0===e)return void 0;for(var f="",g=0;c>g;g++){var h=a.getUint8(b+g);f+=127>=h?String.fromCharCode(h):e[127&h]}return f};var l,m="function"==typeof WeakMap&&new WeakMap,n=function(a){if(!l){l={};for(var b in k)l[b]=new String(b)}var c=l[a];if(void 0===c)return void 0;if(m){var d=m.get(c);if(void 0!==d)return d}var e=k[a];if(void 0===e)return void 0;for(var f={},g=0;g<e.length;g++)f[e.charCodeAt(g)]=g+128;return m&&m.set(c,f),f};i.MACSTRING=function(a,b){var c=n(b);if(void 0===c)return void 0;for(var d=[],e=0;e<a.length;e++){var f=a.charCodeAt(e);if(f>=128&&(f=c[f],void 0===f))return void 0;d.push(f)}return d},j.MACSTRING=function(a,b){var c=i.MACSTRING(a,b);return void 0!==c?c.length:0},i.INDEX=function(a){var b,c=1,d=[c],e=[],f=0;for(b=0;b<a.length;b+=1){var g=i.OBJECT(a[b]);Array.prototype.push.apply(e,g),f+=g.length,c+=g.length,d.push(c)}if(0===e.length)return[0,0];var h=[],j=1+Math.floor(Math.log(f)/Math.log(2))/8|0,k=[void 0,i.BYTE,i.USHORT,i.UINT24,i.ULONG][j];for(b=0;b<d.length;b+=1){var l=k(d[b]);Array.prototype.push.apply(h,l)}return Array.prototype.concat(i.Card16(a.length),i.OffSize(j),h,e)},j.INDEX=function(a){return i.INDEX(a).length},i.DICT=function(a){for(var b=[],c=Object.keys(a),d=c.length,e=0;d>e;e+=1){var f=parseInt(c[e],0),g=a[f];b=b.concat(i.OPERAND(g.value,g.type)),b=b.concat(i.OPERATOR(f))}return b},j.DICT=function(a){return i.DICT(a).length},i.OPERATOR=function(a){return 1200>a?[a]:[12,a-1200]},i.OPERAND=function(a,b){var c=[];if(Array.isArray(b))for(var d=0;d<b.length;d+=1)e.argument(a.length===b.length,"Not enough arguments given for type"+b),c=c.concat(i.OPERAND(a[d],b[d]));else if("SID"===b)c=c.concat(i.NUMBER(a));else if("offset"===b)c=c.concat(i.NUMBER32(a));else if("number"===b)c=c.concat(i.NUMBER(a));else{if("real"!==b)throw new Error("Unknown operand type "+b);c=c.concat(i.REAL(a))}return c},i.OP=i.BYTE,j.OP=j.BYTE;var o="function"==typeof WeakMap&&new WeakMap;i.CHARSTRING=function(a){if(o){var b=o.get(a);if(void 0!==b)return b}for(var c=[],d=a.length,e=0;d>e;e+=1){var f=a[e];c=c.concat(i[f.type](f.value))}return o&&o.set(a,c),c},j.CHARSTRING=function(a){return i.CHARSTRING(a).length},i.OBJECT=function(a){var b=i[a.type];return e.argument(void 0!==b,"No encoding function for type "+a.type),b(a.value)},j.OBJECT=function(a){var b=j[a.type];return e.argument(void 0!==b,"No sizeOf function for type "+a.type),b(a.value)},i.TABLE=function(a){for(var b=[],c=a.fields.length,d=0;c>d;d+=1){var f=a.fields[d],g=i[f.type];e.argument(void 0!==g,"No encoding function for field type "+f.type);var h=a[f.name];void 0===h&&(h=f.value);var j=g(h);b=b.concat(j)}return b},j.TABLE=function(a){for(var b=0,c=a.fields.length,d=0;c>d;d+=1){var f=a.fields[d],g=j[f.type];e.argument(void 0!==g,"No sizeOf function for field type "+f.type);var h=a[f.name];void 0===h&&(h=f.value),b+=g(h)}return b},i.LITERAL=function(a){return a},j.LITERAL=function(a){return a.length},c.decode=h,c.encode=i,c.sizeOf=j},{"./check":2}],29:[function(_dereq_,module,exports){!function(a,b,c){"undefined"!=typeof module&&module.exports?module.exports=c():"function"==typeof define&&define.amd?define(c):b[a]=c()}("reqwest",this,function(){function succeed(a){var b=protocolRe.exec(a.url);return b=b&&b[1]||window.location.protocol,httpsRe.test(b)?twoHundo.test(a.request.status):!!a.request.response}function handleReadyState(a,b,c){return function(){return a._aborted?c(a.request):a._timedOut?c(a.request,"Request is aborted: timeout"):void(a.request&&4==a.request[readyState]&&(a.request.onreadystatechange=noop,succeed(a)?b(a.request):c(a.request)))}}function setHeaders(a,b){var c,d=b.headers||{};d.Accept=d.Accept||defaultHeaders.accept[b.type]||defaultHeaders.accept["*"];var e="function"==typeof FormData&&b.data instanceof FormData;b.crossOrigin||d[requestedWith]||(d[requestedWith]=defaultHeaders.requestedWith),d[contentType]||e||(d[contentType]=b.contentType||defaultHeaders.contentType);for(c in d)d.hasOwnProperty(c)&&"setRequestHeader"in a&&a.setRequestHeader(c,d[c])}function setCredentials(a,b){"undefined"!=typeof b.withCredentials&&"undefined"!=typeof a.withCredentials&&(a.withCredentials=!!b.withCredentials)}function generalCallback(a){lastValue=a}function urlappend(a,b){return a+(/\?/.test(a)?"&":"?")+b}function handleJsonp(a,b,c,d){var e=uniqid++,f=a.jsonpCallback||"callback",g=a.jsonpCallbackName||reqwest.getcallbackPrefix(e),h=new RegExp("((^|\\?|&)"+f+")=([^&]+)"),i=d.match(h),j=doc.createElement("script"),k=0,l=-1!==navigator.userAgent.indexOf("MSIE 10.0");return i?"?"===i[3]?d=d.replace(h,"$1="+g):g=i[3]:d=urlappend(d,f+"="+g),win[g]=generalCallback,j.type="text/javascript",j.src=d,j.async=!0,"undefined"==typeof j.onreadystatechange||l||(j.htmlFor=j.id="_reqwest_"+e),j.onload=j.onreadystatechange=function(){return j[readyState]&&"complete"!==j[readyState]&&"loaded"!==j[readyState]||k?!1:(j.onload=j.onreadystatechange=null,j.onclick&&j.onclick(),b(lastValue),lastValue=void 0,head.removeChild(j),void(k=1))},head.appendChild(j),{abort:function(){j.onload=j.onreadystatechange=null,c({},"Request is aborted: timeout",{}),lastValue=void 0,head.removeChild(j),k=1}}}function getRequest(a,b){var c,d=this.o,e=(d.method||"GET").toUpperCase(),f="string"==typeof d?d:d.url,g=d.processData!==!1&&d.data&&"string"!=typeof d.data?reqwest.toQueryString(d.data):d.data||null,h=!1;return"jsonp"!=d.type&&"GET"!=e||!g||(f=urlappend(f,g),g=null),"jsonp"==d.type?handleJsonp(d,a,b,f):(c=d.xhr&&d.xhr(d)||xhr(d),c.open(e,f,d.async===!1?!1:!0),setHeaders(c,d),setCredentials(c,d),win[xDomainRequest]&&c instanceof win[xDomainRequest]?(c.onload=a,c.onerror=b,c.onprogress=function(){},h=!0):c.onreadystatechange=handleReadyState(this,a,b),d.before&&d.before(c),h?setTimeout(function(){c.send(g)},200):c.send(g),c)}function Reqwest(a,b){this.o=a,this.fn=b,init.apply(this,arguments)}function setType(a){return a.match("json")?"json":a.match("javascript")?"js":a.match("text")?"html":a.match("xml")?"xml":void 0}function init(o,fn){function complete(a){for(o.timeout&&clearTimeout(self.timeout),self.timeout=null;self._completeHandlers.length>0;)self._completeHandlers.shift()(a)}function success(resp){var type=o.type||resp&&setType(resp.getResponseHeader("Content-Type"));resp="jsonp"!==type?self.request:resp;var filteredResponse=globalSetupOptions.dataFilter(resp.responseText,type),r=filteredResponse;try{resp.responseText=r}catch(e){}if(r)switch(type){case"json":try{resp=win.JSON?win.JSON.parse(r):eval("("+r+")")}catch(err){return error(resp,"Could not parse JSON in response",err)}break;case"js":resp=eval(r);break;case"html":resp=r;break;case"xml":resp=resp.responseXML&&resp.responseXML.parseError&&resp.responseXML.parseError.errorCode&&resp.responseXML.parseError.reason?null:resp.responseXML}for(self._responseArgs.resp=resp,self._fulfilled=!0,fn(resp),self._successHandler(resp);self._fulfillmentHandlers.length>0;)resp=self._fulfillmentHandlers.shift()(resp);complete(resp)}function timedOut(){self._timedOut=!0,self.request.abort()}function error(a,b,c){for(a=self.request,self._responseArgs.resp=a,self._responseArgs.msg=b,self._responseArgs.t=c,self._erred=!0;self._errorHandlers.length>0;)self._errorHandlers.shift()(a,b,c);complete(a)}this.url="string"==typeof o?o:o.url,this.timeout=null,this._fulfilled=!1,this._successHandler=function(){},this._fulfillmentHandlers=[],this._errorHandlers=[],this._completeHandlers=[],this._erred=!1,this._responseArgs={};var self=this;fn=fn||function(){},o.timeout&&(this.timeout=setTimeout(function(){timedOut()},o.timeout)),o.success&&(this._successHandler=function(){o.success.apply(o,arguments)}),o.error&&this._errorHandlers.push(function(){o.error.apply(o,arguments)}),o.complete&&this._completeHandlers.push(function(){o.complete.apply(o,arguments)}),this.request=getRequest.call(this,success,error)}function reqwest(a,b){return new Reqwest(a,b)}function normalize(a){return a?a.replace(/\r?\n/g,"\r\n"):""}function serial(a,b){var c,d,e,f,g=a.name,h=a.tagName.toLowerCase(),i=function(a){a&&!a.disabled&&b(g,normalize(a.attributes.value&&a.attributes.value.specified?a.value:a.text))};if(!a.disabled&&g)switch(h){case"input":/reset|button|image|file/i.test(a.type)||(c=/checkbox/i.test(a.type),d=/radio/i.test(a.type),e=a.value,(!(c||d)||a.checked)&&b(g,normalize(c&&""===e?"on":e)));break;case"textarea":b(g,normalize(a.value));break;case"select":if("select-one"===a.type.toLowerCase())i(a.selectedIndex>=0?a.options[a.selectedIndex]:null);else for(f=0;a.length&&f<a.length;f++)a.options[f].selected&&i(a.options[f])}}function eachFormElement(){var a,b,c=this,d=function(a,b){var d,e,f;for(d=0;d<b.length;d++)for(f=a[byTag](b[d]),e=0;e<f.length;e++)serial(f[e],c)};for(b=0;b<arguments.length;b++)a=arguments[b],/input|select|textarea/i.test(a.tagName)&&serial(a,c),d(a,["input","select","textarea"])}function serializeQueryString(){return reqwest.toQueryString(reqwest.serializeArray.apply(null,arguments))}function serializeHash(){var a={};return eachFormElement.apply(function(b,c){b in a?(a[b]&&!isArray(a[b])&&(a[b]=[a[b]]),a[b].push(c)):a[b]=c},arguments),a}function buildParams(a,b,c,d){var e,f,g,h=/\[\]$/;if(isArray(b))for(f=0;b&&f<b.length;f++)g=b[f],c||h.test(a)?d(a,g):buildParams(a+"["+("object"==typeof g?f:"")+"]",g,c,d);else if(b&&"[object Object]"===b.toString())for(e in b)buildParams(a+"["+e+"]",b[e],c,d);else d(a,b)}var win=window,doc=document,httpsRe=/^http/,protocolRe=/(^\w+):\/\//,twoHundo=/^(20\d|1223)$/,byTag="getElementsByTagName",readyState="readyState",contentType="Content-Type",requestedWith="X-Requested-With",head=doc[byTag]("head")[0],uniqid=0,callbackPrefix="reqwest_"+ +new Date,lastValue,xmlHttpRequest="XMLHttpRequest",xDomainRequest="XDomainRequest",noop=function(){},isArray="function"==typeof Array.isArray?Array.isArray:function(a){return a instanceof Array},defaultHeaders={contentType:"application/x-www-form-urlencoded",requestedWith:xmlHttpRequest,accept:{"*":"text/javascript, text/html, application/xml, text/xml, */*",xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript",js:"application/javascript, text/javascript"}},xhr=function(a){if(a.crossOrigin===!0){var b=win[xmlHttpRequest]?new XMLHttpRequest:null;if(b&&"withCredentials"in b)return b;if(win[xDomainRequest])return new XDomainRequest;throw new Error("Browser does not support cross-origin requests")}return win[xmlHttpRequest]?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP")},globalSetupOptions={dataFilter:function(a){return a}};return Reqwest.prototype={abort:function(){this._aborted=!0,this.request.abort()},retry:function(){init.call(this,this.o,this.fn)},then:function(a,b){return a=a||function(){},b=b||function(){},this._fulfilled?this._responseArgs.resp=a(this._responseArgs.resp):this._erred?b(this._responseArgs.resp,this._responseArgs.msg,this._responseArgs.t):(this._fulfillmentHandlers.push(a),this._errorHandlers.push(b)),this},always:function(a){return this._fulfilled||this._erred?a(this._responseArgs.resp):this._completeHandlers.push(a),this},fail:function(a){return this._erred?a(this._responseArgs.resp,this._responseArgs.msg,this._responseArgs.t):this._errorHandlers.push(a),this},"catch":function(a){return this.fail(a)}},reqwest.serializeArray=function(){var a=[];return eachFormElement.apply(function(b,c){a.push({name:b,value:c})},arguments),a},reqwest.serialize=function(){if(0===arguments.length)return"";var a,b,c=Array.prototype.slice.call(arguments,0);return a=c.pop(),a&&a.nodeType&&c.push(a)&&(a=null),a&&(a=a.type),b="map"==a?serializeHash:"array"==a?reqwest.serializeArray:serializeQueryString,b.apply(null,c)},reqwest.toQueryString=function(a,b){var c,d,e=b||!1,f=[],g=encodeURIComponent,h=function(a,b){b="function"==typeof b?b():null==b?"":b,f[f.length]=g(a)+"="+g(b)};if(isArray(a))for(d=0;a&&d<a.length;d++)h(a[d].name,a[d].value);else for(c in a)a.hasOwnProperty(c)&&buildParams(c,a[c],e,h);return f.join("&").replace(/%20/g,"+")},reqwest.getcallbackPrefix=function(){return callbackPrefix},reqwest.compat=function(a,b){return a&&(a.type&&(a.method=a.type)&&delete a.type,a.dataType&&(a.type=a.dataType),a.jsonpCallback&&(a.jsonpCallbackName=a.jsonpCallback)&&delete a.jsonpCallback,a.jsonp&&(a.jsonpCallback=a.jsonp)),new Reqwest(a,b)},reqwest.ajaxSetup=function(a){a=a||{};for(var b in a)globalSetupOptions[b]=a[b]; | |
},reqwest})},{}],30:[function(a,b,c){"use strict";var d=a("../core/core");a("./p5.Geometry3D"),d.prototype.plane=function(a,b,c,e){a=a||1,b=b||1,c=c||1,e=e||1;var f="plane|"+a+"|"+b+"|"+c+"|"+e;if(!this._graphics.geometryInHash(f)){var g=new d.Geometry3D,h=function(c,e){var f=2*a*c-a,g=2*b*e-b,h=0;return new d.Vector(f,g,h)};g.parametricGeometry(h,c,e);var i=g.generateObj();this._graphics.initBuffer(f,i)}this._graphics.drawBuffer(f)},d.prototype.sphere=function(a,b,c){a=a||50,b=b||12,c=c||8;var e="sphere|"+a+"|"+b+"|"+c;if(!this._graphics.geometryInHash(e)){var f=new d.Geometry3D,g=function(b,c){var e=2*Math.PI*b,f=Math.PI*c-Math.PI/2,g=a*Math.cos(f)*Math.sin(e),h=a*Math.sin(f),i=a*Math.cos(f)*Math.cos(e);return new d.Vector(g,h,i)};f.parametricGeometry(g,b,c);var h=f.generateObj();this._graphics.initBuffer(e,h)}return this._graphics.drawBuffer(e),this},d.prototype.cylinder=function(a,b,c,e){a=a||50,b=b||50,c=c||12,e=e||8;var f="cylinder|"+a+"|"+b+"|"+c+"|"+e;if(!this._graphics.geometryInHash(f)){var g=new d.Geometry3D,h=function(c,e){var f=2*Math.PI*c,g=a*Math.sin(f),h=2*b*e-b,i=a*Math.cos(f);return new d.Vector(g,h,i)};g.parametricGeometry(h,c,e),g.mergeVertices();var i=function(c,e){var f=2*Math.PI*c,g=a*Math.sin(-f),h=b,i=a*Math.cos(f);return 0===e?new d.Vector(0,b,0):new d.Vector(g,h,i)};g.parametricGeometry(i,c,1,g.vertices.length);var j=function(c,e){var f=2*Math.PI*c,g=a*Math.sin(f),h=-b,i=a*Math.cos(f);return 0===e?new d.Vector(0,-b,0):new d.Vector(g,h,i)};g.parametricGeometry(j,c,1,g.vertices.length);var k=g.generateObj(!0);this._graphics.initBuffer(f,k)}return this._graphics.drawBuffer(f),this},d.prototype.cone=function(a,b,c,e){a=a||50,b=b||50,c=c||12,e=e||8;var f="cone|"+a+"|"+b+"|"+c+"|"+e;if(!this._graphics.geometryInHash(f)){var g=new d.Geometry3D,h=function(c,e){var f=2*Math.PI*c,g=a*(1-e)*Math.sin(f),h=2*b*e-b,i=a*(1-e)*Math.cos(f);return new d.Vector(g,h,i)};g.parametricGeometry(h,c,e),g.mergeVertices();var i=function(c,e){var f=2*Math.PI*c,g=a*(1-e)*Math.sin(-f),h=-b,i=a*(1-e)*Math.cos(f);return new d.Vector(g,h,i)};g.parametricGeometry(i,c,1,g.vertices.length);var j=g.generateObj(!0);this._graphics.initBuffer(f,j)}return this._graphics.drawBuffer(f),this},d.prototype.torus=function(a,b,c,e){a=a||50,b=b||20,c=c||12,e=e||8;var f="torus|"+a+"|"+b+"|"+c+"|"+e;if(!this._graphics.geometryInHash(f)){var g=new d.Geometry3D,h=function(c,e){var f=2*Math.PI*c,g=2*Math.PI*e,h=(a+b*Math.cos(g))*Math.cos(f),i=(a+b*Math.cos(g))*Math.sin(f),j=b*Math.sin(g);return new d.Vector(h,i,j)};g.parametricGeometry(h,c,e);var i=g.generateObj();this._graphics.initBuffer(f,i)}return this._graphics.drawBuffer(f),this},d.prototype.box=function(a,b,c){a=a||10,b=b||a,c=c||a;var e=typeof arguments[3]===Number?arguments[3]:1,f=typeof arguments[4]===Number?arguments[4]:1,g="cube|"+a+"|"+b+"|"+c+"|"+e+"|"+f;if(!this._graphics.geometryInHash(g)){var h=new d.Geometry3D,i=function(e,f){var g=2*a*e-a,h=2*b*f-b,i=c;return new d.Vector(g,h,i)},j=function(e,f){var g=2*a*(1-e)-a,h=2*b*f-b,i=-c;return new d.Vector(g,h,i)},k=function(e,f){var g=2*a*(1-e)-a,h=b,i=2*c*f-c;return new d.Vector(g,h,i)},l=function(e,f){var g=2*a*e-a,h=-b,i=2*c*f-c;return new d.Vector(g,h,i)},m=function(e,f){var g=a,h=2*b*e-b,i=2*c*f-c;return new d.Vector(g,h,i)},n=function(e,f){var g=-a,h=2*b*(1-e)-b,i=2*c*f-c;return new d.Vector(g,h,i)};h.parametricGeometry(i,e,f,h.vertices.length),h.parametricGeometry(j,e,f,h.vertices.length),h.parametricGeometry(k,e,f,h.vertices.length),h.parametricGeometry(l,e,f,h.vertices.length),h.parametricGeometry(m,e,f,h.vertices.length),h.parametricGeometry(n,e,f,h.vertices.length);var o=h.generateObj(!0);this._graphics.initBuffer(g,o)}return this._graphics.drawBuffer(g),this},b.exports=d},{"../core/core":49,"./p5.Geometry3D":35}],31:[function(a,b,c){"use strict";function d(a){var b=[];return a.forEach(function(a){b.push(a/255)}),b}var e=a("../core/core");e.Renderer3D.prototype.primitives2D=function(a){var b=this.GL,c=this.getColorVertexShader(),d=this.verticeBuffer;b.bindBuffer(b.ARRAY_BUFFER,d),b.bufferData(b.ARRAY_BUFFER,new Float32Array(a),b.STATIC_DRAW),b.vertexAttribPointer(c.vertexPositionAttribute,3,b.FLOAT,!1,0,0);var e=this.colorBuffer;b.bindBuffer(b.ARRAY_BUFFER,e);for(var f=this.getCurColor(),g=[],h=0;h<a.length/3;h++)g=g.concat(f);b.bufferData(b.ARRAY_BUFFER,new Float32Array(g),b.STATIC_DRAW),b.vertexAttribPointer(c.vertexColorAttribute,4,b.FLOAT,!1,0,0);var i="vertexColorVert|vertexColorFrag";this.setMatrixUniforms(i)},e.Renderer3D.prototype.point=function(a,b,c){var d=this.GL;return this.primitives2D([a,b,c]),d.drawArrays(d.POINTS,0,1),this},e.Renderer3D.prototype.line=function(a,b,c,d,e,f){var g=this.GL;return this.primitives2D([a,b,c,d,e,f]),g.drawArrays(g.LINES,0,2),this},e.Renderer3D.prototype.triangle=function(a,b,c,d,e,f,g,h,i){var j=this.GL;return this.primitives2D([a,b,c,d,e,f,g,h,i]),this._strokeCheck(),j.drawArrays(j.TRIANGLES,0,3),this},e.Renderer3D.prototype.quad=function(a,b,c,d,e,f,g,h,i,j,k,l){var m=this.GL;return this.primitives2D([a,b,c,d,e,f,g,h,i,j,k,l]),this._strokeCheck(),m.drawArrays(m.TRIANGLE_STRIP,0,4),this},e.Renderer3D.prototype.beginShape=function(a){return this.modeStack.push(a),this.verticeStack=[],this},e.Renderer3D.prototype.vertex=function(a,b,c){return this.verticeStack.push(a,b,c),this},e.Renderer3D.prototype.endShape=function(){var a=this.GL;this.primitives2D(this.verticeStack),this.verticeStack=[];var b=this.modeStack.pop();switch(b){case"POINTS":a.drawArrays(a.POINTS,0,1);break;case"LINES":a.drawArrays(a.LINES,0,2);break;case"TRIANGLES":this._strokeCheck(),a.drawArrays(a.TRIANGLES,0,3);break;case"TRIANGLE_STRIP":this._strokeCheck(),a.drawArrays(a.TRIANGLE_STRIP,0,4);break;default:this._strokeCheck(),a.drawArrays(a.TRIANGLES,0,3)}return this},e.Renderer3D.prototype._strokeCheck=function(){var a=this.drawModeStack[this.drawModeStack.length-1];if("stroke"===a)throw new Error("stroke for shapes in 3D not yet implemented, use fill for now :(")},e.Renderer3D.prototype.fill=function(a,b,c,e){var f=this._pInst.color.apply(this._pInst,arguments),g=d(f.rgba);return g!==this.getCurColor()&&this.colorStack.push(g),this.drawModeStack.push("fill"),this},e.Renderer3D.prototype.stroke=function(a,b,c,e){var f=this._pInst.color.apply(this._pInst,arguments),g=d(f.rgba);return g!==this.getCurColor()&&this.colorStack.push(g),this.drawModeStack.push("stroke"),this},e.Renderer3D.prototype.getColorVertexShader=function(){var a,b=this.GL,c="vertexColorVert|vertexColorFrag";return this.materialInHash(c)?a=this.mHash[c]:(a=this.initShaders("vertexColorVert","vertexColorFrag",!0),a.vertexColorAttribute=b.getAttribLocation(a,"aVertexColor"),b.enableVertexAttribArray(a.vertexColorAttribute)),a},b.exports=e.Renderer3D},{"../core/core":49}],32:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.orbitControl=function(){return this.mouseIsPressed&&(this.rotateX((this.mouseX-this.width/2)/(this.width/2)),this.rotateY((this.mouseY-this.height/2)/(this.width/2))),this},b.exports=d},{"../core/core":49}],33:[function(a,b,c){"use strict";function d(a){var b=[];return a.forEach(function(a){b.push(a/255)}),b}var e=a("../core/core");e.prototype.ambientLight=function(a,b,c,e){var f=this._graphics.GL,g=this._graphics.getShader("directionalLightVert","lightFrag");f.useProgram(g),g.uAmbientColor=f.getUniformLocation(g,"uAmbientColor");var h=this._graphics._pInst.color.apply(this._graphics._pInst,arguments),i=d(h.rgba);return f.uniform3f(g.uAmbientColor,i[0],i[1],i[2]),g.uMaterialColor=f.getUniformLocation(g,"uMaterialColor"),f.uniform4f(g.uMaterialColor,1,1,1,1),this},e.prototype.directionalLight=function(a,b,c,e,f,g,h){var i=this._graphics.GL,j=this._graphics.getShader("directionalLightVert","lightFrag");i.useProgram(j),j.uDirectionalColor=i.getUniformLocation(j,"uDirectionalColor");var k=this._graphics._pInst.color.apply(this._graphics._pInst,[a,b,c]),l=d(k.rgba);return i.uniform3f(j.uDirectionalColor,l[0],l[1],l[2]),j.uLightingDirection=i.getUniformLocation(j,"uLightingDirection"),i.uniform3f(j.uLightingDirection,arguments[arguments.length-3],arguments[arguments.length-2],arguments[arguments.length-1]),j.uMaterialColor=i.getUniformLocation(j,"uMaterialColor"),i.uniform4f(j.uMaterialColor,1,1,1,1),this},e.prototype.pointLight=function(){},b.exports=e},{"../core/core":49}],34:[function(a,b,c){"use strict";function d(a){return 0===(a&a-1)}function e(a){var b=[];return a.forEach(function(a){b.push(a/255)}),b}var f=a("../core/core");f.prototype.normalMaterial=function(){return this._graphics.getShader("normalVert","normalFrag"),this},f.prototype.texture=function(a){var b=this._graphics.GL,c=this._graphics.getShader("normalVert","textureFrag");b.useProgram(c);var e=b.createTexture();if(b.bindTexture(b.TEXTURE_2D,e),a instanceof f.Image){a.loadPixels();var g=new Uint8Array(a.pixels);b.texImage2D(b.TEXTURE_2D,0,b.RGBA,a.width,a.height,0,b.RGBA,b.UNSIGNED_BYTE,g)}return d(a.width)&&d(a.height)?b.generateMipmap(b.TEXTURE_2D):(b.texParameteri(b.TETXURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE),b.texParameteri(b.TETXURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE),b.texParameteri(b.TETXURE_2D,b.TEXTURE_MIN_FILTER,b.LINEAR)),b.bindTexture(b.TEXTURE_2D,e),b.uniform1i(b.getUniformLocation(c,"uSampler"),0),this},f.prototype.basicMaterial=function(){var a=this._graphics.GL,b=this._graphics.getShader("normalVert","basicFrag");a.useProgram(b),b.uMaterialColor=a.getUniformLocation(b,"uMaterialColor");var c=this._graphics._pInst.color.apply(this._graphics._pInst,arguments),d=e(c.rgba);return a.uniform4f(b.uMaterialColor,d[0],d[1],d[2],d[3]),this},f.prototype.ambientMaterial=function(){var a=this._graphics.GL,b=this._graphics.getCurShaderId(),c=this._graphics.mHash[b];a.useProgram(c),c.uMaterialColor=a.getUniformLocation(c,"uMaterialColor");var d=this._graphics._pInst.color.apply(this._graphics._pInst,arguments),f=e(d.rgba);return a.uniform4f(c.uMaterialColor,f[0],f[1],f[2],f[3]),this},f.prototype.specularMaterial=function(){return this},b.exports=f},{"../core/core":49}],35:[function(a,b,c){"use strict";function d(a){return a.reduce(function(a,b){return a.concat(b)})}function e(a){return d(a.map(function(a){return[a.x,a.y,a.z]}))}var f=a("../core/core");f.Geometry3D=function(){this.vertices=[],this.vertexNormals=[],this.faces=[],this.faceNormals=[],this.uvs=[]},f.Geometry3D.prototype.parametricGeometry=function(a,b,c,d){var e,f,g,h,i;d=d||0;var j=b+1;for(e=0;c>=e;e++)for(i=e/c,f=0;b>=f;f++)h=f/b,g=a(h,i),this.vertices.push(g);var k,l,m,n,o,p,q,r;for(e=0;c>e;e++)for(f=0;b>f;f++)k=e*j+f+d,l=e*j+f+1+d,m=(e+1)*j+f+1+d,n=(e+1)*j+f+d,o=[f/b,e/c],p=[(f+1)/b,e/c],q=[(f+1)/b,(e+1)/c],r=[f/b,(e+1)/c],this.faces.push([k,l,n]),this.uvs.push([o,p,r]),this.faces.push([l,m,n]),this.uvs.push([p,q,r])},f.Geometry3D.prototype.mergeVertices=function(){var a,b,c,d,e,f={},g=[],h=[],i=4,j=Math.pow(10,i);for(c=0;c<this.vertices.length;c++)a=this.vertices[c],b=Math.round(a.x*j)+"_"+Math.round(a.y*j)+"_"+Math.round(a.z*j),void 0===f[b]?(f[b]=c,g.push(this.vertices[c]),h[c]=g.length-1):h[c]=h[f[b]];var k=[];for(c=0;c<this.faces.length;c++){d=this.faces[c],d[0]=h[d[0]],d[1]=h[d[1]],d[2]=h[d[2]],e=[d[0],d[1],d[2]];for(var l=-1,m=0;3>m;m++)if(e[m]===e[(m+1)%3]){l=m,k.push(c);break}}for(c=k.length-1;c>=0;c--){var n=k[c];this.faces.splice(n,1)}var o=this.vertices.length-g.length;return this.vertices=g,o},f.Geometry3D.prototype.computeFaceNormals=function(){for(var a=new f.Vector,b=new f.Vector,c=0;c<this.faces.length;c++){var d=this.faces[c],e=this.vertices[d[0]],g=this.vertices[d[1]],h=this.vertices[d[2]];f.Vector.sub(h,g,a),f.Vector.sub(e,g,b);var i=f.Vector.cross(b,a);i.normalize(),i.mult(-1),this.faceNormals[c]=i}},f.Geometry3D.prototype.computeVertexNormals=function(){var a,b,c,d,e,g=[];for(e=new Array(this.vertices.length),a=0;a<this.vertices.length;a++)e[a]=new f.Vector;for(b=0;b<this.faces.length;b++)c=this.faces[b],d=this.faceNormals[b],e[c[0]].add(d),e[c[1]].add(d),e[c[2]].add(d);for(a=0;a<this.vertices.length;a++)e[a].normalize();for(b=0;b<this.faces.length;b++)c=this.faces[b],g[b]=[],g[b][0]=e[c[0]].copy(),g[b][1]=e[c[1]].copy(),g[b][2]=e[c[2]].copy();for(b=0;b<this.faces.length;b++)c=this.faces[b],d=this.faceNormals[b],this.vertexNormals[c[0]]=g[b][0],this.vertexNormals[c[1]]=g[b][1],this.vertexNormals[c[2]]=g[b][2]},f.Geometry3D.prototype.generateUV=function(a,b){a=d(a),b=d(b);var c=[];return a.forEach(function(a,d){c[a]=b[d]}),d(c)},f.Geometry3D.prototype.generateObj=function(a){a||this.mergeVertices(),this.computeFaceNormals(),this.computeVertexNormals();var b={vertices:e(this.vertices),vertexNormals:e(this.vertexNormals),uvs:this.generateUV(this.faces,this.uvs),faces:d(this.faces),len:3*this.faces.length};return b},b.exports=f.Geometry3D},{"../core/core":49}],36:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../math/polargeometry"),f=a("../core/constants"),g="undefined"!=typeof Float32Array?Float32Array:Array;d.Matrix=function(){return arguments[0]instanceof d?(this.p5=arguments[0],this.mat4=arguments[1]||new g([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])):this.mat4=arguments[0]||new g([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),this},d.Matrix.prototype.set=function(a){return a instanceof d.Matrix?(this.mat4=a.mat4,this):a instanceof g?(this.mat4=a,this):this},d.Matrix.prototype.get=function(){return new d.Matrix(this.mat4)},d.Matrix.prototype.copy=function(){var a=new d.Matrix;return a.mat4[0]=this.mat4[0],a.mat4[1]=this.mat4[1],a.mat4[2]=this.mat4[2],a.mat4[3]=this.mat4[3],a.mat4[4]=this.mat4[4],a.mat4[5]=this.mat4[5],a.mat4[6]=this.mat4[6],a.mat4[7]=this.mat4[7],a.mat4[8]=this.mat4[8],a.mat4[9]=this.mat4[9],a.mat4[10]=this.mat4[10],a.mat4[11]=this.mat4[11],a.mat4[12]=this.mat4[12],a.mat4[13]=this.mat4[13],a.mat4[14]=this.mat4[14],a.mat4[15]=this.mat4[15],a},d.Matrix.identity=function(){return new d.Matrix},d.Matrix.prototype.transpose=function(a){var b,c,e,f,h,i;return a instanceof d.Matrix?(b=a.mat4[1],c=a.mat4[2],e=a.mat4[3],f=a.mat4[6],h=a.mat4[7],i=a.mat4[11],this.mat4[0]=a.mat4[0],this.mat4[1]=a.mat4[4],this.mat4[2]=a.mat4[8],this.mat4[3]=a.mat4[12],this.mat4[4]=b,this.mat4[5]=a.mat4[5],this.mat4[6]=a.mat4[9],this.mat4[7]=a.mat4[13],this.mat4[8]=c,this.mat4[9]=f,this.mat4[10]=a.mat4[10],this.mat4[11]=a.mat4[14],this.mat4[12]=e,this.mat4[13]=h,this.mat4[14]=i,this.mat4[15]=a.mat4[15]):a instanceof g&&(b=a[1],c=a[2],e=a[3],f=a[6],h=a[7],i=a[11],this.mat4[0]=a[0],this.mat4[1]=a[4],this.mat4[2]=a[8],this.mat4[3]=a[12],this.mat4[4]=b,this.mat4[5]=a[5],this.mat4[6]=a[9],this.mat4[7]=a[13],this.mat4[8]=c,this.mat4[9]=f,this.mat4[10]=a[10],this.mat4[11]=a[14],this.mat4[12]=e,this.mat4[13]=h,this.mat4[14]=i,this.mat4[15]=a[15]),this},d.Matrix.prototype.invert=function(a){var b,c,e,f,h,i,j,k,l,m,n,o,p,q,r,s;a instanceof d.Matrix?(b=a.mat4[0],c=a.mat4[1],e=a.mat4[2],f=a.mat4[3],h=a.mat4[4],i=a.mat4[5],j=a.mat4[6],k=a.mat4[7],l=a.mat4[8],m=a.mat4[9],n=a.mat4[10],o=a.mat4[11],p=a.mat4[12],q=a.mat4[13],r=a.mat4[14],s=a.mat4[15]):a instanceof g&&(b=a[0],c=a[1],e=a[2],f=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=a[9],n=a[10],o=a[11],p=a[12],q=a[13],r=a[14],s=a[15]);var t=b*i-c*h,u=b*j-e*h,v=b*k-f*h,w=c*j-e*i,x=c*k-f*i,y=e*k-f*j,z=l*q-m*p,A=l*r-n*p,B=l*s-o*p,C=m*r-n*q,D=m*s-o*q,E=n*s-o*r,F=t*E-u*D+v*C+w*B-x*A+y*z;return F?(F=1/F,this.mat4[0]=(i*E-j*D+k*C)*F,this.mat4[1]=(e*D-c*E-f*C)*F,this.mat4[2]=(q*y-r*x+s*w)*F,this.mat4[3]=(n*x-m*y-o*w)*F,this.mat4[4]=(j*B-h*E-k*A)*F,this.mat4[5]=(b*E-e*B+f*A)*F,this.mat4[6]=(r*v-p*y-s*u)*F,this.mat4[7]=(l*y-n*v+o*u)*F,this.mat4[8]=(h*D-i*B+k*z)*F,this.mat4[9]=(c*B-b*D-f*z)*F,this.mat4[10]=(p*x-q*v+s*t)*F,this.mat4[11]=(m*v-l*x-o*t)*F,this.mat4[12]=(i*A-h*C-j*z)*F,this.mat4[13]=(b*C-c*A+e*z)*F,this.mat4[14]=(q*u-p*w-r*t)*F,this.mat4[15]=(l*w-m*u+n*t)*F,this):null},d.Matrix.prototype.determinant=function(){var a=this.mat4[0]*this.mat4[5]-this.mat4[1]*this.mat4[4],b=this.mat4[0]*this.mat4[6]-this.mat4[2]*this.mat4[4],c=this.mat4[0]*this.mat4[7]-this.mat4[3]*this.mat4[4],d=this.mat4[1]*this.mat4[6]-this.mat4[2]*this.mat4[5],e=this.mat4[1]*this.mat4[7]-this.mat4[3]*this.mat4[5],f=this.mat4[2]*this.mat4[7]-this.mat4[3]*this.mat4[6],g=this.mat4[8]*this.mat4[13]-this.mat4[9]*this.mat4[12],h=this.mat4[8]*this.mat4[14]-this.mat4[10]*this.mat4[12],i=this.mat4[8]*this.mat4[15]-this.mat4[11]*this.mat4[12],j=this.mat4[9]*this.mat4[14]-this.mat4[10]*this.mat4[13],k=this.mat4[9]*this.mat4[15]-this.mat4[11]*this.mat4[13],l=this.mat4[10]*this.mat4[15]-this.mat4[11]*this.mat4[14];return a*l-b*k+c*j+d*i-e*h+f*g},d.Matrix.prototype.mult=function(a){var b=new g(16),c=new g(16);a instanceof d.Matrix?c=a.mat4:a instanceof g&&(c=a);var e=this.mat4[0],f=this.mat4[1],h=this.mat4[2],i=this.mat4[3];return b[0]=e*c[0]+f*c[4]+h*c[8]+i*c[12],b[1]=e*c[1]+f*c[5]+h*c[9]+i*c[13],b[2]=e*c[2]+f*c[6]+h*c[10]+i*c[14],b[3]=e*c[3]+f*c[7]+h*c[11]+i*c[15],e=this.mat4[4],f=this.mat4[5],h=this.mat4[6],i=this.mat4[7],b[4]=e*c[0]+f*c[4]+h*c[8]+i*c[12],b[5]=e*c[1]+f*c[5]+h*c[9]+i*c[13],b[6]=e*c[2]+f*c[6]+h*c[10]+i*c[14],b[7]=e*c[3]+f*c[7]+h*c[11]+i*c[15],e=this.mat4[8],f=this.mat4[9],h=this.mat4[10],i=this.mat4[11],b[8]=e*c[0]+f*c[4]+h*c[8]+i*c[12],b[9]=e*c[1]+f*c[5]+h*c[9]+i*c[13],b[10]=e*c[2]+f*c[6]+h*c[10]+i*c[14],b[11]=e*c[3]+f*c[7]+h*c[11]+i*c[15],e=this.mat4[12],f=this.mat4[13],h=this.mat4[14],i=this.mat4[15],b[12]=e*c[0]+f*c[4]+h*c[8]+i*c[12],b[13]=e*c[1]+f*c[5]+h*c[9]+i*c[13],b[14]=e*c[2]+f*c[6]+h*c[10]+i*c[14],b[15]=e*c[3]+f*c[7]+h*c[11]+i*c[15],this.mat4=b,this},d.Matrix.prototype.scale=function(){var a,b,c;arguments[0]instanceof d.Vector?(a=arguments[0].x,b=arguments[0].y,c=arguments[0].z):arguments[0]instanceof Array?(a=arguments[0][0],b=arguments[0][1],c=arguments[0][2]):(a=arguments[0]||1,b=arguments[1]||1,c=arguments[2]||1);for(var e=new g(16),f=0;f<this.mat4.length;f++){var h=f%4;switch(h){case 0:e[f]=this.mat4[f]*a;break;case 1:e[f]=this.mat4[f]*b;break;case 2:e[f]=this.mat4[f]*c;break;case 3:e[f]=this.mat4[f]}}return this.mat4=e,this},d.Matrix.prototype.rotate=function(a,b){var c,g,h,i,j;this.p5?this.p5._angleMode===f.DEGREES&&(i=e.degreesToRadians(a)):i=a,b instanceof d.Vector?(c=b.x,g=b.y,h=b.z):b instanceof Array&&(c=b[0],g=b[1],h=b[2]),j=Math.sqrt(c*c+g*g+h*h),c*=1/j,g*=1/j,h*=1/j;var k=this.mat4[0],l=this.mat4[1],m=this.mat4[2],n=this.mat4[3],o=this.mat4[4],p=this.mat4[5],q=this.mat4[6],r=this.mat4[7],s=this.mat4[8],t=this.mat4[9],u=this.mat4[10],v=this.mat4[11],w=Math.sin(i),x=Math.cos(i),y=1-x,z=c*c*y+x,A=g*c*y+h*w,B=h*c*y-g*w,C=c*g*y-h*w,D=g*g*y+x,E=h*g*y+c*w,F=c*h*y+g*w,G=g*h*y-c*w,H=h*h*y+x;return this.mat4[0]=k*z+o*A+s*B,this.mat4[1]=l*z+p*A+t*B,this.mat4[2]=m*z+q*A+u*B,this.mat4[3]=n*z+r*A+v*B,this.mat4[4]=k*C+o*D+s*E,this.mat4[5]=l*C+p*D+t*E,this.mat4[6]=m*C+q*D+u*E,this.mat4[7]=n*C+r*D+v*E,this.mat4[8]=k*F+o*G+s*H,this.mat4[9]=l*F+p*G+t*H,this.mat4[10]=m*F+q*G+u*H,this.mat4[11]=n*F+r*G+v*H,this},d.Matrix.prototype.translate=function(a){var b=a[0],c=a[1],d=a[2];this.mat4[12]=this.mat4[0]*b+this.mat4[4]*c+this.mat4[8]*d+this.mat4[12],this.mat4[13]=this.mat4[1]*b+this.mat4[5]*c+this.mat4[9]*d+this.mat4[13],this.mat4[14]=this.mat4[2]*b+this.mat4[6]*c+this.mat4[10]*d+this.mat4[14],this.mat4[15]=this.mat4[3]*b+this.mat4[7]*c+this.mat4[11]*d+this.mat4[15]},d.Matrix.prototype.rotateX=function(a){this.rotate(a,[1,0,0])},d.Matrix.prototype.rotateY=function(a){this.rotate(a,[0,1,0])},d.Matrix.prototype.rotateZ=function(a){this.rotate(a,[0,0,1])},d.Matrix.prototype.perspective=function(a,b,c,d){var e=1/Math.tan(a/2),f=1/(c-d);return this.mat4[0]=e/b,this.mat4[1]=0,this.mat4[2]=0,this.mat4[3]=0,this.mat4[4]=0,this.mat4[5]=e,this.mat4[6]=0,this.mat4[7]=0,this.mat4[8]=0,this.mat4[9]=0,this.mat4[10]=(d+c)*f,this.mat4[11]=-1,this.mat4[12]=0,this.mat4[13]=0,this.mat4[14]=2*d*c*f,this.mat4[15]=0,this},d.Matrix.prototype.ortho=function(a,b,c,d,e,f){var g=1/(a-b),h=1/(c-d),i=1/(e-f);return this.mat4[0]=-2*g,this.mat4[1]=0,this.mat4[2]=0,this.mat4[3]=0,this.mat4[4]=0,this.mat4[5]=-2*h,this.mat4[6]=0,this.mat4[7]=0,this.mat4[8]=0,this.mat4[9]=0,this.mat4[10]=2*i,this.mat4[11]=0,this.mat4[12]=(a+b)*g,this.mat4[13]=(d+c)*h,this.mat4[14]=(f+e)*i,this.mat4[15]=1,this},b.exports=d.Matrix},{"../core/constants":48,"../core/core":49,"../math/polargeometry":78}],37:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./shader");a("../core/p5.Renderer"),a("./p5.Matrix");var f=[],g=[],h=1e3,i={alpha:!1,depth:!0,stencil:!0,antialias:!1,premultipliedAlpha:!1,preserveDrawingBuffer:!1};d.Renderer3D=function(a,b,c){d.Renderer.call(this,a,b,c);try{if(this.drawingContext=this.canvas.getContext("webgl",i)||this.canvas.getContext("experimental-webgl",i),null===this.drawingContext)throw new Error("Error creating webgl context");console.log("p5.Renderer3D: enabled webgl context")}catch(e){throw new Error(e)}this.isP3D=!0,this.GL=this.drawingContext;var f=this.GL;return f.clearColor(1,1,1,1),f.clearDepth(1),f.enable(f.DEPTH_TEST),f.depthFunc(f.LEQUAL),f.clear(f.COLOR_BUFFER_BIT|f.DEPTH_BUFFER_BIT),f.viewport(0,0,f.drawingBufferWidth,f.drawingBufferHeight),this.initMatrix(),this.initHash(),this.resetStack(),this.verticeBuffer=f.createBuffer(),this.colorBuffer=f.createBuffer(),this},d.Renderer3D.prototype=Object.create(d.Renderer.prototype),d.Renderer3D.prototype._applyDefaults=function(){return this},d.Renderer3D.prototype.resize=function(a,b){var c=this.GL;d.Renderer.prototype.resize.call(this,a,b),c.viewport(0,0,c.drawingBufferWidth,c.drawingBufferHeight)},d.Renderer3D.prototype.background=function(){var a=this.GL,b=this._pInst.color.apply(this._pInst,arguments),c=b.rgba[0]/255,d=b.rgba[1]/255,e=b.rgba[2]/255,f=b.rgba[3]/255;a.clearColor(c,d,e,f),a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),this.resetMatrix(),this.resetStack()},d.Renderer3D.prototype.initShaders=function(a,b,c){var d=this.GL,f=d.createShader(d.VERTEX_SHADER);if(d.shaderSource(f,e[a]),d.compileShader(f),!d.getShaderParameter(f,d.COMPILE_STATUS))return alert("Yikes! An error occurred compiling the shaders:"+d.getShaderInfoLog(f)),null;var g=d.createShader(d.FRAGMENT_SHADER);if(d.shaderSource(g,e[b]),d.compileShader(g),!d.getShaderParameter(g,d.COMPILE_STATUS))return alert("Darn! An error occurred compiling the shaders:"+d.getShaderInfoLog(g)),null;var i=d.createProgram();return d.attachShader(i,f),d.attachShader(i,g),d.linkProgram(i),d.getProgramParameter(i,d.LINK_STATUS)||alert("Snap! Error linking shader program"),d.useProgram(i),i.uResolution=d.getUniformLocation(i,"uResolution"),d.uniform1f(i.uResolution,h),i.vertexPositionAttribute=d.getAttribLocation(i,"aPosition"),d.enableVertexAttribArray(i.vertexPositionAttribute),void 0===c&&(i.vertexNormalAttribute=d.getAttribLocation(i,"aNormal"),d.enableVertexAttribArray(i.vertexNormalAttribute),i.uNMatrixUniform=d.getUniformLocation(i,"uNormalMatrix"),i.textureCoordAttribute=d.getAttribLocation(i,"aTexCoord"),d.enableVertexAttribArray(i.textureCoordAttribute),i.samplerUniform=d.getUniformLocation(i,"uSampler")),i.uPMatrixUniform=d.getUniformLocation(i,"uTransformMatrix"),i.uMVMatrixUniform=d.getUniformLocation(i,"uModelviewMatrix"),this.mHash[a+"|"+b]=i,i},d.Renderer3D.prototype.getShader=function(a,b){var c=a+"|"+b;return this.materialInHash(c)||this.initShaders(a,b),c!==this.getCurShaderId()&&this.saveShaders(c),this.mHash[c]},d.Renderer3D.prototype.setMatrixUniforms=function(a){var b=this.GL,c=this.mHash[a];b.useProgram(c),b.uniformMatrix4fv(c.uPMatrixUniform,!1,this.uPMatrix.mat4),b.uniformMatrix4fv(c.uMVMatrixUniform,!1,this.uMVMatrix.mat4),this.uNMatrix=new d.Matrix,this.uNMatrix.invert(this.uMVMatrix),this.uNMatrix.transpose(this.uNMatrix),b.uniformMatrix4fv(c.uNMatrixUniform,!1,this.uNMatrix.mat4)},d.Renderer3D.prototype.saveShaders=function(a){g.push(a)},d.Renderer3D.prototype.getCurColor=function(){return this.colorStack[this.colorStack.length-1]||[.5,.5,.5,1]},d.Renderer3D.prototype.getCurShaderId=function(){var a=g[g.length-1];if(void 0===a){a="normalVert|basicFrag";var b=this.GL,c=this.initShaders("normalVert","basicFrag");c.uMaterialColor=b.getUniformLocation(c,"uMaterialColor");var d=this.getCurColor();b.uniform4f(c.uMaterialColor,d[0],d[1],d[2],d[3]),this.saveShaders(a)}return a},d.Renderer3D.prototype.resetStack=function(){g=[],this.colorStack=[],this.modeStack=[],this.drawModeStack=[],this.verticeStack=[],this.lightStack=[]},d.Renderer3D.prototype.initHash=function(){this.gHash={},this.mHash={}},d.Renderer3D.prototype.geometryInHash=function(a){return void 0!==this.gHash[a]},d.Renderer3D.prototype.materialInHash=function(a){return void 0!==this.mHash[a]},d.Renderer3D.prototype.initMatrix=function(){this.uMVMatrix=new d.Matrix,this.uPMatrix=new d.Matrix,this.uNMatrix=new d.Matrix;var a=this.width,b=this.height;this.uPMatrix.perspective(60/180*Math.PI,a/b,.1,100)},d.Renderer3D.prototype.resetMatrix=function(){this.uMVMatrix=d.Matrix.identity()},d.Renderer3D.prototype.translate=function(a,b,c){return a/=h,b=-b/h,c/=h,this.uMVMatrix.translate([a,b,c]),this},d.Renderer3D.prototype.scale=function(a,b,c){return this.uMVMatrix.scale([a,b,c]),this},d.Renderer3D.prototype.rotateX=function(a){return this.uMVMatrix.rotateX(a),this},d.Renderer3D.prototype.rotateY=function(a){return this.uMVMatrix.rotateY(a),this},d.Renderer3D.prototype.rotateZ=function(a){return this.uMVMatrix.rotateZ(a),this},d.Renderer3D.prototype.push=function(){f.push(this.uMVMatrix.copy())},d.Renderer3D.prototype.pop=function(){if(0===f.length)throw"Invalid popMatrix!";this.uMVMatrix=f.pop()},b.exports=d.Renderer3D},{"../core/core":49,"../core/p5.Renderer":55,"./p5.Matrix":36,"./shader":39}],38:[function(a,b,c){"use strict";var d=a("../core/core");d.Renderer3D.prototype.createBuffer=function(a,b){var c=this.GL;this.gHash[a]={},this.gHash[a].len=b.len,this.gHash[a].vertexBuffer=c.createBuffer(),this.gHash[a].normalBuffer=c.createBuffer(),this.gHash[a].uvBuffer=c.createBuffer(),this.gHash[a].indexBuffer=c.createBuffer()},d.Renderer3D.prototype.initBuffer=function(a,b){var c=this.GL;this.createBuffer(a,b);var d=this.mHash[this.getCurShaderId()];c.bindBuffer(c.ARRAY_BUFFER,this.gHash[a].vertexBuffer),c.bufferData(c.ARRAY_BUFFER,new Float32Array(b.vertices),c.STATIC_DRAW),c.vertexAttribPointer(d.vertexPositionAttribute,3,c.FLOAT,!1,0,0),c.bindBuffer(c.ARRAY_BUFFER,this.gHash[a].normalBuffer),c.bufferData(c.ARRAY_BUFFER,new Float32Array(b.vertexNormals),c.STATIC_DRAW),c.vertexAttribPointer(d.vertexNormalAttribute,3,c.FLOAT,!1,0,0),c.bindBuffer(c.ARRAY_BUFFER,this.gHash[a].uvBuffer),c.bufferData(c.ARRAY_BUFFER,new Float32Array(b.uvs),c.STATIC_DRAW),c.vertexAttribPointer(d.textureCoordAttribute,2,c.FLOAT,!1,0,0),c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,this.gHash[a].indexBuffer),c.bufferData(c.ELEMENT_ARRAY_BUFFER,new Uint16Array(b.faces),c.STATIC_DRAW)},d.Renderer3D.prototype.drawBuffer=function(a){var b=this.GL,c=this.getCurShaderId(),d=this.mHash[c];b.bindBuffer(b.ARRAY_BUFFER,this.gHash[a].vertexBuffer),b.vertexAttribPointer(d.vertexPositionAttribute,3,b.FLOAT,!1,0,0),b.bindBuffer(b.ARRAY_BUFFER,this.gHash[a].normalBuffer),b.vertexAttribPointer(d.vertexNormalAttribute,3,b.FLOAT,!1,0,0),b.bindBuffer(b.ARRAY_BUFFER,this.gHash[a].uvBuffer),b.vertexAttribPointer(d.textureCoordAttribute,2,b.FLOAT,!1,0,0),b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,this.gHash[a].indexBuffer),this.setMatrixUniforms(c),b.drawElements(b.TRIANGLES,this.gHash[a].len,b.UNSIGNED_SHORT,0)},b.exports=d.Renderer3D},{"../core/core":49}],39:[function(a,b,c){b.exports={vertexColorVert:"attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelviewMatrix;\nuniform mat4 uTransformMatrix;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n vec3 zeroToOne = aPosition / 1000.0;\n vec4 positionVec4 = vec4(zeroToOne * vec3(1., -1., 1.), 1.);\n gl_Position = uTransformMatrix * uModelviewMatrix * positionVec4;\n vColor = aVertexColor;\n}",vertexColorFrag:"precision mediump float;\nvarying vec4 vColor;\nvoid main(void) {\n gl_FragColor = vColor;\n}",normalVert:"attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelviewMatrix;\nuniform mat4 uTransformMatrix;\nuniform mat4 uNormalMatrix;\nuniform float uResolution;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n vec3 zeroToOne = aPosition / uResolution;\n vec4 positionVec4 = vec4(zeroToOne, 1.);\n gl_Position = uTransformMatrix * uModelviewMatrix * positionVec4;\n vVertexNormal = vec3( uNormalMatrix * vec4( aNormal, 1.0 ) );\n vVertTexCoord = aTexCoord;\n}",normalFrag:"precision mediump float;\nvarying vec3 vVertexNormal;\nvoid main(void) {\n gl_FragColor = vec4(vVertexNormal, 1.0);\n}",basicFrag:"precision mediump float;\nvarying vec3 vVertexNormal;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n gl_FragColor = uMaterialColor;\n}",textureFrag:"precision mediump float;\nvarying highp vec2 vVertTexCoord;\nuniform sampler2D uSampler;\nvoid main(void) {\n gl_FragColor = texture2D(uSampler, vec2(vVertTexCoord.s,vVertTexCoord.t));\n}",lightFrag:"precision mediump float;\n//varying vec2 vTextureCoord;\nvarying vec3 vLightWeighting;\n//uniform sampler2D uSampler;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n //vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));\n gl_FragColor = vec4(vec3(uMaterialColor.rgb * vLightWeighting), uMaterialColor.a);\n}",directionalLightVert:"attribute vec3 position;\nattribute vec3 normal;\nattribute vec2 texCoord;\n\nuniform mat4 modelviewMatrix;\nuniform mat4 transformMatrix;\nuniform mat4 normalMatrix;\nuniform float uResolution;\n\nuniform vec3 uAmbientColor;\nuniform vec3 uLightingDirection;\nuniform vec3 uDirectionalColor;\n\nvarying vec2 vertTexCoord;\nvarying vec3 vLightWeighting;\n\nvoid main(void) {\n vec3 zeroToOne = position / uResolution;\n vec4 positionVec4 = vec4(zeroToOne, 1.);\n gl_Position = transformMatrix * modelviewMatrix * positionVec4;\n vec3 vertexNormal = vec3( normalMatrix * vec4( normal, 1.0 ) );\n vertTexCoord = texCoord;\n\n float directionalLightWeighting = max(dot(vertexNormal, uLightingDirection), 0.0);\n vLightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;\n}",spotLightVert:"attribute vec3 position;\nattribute vec3 normal;\nattribute vec2 texCoord;\n\nuniform mat4 modelviewMatrix;\nuniform mat4 transformMatrix;\nuniform mat4 normalMatrix;\nuniform float uResolution;\n\nuniform vec4 uAmbientColor;\nuniform vec3 uPointLightingLocation;\nuniform vec4 uPointLightingColor;\n\nvarying vec3 vertexNormal;\nvarying vec2 vertTexCoord;\nvarying vec4 vLightWeighting;\n\nvoid main(void) {\n vec3 zeroToOne = position / uResolution;\n vec4 positionVec4 = vec4(zeroToOne, 1.);\n gl_Position = transformMatrix * modelviewMatrix * positionVec4;\n vertTexCoord = texCoord;\n\n vec3 lightDirection = normalize(uPointLightingLocation - mvPosition.xyz);\n vec3 transformedNormal = normalMatrix * vertexNormal;\n float directionalLightWeighting = max(dot(transformedNormal, lightDirection), 0.0);\n vLightWeighting = uAmbientColor + uPointLightingColor * directionalLightWeighting;\n}"}},{}],40:[function(a,b,c){"use strict";var d=a("./core/core");a("./color/p5.Color"),a("./core/p5.Element"),a("./typography/p5.Font"),a("./core/p5.Graphics"),a("./core/p5.Renderer2D"),a("./image/p5.Image"),a("./math/p5.Vector"),a("./io/p5.TableRow"),a("./io/p5.Table"),a("./color/creating_reading"),a("./color/setting"),a("./core/constants"),a("./utilities/conversion"),a("./utilities/array_functions"),a("./utilities/string_functions"),a("./core/environment"),a("./image/image"),a("./image/loading_displaying"),a("./image/pixels"),a("./io/files"),a("./events/keyboard"),a("./events/acceleration"),a("./events/mouse"),a("./utilities/time_date"),a("./events/touch"),a("./math/math"),a("./math/calculation"),a("./math/random"),a("./math/noise"),a("./math/trigonometry"),a("./core/rendering"),a("./core/2d_primitives"),a("./core/attributes"),a("./core/curves"),a("./core/vertex"),a("./core/structure"),a("./core/transform"),a("./typography/attributes"),a("./typography/loading_displaying"),a("./3d/p5.Renderer3D"), | |
a("./3d/p5.Geometry3D"),a("./3d/retainedMode3D"),a("./3d/immediateMode3D"),a("./3d/3d_primitives"),a("./3d/p5.Matrix"),a("./3d/material"),a("./3d/light"),a("./3d/shader"),a("./3d/interaction");var e=function(){window.PHANTOMJS||window.mocha||(window.setup&&"function"==typeof window.setup||window.draw&&"function"==typeof window.draw)&&new d};"complete"===document.readyState?e():window.addEventListener("load",e,!1),b.exports=d},{"./3d/3d_primitives":30,"./3d/immediateMode3D":31,"./3d/interaction":32,"./3d/light":33,"./3d/material":34,"./3d/p5.Geometry3D":35,"./3d/p5.Matrix":36,"./3d/p5.Renderer3D":37,"./3d/retainedMode3D":38,"./3d/shader":39,"./color/creating_reading":42,"./color/p5.Color":43,"./color/setting":44,"./core/2d_primitives":45,"./core/attributes":46,"./core/constants":48,"./core/core":49,"./core/curves":50,"./core/environment":51,"./core/p5.Element":53,"./core/p5.Graphics":54,"./core/p5.Renderer2D":56,"./core/rendering":57,"./core/structure":59,"./core/transform":60,"./core/vertex":61,"./events/acceleration":62,"./events/keyboard":63,"./events/mouse":64,"./events/touch":65,"./image/image":67,"./image/loading_displaying":68,"./image/p5.Image":69,"./image/pixels":70,"./io/files":71,"./io/p5.Table":72,"./io/p5.TableRow":73,"./math/calculation":74,"./math/math":75,"./math/noise":76,"./math/p5.Vector":77,"./math/random":79,"./math/trigonometry":80,"./typography/attributes":81,"./typography/loading_displaying":82,"./typography/p5.Font":83,"./utilities/array_functions":84,"./utilities/conversion":85,"./utilities/string_functions":86,"./utilities/time_date":87}],41:[function(a,b,c){var d=a("../core/core");d.ColorUtils={},d.ColorUtils.hsbaToRGBA=function(a){var b=a[0],c=a[1],d=a[2],e=a[3]||1,f=[];if(0===c)f=[d,d,d,e];else{var g=6*b;6===g&&(g=0);var h,i,j,k=Math.floor(g),l=d*(1-c),m=d*(1-c*(g-k)),n=d*(1-c*(1-(g-k)));0===k?(h=d,i=n,j=l):1===k?(h=m,i=d,j=l):2===k?(h=l,i=d,j=n):3===k?(h=l,i=m,j=d):4===k?(h=n,i=l,j=d):(h=d,i=l,j=m),f=[h,i,j,e]}return f},d.ColorUtils.rgbaToHSBA=function(a){var b,c,d=a[0],e=a[1],f=a[2],g=a[3]||1,h=Math.min(d,e,f),i=Math.max(d,e,f),j=i-h,k=i;if(0===j)b=0,c=0;else{c=j/i;var l=((i-d)/6+j/2)/j,m=((i-e)/6+j/2)/j,n=((i-f)/6+j/2)/j;d===i?b=n-m:e===i?b=1/3+l-n:f===i&&(b=2/3+m-l),0>b&&(b+=1),b>1&&(b-=1)}return[b,c,k,g]},d.ColorUtils.hslaToRGBA=function(a){var b=a[0],c=a[1],d=a[2],e=a[3]||1,f=[];if(0===c)f=[d,d,d,e];else{var g,h,i,j,k;h=.5>d?d*(1+c):d+c-c*d,g=2*d-h;var l=function(a,b,c){return 0>c?c+=1:c>1&&(c-=1),1>6*c?a+6*(b-a)*c:1>2*c?b:2>3*c?a+(b-a)*(2/3-c)*6:a};i=l(g,h,b+1/3),j=l(g,h,b),k=l(g,h,b-1/3),f=[i,j,k,e]}return f},d.ColorUtils.rgbaToHSLA=function(a){var b,c,d,e,f,g=a[0],h=a[1],i=a[2],j=a[3]||1,k=Math.min(g,h,i),l=Math.max(g,h,i),m=l-k,n=(l+k)/2;return 0===m?(b=0,c=0):(d=((l-g)/6+m/2)/m,e=((l-h)/6+m/2)/m,f=((l-i)/6+m/2)/m,g===l?b=f-e:h===l?b=1/3+d-f:i===l&&(b=2/3+e-d),0>b&&(b+=1),b>1&&(b-=1),c=.5>n?m/(l+k):m/(2-l-k)),[b,c,n,j]},d.ColorUtils.hslaToHSBA=function(a){var b,c=a[0],d=a[1],e=a[2],f=a[3]||1;return d*=.5>e?e:1-e,b=e+d,d=2*d/(e+d),[c,d,b,f]},d.ColorUtils.hsbaToHSLA=function(a){var b=a[0],c=a[1],d=a[2],e=a[3]||1,f=(2-c)*d/2;return 0!==f&&(1===f?c=0:.5>f?c/=2-c:c=c*d/(2-2*f)),[b,c,f,e]},b.exports=d.ColorUtils},{"../core/core":49}],42:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants");a("./p5.Color"),d.prototype.alpha=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a).getAlpha();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.blue=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a).getBlue();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.brightness=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a).getBrightness();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.color=function(){if(arguments[0]instanceof d.Color)return arguments[0];if(arguments[0]instanceof Array)return new d.Color(this,arguments[0]);var a=Array.prototype.slice.call(arguments);return new d.Color(this,a)},d.prototype.green=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a).getGreen();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.hue=function(a){if(!(a instanceof d.Color))throw new Error("Needs p5.Color as argument.");return a.getHue()},d.prototype.lerpColor=function(a,b,c){var d,f,g,h,i,j;if(this._colorMode===e.RGB)i=this.color(a).rgba,j=this.color(b).rgba;else if(this._colorMode===e.HSB)i=this.color(a).hsba,j=this.color(b).hsba;else{if(this._colorMode!==e.HSL)return;i=this.color(a).hsla,j=this.color(b).hsla}return d=this.lerp(i[0],j[0],c),f=this.lerp(i[1],j[1],c),g=this.lerp(i[2],j[2],c),h=this.lerp(i[3],j[3],c),this.color(d,f,g,h)},d.prototype.lightness=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a).getLightness();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.red=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a).getRed();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.saturation=function(a){if(!(a instanceof d.Color))throw new Error("Needs p5.Color as argument.");return a.getSaturation()},b.exports=d},{"../core/constants":48,"../core/core":49,"./p5.Color":43}],43:[function(a,b,c){var d=a("../core/core"),e=a("./color_utils"),f=a("../core/constants");d.Color=function(a,b){this.mode=a._colorMode,this.maxes=a._colorMaxes;var c=this.mode===f.HSB,g=this.mode===f.RGB,h=this.mode===f.HSL;if(g)this._array=d.Color._getFormattedColor.apply(a,b);else if(c)this.hsba=d.Color._getFormattedColor.apply(a,b),this._array=e.hsbaToRGBA(this.hsba);else{if(!h)throw new Error(a._colorMode+" is an invalid colorMode.");this.hsla=d.Color._getFormattedColor.apply(a,b),this._array=e.hslaToRGBA(this.hsla)}return this.rgba=[Math.round(255*this._array[0]),Math.round(255*this._array[1]),Math.round(255*this._array[2]),Math.round(255*this._array[3])],this},d.Color.prototype.getHue=function(){return this.hsla?this.hsla[0]*this.maxes[f.HSL][0]:this.hsba?this.hsba[0]*this.maxes[f.HSB][0]:(this.hsla=e.rgbaToHSLA(this._array),this.hsla[0]*this.maxes[f.HSL][0])},d.Color.prototype.getSaturation=function(){return this.hsba&&this.mode===f.HSB?this.hsba[1]*this.maxes[f.HSB][1]:(this.hsla||(this.hsla=e.rgbaToHSLA(this._array)),this.hsla[1]*this.maxes[f.HSL][1])},d.Color.prototype.getBrightness=function(){return this.hsba?this.hsba[2]*this.maxes[f.HSB][2]:(this.hsba=e.rgbaToHSBA(this._array),this.hsba[2]*this.maxes[f.HSB][2])},d.Color.prototype.getLightness=function(){return this.hsla?this.hsla[2]*this.maxes[f.HSL][2]:(this.hsla=e.rgbaToHSLA(this._array),this.hsla[2]*this.maxes[f.HSL][2])},d.Color.prototype.getRed=function(){return this._array[0]*this.maxes[f.RGB][0]},d.Color.prototype.getGreen=function(){return this._array[1]*this.maxes[f.RGB][1]},d.Color.prototype.getBlue=function(){return this._array[2]*this.maxes[f.RGB][2]},d.Color.prototype.getAlpha=function(){return this._array[3]*this.maxes[this.mode][3]},d.Color.prototype.toString=function(){var a=this.rgba;return a[3]=this._array[3],"rgba("+a[0]+","+a[1]+","+a[2]+","+a[3]+")"};var g=/\s*/,h=/(\d{1,3})/,i=/((?:\d+(?:\.\d+)?)|(?:\.\d+))/,j=new RegExp(i.source+"%"),k={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},l={HEX3:/^#([a-f0-9])([a-f0-9])([a-f0-9])$/i,HEX6:/^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i,RGB:new RegExp(["^rgb\\(",h.source,",",h.source,",",h.source,"\\)$"].join(g.source),"i"),RGB_PERCENT:new RegExp(["^rgb\\(",j.source,",",j.source,",",j.source,"\\)$"].join(g.source),"i"),RGBA:new RegExp(["^rgba\\(",h.source,",",h.source,",",h.source,",",i.source,"\\)$"].join(g.source),"i"),RGBA_PERCENT:new RegExp(["^rgba\\(",j.source,",",j.source,",",j.source,",",i.source,"\\)$"].join(g.source),"i"),HSL:new RegExp(["^hsl\\(",h.source,",",j.source,",",j.source,"\\)$"].join(g.source),"i"),HSLA:new RegExp(["^hsla\\(",h.source,",",j.source,",",j.source,",",i.source,"\\)$"].join(g.source),"i"),HSB:new RegExp(["^hsb\\(",h.source,",",j.source,",",j.source,"\\)$"].join(g.source),"i"),HSBA:new RegExp(["^hsba\\(",h.source,",",j.source,",",j.source,",",i.source,"\\)$"].join(g.source),"i")};d.Color._getFormattedColor=function(){var a=arguments.length,b=this._colorMode,c=this._colorMaxes[this._colorMode],g=[];if(a>=3)g[0]=arguments[0]/c[0],g[1]=arguments[1]/c[1],g[2]=arguments[2]/c[2],g[3]="number"==typeof arguments[3]?arguments[3]/c[3]:1;else if(1===a&&"string"==typeof arguments[0]){var h=arguments[0].trim().toLowerCase();if(k[h])return d.Color._getFormattedColor.apply(this,[k[h]]);if(l.HEX3.test(h)?(g=l.HEX3.exec(h).slice(1).map(function(a){return parseInt(a+a,16)/255}),g[3]=1):l.HEX6.test(h)?(g=l.HEX6.exec(h).slice(1).map(function(a){return parseInt(a,16)/255}),g[3]=1):l.RGB.test(h)?(g=l.RGB.exec(h).slice(1).map(function(a){return a/255}),g[3]=1):l.RGB_PERCENT.test(h)?(g=l.RGB_PERCENT.exec(h).slice(1).map(function(a){return parseFloat(a)/100}),g[3]=1):l.RGBA.test(h)?g=l.RGBA.exec(h).slice(1).map(function(a,b){return 3===b?parseFloat(a):a/255}):l.RGBA_PERCENT.test(h)&&(g=l.RGBA_PERCENT.exec(h).slice(1).map(function(a,b){return 3===b?parseFloat(a):parseFloat(a)/100})),g.length){if(b===f.RGB)return g;if(b===f.HSL)return e.rgbaToHSLA(g);if(b===f.HSB)return e.rgbaToHSBA(g)}if(l.HSL.test(h)?(g=l.HSL.exec(h).slice(1).map(function(a,b){return 0===b?parseInt(a,10)/360:parseInt(a,10)/100}),g[3]=1):l.HSLA.test(h)&&(g=l.HSLA.exec(h).slice(1).map(function(a,b){return 0===b?parseInt(a,10)/360:3===b?parseFloat(a):parseInt(a,10)/100})),g.length){if(b===f.RGB)return e.hslaToRGBA(g);if(b===f.HSL)return g;if(b===f.HSB)return e.hslaToHSBA(g)}if(l.HSB.test(h)?(g=l.HSB.exec(h).slice(1).map(function(a,b){return 0===b?parseInt(a,10)/360:parseInt(a,10)/100}),g[3]=1):l.HSBA.test(h)&&(g=l.HSBA.exec(h).slice(1).map(function(a,b){return 0===b?parseInt(a,10)/360:3===b?parseFloat(a):parseInt(a,10)/100})),g.length){if(b===f.RGB)return e.hsbaToRGBA(g);if(b===f.HSB)return g;if(b===f.HSL)return e.hsbaToHSLA(g)}g=[1,1,1,1]}else{if(1!==a&&2!==a||"number"!=typeof arguments[0])throw new Error(arguments+"is not a valid color representation.");b===f.RGB?(g[0]=arguments[0]/c[0],g[1]=arguments[0]/c[1],g[2]=arguments[0]/c[2],g[3]="number"==typeof arguments[1]?arguments[1]/c[3]:1):(g[0]=arguments[0],g[1]=arguments[0],g[2]=arguments[0]/c[2],g[3]="number"==typeof arguments[1]?arguments[1]/c[3]:1)}return g},b.exports=d.Color},{"../core/constants":48,"../core/core":49,"./color_utils":41}],44:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants");a("./p5.Color"),d.prototype._doStroke=!0,d.prototype._doFill=!0,d.prototype._strokeSet=!1,d.prototype._fillSet=!1,d.prototype._colorMode=e.RGB,d.prototype._colorMaxes={rgb:[255,255,255,255],hsb:[360,100,100,1],hsl:[360,100,100,1]},d.prototype.background=function(){return arguments[0]instanceof d.Image?this.image(arguments[0],0,0,this.width,this.height):this._graphics.background.apply(this._graphics,arguments),this},d.prototype.clear=function(){return this._graphics.clear(),this},d.prototype.colorMode=function(){if(arguments[0]===e.RGB||arguments[0]===e.HSB||arguments[0]===e.HSL){this._colorMode=arguments[0];var a=this._colorMaxes[this._colorMode];2===arguments.length?(a[0]=arguments[1],a[1]=arguments[1],a[2]=arguments[1],a[3]=arguments[1]):4===arguments.length&&(a[0]=arguments[1],a[1]=arguments[2],a[2]=arguments[3]),5===arguments.length&&(a[0]=arguments[1],a[1]=arguments[2],a[2]=arguments[3],a[3]=arguments[4])}return this},d.prototype.fill=function(){return this._setProperty("_fillSet",!0),this._setProperty("_doFill",!0),this._graphics.fill.apply(this._graphics,arguments),this},d.prototype.noFill=function(){return this._setProperty("_doFill",!1),this},d.prototype.noStroke=function(){return this._setProperty("_doStroke",!1),this},d.prototype.stroke=function(){return this._setProperty("_strokeSet",!0),this._setProperty("_doStroke",!0),this._graphics.stroke.apply(this._graphics,arguments),this},b.exports=d},{"../core/constants":48,"../core/core":49,"./p5.Color":43}],45:[function(a,b,c){"use strict";var d=a("./core"),e=a("./constants");a("./error_helpers"),d.prototype.arc=function(a,b,c,d,f,g,h){if(this._validateParameters("arc",arguments,[["Number","Number","Number","Number","Number","Number"],["Number","Number","Number","Number","Number","Number","String"]]),!this._doStroke&&!this._doFill)return this;for(this._angleMode===e.DEGREES&&(f=this.radians(f),g=this.radians(g));0>f;)f+=e.TWO_PI;for(;0>g;)g+=e.TWO_PI;return f%=e.TWO_PI,g%=e.TWO_PI,f=f<=e.HALF_PI?Math.atan(c/d*Math.tan(f)):f>e.HALF_PI&&f<=3*e.HALF_PI?Math.atan(c/d*Math.tan(f))+e.PI:Math.atan(c/d*Math.tan(f))+e.TWO_PI,g=g<=e.HALF_PI?Math.atan(c/d*Math.tan(g)):g>e.HALF_PI&&g<=3*e.HALF_PI?Math.atan(c/d*Math.tan(g))+e.PI:Math.atan(c/d*Math.tan(g))+e.TWO_PI,f>g&&(g+=e.TWO_PI),c=Math.abs(c),d=Math.abs(d),this._graphics.arc(a,b,c,d,f,g,h),this},d.prototype.ellipse=function(a,b,c,d){return this._validateParameters("ellipse",arguments,["Number","Number","Number","Number"]),this._doStroke||this._doFill?(c=Math.abs(c),d=Math.abs(d),this._graphics.ellipse(a,b,c,d),this):this},d.prototype.line=function(){return this._validateParameters("line",arguments,[["Number","Number","Number","Number"],["Number","Number","Number","Number","Number","Number"]]),this._doStroke?(this._graphics.isP3D?this._graphics.line(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]):this._graphics.line(arguments[0],arguments[1],arguments[2],arguments[3]),this):this},d.prototype.point=function(){return this._validateParameters("point",arguments,[["Number","Number"],["Number","Number","Number"]]),this._doStroke?(this._graphics.isP3D?this._graphics.point(arguments[0],arguments[1],arguments[2]):this._graphics.point(arguments[0],arguments[1]),this):this},d.prototype.quad=function(){return this._validateParameters("quad",arguments,[["Number","Number","Number","Number","Number","Number","Number","Number"],["Number","Number","Number","Number","Number","Number","Number","Number","Number","Number","Number","Number"]]),this._doStroke||this._doFill?(this._graphics.isP3D?this._graphics.quad(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8],arguments[9],arguments[10],arguments[11]):this._graphics.quad(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7]),this):this},d.prototype.rect=function(a,b,c,d,e,f,g,h){return this._validateParameters("rect",arguments,[["Number","Number","Number","Number"],["Number","Number","Number","Number","Number"],["Number","Number","Number","Number","Number","Number","Number","Number"]]),this._doStroke||this._doFill?(this._graphics.rect(a,b,c,d,e,f,g,h),this):void 0},d.prototype.triangle=function(){return this._validateParameters("triangle",arguments,[["Number","Number","Number","Number","Number","Number"],["Number","Number","Number","Number","Number","Number","Number","Number","Number"]]),this._doStroke||this._doFill?(this._graphics.isP3D?this._graphics.triangle(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8]):this._graphics.triangle(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]),this):this},b.exports=d},{"./constants":48,"./core":49,"./error_helpers":52}],46:[function(a,b,c){"use strict";var d=a("./core"),e=a("./constants");d.prototype._rectMode=e.CORNER,d.prototype._ellipseMode=e.CENTER,d.prototype.ellipseMode=function(a){return(a===e.CORNER||a===e.CORNERS||a===e.RADIUS||a===e.CENTER)&&(this._ellipseMode=a),this},d.prototype.noSmooth=function(){return this._graphics.noSmooth(),this},d.prototype.rectMode=function(a){return(a===e.CORNER||a===e.CORNERS||a===e.RADIUS||a===e.CENTER)&&(this._rectMode=a),this},d.prototype.smooth=function(){return this._graphics.smooth(),this},d.prototype.strokeCap=function(a){return(a===e.ROUND||a===e.SQUARE||a===e.PROJECT)&&this._graphics.strokeCap(a),this},d.prototype.strokeJoin=function(a){return(a===e.ROUND||a===e.BEVEL||a===e.MITER)&&this._graphics.strokeJoin(a),this},d.prototype.strokeWeight=function(a){return this._graphics.strokeWeight(a),this},b.exports=d},{"./constants":48,"./core":49}],47:[function(a,b,c){var d=a("./constants");b.exports={modeAdjust:function(a,b,c,e,f){return f===d.CORNER?{x:a,y:b,w:c,h:e}:f===d.CORNERS?{x:a,y:b,w:c-a,h:e-b}:f===d.RADIUS?{x:a-c,y:b-e,w:2*c,h:2*e}:f===d.CENTER?{x:a-.5*c,y:b-.5*e,w:c,h:e}:void 0},arcModeAdjust:function(a,b,c,e,f){return f===d.CORNER?{x:a+.5*c,y:b+.5*e,w:c,h:e}:f===d.CORNERS?{x:a,y:b,w:c+a,h:e+b}:f===d.RADIUS?{x:a,y:b,w:2*c,h:2*e}:f===d.CENTER?{x:a,y:b,w:c,h:e}:void 0}}},{"./constants":48}],48:[function(a,b,c){var d=Math.PI;b.exports={P2D:"p2d",WEBGL:"webgl",ARROW:"default",CROSS:"crosshair",HAND:"pointer",MOVE:"move",TEXT:"text",WAIT:"wait",HALF_PI:d/2,PI:d,QUARTER_PI:d/4,TAU:2*d,TWO_PI:2*d,DEGREES:"degrees",RADIANS:"radians",CORNER:"corner",CORNERS:"corners",RADIUS:"radius",RIGHT:"right",LEFT:"left",CENTER:"center",TOP:"top",BOTTOM:"bottom",BASELINE:"alphabetic",POINTS:"points",LINES:"lines",TRIANGLES:"triangles",TRIANGLE_FAN:"triangles_fan",TRIANGLE_STRIP:"triangles_strip",QUADS:"quads",QUAD_STRIP:"quad_strip",CLOSE:"close",OPEN:"open",CHORD:"chord",PIE:"pie",PROJECT:"square",SQUARE:"butt",ROUND:"round",BEVEL:"bevel",MITER:"miter",RGB:"rgb",HSB:"hsb",HSL:"hsl",AUTO:"auto",ALT:18,BACKSPACE:8,CONTROL:17,DELETE:46,DOWN_ARROW:40,ENTER:13,ESCAPE:27,LEFT_ARROW:37,OPTION:18,RETURN:13,RIGHT_ARROW:39,SHIFT:16,TAB:9,UP_ARROW:38,BLEND:"normal",ADD:"lighter",DARKEST:"darken",LIGHTEST:"lighten",DIFFERENCE:"difference",EXCLUSION:"exclusion",MULTIPLY:"multiply",SCREEN:"screen",REPLACE:"source-over",OVERLAY:"overlay",HARD_LIGHT:"hard-light",SOFT_LIGHT:"soft-light",DODGE:"color-dodge",BURN:"color-burn",THRESHOLD:"threshold",GRAY:"gray",OPAQUE:"opaque",INVERT:"invert",POSTERIZE:"posterize",DILATE:"dilate",ERODE:"erode",BLUR:"blur",NORMAL:"normal",ITALIC:"italic",BOLD:"bold",_DEFAULT_TEXT_FILL:"#000000",_DEFAULT_LEADMULT:1.25,_CTX_MIDDLE:"middle",LINEAR:"linear",QUADRATIC:"quadratic",BEZIER:"bezier",CURVE:"curve",_DEFAULT_STROKE:"#000000",_DEFAULT_FILL:"#FFFFFF"}},{}],49:[function(a,b,c){"use strict";a("./shim");var d=a("./constants"),e=function(a,b,c){2===arguments.length&&"boolean"==typeof b&&(c=b,b=void 0),this._setupDone=!1,this.pixelDensity=window.devicePixelRatio||1,this._userNode=b,this._curElement=null,this._elements=[],this._requestAnimId=0,this._preloadCount=0,this._isGlobal=!1,this._loop=!0,this._styles=[],this._defaultCanvasSize={width:100,height:100},this._events={mousemove:null,mousedown:null,mouseup:null,click:null,mouseover:null,mouseout:null,keydown:null,keyup:null,keypress:null,touchstart:null,touchmove:null,touchend:null,resize:null,blur:null},window.DeviceOrientationEvent?this._events.deviceorientation=null:window.DeviceMotionEvent?this._events.devicemotion=null:this._events.MozOrientation=null,/Firefox/i.test(navigator.userAgent)?this._events.DOMMouseScroll=null:this._events.mousewheel=null,this._loadingScreenId="p5_loading",this._start=function(){this._userNode&&"string"==typeof this._userNode&&(this._userNode=document.getElementById(this._userNode)),this.createCanvas(this._defaultCanvasSize.width,this._defaultCanvasSize.height,"p2d",!0);var a=this.preload||window.preload;if(a){var b=document.getElementById(this._loadingScreenId);if(!b){b=document.createElement("div"),b.innerHTML="Loading...",b.style.position="absolute",b.id=this._loadingScreenId;var c=this._userNode||document.body;c.appendChild(b)}for(var d in this._preloadMethods){this._preloadMethods[d]=this._preloadMethods[d]||e;var f=this._preloadMethods[d];(f===e.prototype||f===e)&&(f=this._isGlobal?window:this),this._registeredPreloadMethods[d]=f[d],f[d]=this._wrapPreload(f,d)}a()}else this._setup(),this._runFrames(),this._draw()}.bind(this),this._decrementPreload=function(){var a=this._isGlobal?window:this;if(a._setProperty("_preloadCount",a._preloadCount-1),0===a._preloadCount){var b=document.getElementById(a._loadingScreenId);b&&b.parentNode.removeChild(b),a._setup(),a._runFrames(),a._draw()}},this._wrapPreload=function(a,b){return function(){this._incrementPreload();var c=Array.prototype.slice.call(arguments);return c.push(this._decrementPreload.bind(this)),this._registeredPreloadMethods[b].apply(a,c)}.bind(this)},this._incrementPreload=function(){var a=this._isGlobal?window:this;a._setProperty("_preloadCount",a._preloadCount+1)},this._setup=function(){var a=this._isGlobal?window:this;if("function"==typeof a.preload)for(var b in this._preloadMethods)a[b]=this._preloadMethods[b][b];"function"==typeof a.setup&&a.setup();for(var c=new RegExp(/(^|\s)p5_hidden(?!\S)/g),d=document.getElementsByClassName("p5_hidden"),e=0;e<d.length;e++){var f=d[e];f.style.visibility="",f.className=f.className.replace(c,"")}this._setupDone=!0}.bind(this),this._draw=function(){var a=window.performance.now(),b=a-this._lastFrameTime,c=1e3/this._targetFrameRate,d=5;(!this.loop||b>=c-d)&&(this._setProperty("frameCount",this.frameCount+1),this.redraw(),this._updatePAccelerations(),this._updatePMouseCoords(),this._updatePTouchCoords(),this._frameRate=1e3/(a-this._lastFrameTime),this._lastFrameTime=a),this._loop&&(this._requestAnimId=window.requestAnimationFrame(this._draw))}.bind(this),this._runFrames=function(){this._updateInterval&&clearInterval(this._updateInterval)}.bind(this),this._setProperty=function(a,b){this[a]=b,this._isGlobal&&(window[a]=b)}.bind(this),this.remove=function(){if(this._curElement){this._loop=!1,this._requestAnimId&&window.cancelAnimationFrame(this._requestAnimId);for(var a in this._events)window.removeEventListener(a,this._events[a]);for(var b=0;b<this._elements.length;b++){var c=this._elements[b];c.elt.parentNode&&c.elt.parentNode.removeChild(c.elt);for(var d in c._events)c.elt.removeEventListener(d,c._events[d])}var f=this;if(this._registeredMethods.remove.forEach(function(a){"undefined"!=typeof a&&a.call(f)}),this._isGlobal){for(var g in e.prototype)try{delete window[g]}catch(h){window[g]=void 0}for(var i in this)if(this.hasOwnProperty(i))try{delete window[i]}catch(h){window[i]=void 0}}}}.bind(this);for(var f in d)e.prototype[f]=d[f];if(a)a(this);else{this._isGlobal=!0;for(var g in e.prototype)if("function"==typeof e.prototype[g]){var h=g.substring(2);this._events.hasOwnProperty(h)||(window[g]=e.prototype[g].bind(this))}else window[g]=e.prototype[g];for(var i in this)this.hasOwnProperty(i)&&(window[i]=this[i])}for(var j in this._events){var k=this["_on"+j];if(k){var l=k.bind(this);window.addEventListener(j,l),this._events[j]=l}}var m=this;window.addEventListener("focus",function(){m._setProperty("focused",!0)}),window.addEventListener("blur",function(){m._setProperty("focused",!1)}),c?this._start():"complete"===document.readyState?this._start():window.addEventListener("load",this._start.bind(this),!1)};e.prototype._preloadMethods={loadJSON:e.prototype,loadImage:e.prototype,loadStrings:e.prototype,loadXML:e.prototype,loadShape:e.prototype,loadTable:e.prototype,loadFont:e.prototype},e.prototype._registeredMethods={pre:[],post:[],remove:[]},e.prototype._registeredPreloadMethods={},e.prototype.registerPreloadMethod=function(a,b){e.prototype._preloadMethods.hasOwnProperty(a)||(e.prototype._preloadMethods[a]=b)},e.prototype.registerMethod=function(a,b){e.prototype._registeredMethods.hasOwnProperty(a)||(e.prototype._registeredMethods[a]=[]),e.prototype._registeredMethods[a].push(b)},b.exports=e},{"./constants":48,"./shim":58}],50:[function(a,b,c){"use strict";var d=a("./core");a("./error_helpers");var e=20,f=20;d.prototype._curveTightness=0,d.prototype.bezier=function(a,b,c,d,e,f,g,h){return this._validateParameters("bezier",arguments,["Number","Number","Number","Number","Number","Number","Number","Number"]),this._doStroke?(this._graphics.bezier(a,b,c,d,e,f,g,h),this):this},d.prototype.bezierDetail=function(a){return e=a,this},d.prototype.bezierPoint=function(a,b,c,d,e){var f=1-e;return Math.pow(f,3)*a+3*Math.pow(f,2)*e*b+3*f*Math.pow(e,2)*c+Math.pow(e,3)*d},d.prototype.bezierTangent=function(a,b,c,d,e){var f=1-e;return 3*d*Math.pow(e,2)-3*c*Math.pow(e,2)+6*c*f*e-6*b*f*e+3*b*Math.pow(f,2)-3*a*Math.pow(f,2)},d.prototype.curve=function(a,b,c,d,e,f,g,h){return this._validateParameters("curve",arguments,["Number","Number","Number","Number","Number","Number","Number","Number"]),this._doStroke?(this._graphics.curve(a,b,c,d,e,f,g,h),this):void 0},d.prototype.curveDetail=function(a){return f=a,this},d.prototype.curveTightness=function(a){this._setProperty("_curveTightness",a)},d.prototype.curvePoint=function(a,b,c,d,e){var f=e*e*e,g=e*e,h=-.5*f+g-.5*e,i=1.5*f-2.5*g+1,j=-1.5*f+2*g+.5*e,k=.5*f-.5*g;return a*h+b*i+c*j+d*k},d.prototype.curveTangent=function(a,b,c,d,e){var f=e*e,g=-3*f/2+2*e-.5,h=9*f/2-5*e,i=-9*f/2+4*e+.5,j=3*f/2-e;return a*g+b*h+c*i+d*j},b.exports=d},{"./core":49,"./error_helpers":52}],51:[function(a,b,c){"use strict";function d(a){var b=document.fullscreenEnabled||document.webkitFullscreenEnabled||document.mozFullScreenEnabled||document.msFullscreenEnabled;if(!b)throw new Error("Fullscreen not enabled in this browser.");a.requestFullscreen?a.requestFullscreen():a.mozRequestFullScreen?a.mozRequestFullScreen():a.webkitRequestFullscreen?a.webkitRequestFullscreen():a.msRequestFullscreen&&a.msRequestFullscreen()}function e(){document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():document.msExitFullscreen&&document.msExitFullscreen()}var f=a("./core"),g=a("./constants"),h=[g.ARROW,g.CROSS,g.HAND,g.MOVE,g.TEXT,g.WAIT];f.prototype._frameRate=0,f.prototype._lastFrameTime=window.performance.now(),f.prototype._targetFrameRate=60,window.console&&console.log?f.prototype.print=function(a){try{var b=JSON.parse(JSON.stringify(a));console.log(b)}catch(c){console.log(a)}}:f.prototype.print=function(){},f.prototype.println=f.prototype.print,f.prototype.frameCount=0,f.prototype.focused=document.hasFocus(),f.prototype.cursor=function(a,b,c){var d="auto",e=this._curElement.elt;if(h.indexOf(a)>-1)d=a;else if("string"==typeof a){var f="";b&&c&&"number"==typeof b&&"number"==typeof c&&(f=b+" "+c),d="http://"!==a.substring(0,6)?"url("+a+") "+f+", auto":/\.(cur|jpg|jpeg|gif|png|CUR|JPG|JPEG|GIF|PNG)$/.test(a)?"url("+a+") "+f+", auto":a}e.style.cursor=d},f.prototype.frameRate=function(a){return"undefined"==typeof a?this._frameRate:(this._setProperty("_targetFrameRate",a),this._runFrames(),this)},f.prototype.getFrameRate=function(){return this.frameRate()},f.prototype.setFrameRate=function(a){return this.frameRate(a)},f.prototype.noCursor=function(){this._curElement.elt.style.cursor="none"},f.prototype.displayWidth=screen.width,f.prototype.displayHeight=screen.height,f.prototype.windowWidth=window.innerWidth,f.prototype.windowHeight=window.innerHeight,f.prototype._onresize=function(a){this._setProperty("windowWidth",window.innerWidth),this._setProperty("windowHeight",window.innerHeight);var b,c=this._isGlobal?window:this;"function"==typeof c.windowResized&&(b=c.windowResized(a),void 0===b||b||a.preventDefault())},f.prototype.width=0,f.prototype.height=0,f.prototype.fullScreen=function(a){return"undefined"==typeof a?document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement:void(a?d(document.documentElement):e())},f.prototype.devicePixelScaling=function(a){a?"number"==typeof a?this.pixelDensity=a:this.pixelDensity=window.devicePixelRatio||1:this.pixelDensity=1,this.resizeCanvas(this.width,this.height,!0)},f.prototype.getURL=function(){return location.href},f.prototype.getURLPath=function(){return location.pathname.split("/").filter(function(a){return""!==a})},f.prototype.getURLParams=function(){for(var a,b=/[?&]([^&=]+)(?:[&=])([^&=]+)/gim,c={};null!=(a=b.exec(location.search));)a.index===b.lastIndex&&b.lastIndex++,c[a[1]]=a[2];return c},b.exports=f},{"./constants":48,"./core":49}],52:[function(a,b,c){"use strict";function d(a,b,c){if(a.match(/^p5\./)){var d=a.split(".");return c instanceof g[d[1]]}return"Boolean"===a||a.toLowerCase()===b||p.indexOf(a)>-1&&o(c)}function e(a,b,c){h&&(f(),h=!1),"undefined"===m(c)?c="#B40033":"number"===m(c)&&(c=u[c]),"load"===b.substring(0,4)?console.log("%c> p5.js says: "+a+"%c[https://github.com/processing/p5.js/wiki/Local-server]","background-color:"+c+";color:#FFF;","background-color:transparent;color:"+c+";","background-color:"+c+";color:#FFF;","background-color:transparent;color:"+c+";"):console.log("%c> p5.js says: "+a+"%c [http://p5js.org/reference/#p5/"+b+"]","background-color:"+c+";color:#FFF;","background-color:transparent;color:"+c+";")}function f(){var a="transparent",b="#ED225D",c="#ED225D",d="white";console.log("%c _ \n /\\| |/\\ \n \\ ` ' / \n / , . \\ \n \\/|_|\\/ \n\n%c> p5.js says: Welcome! This is your friendly debugger. To turn me off switch to using “p5.min.js”.","background-color:"+a+";color:"+b+";","background-color:"+c+";color:"+d+";")}for(var g=a("./core"),h=!0,i={},j=i.toString,k=["Boolean","Number","String","Function","Array","Date","RegExp","Object","Error"],l=0;l<k.length;l++)i["[object "+k[l]+"]"]=k[l].toLowerCase(); | |
var m=function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},n=Array.isArray||function(a){return"array"===m(a)},o=function(a){return!n(a)&&a-parseFloat(a)+1>=0},p=["Number","Integer","Number/Constant"],q=0,r=1,s=2,t=3,u=["#2D7BB6","#EE9900","#4DB200","#C83C00"];g.prototype._validateParameters=function(a,b,c){n(c[0])||(c=[c]);for(var f,g=Math.abs(b.length-c[0].length),h=0,i=1,j=c.length;j>i;i++){var k=Math.abs(b.length-c[i].length);g>=k&&(h=i,g=k)}var l="X";g>0&&(f="You wrote "+a+"(",b.length>0&&(f+=l+Array(b.length).join(","+l)),f+="). "+a+" was expecting "+c[h].length+" parameters. Try "+a+"(",c[h].length>0&&(f+=l+Array(c[h].length).join(","+l)),f+=").",c.length>1&&(f+=" "+a+" takes different numbers of parameters depending on what you want to do. Click this link to learn more: "),e(f,a,q));for(var o=0;o<c.length;o++)for(var p=0;p<c[o].length&&p<b.length;p++){var t=c[o][p],u=m(b[p]);"undefined"===u||null===u?e("It looks like "+a+" received an empty variable in spot #"+(p+1)+". If not intentional, this is often a problem with scope: [link to scope].",a,r):"*"===t||d(t,u,b[p])||(f=a+" was expecting a "+t.toLowerCase()+" for parameter #"+(p+1)+", received ",f+="string"===u?'"'+b[p]+'"':b[p],f+=" instead.",c.length>1&&(f+=" "+a+" takes different numbers of parameters depending on what you want to do. Click this link to learn more:"),e(f,a,s))}};var v={0:{fileType:"image",method:"loadImage",message:" hosting the image online,"},1:{fileType:"XML file",method:"loadXML"},2:{fileType:"table file",method:"loadTable"},3:{fileType:"text file",method:"loadStrings"}};g._friendlyFileLoadError=function(a,b){var c=v[a],d="It looks like there was a problem loading your "+c.fileType+". Try checking if the file path%c ["+b+"] %cis correct,"+(c.message||"")+" or running a local server.";e(d,c.method,t)},b.exports=g},{"./core":49}],53:[function(a,b,c){function d(a,b,c){var d=b.bind(c);c.elt.addEventListener(a,d,!1),c._events[a]=d}var e=a("./core");e.Element=function(a,b){this.elt=a,this._pInst=b,this._events={},this.width=this.elt.offsetWidth,this.height=this.elt.offsetHeight},e.Element.prototype.parent=function(a){return"string"==typeof a?a=document.getElementById(a):a instanceof e.Element&&(a=a.elt),a.appendChild(this.elt),this},e.Element.prototype.id=function(a){return this.elt.id=a,this},e.Element.prototype["class"]=function(a){return this.elt.className+=" "+a,this},e.Element.prototype.mousePressed=function(a){return d("mousedown",a,this),d("touchstart",a,this),this},e.Element.prototype.mouseWheel=function(a){return d("mousewheel",a,this),this},e.Element.prototype.mouseReleased=function(a){return d("mouseup",a,this),d("touchend",a,this),this},e.Element.prototype.mouseClicked=function(a){return d("click",a,this),this},e.Element.prototype.mouseMoved=function(a){return d("mousemove",a,this),d("touchmove",a,this),this},e.Element.prototype.mouseOver=function(a){return d("mouseover",a,this),this},e.Element.prototype.changed=function(a){return d("change",a,this),this},e.Element.prototype.mouseOut=function(a){return d("mouseout",a,this),this},e.Element.prototype.touchStarted=function(a){return d("touchstart",a,this),d("mousedown",a,this),this},e.Element.prototype.touchMoved=function(a){return d("touchmove",a,this),d("mousemove",a,this),this},e.Element.prototype.touchEnded=function(a){return d("touchend",a,this),d("mouseup",a,this),this},e.Element.prototype.dragOver=function(a){return d("dragover",a,this),this},e.Element.prototype.dragLeave=function(a){return d("dragleave",a,this),this},e.Element.prototype.drop=function(a,b){function c(b){var c=new e.File(b);return function(b){c.data=b.target.result,a(c)}}return window.File&&window.FileReader&&window.FileList&&window.Blob?(d("dragover",function(a){a.stopPropagation(),a.preventDefault()},this),d("dragleave",function(a){a.stopPropagation(),a.preventDefault()},this),arguments.length>1&&d("drop",b,this),d("drop",function(a){a.stopPropagation(),a.preventDefault();for(var b=a.dataTransfer.files,d=0;d<b.length;d++){var e=b[d],f=new FileReader;f.onload=c(e),"text"===e.type?f.readAsText(e):f.readAsDataURL(e)}},this)):console.log("The File APIs are not fully supported in this browser."),this},e.Element.prototype._setProperty=function(a,b){this[a]=b},b.exports=e.Element},{"./core":49}],54:[function(a,b,c){var d=a("./core"),e=a("./constants");d.Graphics=function(a,b,c,f){var g=c||e.P2D,h=document.createElement("canvas"),i=this._userNode||document.body;i.appendChild(h),d.Element.call(this,h,f,!1),this._styles=[],this.width=a,this.height=b,this.pixelDensity=f.pixelDensity,g===e.WEBGL?this._graphics=new d.Renderer3D(h,f,!1):this._graphics=new d.Renderer2D(h,f,!1),this._graphics.resize(a,b),this._graphics._applyDefaults(),f._elements.push(this);for(var j in d.prototype)this[j]||("function"==typeof d.prototype[j]?this[j]=d.prototype[j].bind(this):this[j]=d.prototype[j]);return this},d.Graphics.prototype=Object.create(d.Element.prototype),b.exports=d.Graphics},{"./constants":48,"./core":49}],55:[function(a,b,c){var d=a("./core");d.Renderer=function(a,b,c){d.Element.call(this,a,b),this.canvas=a,this._pInst=b,c?(this._isMainCanvas=!0,this._pInst._setProperty("_curElement",this),this._pInst._setProperty("canvas",this.canvas),this._pInst._setProperty("width",this.width),this._pInst._setProperty("height",this.height)):(this.canvas.style.display="none",this._styles=[])},d.Renderer.prototype=Object.create(d.Element.prototype),d.Renderer.prototype.resize=function(a,b){this.width=a,this.height=b,this.elt.width=a*this._pInst.pixelDensity,this.elt.height=b*this._pInst.pixelDensity,this.elt.style.width=a+"px",this.elt.style.height=b+"px",this._isMainCanvas&&(this._pInst._setProperty("width",this.width),this._pInst._setProperty("height",this.height))},b.exports=d.Renderer},{"./core":49}],56:[function(a,b,c){var d=a("./core"),e=a("./canvas"),f=a("./constants"),g=a("../image/filters");a("./p5.Renderer");var h="rgba(0,0,0,0)";d.Renderer2D=function(a,b,c){return d.Renderer.call(this,a,b,c),this.drawingContext=this.canvas.getContext("2d"),this._pInst._setProperty("drawingContext",this.drawingContext),this},d.Renderer2D.prototype=Object.create(d.Renderer.prototype),d.Renderer2D.prototype._applyDefaults=function(){this.drawingContext.fillStyle=f._DEFAULT_FILL,this.drawingContext.strokeStyle=f._DEFAULT_STROKE,this.drawingContext.lineCap=f.ROUND,this.drawingContext.font="normal 12px sans-serif"},d.Renderer2D.prototype.resize=function(a,b){d.Renderer.prototype.resize.call(this,a,b),this.drawingContext.scale(this._pInst.pixelDensity,this._pInst.pixelDensity)},d.Renderer2D.prototype.background=function(){if(this.drawingContext.save(),this.drawingContext.setTransform(1,0,0,1,0,0),this.drawingContext.scale(this._pInst.pixelDensity,this._pInst.pixelDensity),arguments[0]instanceof d.Image)this._pInst.image(arguments[0],0,0,this.width,this.height);else{var a=this.drawingContext.fillStyle,b=this._pInst.color.apply(this._pInst,arguments),c=b.toString();this.drawingContext.fillStyle=c,this.drawingContext.fillRect(0,0,this.width,this.height),this.drawingContext.fillStyle=a}this.drawingContext.restore()},d.Renderer2D.prototype.clear=function(){this.drawingContext.clearRect(0,0,this.width,this.height)},d.Renderer2D.prototype.fill=function(){var a=this.drawingContext,b=this._pInst.color.apply(this._pInst,arguments);a.fillStyle=b.toString()},d.Renderer2D.prototype.stroke=function(){var a=this.drawingContext,b=this._pInst.color.apply(this._pInst,arguments);a.strokeStyle=b.toString()},d.Renderer2D.prototype.image=function(a,b,c,d,e){var f=a.canvas||a.elt;try{this._pInst._tint&&a.canvas?this.drawingContext.drawImage(this._getTintedImageCanvas(a),b,c,d,e):this.drawingContext.drawImage(f,b,c,d,e)}catch(g){if("NS_ERROR_NOT_AVAILABLE"!==g.name)throw g}},d.Renderer2D.prototype._getTintedImageCanvas=function(a){if(!a.canvas)return a;var b=g._toPixels(a.canvas),c=document.createElement("canvas");c.width=a.canvas.width,c.height=a.canvas.height;for(var d=c.getContext("2d"),e=d.createImageData(a.canvas.width,a.canvas.height),f=e.data,h=0;h<b.length;h+=4){var i=b[h],j=b[h+1],k=b[h+2],l=b[h+3];f[h]=i*this._pInst._tint[0]/255,f[h+1]=j*this._pInst._tint[1]/255,f[h+2]=k*this._pInst._tint[2]/255,f[h+3]=l*this._pInst._tint[3]/255}return d.putImageData(e,0,0),c},d.Renderer2D.prototype.blendMode=function(a){this.drawingContext.globalCompositeOperation=a},d.Renderer2D.prototype.blend=function(){var a=this.drawingContext.globalCompositeOperation,b=arguments[arguments.length-1],c=Array.prototype.slice.call(arguments,0,arguments.length-1);this.drawingContext.globalCompositeOperation=b,this._pInst.copy.apply(this._pInst,c),this.drawingContext.globalCompositeOperation=a},d.Renderer2D.prototype.copy=function(){var a,b,c,e,f,g,h,i,j;if(9===arguments.length)a=arguments[0],b=arguments[1],c=arguments[2],e=arguments[3],f=arguments[4],g=arguments[5],h=arguments[6],i=arguments[7],j=arguments[8];else{if(8!==arguments.length)throw new Error("Signature not supported");a=this._pInst,b=arguments[0],c=arguments[1],e=arguments[2],f=arguments[3],g=arguments[4],h=arguments[5],i=arguments[6],j=arguments[7]}d.Renderer2D._copyHelper(a,b,c,e,f,g,h,i,j)},d.Renderer2D._copyHelper=function(a,b,c,d,e,f,g,h,i){var j=a.canvas.width/a.width;this.drawingContext.drawImage(a.canvas,j*b,j*c,j*d,j*e,f,g,h,i)},d.Renderer2D.prototype.get=function(a,b,c,e){if(void 0===a&&void 0===b&&void 0===c&&void 0===e?(a=0,b=0,c=this.width,e=this.height):void 0===c&&void 0===e&&(c=1,e=1),a>this.width||b>this.height||0>a||0>b)return[0,0,0,255];var f=this.pixelDensity||this._pInst.pixelDensity;if(1===c&&1===e)return[this.pixels[4*f*(b*this.width+a)],this.pixels[f*(4*(b*this.width+a)+1)],this.pixels[f*(4*(b*this.width+a)+2)],this.pixels[f*(4*(b*this.width+a)+3)]];var g=a*f,h=b*f,i=Math.min(c,this.width),j=Math.min(e,this.height),k=i*f,l=j*f,m=new d.Image(i,j);return m.canvas.getContext("2d").drawImage(this.canvas,g,h,k,l,0,0,i,j),m},d.Renderer2D.prototype.loadPixels=function(){var a=this.pixelDensity||this._pInst.pixelDensity,b=this.width*a,c=this.height*a,d=this.drawingContext.getImageData(0,0,b,c);this._pInst?(this._pInst._setProperty("imageData",d),this._pInst._setProperty("pixels",d.data)):(this._setProperty("imageData",d),this._setProperty("pixels",d.data))},d.Renderer2D.prototype.set=function(a,b,c){if(c instanceof d.Image)this.drawingContext.save(),this.drawingContext.setTransform(1,0,0,1,0,0),this.drawingContext.scale(this._pInst.pixelDensity,this._pInst.pixelDensity),this.drawingContext.drawImage(c.canvas,a,b),this.loadPixels.call(this._pInst),this.drawingContext.restore();else{var e=this._pInst||this,f=0,g=0,h=0,i=0,j=4*(b*e.pixelDensity*this.width*e.pixelDensity+a*e.pixelDensity);if(e.imageData||e.loadPixels.call(e),"number"==typeof c)j<e.pixels.length&&(f=c,g=c,h=c,i=255);else if(c instanceof Array){if(c.length<4)throw new Error("pixel array must be of the form [R, G, B, A]");j<e.pixels.length&&(f=c[0],g=c[1],h=c[2],i=c[3])}else c instanceof d.Color&&j<e.pixels.length&&(f=c.rgba[0],g=c.rgba[1],h=c.rgba[2],i=c.rgba[3]);for(var k=0;k<e.pixelDensity;k++)for(var l=0;l<e.pixelDensity;l++)j=4*((b*e.pixelDensity+l)*this.width*e.pixelDensity+(a*e.pixelDensity+k)),e.pixels[j]=f,e.pixels[j+1]=g,e.pixels[j+2]=h,e.pixels[j+3]=i}},d.Renderer2D.prototype.updatePixels=function(a,b,c,d){var e=this.pixelDensity||this._pInst.pixelDensity;void 0===a&&void 0===b&&void 0===c&&void 0===d&&(a=0,b=0,c=this.width,d=this.height),c*=e,d*=e,this._pInst?this.drawingContext.putImageData(this._pInst.imageData,a,b,0,0,c,d):this.drawingContext.putImageData(this.imageData,a,b,0,0,c,d)},d.Renderer2D.prototype._acuteArcToBezier=function(a,b){var c=b/2,d=Math.cos(c),e=Math.sin(c),f=1/Math.tan(c),g=a+c,h=Math.cos(g),i=Math.sin(g),j=(4-d)/3,k=e+(d-j)*f;return{ax:Math.cos(a),ay:Math.sin(a),bx:j*h+k*i,by:j*i-k*h,cx:j*h-k*i,cy:j*i+k*h,dx:Math.cos(a+b),dy:Math.sin(a+b)}},d.Renderer2D.prototype.arc=function(a,b,c,d,g,h,i){for(var j=this.drawingContext,k=e.arcModeAdjust(a,b,c,d,this._pInst._ellipseMode),l=k.w/2,m=k.h/2,n=1e-5,o=0,p=[];h-g>n;)o=Math.min(h-g,f.HALF_PI),p.push(this._acuteArcToBezier(g,o)),g+=o;return this._pInst._doFill&&(j.beginPath(),p.forEach(function(a,b){0===b&&j.moveTo(k.x+a.ax*l,k.y+a.ay*m),j.bezierCurveTo(k.x+a.bx*l,k.y+a.by*m,k.x+a.cx*l,k.y+a.cy*m,k.x+a.dx*l,k.y+a.dy*m)}),(i===f.PIE||null==i)&&j.lineTo(k.x,k.y),j.closePath(),j.fill()),this._pInst._doStroke&&(j.beginPath(),p.forEach(function(a,b){0===b&&j.moveTo(k.x+a.ax*l,k.y+a.ay*m),j.bezierCurveTo(k.x+a.bx*l,k.y+a.by*m,k.x+a.cx*l,k.y+a.cy*m,k.x+a.dx*l,k.y+a.dy*m)}),i===f.PIE?(j.lineTo(k.x,k.y),j.closePath()):i===f.CHORD&&j.closePath(),j.stroke()),this},d.Renderer2D.prototype.ellipse=function(a,b,c,d){var f=this.drawingContext,g=this._pInst._doFill,i=this._pInst._doStroke;if(g&&!i){if(f.fillStyle===h)return this}else if(!g&&i&&f.strokeStyle===h)return this;var j=e.modeAdjust(a,b,c,d,this._pInst._ellipseMode),k=.5522847498,l=j.w/2*k,m=j.h/2*k,n=j.x+j.w,o=j.y+j.h,p=j.x+j.w/2,q=j.y+j.h/2;f.beginPath(),f.moveTo(j.x,q),f.bezierCurveTo(j.x,q-m,p-l,j.y,p,j.y),f.bezierCurveTo(p+l,j.y,n,q-m,n,q),f.bezierCurveTo(n,q+m,p+l,o,p,o),f.bezierCurveTo(p-l,o,j.x,q+m,j.x,q),f.closePath(),g&&f.fill(),i&&f.stroke()},d.Renderer2D.prototype.line=function(a,b,c,d){var e=this.drawingContext;return this._pInst._doStroke?e.strokeStyle===h?this:(e.lineWidth%2===1&&e.translate(.5,.5),e.beginPath(),e.moveTo(a,b),e.lineTo(c,d),e.stroke(),e.lineWidth%2===1&&e.translate(-.5,-.5),this):this},d.Renderer2D.prototype.point=function(a,b){var c=this.drawingContext,d=c.strokeStyle,e=c.fillStyle;return this._pInst._doStroke?c.strokeStyle===h?this:(a=Math.round(a),b=Math.round(b),c.fillStyle=d,c.lineWidth>1?(c.beginPath(),c.arc(a,b,c.lineWidth/2,0,f.TWO_PI,!1),c.fill()):c.fillRect(a,b,1,1),void(c.fillStyle=e)):this},d.Renderer2D.prototype.quad=function(a,b,c,d,e,f,g,i){var j=this.drawingContext,k=this._pInst._doFill,l=this._pInst._doStroke;if(k&&!l){if(j.fillStyle===h)return this}else if(!k&&l&&j.strokeStyle===h)return this;return j.beginPath(),j.moveTo(a,b),j.lineTo(c,d),j.lineTo(e,f),j.lineTo(g,i),j.closePath(),k&&j.fill(),l&&j.stroke(),this},d.Renderer2D.prototype.rect=function(a,b,c,d,f,g,i,j){var k=this.drawingContext,l=this._pInst._doFill,m=this._pInst._doStroke;if(l&&!m){if(k.fillStyle===h)return this}else if(!l&&m&&k.strokeStyle===h)return this;var n=e.modeAdjust(a,b,c,d,this._pInst._rectMode);if(this._pInst._doStroke&&k.lineWidth%2===1&&k.translate(.5,.5),k.beginPath(),"undefined"==typeof f)k.rect(n.x,n.y,n.w,n.h);else{"undefined"==typeof g&&(g=f),"undefined"==typeof i&&(i=g),"undefined"==typeof j&&(j=i);var o=n.x,p=n.y,q=n.w,r=n.h,s=q/2,t=r/2;2*f>q&&(f=s),2*f>r&&(f=t),2*g>q&&(g=s),2*g>r&&(g=t),2*i>q&&(i=s),2*i>r&&(i=t),2*j>q&&(j=s),2*j>r&&(j=t),k.beginPath(),k.moveTo(o+f,p),k.arcTo(o+q,p,o+q,p+r,g),k.arcTo(o+q,p+r,o,p+r,i),k.arcTo(o,p+r,o,p,j),k.arcTo(o,p,o+q,p,f),k.closePath()}return this._pInst._doFill&&k.fill(),this._pInst._doStroke&&k.stroke(),this._pInst._doStroke&&k.lineWidth%2===1&&k.translate(-.5,-.5),this},d.Renderer2D.prototype.triangle=function(a,b,c,d,e,f){var g=this.drawingContext,i=this._pInst._doFill,j=this._pInst._doStroke;if(i&&!j){if(g.fillStyle===h)return this}else if(!i&&j&&g.strokeStyle===h)return this;g.beginPath(),g.moveTo(a,b),g.lineTo(c,d),g.lineTo(e,f),g.closePath(),i&&g.fill(),j&&g.stroke()},d.Renderer2D.prototype.endShape=function(a,b,c,d,e,g,h){if(0===b.length)return this;if(!this._pInst._doStroke&&!this._pInst._doFill)return this;var i,j=a===f.CLOSE;j&&!g&&b.push(b[0]);var k,l,m=b.length;if(!c||h!==f.POLYGON&&null!==h)if(!d||h!==f.POLYGON&&null!==h)if(!e||h!==f.POLYGON&&null!==h)if(h===f.POINTS)for(k=0;m>k;k++)i=b[k],this._pInst._doStroke&&this._pInst.stroke(i[6]),this._pInst.point(i[0],i[1]);else if(h===f.LINES)for(k=0;m>k+1;k+=2)i=b[k],this._pInst._doStroke&&this._pInst.stroke(b[k+1][6]),this._pInst.line(i[0],i[1],b[k+1][0],b[k+1][1]);else if(h===f.TRIANGLES)for(k=0;m>k+2;k+=3)i=b[k],this.drawingContext.beginPath(),this.drawingContext.moveTo(i[0],i[1]),this.drawingContext.lineTo(b[k+1][0],b[k+1][1]),this.drawingContext.lineTo(b[k+2][0],b[k+2][1]),this.drawingContext.lineTo(i[0],i[1]),this._pInst._doFill&&(this._pInst.fill(b[k+2][5]),this.drawingContext.fill()),this._pInst._doStroke&&(this._pInst.stroke(b[k+2][6]),this.drawingContext.stroke()),this.drawingContext.closePath();else if(h===f.TRIANGLE_STRIP)for(k=0;m>k+1;k++)i=b[k],this.drawingContext.beginPath(),this.drawingContext.moveTo(b[k+1][0],b[k+1][1]),this.drawingContext.lineTo(i[0],i[1]),this._pInst._doStroke&&this._pInst.stroke(b[k+1][6]),this._pInst._doFill&&this._pInst.fill(b[k+1][5]),m>k+2&&(this.drawingContext.lineTo(b[k+2][0],b[k+2][1]),this._pInst._doStroke&&this._pInst.stroke(b[k+2][6]),this._pInst._doFill&&this._pInst.fill(b[k+2][5])),this._doFillStrokeClose();else if(h===f.TRIANGLE_FAN){if(m>2)for(this.drawingContext.beginPath(),this.drawingContext.moveTo(b[0][0],b[0][1]),this.drawingContext.lineTo(b[1][0],b[1][1]),this.drawingContext.lineTo(b[2][0],b[2][1]),this._pInst._doFill&&this._pInst.fill(b[2][5]),this._pInst._doStroke&&this._pInst.stroke(b[2][6]),this._doFillStrokeClose(),k=3;m>k;k++)i=b[k],this.drawingContext.beginPath(),this.drawingContext.moveTo(b[0][0],b[0][1]),this.drawingContext.lineTo(b[k-1][0],b[k-1][1]),this.drawingContext.lineTo(i[0],i[1]),this._pInst._doFill&&this._pInst.fill(i[5]),this._pInst._doStroke&&this._pInst.stroke(i[6]),this._doFillStrokeClose()}else if(h===f.QUADS)for(k=0;m>k+3;k+=4){for(i=b[k],this.drawingContext.beginPath(),this.drawingContext.moveTo(i[0],i[1]),l=1;4>l;l++)this.drawingContext.lineTo(b[k+l][0],b[k+l][1]);this.drawingContext.lineTo(i[0],i[1]),this._pInst._doFill&&this._pInst.fill(b[k+3][5]),this._pInst._doStroke&&this._pInst.stroke(b[k+3][6]),this._doFillStrokeClose()}else if(h===f.QUAD_STRIP){if(m>3)for(k=0;m>k+1;k+=2)i=b[k],this.drawingContext.beginPath(),m>k+3?(this.drawingContext.moveTo(b[k+2][0],b[k+2][1]),this.drawingContext.lineTo(i[0],i[1]),this.drawingContext.lineTo(b[k+1][0],b[k+1][1]),this.drawingContext.lineTo(b[k+3][0],b[k+3][1]),this._pInst._doFill&&this._pInst.fill(b[k+3][5]),this._pInst._doStroke&&this._pInst.stroke(b[k+3][6])):(this.drawingContext.moveTo(i[0],i[1]),this.drawingContext.lineTo(b[k+1][0],b[k+1][1])),this._doFillStrokeClose()}else{for(this.drawingContext.beginPath(),this.drawingContext.moveTo(b[0][0],b[0][1]),k=1;m>k;k++)i=b[k],i.isVert&&(i.moveTo?this.drawingContext.moveTo(i[0],i[1]):this.drawingContext.lineTo(i[0],i[1]));this._doFillStrokeClose()}else{for(this.drawingContext.beginPath(),k=0;m>k;k++)b[k].isVert?b[k].moveTo?this.drawingContext.moveTo([0],b[k][1]):this.drawingContext.lineTo(b[k][0],b[k][1]):this.drawingContext.quadraticCurveTo(b[k][0],b[k][1],b[k][2],b[k][3]);this._doFillStrokeClose()}else{for(this.drawingContext.beginPath(),k=0;m>k;k++)b[k].isVert?b[k].moveTo?this.drawingContext.moveTo(b[k][0],b[k][1]):this.drawingContext.lineTo(b[k][0],b[k][1]):this.drawingContext.bezierCurveTo(b[k][0],b[k][1],b[k][2],b[k][3],b[k][4],b[k][5]);this._doFillStrokeClose()}else if(m>3){var n=[],o=1-this._pInst._curveTightness;for(this.drawingContext.beginPath(),this.drawingContext.moveTo(b[1][0],b[1][1]),k=1;m>k+2;k++)i=b[k],n[0]=[i[0],i[1]],n[1]=[i[0]+(o*b[k+1][0]-o*b[k-1][0])/6,i[1]+(o*b[k+1][1]-o*b[k-1][1])/6],n[2]=[b[k+1][0]+(o*b[k][0]-o*b[k+2][0])/6,b[k+1][1]+(o*b[k][1]-o*b[k+2][1])/6],n[3]=[b[k+1][0],b[k+1][1]],this.drawingContext.bezierCurveTo(n[1][0],n[1][1],n[2][0],n[2][1],n[3][0],n[3][1]);j&&this.drawingContext.lineTo(b[k+1][0],b[k+1][1]),this._doFillStrokeClose()}return c=!1,d=!1,e=!1,g=!1,j&&b.pop(),this},d.Renderer2D.prototype.noSmooth=function(){return"imageSmoothingEnabled"in this.drawingContext?this.drawingContext.imageSmoothingEnabled=!1:"mozImageSmoothingEnabled"in this.drawingContext?this.drawingContext.mozImageSmoothingEnabled=!1:"webkitImageSmoothingEnabled"in this.drawingContext?this.drawingContext.webkitImageSmoothingEnabled=!1:"msImageSmoothingEnabled"in this.drawingContext&&(this.drawingContext.msImageSmoothingEnabled=!1),this},d.Renderer2D.prototype.smooth=function(){return"imageSmoothingEnabled"in this.drawingContext?this.drawingContext.imageSmoothingEnabled=!0:"mozImageSmoothingEnabled"in this.drawingContext?this.drawingContext.mozImageSmoothingEnabled=!0:"webkitImageSmoothingEnabled"in this.drawingContext?this.drawingContext.webkitImageSmoothingEnabled=!0:"msImageSmoothingEnabled"in this.drawingContext&&(this.drawingContext.msImageSmoothingEnabled=!0),this},d.Renderer2D.prototype.strokeCap=function(a){return(a===f.ROUND||a===f.SQUARE||a===f.PROJECT)&&(this.drawingContext.lineCap=a),this},d.Renderer2D.prototype.strokeJoin=function(a){return(a===f.ROUND||a===f.BEVEL||a===f.MITER)&&(this.drawingContext.lineJoin=a),this},d.Renderer2D.prototype.strokeWeight=function(a){return"undefined"==typeof a||0===a?this.drawingContext.lineWidth=1e-4:this.drawingContext.lineWidth=a,this},d.Renderer2D.prototype._getFill=function(){return this.drawingContext.fillStyle},d.Renderer2D.prototype._getStroke=function(){return this.drawingContext.strokeStyle},d.Renderer2D.prototype.bezier=function(a,b,c,d,e,f,g,h){return this._pInst.beginShape(),this._pInst.vertex(a,b),this._pInst.bezierVertex(c,d,e,f,g,h),this._pInst.endShape(),this},d.Renderer2D.prototype.curve=function(a,b,c,d,e,f,g,h){return this._pInst.beginShape(),this._pInst.curveVertex(a,b),this._pInst.curveVertex(c,d),this._pInst.curveVertex(e,f),this._pInst.curveVertex(g,h),this._pInst.endShape(),this},d.Renderer2D.prototype._doFillStrokeClose=function(){this._pInst._doFill&&this.drawingContext.fill(),this._pInst._doStroke&&this.drawingContext.stroke(),this.drawingContext.closePath()},d.Renderer2D.prototype.applyMatrix=function(a,b,c,d,e,f){this.drawingContext.transform(a,b,c,d,e,f)},d.Renderer2D.prototype.resetMatrix=function(){return this.drawingContext.setTransform(1,0,0,1,0,0),this.drawingContext.scale(this._pInst.pixelDensity,this._pInst.pixelDensity),this},d.Renderer2D.prototype.rotate=function(a){this.drawingContext.rotate(a)},d.Renderer2D.prototype.scale=function(){var a=1,b=1;return 1===arguments.length?a=b=arguments[0]:(a=arguments[0],b=arguments[1]),this.drawingContext.scale(a,b),this},d.Renderer2D.prototype.shearX=function(a){return this._pInst._angleMode===f.DEGREES&&(a=this._pInst.radians(a)),this.drawingContext.transform(1,0,this._pInst.tan(a),1,0,0),this},d.Renderer2D.prototype.shearY=function(a){return this._pInst._angleMode===f.DEGREES&&(a=this._pInst.radians(a)),this.drawingContext.transform(1,this._pInst.tan(a),0,1,0,0),this},d.Renderer2D.prototype.translate=function(a,b){return this.drawingContext.translate(a,b),this},d.Renderer2D.prototype.text=function(a,b,c,d,e){var g,h,i,j,k,l,m,n,o,p,q=this._pInst;if(q._doFill||q._doStroke){if("string"!=typeof a&&(a=a.toString()),a=a.replace(/(\t)/g," "),g=a.split("\n"),"undefined"!=typeof d){for(o=0,i=0;i<g.length;i++)for(k="",n=g[i].split(" "),h=0;h<n.length;h++)l=k+n[h]+" ",m=this.textWidth(l),m>d?(k=n[h]+" ",o+=q.textLeading()):k=l;switch(this._pInst._rectMode===f.CENTER&&(b-=d/2,c-=e/2),this.drawingContext.textAlign){case f.CENTER:b+=d/2;break;case f.RIGHT:b+=d}if("undefined"!=typeof e)switch(this.drawingContext.textBaseline){case f.BOTTOM:c+=e-o;break;case f._CTX_MIDDLE:c+=(e-o)/2;break;case f.BASELINE:p=!0,this.drawingContext.textBaseline=f.TOP}for(i=0;i<g.length;i++){for(k="",n=g[i].split(" "),h=0;h<n.length;h++)l=k+n[h]+" ",m=this.textWidth(l),m>d&&k.length>0?(this._renderText(q,k,b,c),k=n[h]+" ",c+=q.textLeading()):k=l;this._renderText(q,k,b,c),c+=q.textLeading()}}else for(j=0;j<g.length;j++)this._renderText(q,g[j],b,c),c+=q.textLeading();return p&&(this.drawingContext.textBaseline=f.BASELINE),q}},d.Renderer2D.prototype._renderText=function(a,b,c,d){return a.push(),a._isOpenType()?a._textFont._renderPath(b,c,d):(a._doStroke&&a._strokeSet&&this.drawingContext.strokeText(b,c,d),a._doFill&&(this.drawingContext.fillStyle=a._fillSet?this.drawingContext.fillStyle:f._DEFAULT_TEXT_FILL,this.drawingContext.fillText(b,c,d))),a.pop(),a},d.Renderer2D.prototype.textWidth=function(a){return this._pInst._isOpenType()?this._pInst._textFont._textWidth(a):this.drawingContext.measureText(a).width},d.Renderer2D.prototype.textAlign=function(a,b){if(arguments.length)return(a===f.LEFT||a===f.RIGHT||a===f.CENTER)&&(this.drawingContext.textAlign=a),(b===f.TOP||b===f.BOTTOM||b===f.CENTER||b===f.BASELINE)&&(b===f.CENTER?this.drawingContext.textBaseline=f._CTX_MIDDLE:this.drawingContext.textBaseline=b),this._pInst;var c=this.drawingContext.textBaseline;return c===f._CTX_MIDDLE&&(c=f.CENTER),{horizontal:this.drawingContext.textAlign,vertical:c}},d.Renderer2D.prototype._applyTextProperties=function(){var a,b=this._pInst;return b._setProperty("_textAscent",null),b._setProperty("_textDescent",null),a=b._textFont,b._isOpenType()&&(a=b._textFont.font.familyName,b._setProperty("_textStyle",b._textFont.font.styleName)),this.drawingContext.font=b._textStyle+" "+b._textSize+"px "+a,b},d.Renderer2D.prototype.push=function(){this.drawingContext.save()},d.Renderer2D.prototype.pop=function(){this.drawingContext.restore()},b.exports=d.Renderer2D},{"../image/filters":66,"./canvas":47,"./constants":48,"./core":49,"./p5.Renderer":55}],57:[function(a,b,c){var d=a("./core"),e=a("./constants");a("./p5.Graphics"),a("./p5.Renderer2D"),a("../3d/p5.Renderer3D"),d.prototype.createCanvas=function(a,b,c){var f,g,h=c||e.P2D;return arguments[3]&&(f="boolean"==typeof arguments[3]?arguments[3]:!1),h===e.WEBGL?(g=document.getElementById("defaultCanvas"),g&&g.parentNode.removeChild(g),g=document.createElement("canvas"),g.id="defaultCanvas"):f?(g=document.createElement("canvas"),g.id="defaultCanvas"):g=this.canvas,this._setupDone||(g.className+=" p5_hidden",g.style.visibility="hidden"),this._userNode?this._userNode.appendChild(g):document.body.appendChild(g),h===e.WEBGL?(this._setProperty("_graphics",new d.Renderer3D(g,this,!0)),this._isdefaultGraphics=!0):this._isdefaultGraphics||(this._setProperty("_graphics",new d.Renderer2D(g,this,!0)),this._isdefaultGraphics=!0),this._graphics.resize(a,b),this._graphics._applyDefaults(),f&&this._elements.push(this._graphics),this._graphics},d.prototype.resizeCanvas=function(a,b,c){this._graphics&&(this._graphics.resize(a,b),this._graphics._applyDefaults(),c||this.redraw())},d.prototype.noCanvas=function(){this.canvas&&this.canvas.parentNode.removeChild(this.canvas)},d.prototype.createGraphics=function(a,b,c){return new d.Graphics(a,b,c,this)},d.prototype.blendMode=function(a){if(a!==e.BLEND&&a!==e.DARKEST&&a!==e.LIGHTEST&&a!==e.DIFFERENCE&&a!==e.MULTIPLY&&a!==e.EXCLUSION&&a!==e.SCREEN&&a!==e.REPLACE&&a!==e.OVERLAY&&a!==e.HARD_LIGHT&&a!==e.SOFT_LIGHT&&a!==e.DODGE&&a!==e.BURN&&a!==e.ADD&&a!==e.NORMAL)throw new Error("Mode "+a+" not recognized.");this._graphics.blendMode(a)},b.exports=d},{"../3d/p5.Renderer3D":37,"./constants":48,"./core":49,"./p5.Graphics":54,"./p5.Renderer2D":56}],58:[function(a,b,c){window.requestAnimationFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a,b){window.setTimeout(a,1e3/60)}}(),window.performance=window.performance||{},window.performance.now=function(){var a=Date.now();return window.performance.now||window.performance.mozNow||window.performance.msNow||window.performance.oNow||window.performance.webkitNow||function(){return Date.now()-a}}(),function(){"use strict";"undefined"!=typeof Uint8ClampedArray&&(Uint8ClampedArray.prototype.slice=Array.prototype.slice)}()},{}],59:[function(a,b,c){"use strict";var d=a("./core");d.prototype.exit=function(){throw"exit() not implemented, see remove()"},d.prototype.noLoop=function(){this._loop=!1},d.prototype.loop=function(){this._loop=!0,this._draw()},d.prototype.push=function(){this._graphics.push(),this._styles.push({doStroke:this._doStroke,doFill:this._doFill,tint:this._tint,imageMode:this._imageMode,rectMode:this._rectMode,ellipseMode:this._ellipseMode,colorMode:this._colorMode,textFont:this.textFont,textLeading:this.textLeading,textSize:this.textSize,textStyle:this.textStyle})},d.prototype.pop=function(){this._graphics.pop();var a=this._styles.pop();this._doStroke=a.doStroke,this._doFill=a.doFill,this._tint=a.tint,this._imageMode=a.imageMode,this._rectMode=a.rectMode,this._ellipseMode=a.ellipseMode,this._colorMode=a.colorMode,this.textFont=a.textFont,this.textLeading=a.textLeading,this.textSize=a.textSize,this.textStyle=a.textStyle},d.prototype.pushStyle=function(){throw new Error("pushStyle() not used, see push()")},d.prototype.popStyle=function(){throw new Error("popStyle() not used, see pop()")},d.prototype.redraw=function(){var a=this.setup||window.setup,b=this.draw||window.draw;if("function"==typeof b){this.push(),"undefined"==typeof a&&this.scale(this.pixelDensity,this.pixelDensity);var c=this;this._registeredMethods.pre.forEach(function(a){a.call(c)}),b(),this._registeredMethods.post.forEach(function(a){a.call(c)}),this.pop()}},d.prototype.size=function(){var a="size() is not a valid p5 function, to set the size of the ";throw a+="drawing canvas, please use createCanvas() instead"},b.exports=d},{"./core":49}],60:[function(a,b,c){"use strict";var d=a("./core"),e=a("./constants");d.prototype.applyMatrix=function(a,b,c,d,e,f){return this._graphics.applyMatrix(a,b,c,d,e,f),this},d.prototype.popMatrix=function(){throw new Error("popMatrix() not used, see pop()")},d.prototype.printMatrix=function(){throw new Error("printMatrix() not implemented")},d.prototype.pushMatrix=function(){throw new Error("pushMatrix() not used, see push()")},d.prototype.resetMatrix=function(){return this._graphics.resetMatrix(),this},d.prototype.rotate=function(a){return this._angleMode===e.DEGREES&&(a=this.radians(a)),this._graphics.rotate(a),this},d.prototype.rotateX=function(a){if(!this._graphics.isP3D)throw"not yet implemented.";return this._graphics.rotateX(a),this},d.prototype.rotateY=function(a){if(!this._graphics.isP3D)throw"not yet implemented.";return this._graphics.rotateY(a),this},d.prototype.rotateZ=function(a){if(!this._graphics.isP3D)throw"not supported in p2d. Please use webgl mode";return this._graphics.rotateZ(a),this},d.prototype.scale=function(){return this._graphics.isP3D?this._graphics.scale(arguments[0],arguments[1],arguments[2]):this._graphics.scale.apply(this._graphics,arguments),this},d.prototype.shearX=function(a){return this._angleMode===e.DEGREES&&(a=this.radians(a)),this._graphics.shearX(a),this},d.prototype.shearY=function(a){return this._angleMode===e.DEGREES&&(a=this.radians(a)),this._graphics.shearY(a),this},d.prototype.translate=function(a,b,c){return this._graphics.isP3D?this._graphics.translate(a,b,c):this._graphics.translate(a,b),this},b.exports=d},{"./constants":48,"./core":49}],61:[function(a,b,c){"use strict";var d=a("./core"),e=a("./constants"),f=null,g=[],h=[],i=!1,j=!1,k=!1,l=!1;d.prototype.beginContour=function(){return h=[],l=!0,this},d.prototype.beginShape=function(a){return this._graphics.isP3D?this._graphics.beginShape(a):(f=a===e.POINTS||a===e.LINES||a===e.TRIANGLES||a===e.TRIANGLE_FAN||a===e.TRIANGLE_STRIP||a===e.QUADS||a===e.QUAD_STRIP?a:null,g=[],h=[]),this},d.prototype.bezierVertex=function(a,b,c,d,e,f){if(0===g.length)throw"vertex() must be used once before calling bezierVertex()";i=!0;for(var j=[],k=0;k<arguments.length;k++)j[k]=arguments[k];return j.isVert=!1,l?h.push(j):g.push(j),this},d.prototype.curveVertex=function(a,b){return j=!0,this.vertex(a,b),this},d.prototype.endContour=function(){var a=h[0].slice();a.isVert=h[0].isVert,a.moveTo=!1,h.push(a),g.push(g[0]);for(var b=0;b<h.length;b++)g.push(h[b]);return this},d.prototype.endShape=function(a){if(this._graphics.isP3D)this._graphics.endShape();else{if(0===g.length)return this;if(!this._doStroke&&!this._doFill)return this;var b=a===e.CLOSE;b&&!l&&g.push(g[0]),this._graphics.endShape(a,g,j,i,k,l,f),j=!1,i=!1,k=!1,l=!1,b&&g.pop()}return this},d.prototype.quadraticVertex=function(a,b,c,d){ | |
if(this._contourInited){var f={};return f.x=a,f.y=b,f.x3=c,f.y3=d,f.type=e.QUADRATIC,this._contourVertices.push(f),this}if(!(g.length>0))throw"vertex() must be used once before calling quadraticVertex()";k=!0;for(var i=[],j=0;j<arguments.length;j++)i[j]=arguments[j];return i.isVert=!1,l?h.push(i):g.push(i),this},d.prototype.vertex=function(a,b,c){if(this._graphics.isP3D)this._graphics.vertex(arguments[0],arguments[1],arguments[2]);else{var d=[];d.isVert=!0,d[0]=a,d[1]=b,d[2]=0,d[3]=0,d[4]=0,d[5]=this._graphics._getFill(),d[6]=this._graphics._getStroke(),c&&(d.moveTo=c),l?(0===h.length&&(d.moveTo=!0),h.push(d)):g.push(d)}return this},b.exports=d},{"./constants":48,"./core":49}],62:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.deviceOrientation=void 0,d.prototype.accelerationX=0,d.prototype.accelerationY=0,d.prototype.accelerationZ=0,d.prototype.pAccelerationX=0,d.prototype.pAccelerationY=0,d.prototype.pAccelerationZ=0,d.prototype._updatePAccelerations=function(){this._setProperty("pAccelerationX",this.accelerationX),this._setProperty("pAccelerationY",this.accelerationY),this._setProperty("pAccelerationZ",this.accelerationZ)};var e=.5,f=30;d.prototype.setMoveThreshold=function(a){"number"==typeof a&&(e=a)},d.prototype.setShakeThreshold=function(a){"number"==typeof a&&(f=a)};var g="",h="";d.prototype._ondeviceorientation=function(a){this._setProperty("accelerationX",a.beta),this._setProperty("accelerationY",a.gamma),this._setProperty("accelerationZ",a.alpha),this._handleMotion()},d.prototype._ondevicemotion=function(a){this._setProperty("accelerationX",2*a.acceleration.x),this._setProperty("accelerationY",2*a.acceleration.y),this._setProperty("accelerationZ",2*a.acceleration.z),this._handleMotion()},d.prototype._onMozOrientation=function(a){this._setProperty("accelerationX",a.x),this._setProperty("accelerationY",a.y),this._setProperty("accelerationZ",a.z),this._handleMotion()},d.prototype._handleMotion=function(){90===window.orientation||-90===window.orientation?this._setProperty("deviceOrientation","landscape"):0===window.orientation?this._setProperty("deviceOrientation","portrait"):void 0===window.orientation&&this._setProperty("deviceOrientation","undefined");var a=this.deviceMoved||window.deviceMoved;"function"==typeof a&&(Math.abs(this.accelerationX-this.pAccelerationX)>e||Math.abs(this.accelerationY-this.pAccelerationY)>e||Math.abs(this.accelerationZ-this.pAccelerationZ)>e)&&a();var b=this.deviceTurned||window.deviceTurned;if("function"==typeof b){var c=0;Math.abs(this.accelerationX)>c&&(c=this.accelerationX,h="x"),Math.abs(this.accelerationY)>c&&(c=this.accelerationY,h="y"),Math.abs(this.accelerationZ)>c&&(h="z"),""!==g&&g!==h&&b(h),g=h}var d=this.deviceShaken||window.deviceShaken;if("function"==typeof d){var i,j;null!==this.pAccelerationX&&(i=Math.abs(this.accelerationX-this.pAccelerationX),j=Math.abs(this.accelerationY-this.pAccelerationY)),i+j>f&&d()}},b.exports=d},{"../core/core":49}],63:[function(a,b,c){"use strict";var d=a("../core/core"),e={};d.prototype.isKeyPressed=!1,d.prototype.keyIsPressed=!1,d.prototype.key="",d.prototype.keyCode=0,d.prototype._onkeydown=function(a){this._setProperty("isKeyPressed",!0),this._setProperty("keyIsPressed",!0),this._setProperty("keyCode",a.which),e[a.which]=!0;var b=String.fromCharCode(a.which);b||(b=a.which),this._setProperty("key",b);var c=this.keyPressed||window.keyPressed;if("function"==typeof c&&!a.charCode){var d=c(a);d===!1&&a.preventDefault()}},d.prototype._onkeyup=function(a){var b=this.keyReleased||window.keyReleased;this._setProperty("isKeyPressed",!1),this._setProperty("keyIsPressed",!1),e[a.which]=!1;var c=String.fromCharCode(a.which);if(c||(c=a.which),this._setProperty("key",c),this._setProperty("keyCode",a.which),"function"==typeof b){var d=b(a);d===!1&&a.preventDefault()}},d.prototype._onkeypress=function(a){this._setProperty("keyCode",a.which),this._setProperty("key",String.fromCharCode(a.which));var b=this.keyTyped||window.keyTyped;if("function"==typeof b){var c=b(a);c===!1&&a.preventDefault()}},d.prototype._onblur=function(a){e={}},d.prototype.keyIsDown=function(a){return e[a]},b.exports=d},{"../core/core":49}],64:[function(a,b,c){"use strict";function d(a,b){var c=a.getBoundingClientRect();return{x:b.clientX-c.left,y:b.clientY-c.top}}var e=a("../core/core"),f=a("../core/constants");e.prototype.mouseX=0,e.prototype.mouseY=0,e.prototype.pmouseX=0,e.prototype.pmouseY=0,e.prototype.winMouseX=0,e.prototype.winMouseY=0,e.prototype.pwinMouseX=0,e.prototype.pwinMouseY=0,e.prototype.mouseButton=0,e.prototype.mouseIsPressed=!1,e.prototype.isMousePressed=!1,e.prototype._updateMouseCoords=function(a){if("touchstart"===a.type||"touchmove"===a.type||"touchend"===a.type)this._setProperty("mouseX",this.touchX),this._setProperty("mouseY",this.touchY);else if(null!==this._curElement){var b=d(this._curElement.elt,a);this._setProperty("mouseX",b.x),this._setProperty("mouseY",b.y)}this._setProperty("winMouseX",a.pageX),this._setProperty("winMouseY",a.pageY)},e.prototype._updatePMouseCoords=function(a){this._setProperty("pmouseX",this.mouseX),this._setProperty("pmouseY",this.mouseY),this._setProperty("pwinMouseX",this.winMouseX),this._setProperty("pwinMouseY",this.winMouseY)},e.prototype._setMouseButton=function(a){1===a.button?this._setProperty("mouseButton",f.CENTER):2===a.button?this._setProperty("mouseButton",f.RIGHT):(this._setProperty("mouseButton",f.LEFT),("touchstart"===a.type||"touchmove"===a.type)&&(this._setProperty("mouseX",this.touchX),this._setProperty("mouseY",this.touchY)))},e.prototype._onmousemove=function(a){var b,c=this._isGlobal?window:this;this._updateMouseCoords(a),this.isMousePressed?"function"==typeof c.mouseDragged?(b=c.mouseDragged(a),b===!1&&a.preventDefault()):"function"==typeof c.touchMoved&&(b=c.touchMoved(a),b===!1&&a.preventDefault(),this._updateTouchCoords(a)):"function"==typeof c.mouseMoved&&(b=c.mouseMoved(a),b===!1&&a.preventDefault())},e.prototype._onmousedown=function(a){var b,c=this._isGlobal?window:this;this._setProperty("isMousePressed",!0),this._setProperty("mouseIsPressed",!0),this._setMouseButton(a),this._updateMouseCoords(a),"function"==typeof c.mousePressed?(b=c.mousePressed(a),b===!1&&a.preventDefault()):"function"==typeof c.touchStarted&&(b=c.touchStarted(a),b===!1&&a.preventDefault(),this._updateTouchCoords(a))},e.prototype._onmouseup=function(a){var b,c=this._isGlobal?window:this;this._setProperty("isMousePressed",!1),this._setProperty("mouseIsPressed",!1),"function"==typeof c.mouseReleased?(b=c.mouseReleased(a),b===!1&&a.preventDefault()):"function"==typeof c.touchEnded&&(b=c.touchEnded(a),b===!1&&a.preventDefault(),this._updateTouchCoords(a))},e.prototype._onclick=function(a){var b=this._isGlobal?window:this;if("function"==typeof b.mouseClicked){var c=b.mouseClicked(a);c===!1&&a.preventDefault()}},e.prototype._onmousewheel=e.prototype._onDOMMouseScroll=function(a){var b=this._isGlobal?window:this;if("function"==typeof b.mouseWheel){a.delta=Math.max(-1,Math.min(1,a.wheelDelta||-a.detail));var c=b.mouseWheel(a);c===!1&&a.preventDefault()}},b.exports=e},{"../core/constants":48,"../core/core":49}],65:[function(a,b,c){"use strict";function d(a,b,c){c=c||0;var d=a.getBoundingClientRect(),e=b.touches[c]||b.changedTouches[c];return{x:e.clientX-d.left,y:e.clientY-d.top}}var e=a("../core/core");e.prototype.touchX=0,e.prototype.touchY=0,e.prototype.ptouchX=0,e.prototype.ptouchY=0,e.prototype.touches=[],e.prototype.touchIsDown=!1,e.prototype._updateTouchCoords=function(a){if("mousedown"===a.type||"mousemove"===a.type||"mouseup"===a.type)this._setProperty("touchX",this.mouseX),this._setProperty("touchY",this.mouseY);else{var b=d(this._curElement.elt,a,0);this._setProperty("touchX",b.x),this._setProperty("touchY",b.y);for(var c=[],e=0;e<a.touches.length;e++){var f=d(this._curElement.elt,a,e);c[e]={x:f.x,y:f.y}}this._setProperty("touches",c)}},e.prototype._updatePTouchCoords=function(){this._setProperty("ptouchX",this.touchX),this._setProperty("ptouchY",this.touchY)},e.prototype._ontouchstart=function(a){var b,c=this._isGlobal?window:this;this._updateTouchCoords(a),this._setProperty("touchIsDown",!0),"function"==typeof c.touchStarted?(b=c.touchStarted(a),b===!1&&a.preventDefault()):"function"==typeof c.mousePressed&&(b=c.mousePressed(a),b===!1&&a.preventDefault())},e.prototype._ontouchmove=function(a){var b,c=this._isGlobal?window:this;this._updateTouchCoords(a),"function"==typeof c.touchMoved?(b=c.touchMoved(a),b===!1&&a.preventDefault()):"function"==typeof c.mouseDragged&&(b=c.mouseDragged(a),b===!1&&a.preventDefault(),this._updateMouseCoords(a))},e.prototype._ontouchend=function(a){this._updateTouchCoords(a),0===this.touches.length&&this._setProperty("touchIsDown",!1);var b,c=this._isGlobal?window:this;"function"==typeof c.touchEnded?(b=c.touchEnded(a),b===!1&&a.preventDefault()):"function"==typeof c.mouseReleased&&(b=c.mouseReleased(a),b===!1&&a.preventDefault(),this._updateMouseCoords(a))},b.exports=e},{"../core/core":49}],66:[function(a,b,c){"use strict";function d(a){var b=3.5*a|0;if(b=1>b?1:248>b?b:248,g!==b){g=b,h=1+g<<1,i=new Int32Array(h),j=new Array(h);for(var c=0;h>c;c++)j[c]=new Int32Array(256);for(var d,e,f,k,l=1,m=b-1;b>l;l++){i[b+l]=i[m]=e=m*m,f=j[b+l],k=j[m--];for(var n=0;256>n;n++)f[n]=k[n]=e*n}d=i[b]=b*b,f=j[b];for(var o=0;256>o;o++)f[o]=d*o}}function e(a,b){for(var c=f._toPixels(a),e=a.width,k=a.height,l=e*k,m=new Int32Array(l),n=0;l>n;n++)m[n]=f._getARGB(c,n);var o,p,q,r,s,t,u,v,w,x,y=new Int32Array(l),z=new Int32Array(l),A=new Int32Array(l),B=new Int32Array(l),C=0;d(b);var D,E,F,G;for(E=0;k>E;E++){for(D=0;e>D;D++){if(r=q=p=s=o=0,t=D-g,0>t)x=-t,t=0;else{if(t>=e)break;x=0}for(F=x;h>F&&!(t>=e);F++){var H=m[t+C];G=j[F],s+=G[(-16777216&H)>>>24],p+=G[(16711680&H)>>16],q+=G[(65280&H)>>8],r+=G[255&H],o+=i[F],t++}u=C+D,y[u]=s/o,z[u]=p/o,A[u]=q/o,B[u]=r/o}C+=e}for(C=0,v=-g,w=v*e,E=0;k>E;E++){for(D=0;e>D;D++){if(r=q=p=s=o=0,0>v)x=u=-v,t=D;else{if(v>=k)break;x=0,u=v,t=D+w}for(F=x;h>F&&!(u>=k);F++)G=j[F],s+=G[y[t]],p+=G[z[t]],q+=G[A[t]],r+=G[B[t]],o+=i[F],u++,t+=e;m[D+C]=s/o<<24|p/o<<16|q/o<<8|r/o}C+=e,w+=e,v++}f._setPixels(c,m)}var f={};f._toPixels=function(a){return a instanceof ImageData?a.data:a.getContext("2d").getImageData(0,0,a.width,a.height).data},f._getARGB=function(a,b){var c=4*b;return a[c+3]<<24&4278190080|a[c]<<16&16711680|a[c+1]<<8&65280|255&a[c+2]},f._setPixels=function(a,b){for(var c=0,d=0,e=a.length;e>d;d++)c=4*d,a[c+0]=(16711680&b[d])>>>16,a[c+1]=(65280&b[d])>>>8,a[c+2]=255&b[d],a[c+3]=(4278190080&b[d])>>>24},f._toImageData=function(a){return a instanceof ImageData?a:a.getContext("2d").getImageData(0,0,a.width,a.height)},f._createImageData=function(a,b){return f._tmpCanvas=document.createElement("canvas"),f._tmpCtx=f._tmpCanvas.getContext("2d"),this._tmpCtx.createImageData(a,b)},f.apply=function(a,b,c){var d=a.getContext("2d"),e=d.getImageData(0,0,a.width,a.height),f=b(e,c);f instanceof ImageData?d.putImageData(f,0,0,0,0,a.width,a.height):d.putImageData(e,0,0,0,0,a.width,a.height)},f.threshold=function(a,b){var c=f._toPixels(a);void 0===b&&(b=.5);for(var d=Math.floor(255*b),e=0;e<c.length;e+=4){var g,h=c[e],i=c[e+1],j=c[e+2],k=.2126*h+.7152*i+.0722*j;g=k>=d?255:0,c[e]=c[e+1]=c[e+2]=g}},f.gray=function(a){for(var b=f._toPixels(a),c=0;c<b.length;c+=4){var d=b[c],e=b[c+1],g=b[c+2],h=.2126*d+.7152*e+.0722*g;b[c]=b[c+1]=b[c+2]=h}},f.opaque=function(a){for(var b=f._toPixels(a),c=0;c<b.length;c+=4)b[c+3]=255;return b},f.invert=function(a){for(var b=f._toPixels(a),c=0;c<b.length;c+=4)b[c]=255-b[c],b[c+1]=255-b[c+1],b[c+2]=255-b[c+2]},f.posterize=function(a,b){var c=f._toPixels(a);if(2>b||b>255)throw new Error("Level must be greater than 2 and less than 255 for posterize");for(var d=b-1,e=0;e<c.length;e+=4){var g=c[e],h=c[e+1],i=c[e+2];c[e]=255*(g*b>>8)/d,c[e+1]=255*(h*b>>8)/d,c[e+2]=255*(i*b>>8)/d}},f.dilate=function(a){for(var b,c,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t=f._toPixels(a),u=0,v=t.length?t.length/4:0,w=new Int32Array(v);v>u;)for(b=u,c=u+a.width;c>u;)d=e=f._getARGB(t,u),i=u-1,h=u+1,j=u-a.width,k=u+a.width,b>i&&(i=u),h>=c&&(h=u),0>j&&(j=0),k>=v&&(k=u),n=f._getARGB(t,j),m=f._getARGB(t,i),o=f._getARGB(t,k),l=f._getARGB(t,h),g=77*(d>>16&255)+151*(d>>8&255)+28*(255&d),q=77*(m>>16&255)+151*(m>>8&255)+28*(255&m),p=77*(l>>16&255)+151*(l>>8&255)+28*(255&l),r=77*(n>>16&255)+151*(n>>8&255)+28*(255&n),s=77*(o>>16&255)+151*(o>>8&255)+28*(255&o),q>g&&(e=m,g=q),p>g&&(e=l,g=p),r>g&&(e=n,g=r),s>g&&(e=o,g=s),w[u++]=e;f._setPixels(t,w)},f.erode=function(a){for(var b,c,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t=f._toPixels(a),u=0,v=t.length?t.length/4:0,w=new Int32Array(v);v>u;)for(b=u,c=u+a.width;c>u;)d=e=f._getARGB(t,u),i=u-1,h=u+1,j=u-a.width,k=u+a.width,b>i&&(i=u),h>=c&&(h=u),0>j&&(j=0),k>=v&&(k=u),n=f._getARGB(t,j),m=f._getARGB(t,i),o=f._getARGB(t,k),l=f._getARGB(t,h),g=77*(d>>16&255)+151*(d>>8&255)+28*(255&d),q=77*(m>>16&255)+151*(m>>8&255)+28*(255&m),p=77*(l>>16&255)+151*(l>>8&255)+28*(255&l),r=77*(n>>16&255)+151*(n>>8&255)+28*(255&n),s=77*(o>>16&255)+151*(o>>8&255)+28*(255&o),g>q&&(e=m,g=q),g>p&&(e=l,g=p),g>r&&(e=n,g=r),g>s&&(e=o,g=s),w[u++]=e;f._setPixels(t,w)};var g,h,i,j;f.blur=function(a,b){e(a,b)},b.exports=f},{}],67:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants"),f=[];d.prototype._imageMode=e.CORNER,d.prototype._tint=null,d.prototype.createImage=function(a,b){return new d.Image(a,b)},d.prototype.saveCanvas=function(){var a,b,c;if(3===arguments.length?(a=arguments[0],b=arguments[1],c=arguments[2]):2===arguments.length?"object"==typeof arguments[0]?(a=arguments[0],b=arguments[1]):(b=arguments[0],c=arguments[1]):1===arguments.length&&("object"==typeof arguments[0]?a=arguments[0]:b=arguments[0]),a instanceof d.Element&&(a=a.elt),a instanceof HTMLCanvasElement||(a=null),c||(c=d.prototype._checkFileExtension(b,c)[1],""===c&&(c="png")),a||this._curElement&&this._curElement.elt&&(a=this._curElement.elt),d.prototype._isSafari()){var e="Hello, Safari user!\n";e+="Now capturing a screenshot...\n",e+="To save this image,\n",e+="go to File --> Save As.\n",alert(e),window.location.href=a.toDataURL()}else{var f;if("undefined"==typeof c)c="png",f="image/png";else switch(c){case"png":f="image/png";break;case"jpeg":f="image/jpeg";break;case"jpg":f="image/jpeg";break;default:f="image/png"}var g="image/octet-stream",h=a.toDataURL(f);h=h.replace(f,g),d.prototype.downloadFile(h,b,c)}},d.prototype.saveFrames=function(a,b,c,e,g){var h=c||3;h=d.prototype.constrain(h,0,15),h=1e3*h;var i=e||15;i=d.prototype.constrain(i,0,22);var j=0,k=d.prototype._makeFrame,l=this._curElement.elt,m=setInterval(function(){k(a+j,b,l),j++},1e3/i);setTimeout(function(){if(clearInterval(m),g)g(f);else for(var a=0;a<f.length;a++){var b=f[a];d.prototype.downloadFile(b.imageData,b.filename,b.ext)}f=[]},h+.01)},d.prototype._makeFrame=function(a,b,c){var d;d=this?this._curElement.elt:c;var e;if(b)switch(b.toLowerCase()){case"png":e="image/png";break;case"jpeg":e="image/jpeg";break;case"jpg":e="image/jpeg";break;default:e="image/png"}else b="png",e="image/png";var g="image/octet-stream",h=d.toDataURL(e);h=h.replace(e,g);var i={};i.imageData=h,i.filename=a,i.ext=b,f.push(i)},b.exports=d},{"../core/constants":48,"../core/core":49}],68:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./filters"),f=a("../core/canvas"),g=a("../core/constants");a("../core/error_helpers"),d.prototype.loadImage=function(a,b,c){var e=new Image,f=new d.Image(1,1,this);return e.onload=function(){f.width=f.canvas.width=e.width,f.height=f.canvas.height=e.height,f.drawingContext.drawImage(e,0,0),"function"==typeof b&&b(f)},e.onerror=function(a){d._friendlyFileLoadError(0,e.src),"function"==typeof c&&c(a)},0!==a.indexOf("data:image/")&&(e.crossOrigin="Anonymous"),e.src=a,f},d.prototype.image=function(a,b,c,d,e){b=b||0,c=c||0,d=d||a.width,e=e||a.height;var g=f.modeAdjust(b,c,d,e,this._imageMode);this._graphics.image(a,g.x,g.y,g.w,g.h)},d.prototype.tint=function(){var a=this.color.apply(this,arguments);this._tint=a.rgba},d.prototype.noTint=function(){this._tint=null},d.prototype._getTintedImageCanvas=function(a){if(!a.canvas)return a;var b=e._toPixels(a.canvas),c=document.createElement("canvas");c.width=a.canvas.width,c.height=a.canvas.height;for(var d=c.getContext("2d"),f=d.createImageData(a.canvas.width,a.canvas.height),g=f.data,h=0;h<b.length;h+=4){var i=b[h],j=b[h+1],k=b[h+2],l=b[h+3];g[h]=i*this._tint[0]/255,g[h+1]=j*this._tint[1]/255,g[h+2]=k*this._tint[2]/255,g[h+3]=l*this._tint[3]/255}return d.putImageData(f,0,0),c},d.prototype.imageMode=function(a){(a===g.CORNER||a===g.CORNERS||a===g.CENTER)&&(this._imageMode=a)},b.exports=d},{"../core/canvas":47,"../core/constants":48,"../core/core":49,"../core/error_helpers":52,"./filters":66}],69:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./filters");d.Image=function(a,b){this.width=a,this.height=b,this.canvas=document.createElement("canvas"),this.canvas.width=this.width,this.canvas.height=this.height,this.drawingContext=this.canvas.getContext("2d"),this.pixelDensity=1,this.pixels=[]},d.Image.prototype._setProperty=function(a,b){this[a]=b},d.Image.prototype.loadPixels=function(){d.Renderer2D.prototype.loadPixels.call(this)},d.Image.prototype.updatePixels=function(a,b,c,e){d.Renderer2D.prototype.updatePixels.call(this,a,b,c,e)},d.Image.prototype.get=function(a,b,c,e){return d.Renderer2D.prototype.get.call(this,a,b,c,e)},d.Image.prototype.set=function(a,b,c){d.Renderer2D.prototype.set.call(this,a,b,c)},d.Image.prototype.resize=function(a,b){a=a||this.canvas.width,b=b||this.canvas.height;var c=document.createElement("canvas");c.width=a,c.height=b,c.getContext("2d").drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,c.width,c.height),this.canvas.width=this.width=a,this.canvas.height=this.height=b,this.drawingContext.drawImage(c,0,0,a,b,0,0,a,b),this.pixels.length>0&&this.loadPixels()},d.Image.prototype.copy=function(){d.prototype.copy.apply(this,arguments)},d.Image.prototype.mask=function(a){void 0===a&&(a=this);var b=this.drawingContext.globalCompositeOperation,c=1;a instanceof d.Renderer&&(c=a._pInst.pixelDensity);var e=[a,0,0,c*a.width,c*a.height,0,0,this.width,this.height];this.drawingContext.globalCompositeOperation="destination-in",this.copy.apply(this,e),this.drawingContext.globalCompositeOperation=b},d.Image.prototype.filter=function(a,b){e.apply(this.canvas,e[a.toLowerCase()],b)},d.Image.prototype.blend=function(){d.prototype.blend.apply(this,arguments)},d.Image.prototype.save=function(a,b){var c;if(b)switch(b.toLowerCase()){case"png":c="image/png";break;case"jpeg":c="image/jpeg";break;case"jpg":c="image/jpeg";break;default:c="image/png"}else b="png",c="image/png";var e="image/octet-stream",f=this.canvas.toDataURL(c);f=f.replace(c,e),d.prototype.downloadFile(f,a,b)},b.exports=d.Image},{"../core/core":49,"./filters":66}],70:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./filters");a("../color/p5.Color"),d.prototype.pixels=[],d.prototype.blend=function(){this._graphics.blend.apply(this._graphics,arguments)},d.prototype.copy=function(){d.Renderer2D._copyHelper.apply(this,arguments)},d.prototype.filter=function(a,b){e.apply(this.canvas,e[a.toLowerCase()],b)},d.prototype.get=function(a,b,c,d){return this._graphics.get(a,b,c,d)},d.prototype.loadPixels=function(){this._graphics.loadPixels()},d.prototype.set=function(a,b,c){this._graphics.set(a,b,c)},d.prototype.updatePixels=function(a,b,c,d){this._graphics.updatePixels(a,b,c,d)},b.exports=d},{"../color/p5.Color":43,"../core/core":49,"./filters":66}],71:[function(a,b,c){"use strict";function d(a,b){var c={};if(b=b||[],"undefined"==typeof b)for(var d=0;d<a.length;d++)b[d.toString()]=d;for(var e=0;e<b.length;e++){var f=b[e],g=a[e];c[f]=g}return c}function e(a){return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function f(a,b){b&&b!==!0&&"true"!==b||(b=""),a||(a="untitled");var c="";return a&&a.indexOf(".")>-1&&(c=a.split(".").pop()),b&&c!==b&&(c=b,a=a+"."+c),[a,c]}function g(a){document.body.removeChild(a.target)}var h=a("../core/core"),i=a("reqwest"),j=a("opentype.js");a("../core/error_helpers"),h.prototype.loadFont=function(a,b,c){var d=new h.Font(this);return j.load(a,function(a,e){if(a){if("undefined"!=typeof c)return c(a);throw a}d.font=e,"undefined"!=typeof b&&b(d)}),d},h.prototype.createInput=function(){throw"not yet implemented"},h.prototype.createReader=function(){throw"not yet implemented"},h.prototype.loadBytes=function(){throw"not yet implemented"},h.prototype.loadJSON=function(){var a=arguments[0],b=arguments[1],c=[],d="json";return"string"==typeof arguments[2]&&("jsonp"===arguments[2]||"json"===arguments[2])&&(d=arguments[2]),i({url:a,type:d,crossOrigin:!0}).then(function(a){for(var d in a)c[d]=a[d];"undefined"!=typeof b&&b(a)}),c},h.prototype.loadStrings=function(a,b){var c=[],d=new XMLHttpRequest;return d.open("GET",a,!0),d.onreadystatechange=function(){if(4===d.readyState&&200===d.status){var e=d.responseText.match(/[^\r\n]+/g);for(var f in e)c[f]=e[f];"undefined"!=typeof b&&b(c)}else h._friendlyFileLoadError(3,a)},d.send(null),c},h.prototype.loadTable=function(a){for(var b=null,c=[],e=!1,f=",",g=!1,j=1;j<arguments.length;j++)if("function"==typeof arguments[j])b=arguments[j];else if("string"==typeof arguments[j])if(c.push(arguments[j]),"header"===arguments[j]&&(e=!0),"csv"===arguments[j]){if(g)throw new Error("Cannot set multiple separator types.");f=",",g=!0}else if("tsv"===arguments[j]){if(g)throw new Error("Cannot set multiple separator types.");f=" ",g=!0}var k=new h.Table;return i({url:a,crossOrigin:!0,type:"csv"}).then(function(a){a=a.responseText;for(var c,g={},i=0,l=1,m=2,n=4,o='"',p="\r",q="\n",r=[],s=0,t=null,u=function(){g.escaped=!1,t=[],w()},v=function(){g.currentState=n,r.push(t),t=null},w=function(){g.currentState=i,g.token=""},x=function(){t.push(g.token),w()};;){if(c=a[s++],null==c){if(g.escaped)throw new Error("Unclosed quote in file.");if(t){x(),v();break}}if(null===t&&u(),g.currentState===i){if(c===o){g.escaped=!0,g.currentState=l;continue}g.currentState=l}g.currentState===l&&g.escaped?c===o?a[s]===o?(g.token+=o,s++):(g.escaped=!1,g.currentState=m):g.token+=c:c===p?(a[s]===q&&s++,x(),v()):c===q?(x(),v()):c===f?x():g.currentState===l&&(g.token+=c)}if(e)k.columns=r.shift();else for(j=0;j<r.length;j++)k.columns[j]=j.toString();var y;for(j=0;j<r.length&&(j!==r.length-1||1!==r[j].length||"undefined"!==r[j][0]);j++)y=new h.TableRow,y.arr=r[j],y.obj=d(r[j],k.columns),k.addRow(y);null!==b&&b(k)}).fail(function(c,d){h._friendlyFileLoadError(2,a),"undefined"!=typeof b&&b(!1)}),k},h.prototype.loadXML=function(a,b){var c=document.implementation.createDocument(null,null);return i({url:a,type:"xml",crossOrigin:!0,error:function(b){h._friendlyFileLoadError(1,a)}}).then(function(a){var d=a.documentElement;c.appendChild(d),"undefined"!=typeof b&&b(a)}),c},h.prototype.parseXML=function(){throw"not yet implemented"},h.prototype.selectFolder=function(){throw"not yet implemented"},h.prototype.selectInput=function(){throw"not yet implemented"},h.prototype.httpGet=function(){var a=Array.prototype.slice.call(arguments);a.push("GET"),h.prototype.httpDo.apply(this,a)},h.prototype.httpPost=function(){var a=Array.prototype.slice.call(arguments);a.push("POST"),h.prototype.httpDo.apply(this,a)},h.prototype.httpDo=function(){for(var a,b="GET",c=arguments[0],d={},e="",f=1;f<arguments.length;f++){var g=arguments[f];"string"==typeof g?"GET"===g||"POST"===g||"PUT"===g?b=g:e=g:"object"==typeof g?d=g:"function"==typeof g&&(a=g)}""===e&&(e=-1!==c.indexOf("json")?"json":-1!==c.indexOf("xml")?"xml":"text"),i({url:c,method:b,data:d,type:e,crossOrigin:!0,success:function(b){"undefined"!=typeof a&&a("text"===e?b.response:b)}})},window.URL=window.URL||window.webkitURL,h.prototype._pWriters=[],h.prototype.beginRaw=function(){throw"not yet implemented"},h.prototype.beginRecord=function(){throw"not yet implemented"},h.prototype.createOutput=function(){throw"not yet implemented"},h.prototype.createWriter=function(a,b){var c;for(var d in h.prototype._pWriters)if(h.prototype._pWriters[d].name===a)return c=new h.PrintWriter(a+window.millis(),b),h.prototype._pWriters.push(c),c;return c=new h.PrintWriter(a,b),h.prototype._pWriters.push(c),c},h.prototype.endRaw=function(){throw"not yet implemented"},h.prototype.endRecord=function(){throw"not yet implemented"},h.PrintWriter=function(a,b){var c=this;this.name=a,this.content="",this.print=function(a){this.content+=a},this.println=function(a){this.content+=a+"\n"},this.flush=function(){this.content=""},this.close=function(){var d=[];d.push(this.content),h.prototype.writeFile(d,a,b);for(var e in h.prototype._pWriters)h.prototype._pWriters[e].name===this.name&&h.prototype._pWriters.splice(e,1);c.flush(),c={}}},h.prototype.saveBytes=function(){throw"not yet implemented"},h.prototype.save=function(a,b,c){var d=arguments,e=this._curElement.elt;if(0===d.length)return void h.prototype.saveCanvas(e);if(d[0]instanceof h.Renderer||d[0]instanceof h.Graphics)return void h.prototype.saveCanvas(d[0].elt,d[1],d[2]);if(1===d.length&&"string"==typeof d[0])h.prototype.saveCanvas(e,d[0]);else{var g=f(d[1],d[2])[1];switch(g){case"json":return void h.prototype.saveJSON(d[0],d[1],d[2]);case"txt":return void h.prototype.saveStrings(d[0],d[1],d[2]);default:d[0]instanceof Array?h.prototype.saveStrings(d[0],d[1],d[2]):d[0]instanceof h.Table?h.prototype.saveTable(d[0],d[1],d[2],d[3]):d[0]instanceof h.Image?h.prototype.saveCanvas(d[0].canvas,d[1]):d[0]instanceof h.SoundFile&&h.prototype.saveSound(d[0],d[1],d[2],d[3])}}},h.prototype.saveJSON=function(a,b,c){var d;d=c?JSON.stringify(a):JSON.stringify(a,void 0,2),console.log(d),this.saveStrings(d.split("\n"),b,"json")},h.prototype.saveJSONObject=h.prototype.saveJSON,h.prototype.saveJSONArray=h.prototype.saveJSON,h.prototype.saveStream=function(){throw"not yet implemented"},h.prototype.saveStrings=function(a,b,c){var d=c||"txt",e=this.createWriter(b,d);for(var f in a)f<a.length-1?e.println(a[f]):e.print(a[f]);e.close(),e.flush()},h.prototype.saveXML=function(){throw"not yet implemented"},h.prototype.selectOutput=function(){throw"not yet implemented"},h.prototype.saveTable=function(a,b,c){var d=this.createWriter(b,c),f=a.columns,g=",";if("tsv"===c&&(g=" "),"html"!==c){if("0"!==f[0])for(var h=0;h<f.length;h++)h<f.length-1?d.print(f[h]+g):d.println(f[h]);for(var i=0;i<a.rows.length;i++){var j;for(j=0;j<a.rows[i].arr.length;j++)j<a.rows[i].arr.length-1?d.print(a.rows[i].arr[j]+g):i<a.rows.length-1?d.println(a.rows[i].arr[j]):d.print(a.rows[i].arr[j])}}else{d.println("<html>"),d.println("<head>");var k=' <meta http-equiv="content-type" content';if(k+='="text/html;charset=utf-8" />',d.println(k),d.println("</head>"),d.println("<body>"),d.println(" <table>"),"0"!==f[0]){d.println(" <tr>");for(var l=0;l<f.length;l++){var m=e(f[l]);d.println(" <td>"+m),d.println(" </td>")}d.println(" </tr>")}for(var n=0;n<a.rows.length;n++){d.println(" <tr>");for(var o=0;o<a.columns.length;o++){var p=a.rows[n].getString(o),q=e(p);d.println(" <td>"+q),d.println(" </td>")}d.println(" </tr>")}d.println(" </table>"),d.println("</body>"),d.print("</html>")}d.close(),d.flush()},h.prototype.writeFile=function(a,b,c){var d="application/octet-stream";h.prototype._isSafari()&&(d="text/plain");var e=new Blob(a,{type:d}),f=window.URL.createObjectURL(e);h.prototype.downloadFile(f,b,c)},h.prototype.downloadFile=function(a,b,c){var d=f(b,c),e=d[0],i=d[1],j=document.createElement("a");if(j.href=a,j.download=e,j.onclick=g,j.style.display="none",document.body.appendChild(j),h.prototype._isSafari()){var k="Hello, Safari user! To download this file...\n";k+="1. Go to File --> Save As.\n",k+='2. Choose "Page Source" as the Format.\n',k+='3. Name it with this extension: ."'+i+'"',alert(k)}j.click(),a=null},h.prototype._checkFileExtension=f,h.prototype._isSafari=function(){var a=Object.prototype.toString.call(window.HTMLElement);return a.indexOf("Constructor")>0},b.exports=h},{"../core/core":49,"../core/error_helpers":52,"opentype.js":8,reqwest:29}],72:[function(a,b,c){"use strict";var d=a("../core/core");d.Table=function(a){this.columns=[],this.rows=[]},d.Table.prototype.addRow=function(a){var b=a||new d.TableRow;if("undefined"==typeof b.arr||"undefined"==typeof b.obj)throw"invalid TableRow: "+b;return b.table=this,this.rows.push(b),b},d.Table.prototype.removeRow=function(a){this.rows[a].table=null;var b=this.rows.splice(a+1,this.rows.length);this.rows.pop(),this.rows=this.rows.concat(b)},d.Table.prototype.getRow=function(a){return this.rows[a]},d.Table.prototype.getRows=function(){return this.rows},d.Table.prototype.findRow=function(a,b){if("string"==typeof b){for(var c=0;c<this.rows.length;c++)if(this.rows[c].obj[b]===a)return this.rows[c]}else for(var d=0;d<this.rows.length;d++)if(this.rows[d].arr[b]===a)return this.rows[d];return null},d.Table.prototype.findRows=function(a,b){var c=[];if("string"==typeof b)for(var d=0;d<this.rows.length;d++)this.rows[d].obj[b]===a&&c.push(this.rows[d]);else for(var e=0;e<this.rows.length;e++)this.rows[e].arr[b]===a&&c.push(this.rows[e]);return c},d.Table.prototype.matchRow=function(a,b){if("number"==typeof b){for(var c=0;c<this.rows.length;c++)if(this.rows[c].arr[b].match(a))return this.rows[c]}else for(var d=0;d<this.rows.length;d++)if(this.rows[d].obj[b].match(a))return this.rows[d];return null},d.Table.prototype.matchRows=function(a,b){var c=[];if("number"==typeof b)for(var d=0;d<this.rows.length;d++)this.rows[d].arr[b].match(a)&&c.push(this.rows[d]);else for(var e=0;e<this.rows.length;e++)this.rows[e].obj[b].match(a)&&c.push(this.rows[e]);return c},d.Table.prototype.getColumn=function(a){var b=[];if("string"==typeof a)for(var c=0;c<this.rows.length;c++)b.push(this.rows[c].obj[a]);else for(var d=0;d<this.rows.length;d++)b.push(this.rows[d].arr[a]);return b},d.Table.prototype.clearRows=function(){delete this.rows,this.rows=[]},d.Table.prototype.addColumn=function(a){var b=a||null;this.columns.push(b)},d.Table.prototype.getColumnCount=function(){return this.columns.length},d.Table.prototype.getRowCount=function(){return this.rows.length},d.Table.prototype.removeTokens=function(a,b){for(var c=function(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")},d=[],e=0;e<a.length;e++)d.push(c(a.charAt(e)));var f=new RegExp(d.join("|"),"g");if("undefined"==typeof b)for(var g=0;g<this.columns.length;g++)for(var h=0;h<this.rows.length;h++){var i=this.rows[h].arr[g];i=i.replace(f,""),this.rows[h].arr[g]=i,this.rows[h].obj[this.columns[g]]=i}else if("string"==typeof b)for(var j=0;j<this.rows.length;j++){var k=this.rows[j].obj[b];k=k.replace(f,""),this.rows[j].obj[b]=k;var l=this.columns.indexOf(b);this.rows[j].arr[l]=k}else for(var m=0;m<this.rows.length;m++){var n=this.rows[m].arr[b];n=n.replace(f,""),this.rows[m].arr[b]=n,this.rows[m].obj[this.columns[b]]=n}},d.Table.prototype.trim=function(a){var b=new RegExp(" ","g");if("undefined"==typeof a)for(var c=0;c<this.columns.length;c++)for(var d=0;d<this.rows.length;d++){var e=this.rows[d].arr[c];e=e.replace(b,""),this.rows[d].arr[c]=e,this.rows[d].obj[this.columns[c]]=e}else if("string"==typeof a)for(var f=0;f<this.rows.length;f++){var g=this.rows[f].obj[a];g=g.replace(b,""),this.rows[f].obj[a]=g;var h=this.columns.indexOf(a);this.rows[f].arr[h]=g}else for(var i=0;i<this.rows.length;i++){var j=this.rows[i].arr[a];j=j.replace(b,""),this.rows[i].arr[a]=j,this.rows[i].obj[this.columns[a]]=j}},d.Table.prototype.removeColumn=function(a){var b,c;"string"==typeof a?(b=a,c=this.columns.indexOf(a),console.log("string")):(c=a,b=this.columns[a]);var d=this.columns.splice(c+1,this.columns.length);this.columns.pop(),this.columns=this.columns.concat(d);for(var e=0;e<this.rows.length;e++){var f=this.rows[e].arr,g=f.splice(c+1,f.length);f.pop(),this.rows[e].arr=f.concat(g),delete this.rows[e].obj[b]}},d.Table.prototype.set=function(a,b,c){this.rows[a].set(b,c)},d.Table.prototype.setNum=function(a,b,c){this.rows[a].set(b,c); | |
},d.Table.prototype.setString=function(a,b,c){this.rows[a].set(b,c)},d.Table.prototype.get=function(a,b){return this.rows[a].get(b)},d.Table.prototype.getNum=function(a,b){return this.rows[a].getNum(b)},d.Table.prototype.getString=function(a,b){return this.rows[a].getString(b)},d.Table.prototype.getObject=function(a){for(var b,c,d,e={},f=0;f<this.rows.length;f++)if(b=this.rows[f].obj,"string"==typeof a){if(c=this.columns.indexOf(a),!(c>=0))throw'This table has no column named "'+a+'"';d=b[a],e[d]=b}else e[f]=this.rows[f].obj;return e},d.Table.prototype.getArray=function(){for(var a=[],b=0;b<this.rows.length;b++)a.push(this.rows[b].arr);return a},b.exports=d.Table},{"../core/core":49}],73:[function(a,b,c){"use strict";var d=a("../core/core");d.TableRow=function(a,b){var c=[],d={};a&&(b=b||",",c=a.split(b));for(var e=0;e<c.length;e++){var f=e,g=c[e];d[f]=g}this.arr=c,this.obj=d,this.table=null},d.TableRow.prototype.set=function(a,b){if("string"==typeof a){var c=this.table.columns.indexOf(a);if(!(c>=0))throw'This table has no column named "'+a+'"';this.obj[a]=b,this.arr[c]=b}else{if(!(a<this.table.columns.length))throw"Column #"+a+" is out of the range of this table";this.arr[a]=b;var d=this.table.columns[a];this.obj[d]=b}},d.TableRow.prototype.setNum=function(a,b){var c=parseFloat(b,10);this.set(a,c)},d.TableRow.prototype.setString=function(a,b){var c=b.toString();this.set(a,c)},d.TableRow.prototype.get=function(a){return"string"==typeof a?this.obj[a]:this.arr[a]},d.TableRow.prototype.getNum=function(a){var b;if(b="string"==typeof a?parseFloat(this.obj[a],10):parseFloat(this.arr[a],10),"NaN"===b.toString())throw"Error: "+this.obj[a]+" is NaN (Not a Number)";return b},d.TableRow.prototype.getString=function(a){return"string"==typeof a?this.obj[a].toString():this.arr[a].toString()},b.exports=d.TableRow},{"../core/core":49}],74:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.abs=Math.abs,d.prototype.ceil=Math.ceil,d.prototype.constrain=function(a,b,c){return Math.max(Math.min(a,c),b)},d.prototype.dist=function(a,b,c,d){return Math.sqrt((c-a)*(c-a)+(d-b)*(d-b))},d.prototype.exp=Math.exp,d.prototype.floor=Math.floor,d.prototype.lerp=function(a,b,c){return c*(b-a)+a},d.prototype.log=Math.log,d.prototype.mag=function(a,b){return Math.sqrt(a*a+b*b)},d.prototype.map=function(a,b,c,d,e){return(a-b)/(c-b)*(e-d)+d},d.prototype.max=function(){return arguments[0]instanceof Array?Math.max.apply(null,arguments[0]):Math.max.apply(null,arguments)},d.prototype.min=function(){return arguments[0]instanceof Array?Math.min.apply(null,arguments[0]):Math.min.apply(null,arguments)},d.prototype.norm=function(a,b,c){return this.map(a,b,c,0,1)},d.prototype.pow=Math.pow,d.prototype.round=Math.round,d.prototype.sq=function(a){return a*a},d.prototype.sqrt=Math.sqrt,b.exports=d},{"../core/core":49}],75:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.createVector=function(a,b,c){return this instanceof d?new d.Vector(this,arguments):new d.Vector(a,b,c)},b.exports=d},{"../core/core":49}],76:[function(a,b,c){"use strict";var d,e=a("../core/core"),f=4,g=1<<f,h=8,i=1<<h,j=4095,k=4,l=.5,m=function(a){return.5*(1-Math.cos(a*Math.PI))};e.prototype.noise=function(a,b,c){if(b=b||0,c=c||0,null==d){d=new Array(j+1);for(var e=0;j+1>e;e++)d[e]=Math.random()}0>a&&(a=-a),0>b&&(b=-b),0>c&&(c=-c);for(var n,o,p,q,r,s=Math.floor(a),t=Math.floor(b),u=Math.floor(c),v=a-s,w=b-t,x=c-u,y=0,z=.5,A=0;k>A;A++){var B=s+(t<<f)+(u<<h);n=m(v),o=m(w),p=d[B&j],p+=n*(d[B+1&j]-p),q=d[B+g&j],q+=n*(d[B+g+1&j]-q),p+=o*(q-p),B+=i,q=d[B&j],q+=n*(d[B+1&j]-q),r=d[B+g&j],r+=n*(d[B+g+1&j]-r),q+=o*(r-q),p+=m(x)*(q-p),y+=p*z,z*=l,s<<=1,v*=2,t<<=1,w*=2,u<<=1,x*=2,v>=1&&(s++,v--),w>=1&&(t++,w--),x>=1&&(u++,x--)}return y},e.prototype.noiseDetail=function(a,b){a>0&&(k=a),b>0&&(l=b)},e.prototype.noiseSeed=function(a){var b=function(){var a,b,c=4294967296,d=1664525,e=1013904223;return{setSeed:function(d){b=a=(null==d?Math.random()*c:d)>>>0},getSeed:function(){return a},rand:function(){return b=(d*b+e)%c,b/c}}}();b.setSeed(a),d=new Array(j+1);for(var c=0;j+1>c;c++)d[c]=b.rand()},b.exports=e},{"../core/core":49}],77:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./polargeometry"),f=a("../core/constants");d.Vector=function(){var a,b,c;arguments[0]instanceof d?(this.p5=arguments[0],a=arguments[1][0]||0,b=arguments[1][1]||0,c=arguments[1][2]||0):(a=arguments[0]||0,b=arguments[1]||0,c=arguments[2]||0),this.x=a,this.y=b,this.z=c},d.Vector.prototype.toString=function(){return"p5.Vector Object : ["+this.x+", "+this.y+", "+this.z+"]"},d.Vector.prototype.set=function(a,b,c){return a instanceof d.Vector?(this.x=a.x||0,this.y=a.y||0,this.z=a.z||0,this):a instanceof Array?(this.x=a[0]||0,this.y=a[1]||0,this.z=a[2]||0,this):(this.x=a||0,this.y=b||0,this.z=c||0,this)},d.Vector.prototype.copy=function(){return this.p5?new d.Vector(this.p5,[this.x,this.y,this.z]):new d.Vector(this.x,this.y,this.z)},d.Vector.prototype.add=function(a,b,c){return a instanceof d.Vector?(this.x+=a.x||0,this.y+=a.y||0,this.z+=a.z||0,this):a instanceof Array?(this.x+=a[0]||0,this.y+=a[1]||0,this.z+=a[2]||0,this):(this.x+=a||0,this.y+=b||0,this.z+=c||0,this)},d.Vector.prototype.sub=function(a,b,c){return a instanceof d.Vector?(this.x-=a.x||0,this.y-=a.y||0,this.z-=a.z||0,this):a instanceof Array?(this.x-=a[0]||0,this.y-=a[1]||0,this.z-=a[2]||0,this):(this.x-=a||0,this.y-=b||0,this.z-=c||0,this)},d.Vector.prototype.mult=function(a){return this.x*=a||0,this.y*=a||0,this.z*=a||0,this},d.Vector.prototype.div=function(a){return this.x/=a,this.y/=a,this.z/=a,this},d.Vector.prototype.mag=function(){return Math.sqrt(this.magSq())},d.Vector.prototype.magSq=function(){var a=this.x,b=this.y,c=this.z;return a*a+b*b+c*c},d.Vector.prototype.dot=function(a,b,c){return a instanceof d.Vector?this.dot(a.x,a.y,a.z):this.x*(a||0)+this.y*(b||0)+this.z*(c||0)},d.Vector.prototype.cross=function(a){var b=this.y*a.z-this.z*a.y,c=this.z*a.x-this.x*a.z,e=this.x*a.y-this.y*a.x;return this.p5?new d.Vector(this.p5,[b,c,e]):new d.Vector(b,c,e)},d.Vector.prototype.dist=function(a){var b=a.copy().sub(this);return b.mag()},d.Vector.prototype.normalize=function(){return this.div(this.mag())},d.Vector.prototype.limit=function(a){var b=this.magSq();return b>a*a&&(this.div(Math.sqrt(b)),this.mult(a)),this},d.Vector.prototype.setMag=function(a){return this.normalize().mult(a)},d.Vector.prototype.heading=function(){var a=Math.atan2(this.y,this.x);return this.p5?this.p5._angleMode===f.RADIANS?a:e.radiansToDegrees(a):a},d.Vector.prototype.rotate=function(a){this.p5&&this.p5._angleMode===f.DEGREES&&(a=e.degreesToRadians(a));var b=this.heading()+a,c=this.mag();return this.x=Math.cos(b)*c,this.y=Math.sin(b)*c,this},d.Vector.prototype.lerp=function(a,b,c,e){return a instanceof d.Vector?this.lerp(a.x,a.y,a.z,b):(this.x+=(a-this.x)*e||0,this.y+=(b-this.y)*e||0,this.z+=(c-this.z)*e||0,this)},d.Vector.prototype.array=function(){return[this.x||0,this.y||0,this.z||0]},d.Vector.prototype.equals=function(a,b,c){var e,f,g;return a instanceof d.Vector?(e=a.x||0,f=a.y||0,g=a.z||0):a instanceof Array?(e=a[0]||0,f=a[1]||0,g=a[2]||0):(e=a||0,f=b||0,g=c||0),this.x===e&&this.y===f&&this.z===g},d.Vector.fromAngle=function(a){return this.p5&&this.p5._angleMode===f.DEGREES&&(a=e.degreesToRadians(a)),this.p5?new d.Vector(this.p5,[Math.cos(a),Math.sin(a),0]):new d.Vector(Math.cos(a),Math.sin(a),0)},d.Vector.random2D=function(){var a;return a=this.p5?this.p5._angleMode===f.DEGREES?this.p5.random(360):this.p5.random(f.TWO_PI):Math.random()*Math.PI*2,this.fromAngle(a)},d.Vector.random3D=function(){var a,b;this.p5?(a=this.p5.random(0,f.TWO_PI),b=this.p5.random(-1,1)):(a=Math.random()*Math.PI*2,b=2*Math.random()-1);var c=Math.sqrt(1-b*b)*Math.cos(a),e=Math.sqrt(1-b*b)*Math.sin(a);return this.p5?new d.Vector(this.p5,[c,e,b]):new d.Vector(c,e,b)},d.Vector.add=function(a,b,c){return c?c.set(a):c=a.copy(),c.add(b),c},d.Vector.sub=function(a,b,c){return c?c.set(a):c=a.copy(),c.sub(b),c},d.Vector.mult=function(a,b,c){return c?c.set(a):c=a.copy(),c.mult(b),c},d.Vector.div=function(a,b,c){return c?c.set(a):c=a.copy(),c.div(b),c},d.Vector.dot=function(a,b){return a.dot(b)},d.Vector.cross=function(a,b){return a.cross(b)},d.Vector.dist=function(a,b){return a.dist(b)},d.Vector.lerp=function(a,b,c,d){return d?d.set(a):d=a.copy(),d.lerp(b,c),d},d.Vector.angleBetween=function(a,b){var c=Math.acos(a.dot(b)/(a.mag()*b.mag()));return this.p5&&this.p5._angleMode===f.DEGREES&&(c=e.radiansToDegrees(c)),c},b.exports=d.Vector},{"../core/constants":48,"../core/core":49,"./polargeometry":78}],78:[function(a,b,c){b.exports={degreesToRadians:function(a){return 2*Math.PI*a/360},radiansToDegrees:function(a){return 360*a/(2*Math.PI)}}},{}],79:[function(a,b,c){"use strict";var d=a("../core/core"),e=!1,f=function(){var a,b,c=4294967296,d=1664525,e=1013904223;return{setSeed:function(d){b=a=(null==d?Math.random()*c:d)>>>0},getSeed:function(){return a},rand:function(){return b=(d*b+e)%c,b/c}}}();d.prototype.randomSeed=function(a){f.setSeed(a),e=!0},d.prototype.random=function(a,b){var c;if(c=e?f.rand():Math.random(),0===arguments.length)return c;if(1===arguments.length)return c*a;if(a>b){var d=a;a=b,b=d}return c*(b-a)+a};var g,h=!1;d.prototype.randomGaussian=function(a,b){var c,d,e,f;if(h)c=g,h=!1;else{do d=this.random(2)-1,e=this.random(2)-1,f=d*d+e*e;while(f>=1);f=Math.sqrt(-2*Math.log(f)/f),c=d*f,g=e*f,h=!0}var i=a||0,j=b||1;return c*j+i},b.exports=d},{"../core/core":49}],80:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./polargeometry"),f=a("../core/constants");d.prototype._angleMode=f.RADIANS,d.prototype.acos=function(a){return this._angleMode===f.RADIANS?Math.acos(a):e.radiansToDegrees(Math.acos(a))},d.prototype.asin=function(a){return this._angleMode===f.RADIANS?Math.asin(a):e.radiansToDegrees(Math.asin(a))},d.prototype.atan=function(a){return this._angleMode===f.RADIANS?Math.atan(a):e.radiansToDegrees(Math.atan(a))},d.prototype.atan2=function(a,b){return this._angleMode===f.RADIANS?Math.atan2(a,b):e.radiansToDegrees(Math.atan2(a,b))},d.prototype.cos=function(a){return this._angleMode===f.RADIANS?Math.cos(a):Math.cos(this.radians(a))},d.prototype.sin=function(a){return this._angleMode===f.RADIANS?Math.sin(a):Math.sin(this.radians(a))},d.prototype.tan=function(a){return this._angleMode===f.RADIANS?Math.tan(a):Math.tan(this.radians(a))},d.prototype.degrees=function(a){return e.radiansToDegrees(a)},d.prototype.radians=function(a){return e.degreesToRadians(a)},d.prototype.angleMode=function(a){(a===f.DEGREES||a===f.RADIANS)&&(this._angleMode=a)},b.exports=d},{"../core/constants":48,"../core/core":49,"./polargeometry":78}],81:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants");d.prototype._textSize=12,d.prototype._textLeading=15,d.prototype._textFont="sans-serif",d.prototype._textStyle=e.NORMAL,d.prototype._textAscent=null,d.prototype._textDescent=null,d.prototype.textAlign=function(a,b){return this._graphics.textAlign(a,b)},d.prototype.textLeading=function(a){return arguments.length?(this._setProperty("_textLeading",a),this):this._textLeading},d.prototype.textSize=function(a){return arguments.length?(this._setProperty("_textSize",a),this._setProperty("_textLeading",a*e._DEFAULT_LEADMULT),this._graphics._applyTextProperties()):this._textSize},d.prototype.textStyle=function(a){return arguments.length?((a===e.NORMAL||a===e.ITALIC||a===e.BOLD)&&this._setProperty("_textStyle",a),this._graphics._applyTextProperties()):this._textStyle},d.prototype.textWidth=function(a){return this._graphics.textWidth(a)},d.prototype.textAscent=function(){return null===this._textAscent&&this._updateTextMetrics(),this._textAscent},d.prototype.textDescent=function(){return null===this._textDescent&&this._updateTextMetrics(),this._textDescent},d.prototype._isOpenType=function(a){return a=a||this._textFont,"object"==typeof a&&a.font&&a.font.supported},d.prototype._updateTextMetrics=function(){if(this._isOpenType())return this._setProperty("_textAscent",this._textFont._textAscent()),this._setProperty("_textDescent",this._textFont._textDescent()),this;var a=document.createElement("span");a.style.fontFamily=this._textFont,a.style.fontSize=this._textSize+"px",a.innerHTML="ABCjgq|";var b=document.createElement("div");b.style.display="inline-block",b.style.width="1px",b.style.height="0px";var c=document.createElement("div");c.appendChild(a),c.appendChild(b),c.style.height="0px",c.style.overflow="hidden",document.body.appendChild(c),b.style.verticalAlign="baseline";var d=this._calculateOffset(b),e=this._calculateOffset(a),f=d[1]-e[1];b.style.verticalAlign="bottom",d=this._calculateOffset(b),e=this._calculateOffset(a);var g=d[1]-e[1],h=g-f;return document.body.removeChild(c),this._setProperty("_textAscent",f),this._setProperty("_textDescent",h),this},d.prototype._calculateOffset=function(a){var b=0,c=0;if(a.offsetParent){do b+=a.offsetLeft,c+=a.offsetTop;while(a=a.offsetParent)}else b+=a.offsetLeft,c+=a.offsetTop;return[b,c]},b.exports=d},{"../core/constants":48,"../core/core":49}],82:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants");a("../core/error_helpers"),d.prototype.text=function(a,b,c,d,e){return this._validateParameters("text",arguments,[["*","Number","Number"],["*","Number","Number","Number","Number"]]),this._doFill||this._doStroke?this._graphics.text.apply(this._graphics,arguments):this},d.prototype.textFont=function(a,b){if(arguments.length){if(!a)throw Error("null font passed to textFont");return this._setProperty("_textFont",a),b&&(this._setProperty("_textSize",b),this._setProperty("_textLeading",b*e._DEFAULT_LEADMULT)),this._graphics._applyTextProperties()}return this},b.exports=d},{"../core/constants":48,"../core/core":49,"../core/error_helpers":52}],83:[function(a,b,c){"use strict";function d(){for(var a=Array.prototype.slice.call(arguments),b=a.length,c="";b--;)c+=a[b]===Object(a[b])?JSON.stringify(a[b]):a[b];return c}var e=a("../core/core"),f=a("../core/constants");e.Font=function(a){this.parent=a,this.cache={},this.font=void 0},e.Font.prototype.list=function(){throw"not yet implemented"},e.Font.prototype.textBounds=function(a,b,c,e,f){b=void 0!==b?b:0,c=void 0!==c?c:0,e=e||this.parent._textSize;var g=this.cache[d("textBounds",a,b,c,e)];if(!g){var h,i,j,k,l=[],m=[],n=this,o=this._scale(e);this.font.forEachGlyph(a,b,c,e,f,function(a,b,c,d){if(l.push(b),m.push(c),"space"!==a.name){var f=a.getMetrics();l.push(b+f.xMax*o),m.push(c+-f.yMin*o),m.push(c+-f.yMax*o)}else l.push(b+n.font.charToGlyph(" ").advanceWidth*n._scale(e))}),h=Math.max(0,Math.min.apply(null,l)),i=Math.max(0,Math.min.apply(null,m)),j=Math.max(0,Math.max.apply(null,l)),k=Math.max(0,Math.max.apply(null,m)),g={x:h,y:i,h:k-i,w:j-h,advance:h-b},this.cache[d("textBounds",a,b,c,e)]=g}return g},e.Font.prototype._getGlyphs=function(a){return this.font.stringToGlyphs(a)},e.Font.prototype._getPath=function(a,b,c,d){var e=this.parent,f=e._graphics.drawingContext,g=this._handleAlignment(e,f,a,b,c);return this.font.getPath(a,g.x,g.y,e._textSize,d)},e.Font.prototype._getPathData=function(a,b,c,d){var e=3;return"string"==typeof a&&arguments.length>2?a=this._getPath(a,b,c,d):"object"==typeof b&&(d=b),d&&"number"==typeof d.decimals&&(e=d.decimals),a.toPathData(e)},e.Font.prototype._getSVG=function(a,b,c,d){var e=3;return"string"==typeof a&&arguments.length>2?a=this._getPath(a,b,c,d):"object"==typeof b&&(d=b),d&&("number"==typeof d.decimals&&(e=d.decimals),"number"==typeof d.strokeWidth&&(a.strokeWidth=d.strokeWidth),"undefined"!=typeof d.fill&&(a.fill=d.fill),"undefined"!=typeof d.stroke&&(a.stroke=d.stroke)),a.toSVG(e)},e.Font.prototype._renderPath=function(a,b,c,d){var e,g=this.parent,h=g._graphics,i=h.drawingContext;e="object"==typeof a&&a.commands?a.commands:this._getPath(a,b,c,g._textSize,d).commands,i.beginPath();for(var j=0;j<e.length;j+=1){var k=e[j];"M"===k.type?i.moveTo(k.x,k.y):"L"===k.type?i.lineTo(k.x,k.y):"C"===k.type?i.bezierCurveTo(k.x1,k.y1,k.x2,k.y2,k.x,k.y):"Q"===k.type?i.quadraticCurveTo(k.x1,k.y1,k.x,k.y):"Z"===k.type&&i.closePath()}return g._doStroke&&g._strokeSet&&i.stroke(),g._doFill&&(i.fillStyle=g._fillSet?i.fillStyle:f._DEFAULT_TEXT_FILL,i.fill()),this},e.Font.prototype._textWidth=function(a,b){if(" "===a)return this.font.charToGlyph(" ").advanceWidth*this._scale(b);var c=this.textBounds(a,0,0,b);return c.w+c.advance},e.Font.prototype._textAscent=function(a){return this.font.ascender*this._scale(a)},e.Font.prototype._textDescent=function(a){return-this.font.descender*this._scale(a)},e.Font.prototype._scale=function(a){return 1/this.font.unitsPerEm*(a||this.parent._textSize)},e.Font.prototype._handleAlignment=function(a,b,c,d,e){var g=this._textWidth(c),h=this._textAscent(),i=this._textDescent(),j=h+i;return b.textAlign===f.CENTER?d-=g/2:b.textAlign===f.RIGHT&&(d-=g),b.textBaseline===f.TOP?e+=j:b.textBaseline===f._CTX_MIDDLE?e+=j/2-i:b.textBaseline===f.BOTTOM&&(e-=i),{x:d,y:e}},b.exports=e.Font},{"../core/constants":48,"../core/core":49}],84:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.append=function(a,b){return a.push(b),a},d.prototype.arrayCopy=function(a,b,c,d,e){var f,g;"undefined"!=typeof e?(g=Math.min(e,a.length),f=d,a=a.slice(b,g+b)):("undefined"!=typeof c?(g=c,g=Math.min(g,a.length)):g=a.length,f=0,c=b,a=a.slice(0,g)),Array.prototype.splice.apply(c,[f,g].concat(a))},d.prototype.concat=function(a,b){return a.concat(b)},d.prototype.reverse=function(a){return a.reverse()},d.prototype.shorten=function(a){return a.pop(),a},d.prototype.shuffle=function(a,b){a=b||ArrayBuffer.isView(a)?a:a.slice();for(var c,d,e=a.length;e>1;)c=Math.random()*e|0,d=a[--e],a[e]=a[c],a[c]=d;return a},d.prototype.sort=function(a,b){var c=b?a.slice(0,Math.min(b,a.length)):a,d=b?a.slice(Math.min(b,a.length)):[];return c="string"==typeof c[0]?c.sort():c.sort(function(a,b){return a-b}),c.concat(d)},d.prototype.splice=function(a,b,c){return Array.prototype.splice.apply(a,[c,0].concat(b)),a},d.prototype.subset=function(a,b,c){return"undefined"!=typeof c?a.slice(b,b+c):a.slice(b,a.length)},b.exports=d},{"../core/core":49}],85:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype["float"]=function(a){return parseFloat(a)},d.prototype["int"]=function(a,b){return"string"==typeof a?(b=b||10,parseInt(a,b)):"number"==typeof a?0|a:"boolean"==typeof a?a?1:0:a instanceof Array?a.map(function(a){return d.prototype["int"](a,b)}):void 0},d.prototype.str=function(a){return a instanceof Array?a.map(d.prototype.str):String(a)},d.prototype["boolean"]=function(a){return"number"==typeof a?0!==a:"string"==typeof a?"true"===a.toLowerCase():"boolean"==typeof a?a:a instanceof Array?a.map(d.prototype["boolean"]):void 0},d.prototype["byte"]=function(a){var b=d.prototype["int"](a,10);return"number"==typeof b?(b+128)%256-128:b instanceof Array?b.map(d.prototype["byte"]):void 0},d.prototype["char"]=function(a){return"number"!=typeof a||isNaN(a)?a instanceof Array?a.map(d.prototype["char"]):"string"==typeof a?d.prototype["char"](parseInt(a,10)):void 0:String.fromCharCode(a)},d.prototype.unchar=function(a){return"string"==typeof a&&1===a.length?a.charCodeAt(0):a instanceof Array?a.map(d.prototype.unchar):void 0},d.prototype.hex=function(a,b){if(b=void 0===b||null===b?b=8:b,a instanceof Array)return a.map(function(a){return d.prototype.hex(a,b)});if("number"==typeof a){0>a&&(a=4294967295+a+1);for(var c=Number(a).toString(16).toUpperCase();c.length<b;)c="0"+c;return c.length>=b&&(c=c.substring(c.length-b,c.length)),c}},d.prototype.unhex=function(a){return a instanceof Array?a.map(d.prototype.unhex):parseInt("0x"+a,16)},b.exports=d},{"../core/core":49}],86:[function(a,b,c){"use strict";function d(){var a=arguments[0],b=0>a,c=b?a.toString().substring(1):a.toString(),d=c.indexOf("."),e=-1!==d?c.substring(0,d):c,f=-1!==d?c.substring(d+1):"",g=b?"-":"";if(3===arguments.length){var h="";(-1!==d||arguments[2]-f.length>0)&&(h="."),f.length>arguments[2]&&(f=f.substring(0,arguments[2]));for(var i=0;i<arguments[1]-e.length;i++)g+="0";g+=e,g+=h,g+=f;for(var j=0;j<arguments[2]-f.length;j++)g+="0";return g}for(var k=0;k<Math.max(arguments[1]-e.length,0);k++)g+="0";return g+=c}function e(){var a=arguments[0].toString(),b=a.indexOf("."),c=-1!==b?a.substring(b):"",d=-1!==b?a.substring(0,b):a;if(d=d.toString().replace(/\B(?=(\d{3})+(?!\d))/g,","),0===arguments[1])c="";else if(void 0!==arguments[1])if(arguments[1]>c.length){c+=-1===b?".":"";for(var e=arguments[1]-c.length+1,f=0;e>f;f++)c+="0"}else c=c.substring(0,arguments[1]+1);return d+c}function f(){return parseFloat(arguments[0])>0?"+"+arguments[0].toString():arguments[0].toString()}function g(){return parseFloat(arguments[0])>0?" "+arguments[0].toString():arguments[0].toString()}var h=a("../core/core");h.prototype.join=function(a,b){return a.join(b)},h.prototype.match=function(a,b){return a.match(b)},h.prototype.matchAll=function(a,b){for(var c=new RegExp(b,"g"),d=c.exec(a),e=[];null!==d;)e.push(d),d=c.exec(a);return e},h.prototype.nf=function(){if(arguments[0]instanceof Array){var a=arguments[1],b=arguments[2];return arguments[0].map(function(c){return d(c,a,b)})}var c=Object.prototype.toString.call(arguments[0]);return"[object Arguments]"===c?3===arguments[0].length?this.nf(arguments[0][0],arguments[0][1],arguments[0][2]):2===arguments[0].length?this.nf(arguments[0][0],arguments[0][1]):this.nf(arguments[0][0]):d.apply(this,arguments)},h.prototype.nfc=function(){if(arguments[0]instanceof Array){var a=arguments[1];return arguments[0].map(function(b){return e(b,a)})}return e.apply(this,arguments)},h.prototype.nfp=function(){var a=this.nf.apply(this,arguments);return a instanceof Array?a.map(f):f(a)},h.prototype.nfs=function(){var a=this.nf.apply(this,arguments);return a instanceof Array?a.map(g):g(a)},h.prototype.split=function(a,b){return a.split(b)},h.prototype.splitTokens=function(){var a=arguments.length>0?arguments[1]:/\s/g;return arguments[0].split(a).filter(function(a){return a})},h.prototype.trim=function(a){return a instanceof Array?a.map(this.trim):a.trim()},b.exports=h},{"../core/core":49}],87:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.day=function(){return(new Date).getDate()},d.prototype.hour=function(){return(new Date).getHours()},d.prototype.minute=function(){return(new Date).getMinutes()},d.prototype.millis=function(){return window.performance.now()},d.prototype.month=function(){return(new Date).getMonth()+1},d.prototype.second=function(){return(new Date).getSeconds()},d.prototype.year=function(){return(new Date).getFullYear()},b.exports=d},{"../core/core":49}]},{},[40])(40)});p5.prototype._validateParameters = function() {};p5.prototype._friendlyFileLoadError = function() {}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! p5.sound.min.js v0.2.13 2015-07-17 */ | |
!function(t,e){"function"==typeof define&&define.amd?define("p5.sound",["p5"],function(t){e(t)}):e("object"==typeof exports?require("../p5"):t.p5)}(this,function(t){var e;e=function(){"use strict";window.AudioContext=window.AudioContext||window.webkitAudioContext;var e=new window.AudioContext;t.prototype.getAudioContext=function(){return e},"function"!=typeof e.createGain&&(window.audioContext.createGain=window.audioContext.createGainNode),"function"!=typeof e.createDelay&&(window.audioContext.createDelay=window.audioContext.createDelayNode),"function"!=typeof window.AudioBufferSourceNode.prototype.start&&(window.AudioBufferSourceNode.prototype.start=window.AudioBufferSourceNode.prototype.noteGrainOn),"function"!=typeof window.AudioBufferSourceNode.prototype.stop&&(window.AudioBufferSourceNode.prototype.stop=window.AudioBufferSourceNode.prototype.noteOff),"function"!=typeof window.OscillatorNode.prototype.start&&(window.OscillatorNode.prototype.start=window.OscillatorNode.prototype.noteOn),"function"!=typeof window.OscillatorNode.prototype.stop&&(window.OscillatorNode.prototype.stop=window.OscillatorNode.prototype.noteOff),window.AudioContext.prototype.hasOwnProperty("createScriptProcessor")||(window.AudioContext.prototype.createScriptProcessor=window.AudioContext.prototype.createJavaScriptNode),navigator.getUserMedia=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;var i=document.createElement("audio");t.prototype.isSupported=function(){return!!i.canPlayType};var o=function(){return!!i.canPlayType&&i.canPlayType('audio/ogg; codecs="vorbis"')},n=function(){return!!i.canPlayType&&i.canPlayType("audio/mpeg;")},s=function(){return!!i.canPlayType&&i.canPlayType('audio/wav; codecs="1"')},r=function(){return!!i.canPlayType&&(i.canPlayType("audio/x-m4a;")||i.canPlayType("audio/aac;"))},a=function(){return!!i.canPlayType&&i.canPlayType("audio/x-aiff;")};t.prototype.isFileSupported=function(t){switch(t.toLowerCase()){case"mp3":return n();case"wav":return s();case"ogg":return o();case"mp4":return r();case"aiff":return a();default:return!1}};var u=navigator.userAgent.match(/(iPad|iPhone|iPod)/g)?!0:!1;u&&window.addEventListener("touchstart",function(){var t=e.createBuffer(1,1,22050),i=e.createBufferSource();i.buffer=t,i.connect(e.destination),i.start(0)},!1)}();var i;i=function(){"use strict";var e=function(){var e=t.prototype.getAudioContext();this.input=e.createGain(),this.output=e.createGain(),this.limiter=e.createDynamicsCompressor(),this.limiter.threshold.value=0,this.limiter.ratio.value=100,this.audiocontext=e,this.output.disconnect(),this.inputSources=[],this.input.connect(this.limiter),this.limiter.connect(this.output),this.meter=e.createGain(),this.fftMeter=e.createGain(),this.output.connect(this.meter),this.output.connect(this.fftMeter),this.output.connect(this.audiocontext.destination),this.soundArray=[],this.parts=[],this.extensions=[]},i=new e;return t.prototype.getMasterVolume=function(){return i.output.gain.value},t.prototype.masterVolume=function(t,e,o){if("number"==typeof t){var e=e||0,o=o||0,n=i.audiocontext.currentTime,s=i.output.gain.value;i.output.gain.cancelScheduledValues(n+o),i.output.gain.linearRampToValueAtTime(s,n+o),i.output.gain.linearRampToValueAtTime(t,n+o+e)}else{if(!t)return i.output.gain;t.connect(i.output.gain)}},t.soundOut=i,t.soundOut._silentNode=i.audiocontext.createGain(),t.soundOut._silentNode.gain.value=0,t.soundOut._silentNode.connect(i.audiocontext.destination),i}(e);var o;o=function(){"use strict";var e=i;t.prototype.sampleRate=function(){return e.audiocontext.sampleRate},t.prototype.freqToMidi=function(t){var e=Math.log(t/440)/Math.log(2),i=Math.round(12*e)+57;return i},t.prototype.midiToFreq=function(t){return 440*Math.pow(2,(t-69)/12)},t.prototype.soundFormats=function(){e.extensions=[];for(var t=0;t<arguments.length;t++){if(arguments[t]=arguments[t].toLowerCase(),!(["mp3","wav","ogg","m4a","aac"].indexOf(arguments[t])>-1))throw arguments[t]+" is not a valid sound format!";e.extensions.push(arguments[t])}},t.prototype.disposeSound=function(){for(var t=0;t<e.soundArray.length;t++)e.soundArray[t].dispose()},t.prototype.registerMethod("remove",t.prototype.disposeSound),t.prototype._checkFileFormats=function(i){var o;if("string"==typeof i){o=i;var n=o.split(".").pop();if(["mp3","wav","ogg","m4a","aac"].indexOf(n)>-1){var s=t.prototype.isFileSupported(n);if(s)o=o;else for(var r=o.split("."),a=r[r.length-1],u=0;u<e.extensions.length;u++){var c=e.extensions[u],s=t.prototype.isFileSupported(c);if(s){a="",2===r.length&&(a+=r[0]);for(var u=1;u<=r.length-2;u++){var h=r[u];a+="."+h}o=a+=".",o=o+=c;break}}}else for(var u=0;u<e.extensions.length;u++){var c=e.extensions[u],s=t.prototype.isFileSupported(c);if(s){o=o+"."+c;break}}}else if("object"==typeof i)for(var u=0;u<i.length;u++){var c=i[u].split(".").pop(),s=t.prototype.isFileSupported(c);if(s){o=i[u];break}}return o},t.prototype._mathChain=function(t,e,i,o,n){for(var s in t.mathOps)t.mathOps[s]instanceof n&&(t.mathOps[s].dispose(),i=s,i<t.mathOps.length-1&&(o=t.mathOps[s+1]));return t.mathOps[i-1].disconnect(),t.mathOps[i-1].connect(e),e.connect(o),t.mathOps[i]=e,t}}(i);var n;n=function(){"use strict";var e=i,o=e.audiocontext;"undefined"!=typeof o.createStereoPanner?(t.Panner=function(t,e){this.stereoPanner=this.input=o.createStereoPanner(),t.connect(this.stereoPanner),this.stereoPanner.connect(e)},t.Panner.prototype.pan=function(t,e){var i=e||0,n=o.currentTime+i;this.stereoPanner.pan.linearRampToValueAtTime(t,n)},t.Panner.prototype.inputChannels=function(){},t.Panner.prototype.connect=function(t){this.stereoPanner.connect(t)},t.Panner.prototype.disconnect=function(){this.stereoPanner.disconnect()}):(t.Panner=function(t,e,i){this.input=o.createGain(),t.connect(this.input),this.left=o.createGain(),this.right=o.createGain(),this.left.channelInterpretation="discrete",this.right.channelInterpretation="discrete",i>1?(this.splitter=o.createChannelSplitter(2),this.input.connect(this.splitter),this.splitter.connect(this.left,1),this.splitter.connect(this.right,0)):(this.input.connect(this.left),this.input.connect(this.right)),this.output=o.createChannelMerger(2),this.left.connect(this.output,0,1),this.right.connect(this.output,0,0),this.output.connect(e)},t.Panner.prototype.pan=function(t,e){var i=e||0,n=o.currentTime+i,s=(t+1)/2,r=Math.cos(s*Math.PI/2),a=Math.sin(s*Math.PI/2);this.left.gain.linearRampToValueAtTime(a,n),this.right.gain.linearRampToValueAtTime(r,n)},t.Panner.prototype.inputChannels=function(t){1===t?(this.input.disconnect(),this.input.connect(this.left),this.input.connect(this.right)):2===t&&(this.splitter=o.createChannelSplitter(2),this.input.disconnect(),this.input.connect(this.splitter),this.splitter.connect(this.left,1),this.splitter.connect(this.right,0))},t.Panner.prototype.connect=function(t){this.output.connect(t)},t.Panner.prototype.disconnect=function(){this.output.disconnect()}),t.Panner3D=function(t,e){var i=o.createPanner();return i.panningModel="HRTF",i.distanceModel="linear",i.setPosition(0,0,0),t.connect(i),i.connect(e),i.pan=function(t,e,o){i.setPosition(t,e,o)},i}}(i);var s;s=function(){"use strict";function e(t,e){for(var i={},o=t.length,n=0;o>n;n++){if(t[n]>e){var s=t[n],r=new h(s,n);i[n]=r,n+=6e3}n++}return i}function o(t){for(var e=[],i=Object.keys(t).sort(),o=0;o<i.length;o++)for(var n=0;10>n;n++){var s=t[i[o]],r=t[i[o+n]];if(s&&r){var a=s.sampleIndex,u=r.sampleIndex,c=u-a;c>0&&s.intervals.push(c);var h=e.some(function(t){return t.interval===c?(t.count++,t):void 0});h||e.push({interval:c,count:1})}}return e}function n(t,e){var i=[];return t.forEach(function(t){try{var o=Math.abs(60/(t.interval/e));o=r(o);var n=i.some(function(e){return e.tempo===o?e.count+=t.count:void 0});if(!n){if(isNaN(o))return;i.push({tempo:Math.round(o),count:t.count})}}catch(s){throw s}}),i}function s(t,e,i,o){for(var n=[],s=Object.keys(t).sort(),a=0;a<s.length;a++)for(var u=s[a],c=t[u],h=0;h<c.intervals.length;h++){var p=Math.round(Math.abs(60/(c.intervals[h]/i)));p=r(p);Math.abs(p-e)<o&&n.push(c.sampleIndex/44100)}return n=n.filter(function(t,e,i){var o=i[e+1]-t;return o>.01?!0:void 0})}function r(t){if(isFinite(t)&&0!=t){for(;90>t;)t*=2;for(;t>180&&t>90;)t/=2;return t}}var a=i,u=a.audiocontext;t.SoundFile=function(e,i,o){if("undefined"!=typeof e){if("string"==typeof e||"string"==typeof e[0]){var n=t.prototype._checkFileFormats(e);this.url=n}else if("object"==typeof e&&!(window.File&&window.FileReader&&window.FileList&&window.Blob))throw"Unable to load file because the File API is not supported";e.file&&(e=e.file),this.file=e}this._looping=!1,this._playing=!1,this._paused=!1,this._pauseTime=0,this._cues=[],this._lastPos=0,this._counterNode,this._scopeNode,this.bufferSourceNodes=[],this.bufferSourceNode=null,this.buffer=null,this.playbackRate=1,this.gain=1,this.input=a.audiocontext.createGain(),this.output=a.audiocontext.createGain(),this.reversed=!1,this.startTime=0,this.endTime=null,this.pauseTime=0,this.mode="sustain",this.startMillis=null,this.amplitude=new t.Amplitude,this.output.connect(this.amplitude.input),this.panPosition=0,this.panner=new t.Panner(this.output,a.input,2),(this.url||this.file)&&this.load(i),a.soundArray.push(this),this.whileLoading="function"==typeof o?o:function(){}},t.prototype.registerPreloadMethod("loadSound"),t.prototype.loadSound=function(e,i,o){window.location.origin.indexOf("file://")>-1&&"undefined"===window.cordova&&alert("This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS");var n=new t.SoundFile(e,i,o);return n},t.SoundFile.prototype.load=function(t){if(void 0!=this.url&&""!=this.url){var e=this,i=new XMLHttpRequest;i.addEventListener("progress",function(t){e._updateProgress(t)},!1),i.open("GET",this.url,!0),i.responseType="arraybuffer";var o=this;i.onload=function(){u.decodeAudioData(i.response,function(e){o.buffer=e,o.panner.inputChannels(e.numberOfChannels),t&&t(o)})},i.send()}else if(void 0!=this.file){var n=new FileReader,o=this;n.onload=function(){u.decodeAudioData(n.result,function(e){o.buffer=e,o.panner.inputChannels(e.numberOfChannels),t&&t(o)})},n.readAsArrayBuffer(this.file)}},t.SoundFile.prototype._updateProgress=function(t){if(t.lengthComputable){var e=Math.log(t.loaded/t.total*9.9);this.whileLoading(e)}else console.log("size unknown")},t.SoundFile.prototype.isLoaded=function(){return this.buffer?!0:!1},t.SoundFile.prototype.play=function(t,e,i,o,n){var s,r,u=this,c=a.audiocontext.currentTime,t=t||0;if(0>t&&(t=0),t+=c,!this.buffer)throw"not ready to play file, buffer has yet to load. Try preload()";if(this._pauseTime=0,"restart"===this.mode&&this.buffer&&this.bufferSourceNode){var c=a.audiocontext.currentTime;this.bufferSourceNode.stop(t),this._counterNode.stop(t)}if(this.bufferSourceNode=this._initSourceNode(),this._counterNode=this._initCounterNode(),o){if(!(o>=0&&o<this.buffer.duration))throw"start time out of range";s=o}else s=0;n=n?n<=this.buffer.duration-s?n:this.buffer.duration:this.buffer.duration-s;var h=i||1;if(this.bufferSourceNode.connect(this.output),this.output.gain.value=h,e=e||Math.abs(this.playbackRate),this.bufferSourceNode.playbackRate.setValueAtTime(e,c),this._paused?(this.bufferSourceNode.start(t,this.pauseTime,n),this._counterNode.start(t,this.pauseTime,n)):(this.bufferSourceNode.start(t,s,n),this._counterNode.start(t,s,n)),this._playing=!0,this._paused=!1,this.bufferSourceNodes.push(this.bufferSourceNode),this.bufferSourceNode._arrayIndex=this.bufferSourceNodes.length-1,this.bufferSourceNode.onended=function(){var t=this;this._playing=!1,setTimeout(function(){u.bufferSourceNodes.splice(t._arrayIndex,1),0===u.bufferSourceNodes.length&&(u._playing=!1)},1)},this.bufferSourceNode.loop=this._looping,this._counterNode.loop=this._looping,this._looping===!0){var r=s+n;this.bufferSourceNode.loopStart=s,this.bufferSourceNode.loopEnd=r,this._counterNode.loopStart=s,this._counterNode.loopEnd=r}},t.SoundFile.prototype.playMode=function(t){var e=t.toLowerCase();if("restart"===e&&this.buffer&&this.bufferSourceNode)for(var i=0;i<this.bufferSourceNodes.length-1;i++){var o=a.audiocontext.currentTime;this.bufferSourceNodes[i].stop(o)}if("restart"!==e&&"sustain"!==e)throw'Invalid play mode. Must be either "restart" or "sustain"';this.mode=e},t.SoundFile.prototype.pause=function(t){var e=a.audiocontext.currentTime,t=t||0,i=t+e;this.isPlaying()&&this.buffer&&this.bufferSourceNode?(this.pauseTime=this.currentTime(),this.bufferSourceNode.stop(i),this._counterNode.stop(i),this._paused=!0,this._playing=!1,this._pauseTime=this.currentTime()):this._pauseTime=0},t.SoundFile.prototype.loop=function(t,e,i,o,n){this._looping=!0,this.play(t,e,i,o,n)},t.SoundFile.prototype.setLoop=function(t){if(t===!0)this._looping=!0;else{if(t!==!1)throw"Error: setLoop accepts either true or false";this._looping=!1}this.bufferSourceNode&&(this.bufferSourceNode.loop=this._looping,this._counterNode.loop=this._looping)},t.SoundFile.prototype.isLooping=function(){return this.bufferSourceNode&&this._looping===!0&&this.isPlaying()===!0?!0:!1},t.SoundFile.prototype.isPlaying=function(){return this._playing},t.SoundFile.prototype.isPaused=function(){return this._paused},t.SoundFile.prototype.stop=function(t){var e=t||0;if("sustain"==this.mode)this.stopAll(e),this._playing=!1,this.pauseTime=0,this._paused=!1;else if(this.buffer&&this.bufferSourceNode){var i=a.audiocontext.currentTime,o=e||0;this.pauseTime=0,this.bufferSourceNode.stop(i+o),this._counterNode.stop(i+o),this._playing=!1,this._paused=!1}},t.SoundFile.prototype.stopAll=function(t){var e=a.audiocontext.currentTime,i=t||0;if(this.buffer&&this.bufferSourceNode){for(var o=0;o<this.bufferSourceNodes.length;o++)if(void 0!=typeof this.bufferSourceNodes[o])try{this.bufferSourceNodes[o].stop(e+i)}catch(n){}this._counterNode.stop(e+i)}},t.SoundFile.prototype.setVolume=function(t,e,i){if("number"==typeof t){var e=e||0,i=i||0,o=a.audiocontext.currentTime,n=this.output.gain.value;this.output.gain.cancelScheduledValues(o+i),this.output.gain.linearRampToValueAtTime(n,o+i),this.output.gain.linearRampToValueAtTime(t,o+i+e)}else{if(!t)return this.output.gain;t.connect(this.output.gain)}},t.SoundFile.prototype.amp=t.SoundFile.prototype.setVolume,t.SoundFile.prototype.fade=t.SoundFile.prototype.setVolume,t.SoundFile.prototype.getVolume=function(){return this.output.gain.value},t.SoundFile.prototype.pan=function(t,e){this.panPosition=t,this.panner.pan(t,e)},t.SoundFile.prototype.getPan=function(){return this.panPosition},t.SoundFile.prototype.rate=function(t){if(this.playbackRate!==t||!this.bufferSourceNode||this.bufferSourceNode.playbackRate.value!==t){this.playbackRate=t;var e=t;if(0===this.playbackRate&&this._playing&&this.pause(),this.playbackRate<0&&!this.reversed){{var i=this.currentTime();this.bufferSourceNode.playbackRate.value}this.reverseBuffer(),e=Math.abs(t);var o=(i-this.duration())/e;this.pauseTime=o}else this.playbackRate>0&&this.reversed&&this.reverseBuffer();if(this.bufferSourceNode){var n=a.audiocontext.currentTime;this.bufferSourceNode.playbackRate.cancelScheduledValues(n),this.bufferSourceNode.playbackRate.linearRampToValueAtTime(Math.abs(e),n),this._counterNode.playbackRate.cancelScheduledValues(n),this._counterNode.playbackRate.linearRampToValueAtTime(Math.abs(e),n)}}},t.SoundFile.prototype.setPitch=function(t){var e=midiToFreq(t)/midiToFreq(60);this.rate(e)},t.SoundFile.prototype.getPlaybackRate=function(){return this.playbackRate},t.SoundFile.prototype.duration=function(){return this.buffer?this.buffer.duration:0},t.SoundFile.prototype.currentTime=function(){return this._pauseTime>0?this._pauseTime:this._lastPos/u.sampleRate},t.SoundFile.prototype.jump=function(t,e){if(0>t||t>this.buffer.duration)throw"jump time out of range";if(e>this.buffer.duration-t)throw"end time out of range";var i=t||0,o=e||this.buffer.duration-t;this.isPlaying()&&this.stop(),this.play(0,this.playbackRate,this.output.gain.value,i,o)},t.SoundFile.prototype.channels=function(){return this.buffer.numberOfChannels},t.SoundFile.prototype.sampleRate=function(){return this.buffer.sampleRate},t.SoundFile.prototype.frames=function(){return this.buffer.length},t.SoundFile.prototype.getPeaks=function(t){if(!this.buffer)throw"Cannot load peaks yet, buffer is not loaded";if(t||(t=5*window.width),this.buffer){for(var e=this.buffer,i=e.length/t,o=~~(i/10)||1,n=e.numberOfChannels,s=new Float32Array(Math.round(t)),r=0;n>r;r++)for(var a=e.getChannelData(r),u=0;t>u;u++){for(var c=~~(u*i),h=~~(c+i),p=0,l=c;h>l;l+=o){var f=a[l];f>p?p=f:-f>p&&(p=f)}(0===r||Math.abs(p)>s[u])&&(s[u]=p)}return s}},t.SoundFile.prototype.reverseBuffer=function(){var t=this.getVolume();if(this.setVolume(0,.01,0),this.pause(),!this.buffer)throw"SoundFile is not done loading";for(var e=0;e<this.buffer.numberOfChannels;e++)Array.prototype.reverse.call(this.buffer.getChannelData(e));this.reversed=!this.reversed,this.setVolume(t,.01,.0101),this.play()},t.SoundFile.prototype._onEnded=function(t){t.onended=function(t){var e=a.audiocontext.currentTime;t.stop(e)}},t.SoundFile.prototype.add=function(){},t.SoundFile.prototype.dispose=function(){var t=a.audiocontext.currentTime;if(this.stop(t),this.buffer&&this.bufferSourceNode){for(var e=0;e<this.bufferSourceNodes.length-1;e++)null!==this.bufferSourceNodes[e]&&(this.bufferSourceNodes[e].stop(t),this.bufferSourceNodes[e]=null);if(this.isPlaying()){try{this._counterNode.stop(t)}catch(i){console.log(i)}this._counterNode=null}}this.output&&(this.output.disconnect(),this.output=null),this.panner&&(this.panner.disconnect(),this.panner=null)},t.SoundFile.prototype.connect=function(t){this.panner.connect(t?t.hasOwnProperty("input")?t.input:t:a.input)},t.SoundFile.prototype.disconnect=function(t){this.panner.disconnect(t)},t.SoundFile.prototype.getLevel=function(t){return t&&(this.amplitude.smoothing=t),this.amplitude.getLevel()},t.SoundFile.prototype.setPath=function(e,i){var o=t.prototype._checkFileFormats(e);this.url=o,this.load(i)},t.SoundFile.prototype.setBuffer=function(t){var e=t.length,i=t[0].length,o=u.createBuffer(e,i,u.sampleRate);!t[0]instanceof Float32Array&&(t[0]=new Float32Array(t[0]));for(var n=0;e>n;n++){var s=o.getChannelData(n);s.set(t[n])}this.buffer=o,this.panner.inputChannels(e)},t.SoundFile.prototype._initCounterNode=function(){var e=this,i=u.currentTime,o=u.createBufferSource();return e._scopeNode&&(e._scopeNode.disconnect(),e._scopeNode=null),e._scopeNode=u.createScriptProcessor(256,1,1),o.buffer=c(e.buffer),o.playbackRate.setValueAtTime(e.playbackRate,i),o.connect(e._scopeNode),e._scopeNode.connect(t.soundOut._silentNode),e._scopeNode.onaudioprocess=function(t){var i=t.inputBuffer.getChannelData(0);e._lastPos=i[i.length-1]||0,e._onTimeUpdate(e._lastPos)},o},t.SoundFile.prototype._initSourceNode=function(){var t=this,e=u.currentTime,i=u.createBufferSource();return i.buffer=t.buffer,i.playbackRate.setValueAtTime(t.playbackRate,e),i};var c=function(t){for(var e=new Float32Array(t.length),i=u.createBuffer(1,t.length,44100),o=0;o<t.length;o++)e[o]=o;return i.getChannelData(0).set(e),i};t.SoundFile.prototype.processPeaks=function(t,i,r,a){var u=this.buffer.length,c=this.buffer.sampleRate,h=this.buffer,l=i||.9,f=l,d=r||.22,y=a||200,m=new OfflineAudioContext(1,u,c),v=m.createBufferSource();v.buffer=h;var g=m.createBiquadFilter();g.type="lowpass",v.connect(g),g.connect(m.destination),v.start(0),m.startRendering(),m.oncomplete=function(i){var r=i.renderedBuffer,a=r.getChannelData(0);do p=e(a,f),f-=.005;while(Object.keys(p).length<y&&f>=d);var u=o(p),c=n(u,r.sampleRate),h=c.sort(function(t,e){return e.count-t.count}).splice(0,5);this.tempo=h[0].tempo;var l=5,m=s(p,h[0].tempo,r.sampleRate,l);t(m)}};var h=function(t,e){this.sampleIndex=e,this.amplitude=t,this.tempos=[],this.intervals=[]},p=[];t.SoundFile.prototype.addCue=function(t,e,i){var o=this._cueIDCounter++,n=new l(e,t,o,i);return this._cues.push(n),o},t.SoundFile.prototype.removeCue=function(t){for(var e=0;e<this._cues.length;e++){var i=this._cues[e];i.id===t&&this.cues.splice(e,1)}0===this._cues.length},t.SoundFile.prototype.clearCues=function(){this._cues=[]},t.SoundFile.prototype._onTimeUpdate=function(t){for(var e=t/this.buffer.sampleRate,i=0;i<this._cues.length;i++){var o=this._cues[i].time,n=this._cues[i].val;this._prevTime<o&&e>=o&&this._cues[i].callback(n)}this._prevTime=e};var l=function(t,e,i,o){this.callback=t,this.time=e,this.id=i,this.val=o}}(e,i);var r;r=function(){"use strict";var e=i;t.Amplitude=function(t){this.bufferSize=2048,this.audiocontext=e.audiocontext,this.processor=this.audiocontext.createScriptProcessor(this.bufferSize,2,1),this.input=this.processor,this.output=this.audiocontext.createGain(),this.smoothing=t||0,this.volume=0,this.average=0,this.stereoVol=[0,0],this.stereoAvg=[0,0],this.stereoVolNorm=[0,0],this.volMax=.001,this.normalize=!1,this.processor.onaudioprocess=this._audioProcess.bind(this),this.processor.connect(this.output),this.output.gain.value=0,this.output.connect(this.audiocontext.destination),e.meter.connect(this.processor)},t.Amplitude.prototype.setInput=function(i,o){e.meter.disconnect(),o&&(this.smoothing=o),null==i?(console.log("Amplitude input source is not ready! Connecting to master output instead"),e.meter.connect(this.processor)):i instanceof t.Signal?i.output.connect(this.processor):i?(i.connect(this.processor),this.processor.disconnect(),this.processor.connect(this.output)):e.meter.connect(this.processor)},t.Amplitude.prototype.connect=function(t){this.output.connect(t?t.hasOwnProperty("input")?t.input:t:this.panner.connect(e.input))},t.Amplitude.prototype.disconnect=function(){this.output.disconnect()},t.Amplitude.prototype._audioProcess=function(t){for(var e=0;e<t.inputBuffer.numberOfChannels;e++){for(var i,o=t.inputBuffer.getChannelData(e),n=o.length,s=0,r=0,a=0;n>a;a++)i=o[a],this.normalize?(s+=Math.max(Math.min(i/this.volMax,1),-1),r+=Math.max(Math.min(i/this.volMax,1),-1)*Math.max(Math.min(i/this.volMax,1),-1)):(s+=i,r+=i*i);var u=s/n,c=Math.sqrt(r/n);this.stereoVol[e]=Math.max(c,this.stereoVol[e]*this.smoothing),this.stereoAvg[e]=Math.max(u,this.stereoVol[e]*this.smoothing),this.volMax=Math.max(this.stereoVol[e],this.volMax)}var h=this,p=this.stereoVol.reduce(function(t,e,i){return h.stereoVolNorm[i-1]=Math.max(Math.min(h.stereoVol[i-1]/h.volMax,1),0),h.stereoVolNorm[i]=Math.max(Math.min(h.stereoVol[i]/h.volMax,1),0),t+e});this.volume=p/this.stereoVol.length,this.volNorm=Math.max(Math.min(this.volume/this.volMax,1),0)},t.Amplitude.prototype.getLevel=function(t){return"undefined"!=typeof t?this.normalize?this.stereoVolNorm[t]:this.stereoVol[t]:this.normalize?this.volNorm:this.volume},t.Amplitude.prototype.toggleNormalize=function(t){this.normalize="boolean"==typeof t?t:!this.normalize},t.Amplitude.prototype.smooth=function(t){t>=0&&1>t?this.smoothing=t:console.log("Error: smoothing must be between 0 and 1")}}(i);var a;a=function(){"use strict";var e=i;t.FFT=function(t,i){this.smoothing=t||.8,this.bins=i||1024;var o=2*i||2048;this.input=this.analyser=e.audiocontext.createAnalyser(),e.fftMeter.connect(this.analyser),this.analyser.smoothingTimeConstant=this.smoothing,this.analyser.fftSize=o,this.freqDomain=new Uint8Array(this.analyser.frequencyBinCount),this.timeDomain=new Uint8Array(this.analyser.frequencyBinCount),this.bass=[20,140],this.lowMid=[140,400],this.mid=[400,2600],this.highMid=[2600,5200],this.treble=[5200,14e3]},t.FFT.prototype.setInput=function(t){t?(t.output?t.output.connect(this.analyser):t.connect&&t.connect(this.analyser),e.fftMeter.disconnect()):e.fftMeter.connect(this.analyser)},t.FFT.prototype.waveform=function(){for(var e,i,o,n=0;n<arguments.length;n++)"number"==typeof arguments[n]&&(e=arguments[n],this.analyser.fftSize=2*e),"string"==typeof arguments[n]&&(i=arguments[n]);if(i&&!t.prototype._isSafari())return s(this,this.timeDomain),this.analyser.getFloatTimeDomainData(this.timeDomain),this.timeDomain;r(this,this.timeDomain),this.analyser.getByteTimeDomainData(this.timeDomain);for(var o=new Array,n=0;n<this.timeDomain.length;n++){var a=t.prototype.map(this.timeDomain[n],0,255,-1,1);o.push(a)}return o},t.FFT.prototype.analyze=function(){for(var t,e,i=0;i<arguments.length;i++)"number"==typeof arguments[i]&&(t=this.bins=arguments[i],this.analyser.fftSize=2*this.bins),"string"==typeof arguments[i]&&(e=arguments[i]);if(e&&"db"===e.toLowerCase())return o(this),this.analyser.getFloatFrequencyData(this.freqDomain),this.freqDomain;n(this,this.freqDomain),this.analyser.getByteFrequencyData(this.freqDomain);var s=Array.apply([],this.freqDomain);return s.length===this.analyser.fftSize,s.constructor===Array,s},t.FFT.prototype.getEnergy=function(t,i){var o=e.audiocontext.sampleRate/2;if("bass"===t?(t=this.bass[0],i=this.bass[1]):"lowMid"===t?(t=this.lowMid[0],i=this.lowMid[1]):"mid"===t?(t=this.mid[0],i=this.mid[1]):"highMid"===t?(t=this.highMid[0],i=this.highMid[1]):"treble"===t&&(t=this.treble[0],i=this.treble[1]),"number"!=typeof t)throw"invalid input for getEnergy()";if(i){if(t&&i){if(t>i){var n=i;i=t,t=n}for(var s=Math.round(t/o*this.freqDomain.length),r=Math.round(i/o*this.freqDomain.length),a=0,u=0,c=s;r>=c;c++)a+=this.freqDomain[c],u+=1;var h=a/u;return h}throw"invalid input for getEnergy()"}var p=Math.round(t/o*this.freqDomain.length);return this.freqDomain[p]},t.FFT.prototype.getFreq=function(t,e){console.log("getFreq() is deprecated. Please use getEnergy() instead.");var i=this.getEnergy(t,e);return i},t.FFT.prototype.smooth=function(t){t&&(this.smoothing=t),this.analyser.smoothingTimeConstant=t};var o=function(t){t.freqDomain instanceof Float32Array==!1&&(t.freqDomain=new Float32Array(t.analyser.frequencyBinCount))},n=function(t){t.freqDomain instanceof Uint8Array==!1&&(t.freqDomain=new Uint8Array(t.analyser.frequencyBinCount))},s=function(t){t.timeDomain instanceof Float32Array==!1&&(t.timeDomain=new Float32Array(t.analyser.frequencyBinCount))},r=function(t){t.timeDomain instanceof Uint8Array==!1&&(t.timeDomain=new Uint8Array(t.analyser.frequencyBinCount))}}(i);var u;u=function(){"use strict";function t(t){return void 0===t}var e;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");e=new AudioContext,"function"!=typeof AudioContext.prototype.createGain&&(AudioContext.prototype.createGain=AudioContext.prototype.createGainNode),"function"!=typeof AudioContext.prototype.createDelay&&(AudioContext.prototype.createDelay=AudioContext.prototype.createDelayNode),"function"!=typeof AudioContext.prototype.createPeriodicWave&&(AudioContext.prototype.createPeriodicWave=AudioContext.prototype.createWaveTable),"function"!=typeof AudioBufferSourceNode.prototype.start&&(AudioBufferSourceNode.prototype.start=AudioBufferSourceNode.prototype.noteGrainOn),"function"!=typeof AudioBufferSourceNode.prototype.stop&&(AudioBufferSourceNode.prototype.stop=AudioBufferSourceNode.prototype.noteOff),"function"!=typeof OscillatorNode.prototype.start&&(OscillatorNode.prototype.start=OscillatorNode.prototype.noteOn),"function"!=typeof OscillatorNode.prototype.stop&&(OscillatorNode.prototype.stop=OscillatorNode.prototype.noteOff),"function"!=typeof OscillatorNode.prototype.setPeriodicWave&&(OscillatorNode.prototype.setPeriodicWave=OscillatorNode.prototype.setWaveTable),window.Tone||(AudioNode.prototype._nativeConnect=AudioNode.prototype.connect,AudioNode.prototype.connect=function(e,i,o){if(e.input)Array.isArray(e.input)?(t(o)&&(o=0),this.connect(e.input[o])):this.connect(e.input,i,o);else try{e instanceof AudioNode?this._nativeConnect(e,i,o):this._nativeConnect(e,i)}catch(n){throw new Error("error connecting to node: "+e)}});var i=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))};i.context=e,i.prototype.context=i.context,i.prototype.bufferSize=2048,i.prototype.bufferTime=i.prototype.bufferSize/i.context.sampleRate,i.prototype.connect=function(t,e,i){Array.isArray(this.output)?(e=this.defaultArg(e,0),this.output[e].connect(t,0,i)):this.output.connect(t,e,i)},i.prototype.disconnect=function(t){Array.isArray(this.output)?(t=this.defaultArg(t,0),this.output[t].disconnect()):this.output.disconnect()},i.prototype.connectSeries=function(){if(arguments.length>1)for(var t=arguments[0],e=1;e<arguments.length;e++){var i=arguments[e];t.connect(i),t=i}},i.prototype.connectParallel=function(){var t=arguments[0];if(arguments.length>1)for(var e=1;e<arguments.length;e++){var i=arguments[e];t.connect(i)}},i.prototype.chain=function(){if(arguments.length>0)for(var t=this,e=0;e<arguments.length;e++){var i=arguments[e];t.connect(i),t=i}},i.prototype.fan=function(){if(arguments.length>0)for(var t=1;t<arguments.length;t++)this.connect(arguments[t])},AudioNode.prototype.chain=i.prototype.chain,AudioNode.prototype.fan=i.prototype.fan,i.prototype.defaultArg=function(e,i){if("object"==typeof e&&"object"==typeof i){var o={};for(var n in e)o[n]=this.defaultArg(e[n],e[n]);for(var s in i)o[s]=this.defaultArg(e[s],i[s]);return o}return t(e)?i:e},i.prototype.optionsObject=function(t,e,i){var o={};if(1===t.length&&"object"==typeof t[0])o=t[0];else for(var n=0;n<e.length;n++)o[e[n]]=t[n];return this.isUndef(i)?o:this.defaultArg(o,i)},i.prototype.isUndef=t,i.prototype.equalPowerScale=function(t){var e=.5*Math.PI;return Math.sin(t*e)},i.prototype.logScale=function(t){return Math.max(this.normalize(this.gainToDb(t),-100,0),0)},i.prototype.expScale=function(t){return this.dbToGain(this.interpolate(t,-100,0))},i.prototype.dbToGain=function(t){return Math.pow(2,t/6)},i.prototype.gainToDb=function(t){return 20*(Math.log(t)/Math.LN10)},i.prototype.interpolate=function(t,e,i){return t*(i-e)+e},i.prototype.normalize=function(t,e,i){if(e>i){var o=i;i=e,e=o}else if(e==i)return 0;return(t-e)/(i-e)},i.prototype.dispose=function(){this.isUndef(this.input)||(this.input instanceof AudioNode&&this.input.disconnect(),this.input=null),this.isUndef(this.output)||(this.output instanceof AudioNode&&this.output.disconnect(),this.output=null)};var o=null;i.prototype.noGC=function(){this.output.connect(o)},AudioNode.prototype.noGC=function(){this.connect(o)},i.prototype.now=function(){return this.context.currentTime},i.prototype.samplesToSeconds=function(t){return t/this.context.sampleRate},i.prototype.toSamples=function(t){var e=this.toSeconds(t);return Math.round(e*this.context.sampleRate)},i.prototype.toSeconds=function(t,e){if(e=this.defaultArg(e,this.now()),"number"==typeof t)return t;if("string"==typeof t){var i=0;return"+"===t.charAt(0)&&(t=t.slice(1),i=e),parseFloat(t)+i}return e},i.prototype.frequencyToSeconds=function(t){return 1/parseFloat(t)},i.prototype.secondsToFrequency=function(t){return 1/t};var n=[];return i._initAudioContext=function(t){t(i.context),n.push(t)},i.setContext=function(t){i.prototype.context=t,i.context=t;for(var e=0;e<n.length;e++)n[e](t)},i.extend=function(e,o){function n(){}t(o)&&(o=i),n.prototype=o.prototype,e.prototype=new n,e.prototype.constructor=e},i.startMobile=function(){var t=i.context.createOscillator(),e=i.context.createGain();e.gain.value=0,t.connect(e),e.connect(i.context.destination);var o=i.context.currentTime;t.start(o),t.stop(o+1)},i._initAudioContext(function(t){i.prototype.bufferTime=i.prototype.bufferSize/t.sampleRate,o=t.createGain(),o.gain.value=0,o.connect(t.destination)}),i}();var c;c=function(t){"use strict";return t.SignalBase=function(){},t.extend(t.SignalBase),t.SignalBase.prototype.connect=function(e,i,o){e instanceof t.Signal?e.setValue(0):e instanceof AudioParam&&(e.value=0),t.prototype.connect.call(this,e,i,o)},t.SignalBase.prototype.dispose=function(){t.prototype.dispose.call(this)},t.SignalBase}(u);var h;h=function(t){"use strict";return t.WaveShaper=function(t,e){this._shaper=this.input=this.output=this.context.createWaveShaper(),this._curve=null,Array.isArray(t)?this.setCurve(t):isFinite(t)||this.isUndef(t)?this._curve=new Float32Array(this.defaultArg(t,1024)):"function"==typeof t&&(this._curve=new Float32Array(this.defaultArg(e,1024)),this.setMap(t)) | |
},t.extend(t.WaveShaper,t.SignalBase),t.WaveShaper.prototype.setMap=function(t){for(var e=0,i=this._curve.length;i>e;e++){var o=e/i*2-1,n=e/(i-1)*2-1;this._curve[e]=t(o,e,n)}this._shaper.curve=this._curve},t.WaveShaper.prototype.setCurve=function(t){if(this._isSafari()){var e=t[0];t.unshift(e)}this._curve=new Float32Array(t),this._shaper.curve=this._curve},t.WaveShaper.prototype.setOversample=function(t){this._shaper.oversample=t},t.WaveShaper.prototype._isSafari=function(){var t=navigator.userAgent.toLowerCase();return-1!==t.indexOf("safari")&&-1===t.indexOf("chrome")},t.WaveShaper.prototype.dispose=function(){t.prototype.dispose.call(this),this._shaper.disconnect(),this._shaper=null,this._curve=null},t.WaveShaper}(u);var p;p=function(t){"use strict";return t.Signal=function(e){this._scalar=this.context.createGain(),this.input=this.output=this.context.createGain(),this._syncRatio=1,this.value=this.defaultArg(e,0),t.Signal._constant.chain(this._scalar,this.output)},t.extend(t.Signal,t.SignalBase),t.Signal.prototype.getValue=function(){return this._scalar.gain.value},t.Signal.prototype.setValue=function(t){0===this._syncRatio?t=0:t*=this._syncRatio,this._scalar.gain.value=t},t.Signal.prototype.setValueAtTime=function(t,e){t*=this._syncRatio,this._scalar.gain.setValueAtTime(t,this.toSeconds(e))},t.Signal.prototype.setCurrentValueNow=function(t){t=this.defaultArg(t,this.now());var e=this.getValue();return this.cancelScheduledValues(t),this._scalar.gain.setValueAtTime(e,t),e},t.Signal.prototype.linearRampToValueAtTime=function(t,e){t*=this._syncRatio,this._scalar.gain.linearRampToValueAtTime(t,this.toSeconds(e))},t.Signal.prototype.exponentialRampToValueAtTime=function(t,e){t*=this._syncRatio;try{this._scalar.gain.exponentialRampToValueAtTime(t,this.toSeconds(e))}catch(i){this._scalar.gain.linearRampToValueAtTime(t,this.toSeconds(e))}},t.Signal.prototype.exponentialRampToValueNow=function(t,e){var i=this.now();this.setCurrentValueNow(i),"+"===e.toString().charAt(0)&&(e=e.substr(1)),this.exponentialRampToValueAtTime(t,i+this.toSeconds(e))},t.Signal.prototype.linearRampToValueNow=function(t,e){var i=this.now();this.setCurrentValueNow(i),t*=this._syncRatio,"+"===e.toString().charAt(0)&&(e=e.substr(1)),this._scalar.gain.linearRampToValueAtTime(t,i+this.toSeconds(e))},t.Signal.prototype.setTargetAtTime=function(t,e,i){t*=this._syncRatio,this._scalar.gain.setTargetAtTime(t,this.toSeconds(e),i)},t.Signal.prototype.setValueCurveAtTime=function(t,e,i){for(var o=0;o<t.length;o++)t[o]*=this._syncRatio;this._scalar.gain.setValueCurveAtTime(t,this.toSeconds(e),this.toSeconds(i))},t.Signal.prototype.cancelScheduledValues=function(t){this._scalar.gain.cancelScheduledValues(this.toSeconds(t))},t.Signal.prototype.sync=function(t,e){this._syncRatio=e?e:0!==t.getValue()?this.getValue()/t.getValue():0,this._scalar.disconnect(),this._scalar=this.context.createGain(),this.connectSeries(t,this._scalar,this.output),this._scalar.gain.value=this._syncRatio},t.Signal.prototype.unsync=function(){var e=this.getValue();this._scalar.disconnect(),this._scalar=this.context.createGain(),this._scalar.gain.value=e/this._syncRatio,this._syncRatio=1,t.Signal._constant.chain(this._scalar,this.output)},t.Signal.prototype.dispose=function(){t.prototype.dispose.call(this),this._scalar.disconnect(),this._scalar=null},Object.defineProperty(t.Signal.prototype,"value",{get:function(){return this.getValue()},set:function(t){this.setValue(t)}}),t.Signal._generator=null,t.Signal._constant=null,t._initAudioContext(function(e){t.Signal._generator=e.createOscillator(),t.Signal._constant=new t.WaveShaper([1,1]),t.Signal._generator.connect(t.Signal._constant),t.Signal._generator.start(0),t.Signal._generator.noGC()}),t.Signal}(u);var l;l=function(t){"use strict";return t.Add=function(e){t.call(this,2,0),this._sum=this.input[0]=this.input[1]=this.output=this.context.createGain(),this._value=null,isFinite(e)&&(this._value=new t.Signal(e),this._value.connect(this._sum))},t.extend(t.Add,t.SignalBase),t.Add.prototype.setValue=function(t){if(null===this._value)throw new Error("cannot switch from signal to number");this._value.setValue(t)},t.Add.prototype.dispose=function(){t.prototype.dispose.call(this),this._sum=null,this._value&&(this._value.dispose(),this._value=null)},t.Add}(u);var f;f=function(t){"use strict";return t.Multiply=function(e){t.call(this,2,0),this._mult=this.input[0]=this.output=this.context.createGain(),this._factor=this.input[1]=this.output.gain,this._factor.value=this.defaultArg(e,0)},t.extend(t.Multiply,t.SignalBase),t.Multiply.prototype.setValue=function(t){this._factor.value=t},t.Multiply.prototype.dispose=function(){t.prototype.dispose.call(this),this._mult=null,this._factor=null},t.Multiply}(u);var d;d=function(t){"use strict";return t.Scale=function(e,i){this._outputMin=this.defaultArg(e,0),this._outputMax=this.defaultArg(i,1),this._scale=this.input=new t.Multiply(1),this._add=this.output=new t.Add(0),this._scale.connect(this._add),this._setRange()},t.extend(t.Scale,t.SignalBase),t.Scale.prototype.setMin=function(t){this._outputMin=t,this._setRange()},t.Scale.prototype.setMax=function(t){this._outputMax=t,this._setRange()},t.Scale.prototype._setRange=function(){this._add.setValue(this._outputMin),this._scale.setValue(this._outputMax-this._outputMin)},t.Scale.prototype.dispose=function(){t.prototype.dispose.call(this),this._add.dispose(),this._add=null,this._scale.dispose(),this._scale=null},t.Scale}(u,l,f);var y;y=function(){"use strict";var e=p,o=l,n=f,s=d,r=u,a=i;r.setContext(a.audiocontext),t.Signal=function(t){var i=new e(t);return i},e.prototype.fade=e.prototype.linearRampToValueAtTime,n.prototype.fade=e.prototype.fade,o.prototype.fade=e.prototype.fade,s.prototype.fade=e.prototype.fade,e.prototype.setInput=function(t){t.connect(this)},n.prototype.setInput=e.prototype.setInput,o.prototype.setInput=e.prototype.setInput,s.prototype.setInput=e.prototype.setInput,e.prototype.add=function(t){var e=new o(t);return this.connect(e),e},n.prototype.add=e.prototype.add,o.prototype.add=e.prototype.add,s.prototype.add=e.prototype.add,e.prototype.mult=function(t){var e=new n(t);return this.connect(e),e},n.prototype.mult=e.prototype.mult,o.prototype.mult=e.prototype.mult,s.prototype.mult=e.prototype.mult,e.prototype.scale=function(e,i,o,n){var r,a;4===arguments.length?(r=t.prototype.map(o,e,i,0,1)-.5,a=t.prototype.map(n,e,i,0,1)-.5):(r=arguments[0],a=arguments[1]);var u=new s(r,a);return this.connect(u),u},n.prototype.scale=e.prototype.scale,o.prototype.scale=e.prototype.scale,s.prototype.scale=e.prototype.scale}(p,l,f,d,u,i);var m;m=function(){"use strict";var e=i,o=l,n=f,s=d;t.Oscillator=function(i,o){if("string"==typeof i){var n=o;o=i,i=n}if("number"==typeof o){var n=o;o=i,i=n}this.started=!1,this.oscillator=e.audiocontext.createOscillator(),this.f=i||440,this.oscillator.frequency.setValueAtTime(this.f,e.audiocontext.currentTime),this.oscillator.type=o||"sine";this.oscillator;this.input=e.audiocontext.createGain(),this.output=e.audiocontext.createGain(),this._freqMods=[],this.output.gain.value=.5,this.output.gain.setValueAtTime(.5,e.audiocontext.currentTime),this.oscillator.connect(this.output),this.panPosition=0,this.connection=e.input,this.panner=new t.Panner(this.output,this.connection,1),this.mathOps=[this.output],e.soundArray.push(this)},t.Oscillator.prototype.start=function(t,i){if(this.started){var o=e.audiocontext.currentTime;this.stop(o)}if(!this.started){var n=i||this.f,s=this.oscillator.type;this.oscillator=e.audiocontext.createOscillator(),this.oscillator.frequency.exponentialRampToValueAtTime(Math.abs(n),e.audiocontext.currentTime),this.oscillator.type=s,this.oscillator.connect(this.output),t=t||0,this.oscillator.start(t+e.audiocontext.currentTime),this.freqNode=this.oscillator.frequency;for(var r in this._freqMods)"undefined"!=typeof this._freqMods[r].connect&&this._freqMods[r].connect(this.oscillator.frequency);this.started=!0}},t.Oscillator.prototype.stop=function(t){if(this.started){var i=t||0,o=e.audiocontext.currentTime;this.oscillator.stop(i+o),this.started=!1}},t.Oscillator.prototype.amp=function(t,i,o){var n=this;if("number"==typeof t){var i=i||0,o=o||0,s=e.audiocontext.currentTime,r=this.output.gain.value;this.output.gain.cancelScheduledValues(s),this.output.gain.linearRampToValueAtTime(r,s+o),this.output.gain.linearRampToValueAtTime(t,s+o+i)}else{if(!t)return this.output.gain;console.log(t),t.connect(n.output.gain)}},t.Oscillator.prototype.fade=t.Oscillator.prototype.amp,t.Oscillator.prototype.getAmp=function(){return this.output.gain.value},t.Oscillator.prototype.freq=function(t,i,o){if("number"!=typeof t||isNaN(t)){if(!t)return this.oscillator.frequency;t.output&&(t=t.output),t.connect(this.oscillator.frequency),this._freqMods.push(t)}else{this.f=t;var n=e.audiocontext.currentTime,i=i||0,o=o||0,s=this.oscillator.frequency.value;this.oscillator.frequency.cancelScheduledValues(n),this.oscillator.frequency.setValueAtTime(s,n+o),t>0?this.oscillator.frequency.exponentialRampToValueAtTime(t,o+i+n):this.oscillator.frequency.linearRampToValueAtTime(t,o+i+n)}},t.Oscillator.prototype.getFreq=function(){return this.oscillator.frequency.value},t.Oscillator.prototype.setType=function(t){this.oscillator.type=t},t.Oscillator.prototype.getType=function(){return this.oscillator.type},t.Oscillator.prototype.connect=function(t){t?t.hasOwnProperty("input")?(this.panner.connect(t.input),this.connection=t.input):(this.panner.connect(t),this.connection=t):this.panner.connect(e.input)},t.Oscillator.prototype.disconnect=function(){this.output.disconnect(),this.panner.disconnect(),this.output.connect(this.panner),this.oscMods=[]},t.Oscillator.prototype.pan=function(t,e){this.panPosition=t,this.panner.pan(t,e)},t.Oscillator.prototype.getPan=function(){return this.panPosition},t.Oscillator.prototype.dispose=function(){if(this.oscillator){var t=e.audiocontext.currentTime;this.stop(t),this.disconnect(),this.oscillator.disconnect(),this.panner=null,this.oscillator=null}this.osc2&&this.osc2.dispose()},t.Oscillator.prototype.phase=function(i){this.dNode||(this.dNode=e.audiocontext.createDelay(),this.output.disconnect(),this.output.connect(this.dNode),this.dNode.connect(this.panner));var o=e.audiocontext.currentTime;this.dNode.delayTime.linearRampToValueAtTime(t.prototype.map(i,0,1,0,1/this.oscillator.frequency.value),o)};var r=function(t,e,i,o,n){var s=t.oscillator;for(var r in t.mathOps)t.mathOps[r]instanceof n&&(s.disconnect(),t.mathOps[r].dispose(),i=r,i<t.mathOps.length-2&&(o=t.mathOps[r+1]));return i==t.mathOps.length-1&&t.mathOps.push(o),r>0&&(s=t.mathOps[r-1]),s.disconnect(),s.connect(e),e.connect(o),t.mathOps[i]=e,t};t.Oscillator.prototype.add=function(t){var e=new o(t),i=this.mathOps.length-1,n=this.output;return r(this,e,i,n,o)},t.Oscillator.prototype.mult=function(t){var e=new n(t),i=this.mathOps.length-1,o=this.output;return r(this,e,i,o,n)},t.Oscillator.prototype.scale=function(e,i,o,n){var a,u;4===arguments.length?(a=t.prototype.map(o,e,i,0,1)-.5,u=t.prototype.map(n,e,i,0,1)-.5):(a=arguments[0],u=arguments[1]);var c=new s(a,u),h=this.mathOps.length-1,p=this.output;return r(this,c,h,p,s)},t.SinOsc=function(e){t.Oscillator.call(this,e,"sine")},t.SinOsc.prototype=Object.create(t.Oscillator.prototype),t.TriOsc=function(e){t.Oscillator.call(this,e,"triangle")},t.TriOsc.prototype=Object.create(t.Oscillator.prototype),t.SawOsc=function(e){t.Oscillator.call(this,e,"sawtooth")},t.SawOsc.prototype=Object.create(t.Oscillator.prototype),t.SqrOsc=function(e){t.Oscillator.call(this,e,"square")},t.SqrOsc.prototype=Object.create(t.Oscillator.prototype)}(i,p,l,f,d);var v;v=function(){"use strict";var e=i,o=l,n=f,s=d,r=u;r.setContext(e.audiocontext);var a=null,c=!1;t.Env=function(i,o,n,s,r,a,u,c){this.aTime=i,this.aLevel=o,this.dTime=n||0,this.dLevel=s||0,this.sTime=r||0,this.sLevel=a||0,this.rTime=u||0,this.rLevel=c||0,this.output=e.audiocontext.createGain(),this.control=new t.Signal,this.control.connect(this.output),this.connection=null,this.mathOps=[this.control],e.soundArray.push(this)},t.Env.prototype.set=function(t,e,i,o,n,s,r,a){this.aTime=t,this.aLevel=e,this.dTime=i||0,this.dLevel=o||0,this.sTime=n||0,this.sLevel=s||0,this.rTime=r||0,this.rLevel=a||0},t.Env.prototype.setInput=function(){for(var t=0;t<arguments.length;t++)this.connect(arguments[t])},t.Env.prototype.ctrl=function(t){this.connect(t)},t.Env.prototype.play=function(t,i){var o=e.audiocontext.currentTime,n=i||0,s=o+n;t&&this.connection!==t&&this.connect(t);var r=this.control.getValue();this.control.cancelScheduledValues(s),this.control.linearRampToValueAtTime(r,s),this.control.linearRampToValueAtTime(this.aLevel,s+this.aTime),this.control.linearRampToValueAtTime(this.dLevel,s+this.aTime+this.dTime),this.control.linearRampToValueAtTime(this.sLevel,s+this.aTime+this.dTime+this.sTime),this.control.linearRampToValueAtTime(this.rLevel,s+this.aTime+this.dTime+this.sTime+this.rTime);s+this.aTime+this.dTime+this.sTime+this.rTime},t.Env.prototype.triggerAttack=function(t,i){var o=e.audiocontext.currentTime,n=i||0,s=o+n;this.lastAttack=s,c=!0;var r=this.control.getValue();console.log(r),this.control.cancelScheduledValues(s),this.control.linearRampToValueAtTime(r,s),t&&this.connection!==t&&this.connect(t),this.control.linearRampToValueAtTime(this.aLevel,s+this.aTime),this.control.linearRampToValueAtTime(this.aLevel,s+this.aTime),this.control.linearRampToValueAtTime(this.dLevel,s+this.aTime+this.dTime),this.control.linearRampToValueAtTime(this.sLevel,s+this.aTime+this.dTime+this.sTime)},t.Env.prototype.triggerRelease=function(t,i){if(c){var o,n=e.audiocontext.currentTime,s=i||0,r=n+s;if(t&&this.connection!==t&&this.connect(t),this.control.cancelScheduledValues(r),n-this.lastAttack<this.aTime){var u=this.aTime-(r-this.lastAttack);this.control.linearRampToValueAtTime(this.aLevel,r+u),this.control.linearRampToValueAtTime(this.dLevel,r+u+this.dTime),this.control.linearRampToValueAtTime(this.sLevel,r+u+this.dTime+this.sTime),this.control.linearRampToValueAtTime(this.rLevel,r+u+this.dTime+this.sTime+this.rTime),o=r+this.dTime+this.sTime+this.rTime}else if(n-this.lastAttack<this.aTime+this.dTime){var h=this.aTime+this.dTime-(n-this.lastAttack);this.control.linearRampToValueAtTime(this.dLevel,r+h),this.control.linearRampToValueAtTime(this.sLevel,r+h+.01),this.control.linearRampToValueAtTime(this.rLevel,r+h+.01+this.rTime),o=r+this.sTime+this.rTime}else if(n-this.lastAttack<this.aTime+this.dTime+this.sTime){var p=this.aTime+this.dTime+this.sTime-(n-this.lastAttack);this.control.linearRampToValueAtTime(this.sLevel,r+p),this.control.linearRampToValueAtTime(this.rLevel,r+p+this.rTime),o=r+this.rTime}else this.control.linearRampToValueAtTime(this.sLevel,r),this.control.linearRampToValueAtTime(this.rLevel,r+this.rTime),o=r+this.dTime+this.sTime+this.rTime;var l=r+this.aTime+this.dTime+this.sTime+this.rTime;this.connection&&this.connection.hasOwnProperty("oscillator")?(a=this.connection.oscillator,a.stop(l+.01)):this.connect&&this.connection.hasOwnProperty("source")&&(a=this.connection.source,a.stop(l+.01)),c=!1}},t.Env.prototype.connect=function(i){this.connection=i,(i instanceof t.Oscillator||i instanceof t.SoundFile||i instanceof t.AudioIn||i instanceof t.Reverb||i instanceof t.Noise||i instanceof t.Filter||i instanceof t.Delay)&&(i=i.output.gain),i instanceof AudioParam&&i.setValueAtTime(0,e.audiocontext.currentTime),i instanceof t.Signal&&i.setValue(0),this.output.connect(i)},t.Env.prototype.disconnect=function(){this.output.disconnect()},t.Env.prototype.add=function(e){var i=new o(e),n=this.mathOps.length,s=this.output;return t.prototype._mathChain(this,i,n,s,o)},t.Env.prototype.mult=function(e){var i=new n(e),o=this.mathOps.length,s=this.output;return t.prototype._mathChain(this,i,o,s,n)},t.Env.prototype.scale=function(e,i,o,n){var r=new s(e,i,o,n),a=this.mathOps.length,u=this.output;return t.prototype._mathChain(this,r,a,u,s)},t.Env.prototype.dispose=function(){e.audiocontext.currentTime;this.disconnect();try{this.control.dispose(),this.control=null}catch(t){}for(var i=1;i<this.mathOps.length;i++)mathOps[i].dispose()}}(i,l,f,d,u);var g;g=function(){"use strict";function e(){for(var t=o.audiocontext,e=t.createBuffer(1,2048,t.sampleRate),i=e.getChannelData(0),n=0;2048>n;n++)i[n]=1;var s=t.createBufferSource();return s.buffer=e,s.loop=!0,s}var o=i;t.Pulse=function(i,n){t.Oscillator.call(this,i,"sawtooth"),this.w=n||0,this.osc2=new t.SawOsc(i),this.dNode=o.audiocontext.createDelay(),this.dcOffset=e(),this.dcGain=o.audiocontext.createGain(),this.dcOffset.connect(this.dcGain),this.dcGain.connect(this.output),this.f=i||440;var s=this.w/this.oscillator.frequency.value;this.dNode.delayTime.value=s,this.dcGain.gain.value=1.7*(.5-this.w),this.osc2.disconnect(),this.osc2.panner.disconnect(),this.osc2.amp(-1),this.osc2.output.connect(this.dNode),this.dNode.connect(this.output),this.output.gain.value=1,this.output.connect(this.panner)},t.Pulse.prototype=Object.create(t.Oscillator.prototype),t.Pulse.prototype.width=function(e){if("number"==typeof e){if(1>=e&&e>=0){this.w=e;var i=this.w/this.oscillator.frequency.value;this.dNode.delayTime.value=i}this.dcGain.gain.value=1.7*(.5-this.w)}else{e.connect(this.dNode.delayTime);var o=new t.SignalAdd(-.5);o.setInput(e),o=o.mult(-1),o=o.mult(1.7),o.connect(this.dcGain.gain)}},t.Pulse.prototype.start=function(t,i){var n=o.audiocontext.currentTime,s=i||0;if(!this.started){var r=t||this.f,a=this.oscillator.type;this.oscillator=o.audiocontext.createOscillator(),this.oscillator.frequency.setValueAtTime(r,n),this.oscillator.type=a,this.oscillator.connect(this.output),this.oscillator.start(s+n),this.osc2.oscillator=o.audiocontext.createOscillator(),this.osc2.oscillator.frequency.setValueAtTime(r,s+n),this.osc2.oscillator.type=a,this.osc2.oscillator.connect(this.osc2.output),this.osc2.start(s+n),this.freqNode=[this.oscillator.frequency,this.osc2.oscillator.frequency],this.dcOffset=e(),this.dcOffset.connect(this.dcGain),this.dcOffset.start(s+n),void 0!==this.mods&&void 0!==this.mods.frequency&&(this.mods.frequency.connect(this.freqNode[0]),this.mods.frequency.connect(this.freqNode[1])),this.started=!0,this.osc2.started=!0}},t.Pulse.prototype.stop=function(t){if(this.started){var e=t||0,i=o.audiocontext.currentTime;this.oscillator.stop(e+i),this.osc2.oscillator.stop(e+i),this.dcOffset.stop(e+i),this.started=!1,this.osc2.started=!1}},t.Pulse.prototype.freq=function(t,e,i){if("number"==typeof t){this.f=t;var n=o.audiocontext.currentTime,e=e||0,i=i||0,s=this.oscillator.frequency.value;this.oscillator.frequency.cancelScheduledValues(n),this.oscillator.frequency.setValueAtTime(s,n+i),this.oscillator.frequency.exponentialRampToValueAtTime(t,i+e+n),this.osc2.oscillator.frequency.cancelScheduledValues(n),this.osc2.oscillator.frequency.setValueAtTime(s,n+i),this.osc2.oscillator.frequency.exponentialRampToValueAtTime(t,i+e+n),this.freqMod&&(this.freqMod.output.disconnect(),this.freqMod=null)}else t.output&&(t.output.disconnect(),t.output.connect(this.oscillator.frequency),t.output.connect(this.osc2.oscillator.frequency),this.freqMod=t)}}(i,m);var T;T=function(){"use strict";var e=i;t.Noise=function(){t.Oscillator.call(this),delete this.f,delete this.freq,delete this.oscillator,this.buffer=o},t.Noise.prototype=Object.create(t.Oscillator.prototype);var o=function(){for(var t=2*e.audiocontext.sampleRate,i=e.audiocontext.createBuffer(1,t,e.audiocontext.sampleRate),o=i.getChannelData(0),n=0;t>n;n++)o[n]=2*Math.random()-1;return i.type="white",i}(),n=function(){var t,i,o,n,s,r,a,u=2*e.audiocontext.sampleRate,c=e.audiocontext.createBuffer(1,u,e.audiocontext.sampleRate),h=c.getChannelData(0);t=i=o=n=s=r=a=0;for(var p=0;u>p;p++){var l=2*Math.random()-1;t=.99886*t+.0555179*l,i=.99332*i+.0750759*l,o=.969*o+.153852*l,n=.8665*n+.3104856*l,s=.55*s+.5329522*l,r=-.7616*r-.016898*l,h[p]=t+i+o+n+s+r+a+.5362*l,h[p]*=.11,a=.115926*l}return c.type="pink",c}(),s=function(){for(var t=2*e.audiocontext.sampleRate,i=e.audiocontext.createBuffer(1,t,e.audiocontext.sampleRate),o=i.getChannelData(0),n=0,s=0;t>s;s++){var r=2*Math.random()-1;o[s]=(n+.02*r)/1.02,n=o[s],o[s]*=3.5}return i.type="brown",i}();t.Noise.prototype.setType=function(t){switch(t){case"white":this.buffer=o;break;case"pink":this.buffer=n;break;case"brown":this.buffer=s;break;default:this.buffer=o}if(this.started){var i=e.audiocontext.currentTime;this.stop(i),this.start(i+.01)}},t.Noise.prototype.getType=function(){return this.buffer.type},t.Noise.prototype.start=function(){this.started&&this.stop(),this.noise=e.audiocontext.createBufferSource(),this.noise.buffer=this.buffer,this.noise.loop=!0,this.noise.connect(this.output);var t=e.audiocontext.currentTime;this.noise.start(t),this.started=!0},t.Noise.prototype.stop=function(){var t=e.audiocontext.currentTime;this.noise&&(this.noise.stop(t),this.started=!1)},t.Noise.prototype.dispose=function(){var t=e.audiocontext.currentTime;this.noise&&(this.noise.disconnect(),this.stop(t)),this.output&&this.output.disconnect(),this.panner&&this.panner.disconnect(),this.output=null,this.panner=null,this.buffer=null,this.noise=null}}(i);var S;S=function(){"use strict";var e=i;t.AudioIn=function(){this.input=e.audiocontext.createGain(),this.output=e.audiocontext.createGain(),this.stream=null,this.mediaStream=null,this.currentSource=0,this.enabled=!1,this.amplitude=new t.Amplitude,this.output.connect(this.amplitude.input),"undefined"==typeof window.MediaStreamTrack?window.alert("This browser does not support MediaStreamTrack"):"function"==typeof window.MediaStreamTrack.getSources&&window.MediaStreamTrack.getSources(this._gotSources),e.soundArray.push(this)},t.AudioIn.prototype.start=function(){var t=this;if(e.inputSources[t.currentSource]){var i=e.inputSources[t.currentSource].id,o={audio:{optional:[{sourceId:i}]}};navigator.getUserMedia(o,this._onStream=function(i){t.stream=i,t.enabled=!0,t.mediaStream=e.audiocontext.createMediaStreamSource(i),t.mediaStream.connect(t.output),t.amplitude.setInput(t.output)},this._onStreamError=function(t){console.error(t)})}else window.navigator.getUserMedia({audio:!0},this._onStream=function(i){t.stream=i,t.enabled=!0,t.mediaStream=e.audiocontext.createMediaStreamSource(i),t.mediaStream.connect(t.output),t.amplitude.setInput(t.output)},this._onStreamError=function(t){console.error(t)})},t.AudioIn.prototype.stop=function(){this.stream&&this.stream.stop()},t.AudioIn.prototype.connect=function(t){this.output.connect(t?t.hasOwnProperty("input")?t.input:t.hasOwnProperty("analyser")?t.analyser:t:e.input)},t.AudioIn.prototype.disconnect=function(t){this.output.disconnect(t),this.output.connect(this.amplitude.input)},t.AudioIn.prototype.getLevel=function(t){return t&&(this.amplitude.smoothing=t),this.amplitude.getLevel()},t.AudioIn.prototype._gotSources=function(t){for(var e=0;e<t.length;e++){var i=t[e];if("audio"===i.kind)return i}},t.AudioIn.prototype.amp=function(t,i){if(i){var o=i||0,n=this.output.gain.value;this.output.gain.cancelScheduledValues(e.audiocontext.currentTime),this.output.gain.setValueAtTime(n,e.audiocontext.currentTime),this.output.gain.linearRampToValueAtTime(t,o+e.audiocontext.currentTime)}else this.output.gain.cancelScheduledValues(e.audiocontext.currentTime),this.output.gain.setValueAtTime(t,e.audiocontext.currentTime)},t.AudioIn.prototype.listSources=function(){return console.log("listSources is deprecated - please use AudioIn.getSources"),console.log("input sources: "),e.inputSources.length>0?e.inputSources:"This browser does not support MediaStreamTrack.getSources()"},t.AudioIn.prototype.getSources=function(t){"function"==typeof window.MediaStreamTrack.getSources?window.MediaStreamTrack.getSources(function(i){for(var o=0,n=i.length;n>o;o++){var s=i[o];"audio"===s.kind&&e.inputSources.push(s)}t(e.inputSources)}):console.log("This browser does not support MediaStreamTrack.getSources()")},t.AudioIn.prototype.setSource=function(t){var i=this;e.inputSources.length>0&&t<e.inputSources.length?(i.currentSource=t,console.log("set source to "+e.inputSources[i.currentSource].id)):console.log("unable to set input source")},t.AudioIn.prototype.dispose=function(){this.stop(),this.output&&this.output.disconnect(),this.amplitude&&this.amplitude.disconnect(),this.amplitude=null,this.output=null}}(i);var _;_=function(){"use strict";var e=i;t.Filter=function(t){this.ac=e.audiocontext,this.input=this.ac.createGain(),this.output=this.ac.createGain(),this.biquad=this.ac.createBiquadFilter(),this.input.connect(this.biquad),this.biquad.connect(this.output),this.connect(),t&&this.setType(t)},t.Filter.prototype.process=function(t,e,i){t.connect(this.input),this.set(e,i)},t.Filter.prototype.set=function(t,e,i){t&&this.freq(t,i),e&&this.res(e,i)},t.Filter.prototype.freq=function(t,e){var i=this,o=e||0;return 0>=t&&(t=1),"number"==typeof t?(i.biquad.frequency.value=t,i.biquad.frequency.cancelScheduledValues(this.ac.currentTime+.01+o),i.biquad.frequency.exponentialRampToValueAtTime(t,this.ac.currentTime+.02+o)):t&&t.connect(this.biquad.frequency),i.biquad.frequency.value},t.Filter.prototype.res=function(t,e){var i=this,o=e||0;return"number"==typeof t?(i.biquad.Q.value=t,i.biquad.Q.cancelScheduledValues(i.ac.currentTime+.01+o),i.biquad.Q.linearRampToValueAtTime(t,i.ac.currentTime+.02+o)):t&&freq.connect(this.biquad.Q),i.biquad.Q.value},t.Filter.prototype.setType=function(t){this.biquad.type=t},t.Filter.prototype.amp=function(t,i,o){var i=i||0,o=o||0,n=e.audiocontext.currentTime,s=this.output.gain.value;this.output.gain.cancelScheduledValues(n),this.output.gain.linearRampToValueAtTime(s,n+o+.001),this.output.gain.linearRampToValueAtTime(t,n+o+i+.001)},t.Filter.prototype.connect=function(e){var i=e||t.soundOut.input;this.output.connect(i)},t.Filter.prototype.disconnect=function(){this.output.disconnect()},t.LowPass=function(){t.Filter.call(this,"lowpass")},t.LowPass.prototype=Object.create(t.Filter.prototype),t.HighPass=function(){t.Filter.call(this,"highpass")},t.HighPass.prototype=Object.create(t.Filter.prototype),t.BandPass=function(){t.Filter.call(this,"bandpass")},t.BandPass.prototype=Object.create(t.Filter.prototype)}(i);var b;b=function(){"use strict";var e=i;t.Delay=function(){this.ac=e.audiocontext,this.input=this.ac.createGain(),this.output=this.ac.createGain(),this._split=this.ac.createChannelSplitter(2),this._merge=this.ac.createChannelMerger(2),this._leftGain=this.ac.createGain(),this._rightGain=this.ac.createGain(),this.leftDelay=this.ac.createDelay(),this.rightDelay=this.ac.createDelay(),this._leftFilter=new t.Filter,this._rightFilter=new t.Filter,this._leftFilter.disconnect(),this._rightFilter.disconnect(),this.lowPass=this._leftFilter,this._leftFilter.biquad.frequency.setValueAtTime(1200,this.ac.currentTime),this._rightFilter.biquad.frequency.setValueAtTime(1200,this.ac.currentTime),this._leftFilter.biquad.Q.setValueAtTime(.3,this.ac.currentTime),this._rightFilter.biquad.Q.setValueAtTime(.3,this.ac.currentTime),this.input.connect(this._split),this.leftDelay.connect(this._leftGain),this.rightDelay.connect(this._rightGain),this._leftGain.connect(this._leftFilter.input),this._rightGain.connect(this._rightFilter.input),this._merge.connect(this.output),this.output.connect(t.soundOut.input),this._leftFilter.biquad.gain.setValueAtTime(1,this.ac.currentTime),this._rightFilter.biquad.gain.setValueAtTime(1,this.ac.currentTime),this.setType(0),this._maxDelay=this.leftDelay.delayTime.maxValue},t.Delay.prototype.process=function(t,e,i,o){var n=i||0,s=e||0;if(n>=1)throw new Error("Feedback value will force a positive feedback loop.");if(s>=this._maxDelay)throw new Error("Delay Time exceeds maximum delay time of "+this._maxDelay+" second.");t.connect(this.input),this.leftDelay.delayTime.setValueAtTime(s,this.ac.currentTime),this.rightDelay.delayTime.setValueAtTime(s,this.ac.currentTime),this._leftGain.gain.setValueAtTime(n,this.ac.currentTime),this._rightGain.gain.setValueAtTime(n,this.ac.currentTime),o&&(this._leftFilter.freq(o),this._rightFilter.freq(o))},t.Delay.prototype.delayTime=function(t){"number"!=typeof t?(t.connect(this.leftDelay.delayTime),t.connect(this.rightDelay.delayTime)):(this.leftDelay.delayTime.cancelScheduledValues(this.ac.currentTime),this.rightDelay.delayTime.cancelScheduledValues(this.ac.currentTime),this.leftDelay.delayTime.linearRampToValueAtTime(t,this.ac.currentTime),this.rightDelay.delayTime.linearRampToValueAtTime(t,this.ac.currentTime))},t.Delay.prototype.feedback=function(t){if("number"!=typeof t)t.connect(this._leftGain.gain),t.connect(this._rightGain.gain);else{if(t>=1)throw new Error("Feedback value will force a positive feedback loop.");this._leftGain.gain.exponentialRampToValueAtTime(t,this.ac.currentTime),this._rightGain.gain.exponentialRampToValueAtTime(t,this.ac.currentTime)}},t.Delay.prototype.filter=function(t,e){this._leftFilter.set(t,e),this._rightFilter.set(t,e)},t.Delay.prototype.setType=function(t){switch(1===t&&(t="pingPong"),this._split.disconnect(),this._leftFilter.disconnect(),this._rightFilter.disconnect(),this._split.connect(this.leftDelay,0),this._split.connect(this.rightDelay,1),t){case"pingPong":this._rightFilter.setType(this._leftFilter.biquad.type),this._leftFilter.output.connect(this._merge,0,0),this._rightFilter.output.connect(this._merge,0,1),this._leftFilter.output.connect(this.rightDelay),this._rightFilter.output.connect(this.leftDelay);break;default:this._leftFilter.output.connect(this._merge,0,0),this._leftFilter.output.connect(this._merge,0,1),this._leftFilter.output.connect(this.leftDelay),this._leftFilter.output.connect(this.rightDelay)}},t.Delay.prototype.amp=function(t,i,o){var i=i||0,o=o||0,n=e.audiocontext.currentTime,s=this.output.gain.value;this.output.gain.cancelScheduledValues(n),this.output.gain.linearRampToValueAtTime(s,n+o+.001),this.output.gain.linearRampToValueAtTime(t,n+o+i+.001)},t.Delay.prototype.connect=function(e){var i=e||t.soundOut.input;this.output.connect(i)},t.Delay.prototype.disconnect=function(){this.output.disconnect()}}(i,_);var w;w=function(){"use strict";var e=i;t.Reverb=function(){this.ac=e.audiocontext,this.convolverNode=this.ac.createConvolver(),this.input=this.ac.createGain(),this.output=this.ac.createGain(),this.input.gain.value=.5,this.input.connect(this.convolverNode),this.convolverNode.connect(this.output),this._seconds=3,this._decay=2,this._reverse=!1,this._buildImpulse(),this.connect(),e.soundArray.push(this)},t.Reverb.prototype.process=function(t,e,i,o){t.connect(this.input);var n=!1;e&&(this._seconds=e,n=!0),i&&(this._decay=i),o&&(this._reverse=o),n&&this._buildImpulse()},t.Reverb.prototype.set=function(t,e,i){var o=!1;t&&(this._seconds=t,o=!0),e&&(this._decay=e),i&&(this._reverse=i),o&&this._buildImpulse()},t.Reverb.prototype.amp=function(t,i,o){var i=i||0,o=o||0,n=e.audiocontext.currentTime,s=this.output.gain.value;this.output.gain.cancelScheduledValues(n),this.output.gain.linearRampToValueAtTime(s,n+o+.001),this.output.gain.linearRampToValueAtTime(t,n+o+i+.001)},t.Reverb.prototype.connect=function(e){var i=e||t.soundOut.input;this.output.connect(i.input?i.input:i)},t.Reverb.prototype.disconnect=function(){this.output.disconnect()},t.Reverb.prototype._buildImpulse=function(){var t,e,i=this.ac.sampleRate,o=i*this._seconds,n=this._decay,s=this.ac.createBuffer(2,o,i),r=s.getChannelData(0),a=s.getChannelData(1);for(e=0;o>e;e++)t=this.reverse?o-e:e,r[e]=(2*Math.random()-1)*Math.pow(1-t/o,n),a[e]=(2*Math.random()-1)*Math.pow(1-t/o,n);this.convolverNode.buffer=s},t.Reverb.prototype.dispose=function(){this.convolverNode&&(this.convolverNode.buffer=null,this.convolverNode=null),"undefined"!=typeof this.output&&(this.output.disconnect(),this.output=null),"undefined"!=typeof this.panner&&(this.panner.disconnect(),this.panner=null)},t.Convolver=function(t,i){this.ac=e.audiocontext,this.convolverNode=this.ac.createConvolver(),this.input=this.ac.createGain(),this.output=this.ac.createGain(),this.input.gain.value=.5,this.input.connect(this.convolverNode),this.convolverNode.connect(this.output),t?(this.impulses=[],this._loadBuffer(t,i)):(this._seconds=3,this._decay=2,this._reverse=!1,this._buildImpulse()),this.connect(),e.soundArray.push(this) | |
},t.Convolver.prototype=Object.create(t.Reverb.prototype),t.prototype.registerPreloadMethod("createConvolver"),t.prototype.createConvolver=function(e,i){window.location.origin.indexOf("file://")>-1&&"undefined"===window.cordova&&alert("This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS");var o=new t.Convolver(e,i);return o.impulses=[],o},t.Convolver.prototype._loadBuffer=function(e,i){e=t.prototype._checkFileFormats(e);var o=new XMLHttpRequest;o.open("GET",e,!0),o.responseType="arraybuffer";var n=this;o.onload=function(){var s=t.prototype.getAudioContext();s.decodeAudioData(o.response,function(t){var o={},s=e.split("/");o.name=s[s.length-1],o.audioBuffer=t,n.impulses.push(o),n.convolverNode.buffer=o.audioBuffer,i&&i(o)})},o.send()},t.Convolver.prototype.set=null,t.Convolver.prototype.process=function(t){t.connect(this.input)},t.Convolver.prototype.impulses=[],t.Convolver.prototype.addImpulse=function(t,e){window.location.origin.indexOf("file://")>-1&&"undefined"===window.cordova&&alert("This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS"),this._loadBuffer(t,e)},t.Convolver.prototype.resetImpulse=function(t,e){window.location.origin.indexOf("file://")>-1&&"undefined"===window.cordova&&alert("This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS"),this.impulses=[],this._loadBuffer(t,e)},t.Convolver.prototype.toggleImpulse=function(t){if("number"==typeof t&&t<this.impulses.length&&(this.convolverNode.buffer=this.impulses[t].audioBuffer),"string"==typeof t)for(var e=0;e<this.impulses.length;e++)if(this.impulses[e].name===t){this.convolverNode.buffer=this.impulses[e].audioBuffer;break}},t.Convolver.prototype.dispose=function(){for(var t in this.impulses)this.impulses[t]=null;this.convolverNode.disconnect(),this.concolverNode=null,"undefined"!=typeof this.output&&(this.output.disconnect(),this.output=null),"undefined"!=typeof this.panner&&(this.panner.disconnect(),this.panner=null)}}(i,e);var A;A=function(t){"use strict";return t.Clock=function(e,i){this._oscillator=null,this._jsNode=this.context.createScriptProcessor(this.bufferSize,1,1),this._jsNode.onaudioprocess=this._processBuffer.bind(this),this._controlSignal=new t.Signal(1),this._upTick=!1,this.tick=this.defaultArg(i,function(){}),this._jsNode.noGC(),this.setRate(e)},t.extend(t.Clock),t.Clock.prototype.setRate=function(t,e){var i=this.secondsToFrequency(this.toSeconds(t));e?this._controlSignal.exponentialRampToValueNow(i,e):(this._controlSignal.cancelScheduledValues(0),this._controlSignal.setValue(i))},t.Clock.prototype.getRate=function(){return this._controlSignal.getValue()},t.Clock.prototype.start=function(t){this._oscillator=this.context.createOscillator(),this._oscillator.type="square",this._oscillator.connect(this._jsNode),this._controlSignal.connect(this._oscillator.frequency),this._upTick=!1;var e=this.toSeconds(t);this._oscillator.start(e),this._oscillator.onended=function(){}},t.Clock.prototype.stop=function(t,e){var i=this.toSeconds(t);this._oscillator.onended=e,this._oscillator.stop(i)},t.Clock.prototype._processBuffer=function(t){for(var e=this.defaultArg(t.playbackTime,this.now()),i=this._jsNode.bufferSize,o=t.inputBuffer.getChannelData(0),n=this._upTick,s=this,r=0;i>r;r++){var a=o[r];a>0&&!n?(n=!0,setTimeout(function(){var t=e+s.samplesToSeconds(r+2*i);return function(){s.tick(t)}}(),0)):0>a&&n&&(n=!1)}this._upTick=n},t.Clock.prototype.dispose=function(){this._jsNode.disconnect(),this._controlSignal.dispose(),this._oscillator&&(this._oscillator.onended(),this._oscillator.disconnect()),this._jsNode.onaudioprocess=function(){},this._jsNode=null,this._controlSignal=null,this._oscillator=null},t.Clock}(u);var x;x=function(){"use strict";var e=i,o=A,n=e.audiocontext;t.Metro=function(){this.clock=new o(n.sampleRate,this.ontick.bind(this)),this.syncedParts=[],this.bpm=120,this._init(),this.tickCallback=function(){}};var s=0,r=0;t.Metro.prototype.ontick=function(t){var i=t-s,o=t-e.audiocontext.currentTime;if(!(-.02>=i-r)){s=t;for(var n in this.syncedParts){var a=this.syncedParts[n];a.incrementStep(o);for(var u in a.phrases){var c=a.phrases[u],h=c.sequence,p=this.metroTicks%h.length;0!==h[p]&&(this.metroTicks<h.length||!c.looping)&&c.callback(o,h[p])}}this.metroTicks+=1,this.tickCallback(o)}},t.Metro.prototype.setBPM=function(t,i){var o=60/(t*this.tatums);r=o;this.clock.setRate(o,i+e.audiocontext.currentTime),this.bpm=t},t.Metro.prototype.getBPM=function(){return this.clock.getRate()/this.tatums*60},t.Metro.prototype._init=function(){this.metroTicks=0},t.Metro.prototype.resetSync=function(t){this.syncedParts=[t]},t.Metro.prototype.pushSync=function(t){this.syncedParts.push(t)},t.Metro.prototype.start=function(t){var e=t||0;this.clock.start(e),this.setBPM(this.bpm)},t.Metro.prototype.stop=function(t){var e=t||0;this.clock._oscillator&&this.clock.stop(e)},t.Metro.prototype.beatLength=function(t){this.tatums=1/t/4}}(i,A);var P;P=function(){"use strict";function e(t){t.currentPart++,t.currentPart>=t.parts.length?(t.scoreStep=0,t.onended()):(t.scoreStep=0,t.parts[t.currentPart-1].stop(),t.parts[t.currentPart].start())}var o=i,n=120;t.prototype.setBPM=function(t,e){n=t;for(var i in o.parts)o.parts[i].setBPM(n,e)},t.Phrase=function(t,e,i){this.phraseStep=0,this.name=t,this.callback=e,this.sequence=i},t.Part=function(e,i){this.length=e||0,this.partStep=0,this.phrases=[],this.looping=!1,this.isPlaying=!1,this.onended=function(){this.stop()},this.tatums=i||.0625,this.metro=new t.Metro,this.metro._init(),this.metro.beatLength(this.tatums),this.metro.setBPM(n),o.parts.push(this),this.callback=function(){}},t.Part.prototype.setBPM=function(t,e){this.metro.setBPM(t,e)},t.Part.prototype.getBPM=function(){return this.metro.getBPM()},t.Part.prototype.start=function(t){if(!this.isPlaying){this.isPlaying=!0,this.metro.resetSync(this);var e=t||0;this.metro.start(e)}},t.Part.prototype.loop=function(t){this.looping=!0,this.onended=function(){this.partStep=0};var e=t||0;this.start(e)},t.Part.prototype.noLoop=function(){this.looping=!1,this.onended=function(){this.stop()}},t.Part.prototype.stop=function(t){this.partStep=0,this.pause(t)},t.Part.prototype.pause=function(t){this.isPlaying=!1;var e=t||0;this.metro.stop(e)},t.Part.prototype.addPhrase=function(e,i,o){var n;if(3===arguments.length)n=new t.Phrase(e,i,o);else{if(!(arguments[0]instanceof t.Phrase))throw"invalid input. addPhrase accepts name, callback, array or a p5.Phrase";n=arguments[0]}this.phrases.push(n),n.sequence.length>this.length&&(this.length=n.sequence.length)},t.Part.prototype.removePhrase=function(t){for(var e in this.phrases)this.phrases[e].name===t&&this.phrases.split(e,1)},t.Part.prototype.getPhrase=function(t){for(var e in this.phrases)if(this.phrases[e].name===t)return this.phrases[e]},t.Part.prototype.replaceSequence=function(t,e){for(var i in this.phrases)this.phrases[i].name===t&&(this.phrases[i].sequence=e)},t.Part.prototype.incrementStep=function(t){this.partStep<this.length-1?(this.callback(t),this.partStep+=1):(this.looping&&this.callback(t),this.onended(),this.partStep=0)},t.Part.prototype.onStep=function(t){this.callback=t},t.Score=function(){this.parts=[],this.currentPart=0;var t=this;for(var i in arguments)this.parts[i]=arguments[i],this.parts[i].nextPart=this.parts[i+1],this.parts[i].onended=function(){t.resetPart(i),e(t)};this.looping=!1},t.Score.prototype.onended=function(){this.looping?this.parts[0].start():this.parts[this.parts.length-1].onended=function(){this.stop(),this.resetParts()},this.currentPart=0},t.Score.prototype.start=function(){this.parts[this.currentPart].start(),this.scoreStep=0},t.Score.prototype.stop=function(){this.parts[this.currentPart].stop(),this.currentPart=0,this.scoreStep=0},t.Score.prototype.pause=function(){this.parts[this.currentPart].stop()},t.Score.prototype.loop=function(){this.looping=!0,this.start()},t.Score.prototype.noLoop=function(){this.looping=!1},t.Score.prototype.resetParts=function(){for(var t in this.parts)this.resetPart(t)},t.Score.prototype.resetPart=function(t){this.parts[t].stop(),this.parts[t].partStep=0;for(var e in this.parts[t].phrases)this.parts[t].phrases[e].phraseStep=0},t.Score.prototype.setBPM=function(t,e){for(var i in this.parts)this.parts[i].setBPM(t,e)}}(i);var N;N=function(){"use strict";function e(t,e){for(var i=t.length+e.length,o=new Float32Array(i),n=0,s=0;i>s;)o[s++]=t[n],o[s++]=e[n],n++;return o}function o(t,e,i){for(var o=i.length,n=0;o>n;n++)t.setUint8(e+n,i.charCodeAt(n))}var n=i,s=n.audiocontext;t.SoundRecorder=function(){this.input=s.createGain(),this.output=s.createGain(),this.recording=!1,this.bufferSize=1024,this._channels=2,this._clear(),this._jsNode=s.createScriptProcessor(this.bufferSize,this._channels,2),this._jsNode.onaudioprocess=this._audioprocess.bind(this),this._callback=function(){},this._jsNode.connect(t.soundOut._silentNode),this.setInput(),n.soundArray.push(this)},t.SoundRecorder.prototype.setInput=function(e){this.input.disconnect(),this.input=null,this.input=s.createGain(),this.input.connect(this._jsNode),this.input.connect(this.output),e?e.connect(this.input):t.soundOut.output.connect(this.input)},t.SoundRecorder.prototype.record=function(t,e,i){this.recording=!0,e&&(this.sampleLimit=Math.round(e*s.sampleRate)),t&&i?this._callback=function(){this.buffer=this._getBuffer(),t.setBuffer(this.buffer),i()}:t&&(this._callback=function(){this.buffer=this._getBuffer(),t.setBuffer(this.buffer)})},t.SoundRecorder.prototype.stop=function(){this.recording=!1,this._callback(),this._clear()},t.SoundRecorder.prototype._clear=function(){this._leftBuffers=[],this._rightBuffers=[],this.recordedSamples=0,this.sampleLimit=null},t.SoundRecorder.prototype._audioprocess=function(t){if(this.recording!==!1&&this.recording===!0)if(this.sampleLimit&&this.recordedSamples>=this.sampleLimit)this.stop();else{var e=t.inputBuffer.getChannelData(0),i=t.inputBuffer.getChannelData(1);this._leftBuffers.push(new Float32Array(e)),this._rightBuffers.push(new Float32Array(i)),this.recordedSamples+=this.bufferSize}},t.SoundRecorder.prototype._getBuffer=function(){var t=[];return t.push(this._mergeBuffers(this._leftBuffers)),t.push(this._mergeBuffers(this._rightBuffers)),t},t.SoundRecorder.prototype._mergeBuffers=function(t){for(var e=new Float32Array(this.recordedSamples),i=0,o=t.length,n=0;o>n;n++){var s=t[n];e.set(s,i),i+=s.length}return e},t.SoundRecorder.prototype.dispose=function(){this._clear(),this._callback=function(){},this.input&&this.input.disconnect(),this.input=null,this._jsNode=null},t.prototype.saveSound=function(i,n){var s=i.buffer.getChannelData(0),r=i.buffer.getChannelData(1),a=e(s,r),u=new ArrayBuffer(44+2*a.length),c=new DataView(u);o(c,0,"RIFF"),c.setUint32(4,44+2*a.length,!0),o(c,8,"WAVE"),o(c,12,"fmt "),c.setUint32(16,16,!0),c.setUint16(20,1,!0),c.setUint16(22,2,!0),c.setUint32(24,44100,!0),c.setUint32(28,176400,!0),c.setUint16(32,4,!0),c.setUint16(34,16,!0),o(c,36,"data"),c.setUint32(40,2*a.length,!0);for(var h=a.length,p=44,l=1,f=0;h>f;f++)c.setInt16(p,32767*a[f]*l,!0),p+=2;t.prototype.writeFile([c],n,"wav")}}(e,i);var F;F=function(){"use strict";t.PeakDetect=function(t,e,i,o){this.framesPerPeak=o||20,this.framesSinceLastPeak=0,this.decayRate=.95,this.threshold=i||.35,this.cutoff=0,this.cutoffMult=1.5,this.energy=0,this.penergy=0,this.currentValue=0,this.isDetected=!1,this.f1=t||40,this.f2=e||2e4,this._onPeak=function(){}},t.PeakDetect.prototype.update=function(t){var e=this.energy=t.getEnergy(this.f1,this.f2)/255;e>this.cutoff&&e>this.threshold&&e-this.penergy>0?(this._onPeak(),this.isDetected=!0,this.cutoff=e*this.cutoffMult,this.framesSinceLastPeak=0):(this.isDetected=!1,this.framesSinceLastPeak<=this.framesPerPeak?this.framesSinceLastPeak++:(this.cutoff*=this.decayRate,this.cutoff=Math.max(this.cutoff,this.threshold))),this.currentValue=e,this.penergy=e},t.PeakDetect.prototype.onPeak=function(t,e){var i=this;i._onPeak=function(){t(i.energy,e)}}}(i);var V;V=function(){"use strict";var e=i;t.Gain=function(){this.ac=e.audiocontext,this.input=this.ac.createGain(),this.output=this.ac.createGain(),this.input.gain.value=.5,this.input.connect(this.output)},t.Gain.prototype.setInput=function(t){t.connect(this.input)},t.Gain.prototype.connect=function(e){var i=e||t.soundOut.input;this.output.connect(i.input?i.input:i)},t.Gain.prototype.disconnect=function(){this.output.disconnect()},t.Gain.prototype.amp=function(t,i,o){var i=i||0,o=o||0,n=e.audiocontext.currentTime,s=this.output.gain.value;this.output.gain.cancelScheduledValues(n),this.output.gain.linearRampToValueAtTime(s,n+o),this.output.gain.linearRampToValueAtTime(t,n+o+i)}}(i,e);var M;M=function(){"use strict";var t=e;return t}(e,i,o,n,s,r,a,y,m,v,g,T,S,_,b,w,x,P,N,F,V)}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var baseNote = 36; | |
// var noteScale = [0, 2, 4, 5, 7, 9, 10, 12]; | |
var noteScale = [0, 2, 4, 7, 9, 12]; | |
var octaveRange = 2; | |
// initialize audio components | |
var osc = new Tone.OmniOscillator('D2', 'sawtooth24'); | |
var filter = new Tone.LowpassCombFilter(); | |
filter.resonance.value = 0.2; | |
filter.delayTime.value = 0.2; | |
var gainNode = Tone.context.createGain(); | |
var env = new Tone.Envelope({ | |
"attack" : 0.01, | |
"decay" : 0.8, | |
"sustain" : 0.01, | |
"release" : 0.2, | |
}); | |
env.connect(gainNode.gain); | |
// routing | |
osc.connect(gainNode); | |
gainNode.connect(filter); | |
filter.toMaster(); | |
// start audio | |
osc.start(); | |
Tone.Transport.bpm.value = 96; | |
// start loop | |
var loop = new Tone.Loop(function(time){ | |
counter++; | |
curMatrixIndex = counter % (numRows * numCols); | |
var noteIndex = ~~map(curColor[0], 0, 0.7, 0, noteScale.length*octaveRange); | |
var note = noteScale[noteIndex % noteScale.length]; | |
var root = baseNote + Math.floor(noteIndex/noteScale.length)*12; | |
osc.frequency.value = osc.midiToFrequency(note + root); | |
filter.dampening.value = map(curColor[2], 0, 1, 30, 3500); | |
if (curMatrixIndex !== prevMatrixIndex) { | |
// trigger note! | |
var vel = map(curColor[1], 0, 1, 0, 1); | |
if (vel > 0.1) { | |
env.triggerAttackRelease(0.2, undefined, vel); | |
} | |
} | |
prevMatrixIndex = curMatrixIndex; | |
}, "16n").start(0); | |
Tone.Transport.start(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html, body { | |
overflow: hidden; | |
margin: 0; | |
padding: 0; | |
/*background: #c8c5c2;*/ | |
/*background: #AFAFAF;*/ | |
background:#58585B; | |
} | |
#defaultCanvas { | |
margin-top: auto; | |
margin-bottom: auto; | |
margin-left: auto; | |
position: absolute; | |
top: 0; left: 0; bottom: 0; right: 0; | |
} |
This file has been truncated, but you can view the full file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (root) { | |
"use strict"; | |
var Tone; | |
//constructs the main Tone object | |
function Main(func){ | |
Tone = func(); | |
} | |
//invokes each of the modules with the main Tone object as the argument | |
function Module(func){ | |
func(Tone); | |
} | |
/** | |
* Tone.js | |
* @author Yotam Mann | |
* @license http://opensource.org/licenses/MIT MIT License | |
* @copyright 2014-2015 Yotam Mann | |
*/ | |
Main(function () { | |
////////////////////////////////////////////////////////////////////////// | |
// WEB AUDIO CONTEXT | |
/////////////////////////////////////////////////////////////////////////// | |
//borrowed from underscore.js | |
function isUndef(val) { | |
return val === void 0; | |
} | |
//borrowed from underscore.js | |
function isFunction(val) { | |
return typeof val === 'function'; | |
} | |
var audioContext; | |
//polyfill for AudioContext and OfflineAudioContext | |
if (isUndef(window.AudioContext)) { | |
window.AudioContext = window.webkitAudioContext; | |
} | |
if (isUndef(window.OfflineAudioContext)) { | |
window.OfflineAudioContext = window.webkitOfflineAudioContext; | |
} | |
if (!isUndef(AudioContext)) { | |
audioContext = new AudioContext(); | |
} else { | |
throw new Error('Web Audio is not supported in this browser'); | |
} | |
//SHIMS//////////////////////////////////////////////////////////////////// | |
if (!isFunction(AudioContext.prototype.createGain)) { | |
AudioContext.prototype.createGain = AudioContext.prototype.createGainNode; | |
} | |
if (!isFunction(AudioContext.prototype.createDelay)) { | |
AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode; | |
} | |
if (!isFunction(AudioContext.prototype.createPeriodicWave)) { | |
AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable; | |
} | |
if (!isFunction(AudioBufferSourceNode.prototype.start)) { | |
AudioBufferSourceNode.prototype.start = AudioBufferSourceNode.prototype.noteGrainOn; | |
} | |
if (!isFunction(AudioBufferSourceNode.prototype.stop)) { | |
AudioBufferSourceNode.prototype.stop = AudioBufferSourceNode.prototype.noteOff; | |
} | |
if (!isFunction(OscillatorNode.prototype.start)) { | |
OscillatorNode.prototype.start = OscillatorNode.prototype.noteOn; | |
} | |
if (!isFunction(OscillatorNode.prototype.stop)) { | |
OscillatorNode.prototype.stop = OscillatorNode.prototype.noteOff; | |
} | |
if (!isFunction(OscillatorNode.prototype.setPeriodicWave)) { | |
OscillatorNode.prototype.setPeriodicWave = OscillatorNode.prototype.setWaveTable; | |
} | |
//extend the connect function to include Tones | |
AudioNode.prototype._nativeConnect = AudioNode.prototype.connect; | |
AudioNode.prototype.connect = function (B, outNum, inNum) { | |
if (B.input) { | |
if (Array.isArray(B.input)) { | |
if (isUndef(inNum)) { | |
inNum = 0; | |
} | |
this.connect(B.input[inNum]); | |
} else { | |
this.connect(B.input, outNum, inNum); | |
} | |
} else { | |
try { | |
if (B instanceof AudioNode) { | |
this._nativeConnect(B, outNum, inNum); | |
} else { | |
this._nativeConnect(B, outNum); | |
} | |
} catch (e) { | |
throw new Error('error connecting to node: ' + B); | |
} | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// TONE | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* @class Tone is the base class of all other classes. It provides | |
* a lot of methods and functionality to all classes that extend | |
* it. | |
* | |
* @constructor | |
* @alias Tone | |
* @param {number} [inputs=1] the number of input nodes | |
* @param {number} [outputs=1] the number of output nodes | |
*/ | |
var Tone = function (inputs, outputs) { | |
/** | |
* the input node(s) | |
* @type {GainNode|Array} | |
*/ | |
if (isUndef(inputs) || inputs === 1) { | |
this.input = this.context.createGain(); | |
} else if (inputs > 1) { | |
this.input = new Array(inputs); | |
} | |
/** | |
* the output node(s) | |
* @type {GainNode|Array} | |
*/ | |
if (isUndef(outputs) || outputs === 1) { | |
this.output = this.context.createGain(); | |
} else if (outputs > 1) { | |
this.output = new Array(inputs); | |
} | |
}; | |
/** | |
* Set the parameters at once. Either pass in an | |
* object mapping parameters to values, or to set a | |
* single parameter, by passing in a string and value. | |
* The last argument is an optional ramp time which | |
* will ramp any signal values to their destination value | |
* over the duration of the rampTime. | |
* @param {Object|string} params | |
* @param {number=} value | |
* @param {Time=} rampTime | |
* @returns {Tone} this | |
* @example | |
* //set values using an object | |
* filter.set({ | |
* "frequency" : 300, | |
* "type" : highpass | |
* }); | |
* @example | |
* filter.set("type", "highpass"); | |
* @example | |
* //ramp to the value 220 over 3 seconds. | |
* oscillator.set({ | |
* "frequency" : 220 | |
* }, 3); | |
*/ | |
Tone.prototype.set = function (params, value, rampTime) { | |
if (this.isObject(params)) { | |
rampTime = value; | |
} else if (this.isString(params)) { | |
var tmpObj = {}; | |
tmpObj[params] = value; | |
params = tmpObj; | |
} | |
for (var attr in params) { | |
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]]; | |
} | |
attr = attrSplit[attrSplit.length - 1]; | |
} | |
var param = parent[attr]; | |
if (isUndef(param)) { | |
continue; | |
} | |
if (Tone.Signal && param instanceof Tone.Signal || Tone.Param && param instanceof Tone.Param) { | |
if (param.value !== value) { | |
if (isUndef(rampTime)) { | |
param.value = value; | |
} else { | |
param.rampTo(value, rampTime); | |
} | |
} | |
} else if (param instanceof AudioParam) { | |
if (param.value !== value) { | |
param.value = value; | |
} | |
} else if (param instanceof Tone) { | |
param.set(value); | |
} else if (param !== value) { | |
parent[attr] = value; | |
} | |
} | |
return this; | |
}; | |
/** | |
* Get the object's attributes. Given no arguments get | |
* 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. | |
* @example | |
* 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}} | |
* @param {Array=|string|undefined} params the parameters to get, otherwise will return | |
* all available. | |
* @returns {Object} | |
*/ | |
Tone.prototype.get = function (params) { | |
if (isUndef(params)) { | |
params = this._collectDefaults(this.constructor); | |
} else if (this.isString(params)) { | |
params = [params]; | |
} | |
var ret = {}; | |
for (var i = 0; i < params.length; i++) { | |
var attr = params[i]; | |
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]; | |
if (this.isObject(params[attr])) { | |
subRet[attr] = param.get(); | |
} 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; | |
} else if (param instanceof Tone) { | |
subRet[attr] = param.get(); | |
} else if (!isFunction(param) && !isUndef(param)) { | |
subRet[attr] = param; | |
} | |
} | |
return ret; | |
}; | |
/** | |
* collect all of the default attributes in one | |
* @private | |
* @param {function} constr the constructor to find the defaults from | |
* @return {Array} all of the attributes which belong to the class | |
*/ | |
Tone.prototype._collectDefaults = function (constr) { | |
var ret = []; | |
if (!isUndef(constr.defaults)) { | |
ret = Object.keys(constr.defaults); | |
} | |
if (!isUndef(constr._super)) { | |
var superDefs = this._collectDefaults(constr._super); | |
//filter out repeats | |
for (var i = 0; i < superDefs.length; i++) { | |
if (ret.indexOf(superDefs[i]) === -1) { | |
ret.push(superDefs[i]); | |
} | |
} | |
} | |
return ret; | |
}; | |
/** | |
* @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]$/); | |
var sameConstructor = Tone[className] === this.constructor; | |
if (isFunction(Tone[className]) && isLetter && sameConstructor) { | |
return className; | |
} | |
} | |
return 'Tone'; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// CLASS VARS | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* A static pointer to the audio context accessible as Tone.context. | |
* @type {AudioContext} | |
*/ | |
Tone.context = audioContext; | |
/** | |
* The audio context. | |
* @type {AudioContext} | |
*/ | |
Tone.prototype.context = Tone.context; | |
/** | |
* the default buffer size | |
* @type {number} | |
* @static | |
* @const | |
*/ | |
Tone.prototype.bufferSize = 2048; | |
/** | |
* The delay time of a single frame (128 samples according to the spec). | |
* @type {number} | |
* @static | |
* @const | |
*/ | |
Tone.prototype.blockTime = 128 / Tone.context.sampleRate; | |
/////////////////////////////////////////////////////////////////////////// | |
// CONNECTIONS | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* disconnect and dispose | |
* @returns {Tone} this | |
*/ | |
Tone.prototype.dispose = function () { | |
if (!this.isUndef(this.input)) { | |
if (this.input instanceof AudioNode) { | |
this.input.disconnect(); | |
} | |
this.input = null; | |
} | |
if (!this.isUndef(this.output)) { | |
if (this.output instanceof AudioNode) { | |
this.output.disconnect(); | |
} | |
this.output = null; | |
} | |
return this; | |
}; | |
/** | |
* a silent connection to the DesinationNode | |
* which will ensure that anything connected to it | |
* will not be garbage collected | |
* | |
* @private | |
*/ | |
var _silentNode = null; | |
/** | |
* makes a connection to ensure that the node will not be garbage collected | |
* until 'dispose' is explicitly called | |
* | |
* use carefully. circumvents JS and WebAudio's normal Garbage Collection behavior | |
* @returns {Tone} this | |
*/ | |
Tone.prototype.noGC = function () { | |
this.output.connect(_silentNode); | |
return this; | |
}; | |
AudioNode.prototype.noGC = function () { | |
this.connect(_silentNode); | |
return this; | |
}; | |
/** | |
* connect the output of a ToneNode to an AudioParam, AudioNode, or ToneNode | |
* @param {Tone | AudioParam | AudioNode} unit | |
* @param {number} [outputNum=0] optionally which output to connect from | |
* @param {number} [inputNum=0] optionally which input to connect to | |
* @returns {Tone} this | |
*/ | |
Tone.prototype.connect = function (unit, outputNum, inputNum) { | |
if (Array.isArray(this.output)) { | |
outputNum = this.defaultArg(outputNum, 0); | |
this.output[outputNum].connect(unit, 0, inputNum); | |
} else { | |
this.output.connect(unit, outputNum, inputNum); | |
} | |
return this; | |
}; | |
/** | |
* disconnect the output | |
* @returns {Tone} this | |
*/ | |
Tone.prototype.disconnect = function (outputNum) { | |
if (Array.isArray(this.output)) { | |
outputNum = this.defaultArg(outputNum, 0); | |
this.output[outputNum].disconnect(); | |
} else { | |
this.output.disconnect(); | |
} | |
return this; | |
}; | |
/** | |
* connect together all of the arguments in series | |
* @param {...AudioParam|Tone|AudioNode} nodes | |
* @returns {Tone} this | |
*/ | |
Tone.prototype.connectSeries = function () { | |
if (arguments.length > 1) { | |
var currentUnit = arguments[0]; | |
for (var i = 1; i < arguments.length; i++) { | |
var toUnit = arguments[i]; | |
currentUnit.connect(toUnit); | |
currentUnit = toUnit; | |
} | |
} | |
return this; | |
}; | |
/** | |
* fan out the connection from the first argument to the rest of the arguments | |
* @param {...AudioParam|Tone|AudioNode} nodes | |
* @returns {Tone} this | |
*/ | |
Tone.prototype.connectParallel = function () { | |
var connectFrom = arguments[0]; | |
if (arguments.length > 1) { | |
for (var i = 1; i < arguments.length; i++) { | |
var connectTo = arguments[i]; | |
connectFrom.connect(connectTo); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Connect the output of this node to the rest of the nodes in series. | |
* @example | |
* //connect a node to an effect, panVol and then to the master output | |
* node.chain(effect, panVol, Tone.Master); | |
* @param {...AudioParam|Tone|AudioNode} nodes | |
* @returns {Tone} this | |
*/ | |
Tone.prototype.chain = function () { | |
if (arguments.length > 0) { | |
var currentUnit = this; | |
for (var i = 0; i < arguments.length; i++) { | |
var toUnit = arguments[i]; | |
currentUnit.connect(toUnit); | |
currentUnit = toUnit; | |
} | |
} | |
return this; | |
}; | |
/** | |
* connect the output of this node to the rest of the nodes in parallel. | |
* @param {...AudioParam|Tone|AudioNode} nodes | |
* @returns {Tone} this | |
*/ | |
Tone.prototype.fan = function () { | |
if (arguments.length > 0) { | |
for (var i = 0; i < arguments.length; i++) { | |
this.connect(arguments[i]); | |
} | |
} | |
return this; | |
}; | |
//give native nodes chain and fan methods | |
AudioNode.prototype.chain = Tone.prototype.chain; | |
AudioNode.prototype.fan = Tone.prototype.fan; | |
/////////////////////////////////////////////////////////////////////////// | |
// UTILITIES / HELPERS / MATHS | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* If the `given` parameter is undefined, use the `fallback`. | |
* If both `given` and `fallback` are object literals, it will | |
* return a deep copy which includes all of the parameters from both | |
* objects. If a parameter is undefined in given, it will return | |
* the fallback property. | |
* <br><br> | |
* WARNING: if object is self referential, it will go into an an | |
* infinite recursive loop. | |
* | |
* @param {*} given | |
* @param {*} fallback | |
* @return {*} | |
*/ | |
Tone.prototype.defaultArg = function (given, fallback) { | |
if (this.isObject(given) && this.isObject(fallback)) { | |
var ret = {}; | |
//make a deep copy of the given object | |
for (var givenProp in given) { | |
ret[givenProp] = this.defaultArg(fallback[givenProp], given[givenProp]); | |
} | |
for (var fallbackProp in fallback) { | |
ret[fallbackProp] = this.defaultArg(given[fallbackProp], fallback[fallbackProp]); | |
} | |
return ret; | |
} else { | |
return isUndef(given) ? fallback : given; | |
} | |
}; | |
/** | |
* returns the args as an options object with given arguments | |
* mapped to the names provided. | |
* | |
* 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 | |
* @param {Array} keys the names of the arguments as they | |
* should appear in the options object | |
* @param {Object=} defaults optional defaults to mixin to the returned | |
* options object | |
* @return {Object} the options object with the names mapped to the arguments | |
*/ | |
Tone.prototype.optionsObject = function (values, keys, defaults) { | |
var options = {}; | |
if (values.length === 1 && this.isObject(values[0])) { | |
options = values[0]; | |
} else { | |
for (var i = 0; i < keys.length; i++) { | |
options[keys[i]] = values[i]; | |
} | |
} | |
if (!this.isUndef(defaults)) { | |
return this.defaultArg(options, defaults); | |
} else { | |
return options; | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// TYPE CHECKING | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* test if the arg is undefined | |
* @param {*} arg the argument to test | |
* @returns {boolean} true if the arg is undefined | |
* @function | |
*/ | |
Tone.prototype.isUndef = isUndef; | |
/** | |
* test if the arg is a function | |
* @param {*} arg the argument to test | |
* @returns {boolean} true if the arg is a function | |
* @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 given argument is an object literal (i.e. `{}`); | |
* @param {*} arg the argument to test | |
* @returns {boolean} true if the arg is an object literal. | |
*/ | |
Tone.prototype.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 | |
*/ | |
Tone.prototype.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 | |
*/ | |
Tone.prototype.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 | |
*/ | |
Tone.prototype.isString = function (arg) { | |
return typeof arg === 'string'; | |
}; | |
/** | |
* An empty function. | |
* @static | |
*/ | |
Tone.noOp = function () { | |
}; | |
/** | |
* Make the property not writable. Internal use only. | |
* @private | |
* @param {string} property the property to make not writable | |
*/ | |
Tone.prototype._readOnly = function (property) { | |
if (Array.isArray(property)) { | |
for (var i = 0; i < property.length; i++) { | |
this._readOnly(property[i]); | |
} | |
} else { | |
Object.defineProperty(this, property, { | |
writable: false, | |
enumerable: true | |
}); | |
} | |
}; | |
/** | |
* Make an attribute writeable. Interal use only. | |
* @private | |
* @param {string} property the property to make writable | |
*/ | |
Tone.prototype._writable = function (property) { | |
if (Array.isArray(property)) { | |
for (var i = 0; i < property.length; i++) { | |
this._writable(property[i]); | |
} | |
} else { | |
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 {NormalRange} percent (0-1) | |
* @return {Number} output gain (0-1) | |
*/ | |
Tone.prototype.equalPowerScale = function (percent) { | |
var piFactor = 0.5 * Math.PI; | |
return Math.sin(percent * piFactor); | |
}; | |
/** | |
* Convert decibels into gain. | |
* @param {Decibels} db | |
* @return {Number} | |
*/ | |
Tone.prototype.dbToGain = function (db) { | |
return Math.pow(2, db / 6); | |
}; | |
/** | |
* Convert gain to decibels. | |
* @param {Number} gain (0-1) | |
* @return {Decibels} | |
*/ | |
Tone.prototype.gainToDb = function (gain) { | |
return 20 * (Math.log(gain) / Math.LN10); | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// 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; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// INHERITANCE | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* have a child inherit all of Tone's (or a parent's) prototype | |
* to inherit the parent's properties, make sure to call | |
* Parent.call(this) in the child's constructor | |
* | |
* based on closure library's inherit function | |
* | |
* @static | |
* @param {function} child | |
* @param {function=} parent (optional) parent to inherit from | |
* if no parent is supplied, the child | |
* will inherit from Tone | |
*/ | |
Tone.extend = function (child, parent) { | |
if (isUndef(parent)) { | |
parent = Tone; | |
} | |
function TempConstructor() { | |
} | |
TempConstructor.prototype = parent.prototype; | |
child.prototype = new TempConstructor(); | |
/** @override */ | |
child.prototype.constructor = child; | |
child._super = parent; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// CONTEXT | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* array of callbacks to be invoked when a new context is added | |
* @private | |
* @private | |
*/ | |
var newContextCallbacks = []; | |
/** | |
* invoke this callback when a new context is added | |
* will be invoked initially with the first context | |
* @private | |
* @static | |
* @param {function(AudioContext)} callback the callback to be invoked | |
* with the audio context | |
*/ | |
Tone._initAudioContext = function (callback) { | |
//invoke the callback with the existing AudioContext | |
callback(Tone.context); | |
//add it to the array | |
newContextCallbacks.push(callback); | |
}; | |
/** | |
* Tone automatically creates a context on init, but if you are working | |
* with other libraries which also create an AudioContext, it can be | |
* useful to set your own. If you are going to set your own context, | |
* be sure to do it at the start of your code, before creating any objects. | |
* @static | |
* @param {AudioContext} ctx The new audio context to set | |
*/ | |
Tone.setContext = function (ctx) { | |
//set the prototypes | |
Tone.prototype.context = ctx; | |
Tone.context = ctx; | |
//invoke all the callbacks | |
for (var i = 0; i < newContextCallbacks.length; i++) { | |
newContextCallbacks[i](ctx); | |
} | |
}; | |
/** | |
* Bind this to a touchstart event to start the audio on mobile devices. | |
* <br> | |
* http://stackoverflow.com/questions/12517000/no-sound-on-ios-6-web-audio-api/12569290#12569290 | |
* @static | |
*/ | |
Tone.startMobile = function () { | |
var osc = Tone.context.createOscillator(); | |
var silent = Tone.context.createGain(); | |
silent.gain.value = 0; | |
osc.connect(silent); | |
silent.connect(Tone.context.destination); | |
var now = Tone.context.currentTime; | |
osc.start(now); | |
osc.stop(now + 1); | |
}; | |
//setup the context | |
Tone._initAudioContext(function (audioContext) { | |
//set the blockTime | |
Tone.prototype.blockTime = 128 / audioContext.sampleRate; | |
_silentNode = audioContext.createGain(); | |
_silentNode.gain.value = 0; | |
_silentNode.connect(audioContext.destination); | |
}); | |
Tone.version = 'r6'; | |
console.log('%c * Tone.js ' + Tone.version + ' * ', 'background: #000; color: #fff'); | |
return Tone; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Base class for all Signals. Used Internally. | |
* | |
* @constructor | |
* @extends {Tone} | |
*/ | |
Tone.SignalBase = function () { | |
}; | |
Tone.extend(Tone.SignalBase); | |
/** | |
* 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 <code>connect</code>. | |
* | |
* @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 | |
*/ | |
Tone.SignalBase.prototype.connect = function (node, outputNumber, inputNumber) { | |
//zero it out so that the signal can have full control | |
if (Tone.Signal && Tone.Signal === node.constructor || Tone.Param && Tone.Param === node.constructor || Tone.TimelineSignal && Tone.TimelineSignal === node.constructor) { | |
//cancel changes | |
node._param.cancelScheduledValues(0); | |
//reset the value | |
node._param.value = 0; | |
//mark the value as overridden | |
node.overridden = true; | |
} else if (node instanceof AudioParam) { | |
node.cancelScheduledValues(0); | |
node.value = 0; | |
} | |
Tone.prototype.connect.call(this, node, outputNumber, inputNumber); | |
return this; | |
}; | |
return Tone.SignalBase; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Wraps the native Web Audio API | |
* [WaveShaperNode](http://webaudio.github.io/web-audio-api/#the-waveshapernode-interface). | |
* | |
* @extends {Tone.SignalBase} | |
* @constructor | |
* @param {function|Array|Number} mapping The function used to define the values. | |
* The mapping function should take two arguments: | |
* the first is the value at the current position | |
* and the second is the array position. | |
* If the argument is an array, that array will be | |
* set as the wave shaping function. The input | |
* signal is an AudioRange [-1, 1] value and the output | |
* signal can take on any numerical values. | |
* | |
* @param {Number} [bufferLen=1024] The length of the WaveShaperNode buffer. | |
* @example | |
* var timesTwo = new Tone.WaveShaper(function(val){ | |
* return val * 2; | |
* }, 2048); | |
* @example | |
* //a waveshaper can also be constructed with an array of values | |
* var invert = new Tone.WaveShaper([1, -1]); | |
*/ | |
Tone.WaveShaper = function (mapping, bufferLen) { | |
/** | |
* the waveshaper | |
* @type {WaveShaperNode} | |
* @private | |
*/ | |
this._shaper = this.input = this.output = this.context.createWaveShaper(); | |
/** | |
* the waveshapers curve | |
* @type {Float32Array} | |
* @private | |
*/ | |
this._curve = null; | |
if (Array.isArray(mapping)) { | |
this.curve = mapping; | |
} else if (isFinite(mapping) || this.isUndef(mapping)) { | |
this._curve = new Float32Array(this.defaultArg(mapping, 1024)); | |
} else if (this.isFunction(mapping)) { | |
this._curve = new Float32Array(this.defaultArg(bufferLen, 1024)); | |
this.setMap(mapping); | |
} | |
}; | |
Tone.extend(Tone.WaveShaper, Tone.SignalBase); | |
/** | |
* Uses a mapping function to set the value of the curve. | |
* @param {function} mapping The function used to define the values. | |
* The mapping function take two arguments: | |
* the first is the value at the current position | |
* which goes from -1 to 1 over the number of elements | |
* in the curve array. The second argument is the array position. | |
* @returns {Tone.WaveShaper} this | |
* @example | |
* //map the input signal from [-1, 1] to [0, 10] | |
* shaper.setMap(function(val, index){ | |
* return (val + 1) * 5; | |
* }) | |
*/ | |
Tone.WaveShaper.prototype.setMap = function (mapping) { | |
for (var i = 0, len = this._curve.length; i < len; i++) { | |
var normalized = i / len * 2 - 1; | |
this._curve[i] = mapping(normalized, i); | |
} | |
this._shaper.curve = this._curve; | |
return this; | |
}; | |
/** | |
* The array to set as the waveshaper curve. For linear curves | |
* array length does not make much difference, but for complex curves | |
* longer arrays will provide smoother interpolation. | |
* @memberOf Tone.WaveShaper# | |
* @type {Array} | |
* @name curve | |
*/ | |
Object.defineProperty(Tone.WaveShaper.prototype, 'curve', { | |
get: function () { | |
return this._shaper.curve; | |
}, | |
set: function (mapping) { | |
this._curve = new Float32Array(mapping); | |
this._shaper.curve = this._curve; | |
} | |
}); | |
/** | |
* Specifies what type of oversampling (if any) should be used when | |
* applying the shaping curve. Can either be "none", "2x" or "4x". | |
* @memberOf Tone.WaveShaper# | |
* @type {string} | |
* @name oversample | |
*/ | |
Object.defineProperty(Tone.WaveShaper.prototype, 'oversample', { | |
get: function () { | |
return this._shaper.oversample; | |
}, | |
set: function (oversampling) { | |
if ([ | |
'none', | |
'2x', | |
'4x' | |
].indexOf(oversampling) !== -1) { | |
this._shaper.oversample = oversampling; | |
} else { | |
throw new Error('invalid oversampling: ' + oversampling); | |
} | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.WaveShaper} this | |
*/ | |
Tone.WaveShaper.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._shaper.disconnect(); | |
this._shaper = null; | |
this._curve = null; | |
return this; | |
}; | |
return Tone.WaveShaper; | |
}); | |
Module(function (Tone) { | |
/////////////////////////////////////////////////////////////////////////// | |
// TYPES | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* Units 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/Tonejs/Tone.js/wiki/Time). | |
* | |
* <ul> | |
* <li>Numbers, which will be taken literally as the time (in seconds).</li> | |
* <li>Notation, ("4n", "8t") describes time in BPM and time signature relative values.</li> | |
* <li>TransportTime, ("4:3:2") will also provide tempo and time signature relative times | |
* in the form BARS:QUARTERS:SIXTEENTHS.</li> | |
* <li>Frequency, ("8hz") is converted to the length of the cycle in seconds.</li> | |
* <li>Now-Relative, ("+1") prefix any of the above with "+" and it will be interpreted as | |
* "the current time plus whatever expression follows".</li> | |
* <li>Expressions, ("3:0 + 2 - (1m / 7)") any of the above can also be combined | |
* into a mathematical expression which will be evaluated to compute the desired time.</li> | |
* <li>No Argument, for methods which accept time, no argument will be interpreted as | |
* "now" (i.e. the currentTime).</li> | |
* </ul> | |
* | |
* @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', | |
/** | |
* 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 or equal to 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. | |
* <ul> | |
* <li>"4n" = quarter note</li> | |
* <li>"2m" = two measures</li> | |
* <li>"8t" = eighth-note triplet</li> | |
* </ul> | |
* @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(/^\s*\+(.)+/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.: | |
* <ul> | |
* <li>4n = quarter note</li> | |
* <li>2m = two measures</li> | |
* <li>8t = eighth-note triplet</li> | |
* </ul> | |
* | |
* @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 | |
*/ | |
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 * (60 / bpm); | |
}; | |
/** | |
* Convert ticks into seconds | |
* @param {Ticks} ticks | |
* @param {BPM=} bpm | |
* @return {number} seconds | |
*/ | |
Tone.prototype.ticksToSeconds = function (ticks, bpm) { | |
if (this.isUndef(Tone.Transport)) { | |
return 0; | |
} | |
ticks = parseFloat(ticks); | |
bpm = this.defaultArg(bpm, getTransportBpm()); | |
var tickTime = 60 / bpm / Tone.Transport.PPQ; | |
return tickTime * ticks; | |
}; | |
/** | |
* 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 = 60 / bpm; | |
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); | |
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} | |
*/ | |
Tone.prototype.toTicks = function (time) { | |
if (this.isUndef(Tone.Transport)) { | |
return 0; | |
} | |
var bpm = Tone.Transport.bpm.value; | |
//get the seconds | |
var plusNow = 0; | |
if (this.isNowRelative(time)) { | |
time = time.replace('+', ''); | |
plusNow = Tone.Transport.ticks; | |
} else if (this.isUndef(time)) { | |
return Tone.Transport.ticks; | |
} | |
var seconds = this.toSeconds(time); | |
var quarter = 60 / bpm; | |
var quarters = seconds / quarter; | |
var tickNum = quarters * Tone.Transport.PPQ; | |
//align the 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 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 (this.isNumber(time)) { | |
return time; //assuming that it's seconds | |
} else if (this.isString(time)) { | |
var plusTime = 0; | |
if (this.isNowRelative(time)) { | |
time = time.replace('+', ''); | |
plusTime = now; | |
} | |
var betweenParens = time.match(/\(([^)(]+)\)/g); | |
if (betweenParens) { | |
//evaluate the expressions between the parenthesis | |
for (var j = 0; j < betweenParens.length; j++) { | |
//remove the parens | |
var symbol = betweenParens[j].replace(/[\(\)]/g, ''); | |
var symbolVal = this.toSeconds(symbol); | |
time = time.replace(betweenParens[j], symbolVal); | |
} | |
} | |
//test if it is quantized | |
if (time.indexOf('@') !== -1) { | |
var quantizationSplit = time.split('@'); | |
if (!this.isUndef(Tone.Transport)) { | |
var toQuantize = quantizationSplit[0].trim(); | |
//if there's no argument it should be evaluated as the current time | |
if (toQuantize === '') { | |
toQuantize = undefined; | |
} | |
//if it's now-relative, it should be evaluated by `quantize` | |
if (plusTime > 0) { | |
toQuantize = '+' + toQuantize; | |
plusTime = 0; | |
} | |
var subdivision = quantizationSplit[1].trim(); | |
time = Tone.Transport.quantize(toQuantize, subdivision); | |
} else { | |
throw new Error('quantization requires Tone.Transport'); | |
} | |
} else { | |
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 | |
time = eval(time); // jshint ignore:line | |
} catch (e) { | |
throw new EvalError('cannot evaluate 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 += ' + '; | |
} | |
} | |
} | |
if (retNotation === '') { | |
retNotation = '0'; | |
} | |
return retNotation; | |
} | |
/** | |
* Convert the given value from the type specified by units | |
* into a number. | |
* @param {*} val the value to convert | |
* @return {Number} the number which the value should be set to | |
*/ | |
Tone.prototype.fromUnits = function (val, units) { | |
if (this.convert || this.isUndef(this.convert)) { | |
switch (units) { | |
case Tone.Type.Time: | |
return this.toSeconds(val); | |
case Tone.Type.Frequency: | |
return this.toFrequency(val); | |
case Tone.Type.Decibels: | |
return this.dbToGain(val); | |
case Tone.Type.NormalRange: | |
return Math.min(Math.max(val, 0), 1); | |
case Tone.Type.AudioRange: | |
return Math.min(Math.max(val, -1), 1); | |
case Tone.Type.Positive: | |
return Math.max(val, 0); | |
default: | |
return val; | |
} | |
} else { | |
return val; | |
} | |
}; | |
/** | |
* Convert a number to the specified units. | |
* @param {number} val the value to convert | |
* @return {number} | |
*/ | |
Tone.prototype.toUnits = function (val, units) { | |
if (this.convert || this.isUndef(this.convert)) { | |
switch (units) { | |
case Tone.Type.Decibels: | |
return this.gainToDb(val); | |
default: | |
return val; | |
} | |
} else { | |
return val; | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// 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; | |
/** | |
* If the value should be converted or not | |
* @type {Boolean} | |
*/ | |
this.convert = options.convert; | |
/** | |
* True if the signal value is being overridden by | |
* a connected signal. | |
* @readOnly | |
* @type {boolean} | |
* @private | |
*/ | |
this.overridden = false; | |
if (!this.isUndef(options.value)) { | |
this.value = options.value; | |
} | |
}; | |
Tone.extend(Tone.Param); | |
/** | |
* Defaults | |
* @type {Object} | |
* @const | |
*/ | |
Tone.Param.defaults = { | |
'units': Tone.Type.Default, | |
'convert': true, | |
'param': undefined | |
}; | |
/** | |
* The current value of the parameter. | |
* @memberOf Tone.Param# | |
* @type {Number} | |
* @name value | |
*/ | |
Object.defineProperty(Tone.Param.prototype, 'value', { | |
get: function () { | |
return this._toUnits(this._param.value); | |
}, | |
set: function (value) { | |
var convertedVal = this._fromUnits(value); | |
this._param.value = convertedVal; | |
} | |
}); | |
/** | |
* 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.Param.prototype._fromUnits = function (val) { | |
if (this.convert || this.isUndef(this.convert)) { | |
switch (this.units) { | |
case Tone.Type.Time: | |
return this.toSeconds(val); | |
case Tone.Type.Frequency: | |
return this.toFrequency(val); | |
case Tone.Type.Decibels: | |
return this.dbToGain(val); | |
case Tone.Type.NormalRange: | |
return Math.min(Math.max(val, 0), 1); | |
case Tone.Type.AudioRange: | |
return Math.min(Math.max(val, -1), 1); | |
case Tone.Type.Positive: | |
return Math.max(val, 0); | |
default: | |
return val; | |
} | |
} else { | |
return val; | |
} | |
}; | |
/** | |
* Convert the parameters value into the units specified by Tone.Param.units. | |
* @private | |
* @param {number} val the value to convert | |
* @return {number} | |
*/ | |
Tone.Param.prototype._toUnits = function (val) { | |
if (this.convert || this.isUndef(this.convert)) { | |
switch (this.units) { | |
case Tone.Type.Decibels: | |
return this.gainToDb(val); | |
default: | |
return val; | |
} | |
} else { | |
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.Param} this | |
* @example | |
* //set the frequency to "G4" in exactly 1 second from now. | |
* freq.setValueAtTime("G4", "+1"); | |
*/ | |
Tone.Param.prototype.setValueAtTime = function (value, time) { | |
value = this._fromUnits(value); | |
this._param.setValueAtTime(value, this.toSeconds(time)); | |
return this; | |
}; | |
/** | |
* Creates a schedule point with the current value at the current time. | |
* This is useful for creating an automation anchor point in order to | |
* schedule changes from the current value. | |
* | |
* @param {number=} now (Optionally) pass the now value in. | |
* @returns {Tone.Param} this | |
*/ | |
Tone.Param.prototype.setRampPoint = function (now) { | |
now = this.defaultArg(now, this.now()); | |
var currentVal = this._param.value; | |
this._param.setValueAtTime(currentVal, now); | |
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.Param} this | |
*/ | |
Tone.Param.prototype.linearRampToValueAtTime = function (value, endTime) { | |
value = this._fromUnits(value); | |
this._param.linearRampToValueAtTime(value, this.toSeconds(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.Param} this | |
*/ | |
Tone.Param.prototype.exponentialRampToValueAtTime = function (value, endTime) { | |
value = this._fromUnits(value); | |
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 over the | |
* duration of the rampTime. | |
* | |
* @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.Param} this | |
* @example | |
* //exponentially ramp to the value 2 over 4 seconds. | |
* signal.exponentialRampToValue(2, 4); | |
*/ | |
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, 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 over the | |
* duration of the rampTime. | |
* | |
* @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.Param} this | |
* @example | |
* //linearly ramp to the value 4 over 3 seconds. | |
* signal.linearRampToValue(4, 3); | |
*/ | |
Tone.Param.prototype.linearRampToValue = function (value, rampTime) { | |
var now = this.now(); | |
this.setRampPoint(now); | |
this.linearRampToValueAtTime(value, now + this.toSeconds(rampTime)); | |
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.Param} this | |
*/ | |
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. | |
value = Math.max(this._minOutput, value); | |
timeConstant = Math.max(this._minOutput, timeConstant); | |
this._param.setTargetAtTime(value, this.toSeconds(startTime), timeConstant); | |
return this; | |
}; | |
/** | |
* Sets an array of arbitrary parameter values starting at the given time | |
* for the given duration. | |
* | |
* @param {Array} values | |
* @param {Time} startTime | |
* @param {Time} duration | |
* @returns {Tone.Param} this | |
*/ | |
Tone.Param.prototype.setValueCurveAtTime = function (values, startTime, duration) { | |
for (var i = 0; i < values.length; i++) { | |
values[i] = this._fromUnits(values[i]); | |
} | |
this._param.setValueCurveAtTime(values, this.toSeconds(startTime), this.toSeconds(duration)); | |
return this; | |
}; | |
/** | |
* Cancels all scheduled parameter changes with times greater than or | |
* equal to startTime. | |
* | |
* @param {Time} startTime | |
* @returns {Tone.Param} this | |
*/ | |
Tone.Param.prototype.cancelScheduledValues = function (startTime) { | |
this._param.cancelScheduledValues(this.toSeconds(startTime)); | |
return this; | |
}; | |
/** | |
* Ramps to the given value over the duration of the rampTime. | |
* Automatically selects the best ramp type (exponential or linear) | |
* depending on the `units` of the signal | |
* | |
* @param {number} value | |
* @param {Time} rampTime the time that it takes the | |
* value to ramp from it's current value | |
* @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.Param.prototype.rampTo = function (value, rampTime) { | |
rampTime = this.defaultArg(rampTime, 0); | |
if (this.units === Tone.Type.Frequency || this.units === Tone.Type.BPM) { | |
this.exponentialRampToValue(value, rampTime); | |
} else { | |
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=} gain The initial gain of the GainNode | |
* @param {Tone.Type=} units The units of the gain parameter. | |
*/ | |
Tone.Gain = function () { | |
var options = this.optionsObject(arguments, [ | |
'gain', | |
'units' | |
], Tone.Gain.defaults); | |
/** | |
* The GainNode | |
* @type {GainNode} | |
* @private | |
*/ | |
this.input = this.output = this._gainNode = this.context.createGain(); | |
/** | |
* The gain parameter of the gain node. | |
* @type {AudioParam} | |
* @signal | |
*/ | |
this.gain = new Tone.Param({ | |
'param': this._gainNode.gain, | |
'units': options.units, | |
'value': options.gain, | |
'convert': options.convert | |
}); | |
this._readOnly('gain'); | |
}; | |
Tone.extend(Tone.Gain); | |
/** | |
* The defaults | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Gain.defaults = { | |
'gain': 1, | |
'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.dispose(); | |
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.Param} | |
* @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 = this.context.createGain(); | |
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 <code>connect</code>. | |
* | |
* @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.Param.prototype.dispose.call(this); | |
this._param = null; | |
this._gain.disconnect(); | |
this._gain = null; | |
return this; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// STATIC | |
/////////////////////////////////////////////////////////////////////////// | |
/** | |
* Generates a constant output of 1. | |
* @static | |
* @private | |
* @const | |
* @type {AudioBufferSourceNode} | |
*/ | |
Tone.Signal._constant = null; | |
/** | |
* initializer function | |
*/ | |
Tone._initAudioContext(function (audioContext) { | |
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} | |
* @param {Positive} [memory=Infinity] The number of previous events that are retained. | |
*/ | |
Tone.Timeline = function () { | |
var options = this.optionsObject(arguments, ['memory'], Tone.Timeline.defaults); | |
/** | |
* The array of scheduled timeline events | |
* @type {Array} | |
* @private | |
*/ | |
this._timeline = []; | |
/** | |
* An array of items to remove from the list. | |
* @type {Array} | |
* @private | |
*/ | |
this._toRemove = []; | |
/** | |
* Flag if the tieline is mid iteration | |
* @private | |
* @type {Boolean} | |
*/ | |
this._iterating = false; | |
/** | |
* The memory of the timeline, i.e. | |
* how many events in the past it will retain | |
* @type {Positive} | |
*/ | |
this.memory = options.memory; | |
}; | |
Tone.extend(Tone.Timeline); | |
/** | |
* the default parameters | |
* @static | |
* @const | |
*/ | |
Tone.Timeline.defaults = { 'memory': Infinity }; | |
/** | |
* 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); | |
} | |
//if the length is more than the memory, remove the previous ones | |
if (this.length > this.memory) { | |
var diff = this.length - this.memory; | |
this._timeline.splice(0, diff); | |
} | |
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) { | |
if (this._iterating) { | |
this._toRemove.push(event); | |
} else { | |
var index = this._timeline.indexOf(event); | |
if (index !== -1) { | |
this._timeline.splice(index, 1); | |
} | |
} | |
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 > 1) { | |
after = this.toSeconds(after); | |
var index = this._search(after); | |
if (index >= 0) { | |
this._timeline = this._timeline.slice(0, index); | |
} else { | |
this._timeline = []; | |
} | |
} else if (this._timeline.length === 1) { | |
//the first item's time | |
if (this._timeline[0].time >= after) { | |
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; | |
}; | |
/** | |
* Internal iterator. Applies extra safety checks for | |
* removing items from the array. | |
* @param {Function} callback | |
* @param {Number=} lowerBound | |
* @param {Number=} upperBound | |
* @private | |
*/ | |
Tone.Timeline.prototype._iterate = function (callback, lowerBound, upperBound) { | |
this._iterating = true; | |
lowerBound = this.defaultArg(lowerBound, 0); | |
upperBound = this.defaultArg(upperBound, this._timeline.length - 1); | |
for (var i = lowerBound; i <= upperBound; i++) { | |
callback(this._timeline[i]); | |
} | |
this._iterating = false; | |
if (this._toRemove.length > 0) { | |
for (var j = 0; j < this._toRemove.length; j++) { | |
var index = this._timeline.indexOf(this._toRemove[j]); | |
if (index !== -1) { | |
this._timeline.splice(index, 1); | |
} | |
} | |
this._toRemove = []; | |
} | |
}; | |
/** | |
* 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) { | |
this._iterate(callback); | |
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 upperBound = this._search(time); | |
if (upperBound !== -1) { | |
this._iterate(callback, 0, upperBound); | |
} | |
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 lowerBound = this._search(time); | |
this._iterate(callback, lowerBound + 1); | |
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 lowerBound = this._search(time); | |
//work backwards until the event time is less than time | |
while (lowerBound >= 0 && this._timeline[lowerBound].time >= time) { | |
lowerBound--; | |
} | |
this._iterate(callback, lowerBound + 1); | |
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 upperBound = this._search(time); | |
if (upperBound !== -1) { | |
this._iterate(function (event) { | |
if (event.time === time) { | |
callback(event); | |
} | |
}, 0, upperBound); | |
} | |
return this; | |
}; | |
/** | |
* Clean up. | |
* @return {Tone.Timeline} this | |
*/ | |
Tone.Timeline.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._timeline = null; | |
this._toRemove = 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 | |
* @extends {Tone.Param} | |
* @param {Number=} value The initial value of the signal | |
* @param {String=} units The conversion units of the signal. | |
*/ | |
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(10); | |
/** | |
* 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); | |
timeConstant = Math.max(this._minOutput, timeConstant); | |
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.cancel(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. Will cancel events after | |
* the given time and shorten the currently scheduled | |
* linear or exponential ramp so that it ends at `time` . | |
* This is to avoid discontinuities and clicks in envelopes. | |
* @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); | |
//reschedule the next event to end at the given time | |
var after = this._searchAfter(time); | |
if (after) { | |
//cancel the next event(s) | |
this.cancelScheduledValues(time); | |
if (after.type === Tone.TimelineSignal.Type.Linear) { | |
this.linearRampToValueAtTime(val, time); | |
} else if (after.type === Tone.TimelineSignal.Type.Exponential) { | |
this.exponentialRampToValueAtTime(val, 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. This will | |
* return the unconverted (raw) value. | |
* @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); | |
var value = this._initial; | |
//if it was set by | |
if (before === null) { | |
value = this._initial; | |
} else if (before.type === Tone.TimelineSignal.Type.Target) { | |
var previous = this._events.getEventBefore(before.time); | |
var previouVal; | |
if (previous === null) { | |
previouVal = this._initial; | |
} else { | |
previouVal = previous.value; | |
} | |
value = this._exponentialApproach(before.time, previouVal, before.value, before.constant, time); | |
} else if (after === null) { | |
value = before.value; | |
} else if (after.type === Tone.TimelineSignal.Type.Linear) { | |
value = this._linearInterpolate(before.time, before.value, after.time, after.value, time); | |
} else if (after.type === Tone.TimelineSignal.Type.Exponential) { | |
value = this._exponentialInterpolate(before.time, before.value, after.time, after.value, time); | |
} else { | |
value = before.value; | |
} | |
return value; | |
}; | |
/** | |
* 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 <code>connect</code>. | |
* | |
* @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) { | |
/** | |
* @class Pow applies an exponent to the incoming signal. The incoming signal | |
* must be AudioRange. | |
* | |
* @extends {Tone.SignalBase} | |
* @constructor | |
* @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); | |
* //output of pow is 0.25. | |
*/ | |
Tone.Pow = function (exp) { | |
/** | |
* the exponent | |
* @private | |
* @type {number} | |
*/ | |
this._exp = this.defaultArg(exp, 1); | |
/** | |
* @type {WaveShaperNode} | |
* @private | |
*/ | |
this._expScaler = this.input = this.output = new Tone.WaveShaper(this._expFunc(this._exp), 8192); | |
}; | |
Tone.extend(Tone.Pow, Tone.SignalBase); | |
/** | |
* The value of the exponent. | |
* @memberOf Tone.Pow# | |
* @type {number} | |
* @name value | |
*/ | |
Object.defineProperty(Tone.Pow.prototype, 'value', { | |
get: function () { | |
return this._exp; | |
}, | |
set: function (exp) { | |
this._exp = exp; | |
this._expScaler.setMap(this._expFunc(this._exp)); | |
} | |
}); | |
/** | |
* the function which maps the waveshaper | |
* @param {number} exp | |
* @return {function} | |
* @private | |
*/ | |
Tone.Pow.prototype._expFunc = function (exp) { | |
return function (val) { | |
return Math.pow(Math.abs(val), exp); | |
}; | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.Pow} this | |
*/ | |
Tone.Pow.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._expScaler.dispose(); | |
this._expScaler = null; | |
return this; | |
}; | |
return Tone.Pow; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Envelope is an [ADSR](https://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope) | |
* envelope generator. Tone.Envelope outputs a signal which | |
* can be connected to an AudioParam or Tone.Signal. | |
* <img src="https://upload.wikimedia.org/wikipedia/commons/e/ea/ADSR_parameter.svg"> | |
* | |
* @constructor | |
* @extends {Tone} | |
* @param {Time} [attack] The amount of time it takes for the envelope to go from | |
* 0 to it's maximum value. | |
* @param {Time} [decay] The period of time after the attack that it takes for the envelope | |
* to fall to the sustain value. | |
* @param {NormalRange} [sustain] The percent of the maximum value that the envelope rests at until | |
* the release is triggered. | |
* @param {Time} [release] The amount of time after the release is triggered it takes to reach 0. | |
* @example | |
* //an amplitude envelope | |
* var gainNode = Tone.context.createGain(); | |
* var env = new Tone.Envelope({ | |
* "attack" : 0.1, | |
* "decay" : 0.2, | |
* "sustain" : 1, | |
* "release" : 0.8, | |
* }); | |
* env.connect(gainNode.gain); | |
*/ | |
Tone.Envelope = function () { | |
//get all of the defaults | |
var options = this.optionsObject(arguments, [ | |
'attack', | |
'decay', | |
'sustain', | |
'release' | |
], Tone.Envelope.defaults); | |
/** | |
* When triggerAttack is called, the attack time is the amount of | |
* time it takes for the envelope to reach it's maximum value. | |
* @type {Time} | |
*/ | |
this.attack = options.attack; | |
/** | |
* After the attack portion of the envelope, the value will fall | |
* over the duration of the decay time to it's sustain value. | |
* @type {Time} | |
*/ | |
this.decay = options.decay; | |
/** | |
* The sustain value is the value | |
* which the envelope rests at after triggerAttack is | |
* called, but before triggerRelease is invoked. | |
* @type {NormalRange} | |
*/ | |
this.sustain = options.sustain; | |
/** | |
* After triggerRelease is called, the envelope's | |
* value will fall to it's miminum value over the | |
* duration of the release time. | |
* @type {Time} | |
*/ | |
this.release = options.release; | |
/** | |
* the next time the envelope is at standby | |
* @type {number} | |
* @private | |
*/ | |
this._attackCurve = Tone.Envelope.Type.Linear; | |
/** | |
* the next time the envelope is at standby | |
* @type {number} | |
* @private | |
*/ | |
this._releaseCurve = Tone.Envelope.Type.Exponential; | |
/** | |
* the minimum output value | |
* @type {number} | |
* @private | |
*/ | |
this._minOutput = 0.00001; | |
/** | |
* the signal | |
* @type {Tone.TimelineSignal} | |
* @private | |
*/ | |
this._sig = this.output = new Tone.TimelineSignal(); | |
this._sig.setValueAtTime(this._minOutput, 0); | |
//set the attackCurve initially | |
this.attackCurve = options.attackCurve; | |
this.releaseCurve = options.releaseCurve; | |
}; | |
Tone.extend(Tone.Envelope); | |
/** | |
* the default parameters | |
* @static | |
* @const | |
*/ | |
Tone.Envelope.defaults = { | |
'attack': 0.01, | |
'decay': 0.1, | |
'sustain': 0.5, | |
'release': 1, | |
'attackCurve': 'linear', | |
'releaseCurve': 'exponential' | |
}; | |
/** | |
* the envelope time multipler | |
* @type {number} | |
* @private | |
*/ | |
Tone.Envelope.prototype._timeMult = 0.25; | |
/** | |
* Read the current value of the envelope. Useful for | |
* syncronizing visual output to the envelope. | |
* @memberOf Tone.Envelope# | |
* @type {Number} | |
* @name value | |
* @readOnly | |
*/ | |
Object.defineProperty(Tone.Envelope.prototype, 'value', { | |
get: function () { | |
return this._sig.value; | |
} | |
}); | |
/** | |
* The slope of the attack. Either "linear" or "exponential". | |
* @memberOf Tone.Envelope# | |
* @type {string} | |
* @name attackCurve | |
* @example | |
* env.attackCurve = "linear"; | |
*/ | |
Object.defineProperty(Tone.Envelope.prototype, 'attackCurve', { | |
get: function () { | |
return this._attackCurve; | |
}, | |
set: function (type) { | |
if (type === Tone.Envelope.Type.Linear || type === Tone.Envelope.Type.Exponential) { | |
this._attackCurve = type; | |
} else { | |
throw Error('attackCurve must be either "linear" or "exponential". Invalid type: ', type); | |
} | |
} | |
}); | |
/** | |
* The slope of the Release. Either "linear" or "exponential". | |
* @memberOf Tone.Envelope# | |
* @type {string} | |
* @name releaseCurve | |
* @example | |
* env.releaseCurve = "linear"; | |
*/ | |
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 { | |
throw Error('releaseCurve must be either "linear" or "exponential". Invalid type: ', type); | |
} | |
} | |
}); | |
/** | |
* Trigger the attack/decay portion of the ADSR envelope. | |
* @param {Time} [time=now] When the attack should start. | |
* @param {NormalRange} [velocity=1] The velocity of the envelope scales the vales. | |
* number between 0-1 | |
* @returns {Tone.Envelope} this | |
* @example | |
* //trigger the attack 0.5 seconds from now with a velocity of 0.2 | |
* env.triggerAttack("+0.5", 0.2); | |
*/ | |
Tone.Envelope.prototype.triggerAttack = function (time, velocity) { | |
//to seconds | |
var now = this.now() + this.blockTime; | |
time = this.toSeconds(time, now); | |
var attack = this.toSeconds(this.attack) + time; | |
var decay = this.toSeconds(this.decay); | |
velocity = this.defaultArg(velocity, 1); | |
//attack | |
if (this._attackCurve === Tone.Envelope.Type.Linear) { | |
this._sig.linearRampToValueBetween(velocity, time, attack); | |
} else { | |
this._sig.exponentialRampToValueBetween(velocity, time, attack); | |
} | |
//decay | |
this._sig.setValueAtTime(velocity, attack); | |
this._sig.exponentialRampToValueAtTime(this.sustain * velocity, attack + decay); | |
return this; | |
}; | |
/** | |
* Triggers the release of the envelope. | |
* @param {Time} [time=now] When the release portion of the envelope should start. | |
* @returns {Tone.Envelope} this | |
* @example | |
* //trigger release immediately | |
* env.triggerRelease(); | |
*/ | |
Tone.Envelope.prototype.triggerRelease = function (time) { | |
var now = this.now() + this.blockTime; | |
time = this.toSeconds(time, now); | |
var release = this.toSeconds(this.release); | |
if (this._releaseCurve === Tone.Envelope.Type.Linear) { | |
this._sig.linearRampToValueBetween(this._minOutput, time, time + release); | |
} else { | |
this._sig.exponentialRampToValueBetween(this._minOutput, time, release + time); | |
} | |
return this; | |
}; | |
/** | |
* triggerAttackRelease is shorthand for triggerAttack, then waiting | |
* some duration, then triggerRelease. | |
* @param {Time} duration The duration of the sustain. | |
* @param {Time} [time=now] When the attack should be triggered. | |
* @param {number} [velocity=1] The velocity of the envelope. | |
* @returns {Tone.Envelope} this | |
* @example | |
* //trigger the attack and then the release after 0.6 seconds. | |
* env.triggerAttackRelease(0.6); | |
*/ | |
Tone.Envelope.prototype.triggerAttackRelease = function (duration, time, velocity) { | |
time = this.toSeconds(time); | |
this.triggerAttack(time, velocity); | |
this.triggerRelease(time + this.toSeconds(duration)); | |
return this; | |
}; | |
/** | |
* Borrows the connect method from Tone.Signal. | |
* @function | |
* @private | |
*/ | |
Tone.Envelope.prototype.connect = Tone.Signal.prototype.connect; | |
/** | |
* Disconnect and dispose. | |
* @returns {Tone.Envelope} this | |
*/ | |
Tone.Envelope.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._sig.dispose(); | |
this._sig = null; | |
return this; | |
}; | |
/** | |
* The phase of the envelope. | |
* @enum {string} | |
*/ | |
Tone.Envelope.Phase = { | |
Attack: 'attack', | |
Decay: 'decay', | |
Sustain: 'sustain', | |
Release: 'release', | |
Standby: 'standby' | |
}; | |
/** | |
* The phase of the envelope. | |
* @enum {string} | |
*/ | |
Tone.Envelope.Type = { | |
Linear: 'linear', | |
Exponential: 'exponential' | |
}; | |
return Tone.Envelope; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.AmplitudeEnvelope is a Tone.Envelope connected to a gain node. | |
* Unlike Tone.Envelope, which outputs the envelope's value, Tone.AmplitudeEnvelope accepts | |
* an audio signal as the input and will apply the envelope to the amplitude | |
* of the signal. Read more about ADSR Envelopes on [Wikipedia](https://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope). | |
* | |
* @constructor | |
* @extends {Tone.Envelope} | |
* @param {Time|Object} [attack] The amount of time it takes for the envelope to go from | |
* 0 to it's maximum value. | |
* @param {Time} [decay] The period of time after the attack that it takes for the envelope | |
* to fall to the sustain value. | |
* @param {NormalRange} [sustain] The percent of the maximum value that the envelope rests at until | |
* the release is triggered. | |
* @param {Time} [release] The amount of time after the release is triggered it takes to reach 0. | |
* @example | |
* var ampEnv = new Tone.AmplitudeEnvelope({ | |
* "attack": 0.1, | |
* "decay": 0.2, | |
* "sustain": 1.0, | |
* "release": 0.8 | |
* }).toMaster(); | |
* //create an oscillator and connect it | |
* var osc = new Tone.Oscillator().connect(ampEnv).start(); | |
* //trigger the envelopes attack and release "8t" apart | |
* ampEnv.triggerAttackRelease("8t"); | |
*/ | |
Tone.AmplitudeEnvelope = function () { | |
Tone.Envelope.apply(this, arguments); | |
/** | |
* the input node | |
* @type {GainNode} | |
* @private | |
*/ | |
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) { | |
/** | |
* @class Tone.Compressor is a thin wrapper around the Web Audio | |
* [DynamicsCompressorNode](http://webaudio.github.io/web-audio-api/#the-dynamicscompressornode-interface). | |
* Compression reduces the volume of loud sounds or amplifies quiet sounds | |
* by narrowing or "compressing" an audio signal's dynamic range. | |
* Read more on [Wikipedia](https://en.wikipedia.org/wiki/Dynamic_range_compression). | |
* | |
* @extends {Tone} | |
* @constructor | |
* @param {Decibels|Object} [threshold] The value above which the compression starts to be applied. | |
* @param {Positive} [ratio] The gain reduction ratio. | |
* @example | |
* var comp = new Tone.Compressor(-30, 3); | |
*/ | |
Tone.Compressor = function () { | |
var options = this.optionsObject(arguments, [ | |
'threshold', | |
'ratio' | |
], Tone.Compressor.defaults); | |
/** | |
* the compressor node | |
* @type {DynamicsCompressorNode} | |
* @private | |
*/ | |
this._compressor = this.input = this.output = this.context.createDynamicsCompressor(); | |
/** | |
* the threshold vaue | |
* @type {Decibels} | |
* @signal | |
*/ | |
this.threshold = this._compressor.threshold; | |
/** | |
* The attack parameter | |
* @type {Time} | |
* @signal | |
*/ | |
this.attack = new Tone.Param(this._compressor.attack, Tone.Type.Time); | |
/** | |
* The release parameter | |
* @type {Time} | |
* @signal | |
*/ | |
this.release = new Tone.Param(this._compressor.release, Tone.Type.Time); | |
/** | |
* The knee parameter | |
* @type {Decibels} | |
* @signal | |
*/ | |
this.knee = this._compressor.knee; | |
/** | |
* The ratio value | |
* @type {Number} | |
* @signal | |
*/ | |
this.ratio = this._compressor.ratio; | |
//set the defaults | |
this._readOnly([ | |
'knee', | |
'release', | |
'attack', | |
'ratio', | |
'threshold' | |
]); | |
this.set(options); | |
}; | |
Tone.extend(Tone.Compressor); | |
/** | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Compressor.defaults = { | |
'ratio': 12, | |
'threshold': -24, | |
'release': 0.25, | |
'attack': 0.003, | |
'knee': 30 | |
}; | |
/** | |
* clean up | |
* @returns {Tone.Compressor} this | |
*/ | |
Tone.Compressor.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.Compressor; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Add a signal and a number or two signals. When no value is | |
* passed into the constructor, Tone.Add will sum <code>input[0]</code> | |
* and <code>input[1]</code>. If a value is passed into the constructor, | |
* the it will be added to the input. | |
* | |
* @constructor | |
* @extends {Tone.Signal} | |
* @param {number=} value If no value is provided, Tone.Add will sum the first | |
* and second inputs. | |
* @example | |
* var signal = new Tone.Signal(2); | |
* var add = new Tone.Add(2); | |
* signal.connect(add); | |
* //the output of add equals 4 | |
* @example | |
* //if constructed with no arguments | |
* //it will add the first and second inputs | |
* var add = new Tone.Add(); | |
* var sig0 = new Tone.Signal(3).connect(add, 0, 0); | |
* var sig1 = new Tone.Signal(4).connect(add, 0, 1); | |
* //the output of add equals 7. | |
*/ | |
Tone.Add = function (value) { | |
Tone.call(this, 2, 0); | |
/** | |
* the summing node | |
* @type {GainNode} | |
* @private | |
*/ | |
this._sum = this.input[0] = this.input[1] = this.output = this.context.createGain(); | |
/** | |
* @private | |
* @type {Tone.Signal} | |
*/ | |
this._param = this.input[1] = new Tone.Signal(value); | |
this._param.connect(this._sum); | |
}; | |
Tone.extend(Tone.Add, Tone.Signal); | |
/** | |
* Clean up. | |
* @returns {Tone.Add} this | |
*/ | |
Tone.Add.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._sum.disconnect(); | |
this._sum = null; | |
this._param.dispose(); | |
this._param = null; | |
return this; | |
}; | |
return Tone.Add; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Multiply two incoming signals. Or, if a number is given in the constructor, | |
* multiplies the incoming signal by that value. | |
* | |
* @constructor | |
* @extends {Tone.Signal} | |
* @param {number=} value Constant value to multiple. If no value is provided, | |
* it will return the product of the first and second inputs | |
* @example | |
* var mult = new Tone.Multiply(); | |
* var sigA = new Tone.Signal(3); | |
* var sigB = new Tone.Signal(4); | |
* sigA.connect(mult, 0, 0); | |
* sigB.connect(mult, 0, 1); | |
* //output of mult is 12. | |
* @example | |
* var mult = new Tone.Multiply(10); | |
* var sig = new Tone.Signal(2).connect(mult); | |
* //the output of mult is 20. | |
*/ | |
Tone.Multiply = function (value) { | |
Tone.call(this, 2, 0); | |
/** | |
* the input node is the same as the output node | |
* it is also the GainNode which handles the scaling of incoming signal | |
* | |
* @type {GainNode} | |
* @private | |
*/ | |
this._mult = this.input[0] = this.output = this.context.createGain(); | |
/** | |
* the scaling parameter | |
* @type {AudioParam} | |
* @private | |
*/ | |
this._param = this.input[1] = this.output.gain; | |
this._param.value = this.defaultArg(value, 0); | |
}; | |
Tone.extend(Tone.Multiply, Tone.Signal); | |
/** | |
* clean up | |
* @returns {Tone.Multiply} this | |
*/ | |
Tone.Multiply.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._mult.disconnect(); | |
this._mult = null; | |
this._param = null; | |
return this; | |
}; | |
return Tone.Multiply; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Negate the incoming signal. i.e. an input signal of 10 will output -10 | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @example | |
* var neg = new Tone.Negate(); | |
* var sig = new Tone.Signal(-2).connect(neg); | |
* //output of neg is positive 2. | |
*/ | |
Tone.Negate = function () { | |
/** | |
* negation is done by multiplying by -1 | |
* @type {Tone.Multiply} | |
* @private | |
*/ | |
this._multiply = this.input = this.output = new Tone.Multiply(-1); | |
}; | |
Tone.extend(Tone.Negate, Tone.SignalBase); | |
/** | |
* clean up | |
* @returns {Tone.Negate} this | |
*/ | |
Tone.Negate.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._multiply.dispose(); | |
this._multiply = null; | |
return this; | |
}; | |
return Tone.Negate; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Subtract the signal connected to <code>input[1]</code> from the signal connected | |
* to <code>input[0]</code>. If an argument is provided in the constructor, the | |
* signals <code>.value</code> will be subtracted from the incoming signal. | |
* | |
* @extends {Tone.Signal} | |
* @constructor | |
* @param {number=} value The value to subtract from the incoming signal. If the value | |
* is omitted, it will subtract the second signal from the first. | |
* @example | |
* var sub = new Tone.Subtract(1); | |
* var sig = new Tone.Signal(4).connect(sub); | |
* //the output of sub is 3. | |
* @example | |
* var sub = new Tone.Subtract(); | |
* var sigA = new Tone.Signal(10); | |
* var sigB = new Tone.Signal(2.5); | |
* sigA.connect(sub, 0, 0); | |
* sigB.connect(sub, 0, 1); | |
* //output of sub is 7.5 | |
*/ | |
Tone.Subtract = function (value) { | |
Tone.call(this, 2, 0); | |
/** | |
* the summing node | |
* @type {GainNode} | |
* @private | |
*/ | |
this._sum = this.input[0] = this.output = this.context.createGain(); | |
/** | |
* negate the input of the second input before connecting it | |
* to the summing node. | |
* @type {Tone.Negate} | |
* @private | |
*/ | |
this._neg = new Tone.Negate(); | |
/** | |
* the node where the value is set | |
* @private | |
* @type {Tone.Signal} | |
*/ | |
this._param = this.input[1] = new Tone.Signal(value); | |
this._param.chain(this._neg, this._sum); | |
}; | |
Tone.extend(Tone.Subtract, Tone.Signal); | |
/** | |
* Clean up. | |
* @returns {Tone.SignalBase} this | |
*/ | |
Tone.Subtract.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._neg.dispose(); | |
this._neg = null; | |
this._sum.disconnect(); | |
this._sum = null; | |
this._param.dispose(); | |
this._param = null; | |
return this; | |
}; | |
return Tone.Subtract; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class GreaterThanZero outputs 1 when the input is strictly greater than zero | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @example | |
* var gt0 = new Tone.GreaterThanZero(); | |
* var sig = new Tone.Signal(0.01).connect(gt0); | |
* //the output of gt0 is 1. | |
* sig.value = 0; | |
* //the output of gt0 is 0. | |
*/ | |
Tone.GreaterThanZero = function () { | |
/** | |
* @type {Tone.WaveShaper} | |
* @private | |
*/ | |
this._thresh = this.output = new Tone.WaveShaper(function (val) { | |
if (val <= 0) { | |
return 0; | |
} else { | |
return 1; | |
} | |
}); | |
/** | |
* scale the first thresholded signal by a large value. | |
* this will help with values which are very close to 0 | |
* @type {Tone.Multiply} | |
* @private | |
*/ | |
this._scale = this.input = new Tone.Multiply(10000); | |
//connections | |
this._scale.connect(this._thresh); | |
}; | |
Tone.extend(Tone.GreaterThanZero, Tone.SignalBase); | |
/** | |
* dispose method | |
* @returns {Tone.GreaterThanZero} this | |
*/ | |
Tone.GreaterThanZero.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._scale.dispose(); | |
this._scale = null; | |
this._thresh.dispose(); | |
this._thresh = null; | |
return this; | |
}; | |
return Tone.GreaterThanZero; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class EqualZero outputs 1 when the input is equal to | |
* 0 and outputs 0 otherwise. | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @example | |
* var eq0 = new Tone.EqualZero(); | |
* var sig = new Tone.Signal(0).connect(eq0); | |
* //the output of eq0 is 1. | |
*/ | |
Tone.EqualZero = function () { | |
/** | |
* scale the incoming signal by a large factor | |
* @private | |
* @type {Tone.Multiply} | |
*/ | |
this._scale = this.input = new Tone.Multiply(10000); | |
/** | |
* @type {Tone.WaveShaper} | |
* @private | |
*/ | |
this._thresh = new Tone.WaveShaper(function (val) { | |
if (val === 0) { | |
return 1; | |
} else { | |
return 0; | |
} | |
}, 128); | |
/** | |
* threshold the output so that it's 0 or 1 | |
* @type {Tone.GreaterThanZero} | |
* @private | |
*/ | |
this._gtz = this.output = new Tone.GreaterThanZero(); | |
//connections | |
this._scale.chain(this._thresh, this._gtz); | |
}; | |
Tone.extend(Tone.EqualZero, Tone.SignalBase); | |
/** | |
* Clean up. | |
* @returns {Tone.EqualZero} this | |
*/ | |
Tone.EqualZero.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._gtz.dispose(); | |
this._gtz = null; | |
this._scale.dispose(); | |
this._scale = null; | |
this._thresh.dispose(); | |
this._thresh = null; | |
return this; | |
}; | |
return Tone.EqualZero; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Output 1 if the signal is equal to the value, otherwise outputs 0. | |
* Can accept two signals if connected to inputs 0 and 1. | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @param {number=} value The number to compare the incoming signal to | |
* @example | |
* var eq = new Tone.Equal(3); | |
* var sig = new Tone.Signal(3).connect(eq); | |
* //the output of eq is 1. | |
*/ | |
Tone.Equal = function (value) { | |
Tone.call(this, 2, 0); | |
/** | |
* subtract the value from the incoming signal | |
* | |
* @type {Tone.Add} | |
* @private | |
*/ | |
this._sub = this.input[0] = new Tone.Subtract(value); | |
/** | |
* @type {Tone.EqualZero} | |
* @private | |
*/ | |
this._equals = this.output = new Tone.EqualZero(); | |
this._sub.connect(this._equals); | |
this.input[1] = this._sub.input[1]; | |
}; | |
Tone.extend(Tone.Equal, Tone.SignalBase); | |
/** | |
* The value to compare to the incoming signal. | |
* @memberOf Tone.Equal# | |
* @type {number} | |
* @name value | |
*/ | |
Object.defineProperty(Tone.Equal.prototype, 'value', { | |
get: function () { | |
return this._sub.value; | |
}, | |
set: function (value) { | |
this._sub.value = value; | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.Equal} this | |
*/ | |
Tone.Equal.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._equals.dispose(); | |
this._equals = null; | |
this._sub.dispose(); | |
this._sub = null; | |
return this; | |
}; | |
return Tone.Equal; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Select between any number of inputs, sending the one | |
* selected by the gate signal to the output | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @param {number} [sourceCount=2] the number of inputs the switch accepts | |
* @example | |
* var sel = new Tone.Select(2); | |
* var sigA = new Tone.Signal(10).connect(sel, 0, 0); | |
* var sigB = new Tone.Signal(20).connect(sel, 0, 1); | |
* sel.gate.value = 0; | |
* //sel outputs 10 (the value of sigA); | |
* sel.gate.value = 1; | |
* //sel outputs 20 (the value of sigB); | |
*/ | |
Tone.Select = function (sourceCount) { | |
sourceCount = this.defaultArg(sourceCount, 2); | |
Tone.call(this, sourceCount, 1); | |
/** | |
* the control signal | |
* @type {Number} | |
* @signal | |
*/ | |
this.gate = new Tone.Signal(0); | |
this._readOnly('gate'); | |
//make all the inputs and connect them | |
for (var i = 0; i < sourceCount; i++) { | |
var switchGate = new SelectGate(i); | |
this.input[i] = switchGate; | |
this.gate.connect(switchGate.selecter); | |
switchGate.connect(this.output); | |
} | |
}; | |
Tone.extend(Tone.Select, Tone.SignalBase); | |
/** | |
* Open a specific input and close the others. | |
* @param {number} which The gate to open. | |
* @param {Time} [time=now] The time when the switch will open | |
* @returns {Tone.Select} this | |
* @example | |
* //open input 1 in a half second from now | |
* sel.select(1, "+0.5"); | |
*/ | |
Tone.Select.prototype.select = function (which, time) { | |
//make sure it's an integer | |
which = Math.floor(which); | |
this.gate.setValueAtTime(which, this.toSeconds(time)); | |
return this; | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.Select} this | |
*/ | |
Tone.Select.prototype.dispose = function () { | |
this._writable('gate'); | |
this.gate.dispose(); | |
this.gate = null; | |
for (var i = 0; i < this.input.length; i++) { | |
this.input[i].dispose(); | |
this.input[i] = null; | |
} | |
Tone.prototype.dispose.call(this); | |
return this; | |
}; | |
////////////START HELPER//////////// | |
/** | |
* helper class for Tone.Select representing a single gate | |
* @constructor | |
* @extends {Tone} | |
* @private | |
*/ | |
var SelectGate = function (num) { | |
/** | |
* the selector | |
* @type {Tone.Equal} | |
*/ | |
this.selecter = new Tone.Equal(num); | |
/** | |
* the gate | |
* @type {GainNode} | |
*/ | |
this.gate = this.input = this.output = this.context.createGain(); | |
//connect the selecter to the gate gain | |
this.selecter.connect(this.gate.gain); | |
}; | |
Tone.extend(SelectGate); | |
/** | |
* clean up | |
* @private | |
*/ | |
SelectGate.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this.selecter.dispose(); | |
this.gate.disconnect(); | |
this.selecter = null; | |
this.gate = null; | |
}; | |
////////////END HELPER//////////// | |
//return Tone.Select | |
return Tone.Select; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class IfThenElse has three inputs. When the first input (if) is true (i.e. === 1), | |
* then it will pass the second input (then) through to the output, otherwise, | |
* if it's not true (i.e. === 0) then it will pass the third input (else) | |
* through to the output. | |
* | |
* @extends {Tone.SignalBase} | |
* @constructor | |
* @example | |
* var ifThenElse = new Tone.IfThenElse(); | |
* var ifSignal = new Tone.Signal(1).connect(ifThenElse.if); | |
* var pwmOsc = new Tone.PWMOscillator().connect(ifThenElse.then); | |
* var pulseOsc = new Tone.PulseOscillator().connect(ifThenElse.else); | |
* //ifThenElse outputs pwmOsc | |
* signal.value = 0; | |
* //now ifThenElse outputs pulseOsc | |
*/ | |
Tone.IfThenElse = function () { | |
Tone.call(this, 3, 0); | |
/** | |
* the selector node which is responsible for the routing | |
* @type {Tone.Select} | |
* @private | |
*/ | |
this._selector = this.output = new Tone.Select(2); | |
//the input mapping | |
this.if = this.input[0] = this._selector.gate; | |
this.then = this.input[1] = this._selector.input[1]; | |
this.else = this.input[2] = this._selector.input[0]; | |
}; | |
Tone.extend(Tone.IfThenElse, Tone.SignalBase); | |
/** | |
* clean up | |
* @returns {Tone.IfThenElse} this | |
*/ | |
Tone.IfThenElse.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._selector.dispose(); | |
this._selector = null; | |
this.if = null; | |
this.then = null; | |
this.else = null; | |
return this; | |
}; | |
return Tone.IfThenElse; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class [OR](https://en.wikipedia.org/wiki/OR_gate) | |
* the inputs together. True if at least one of the inputs is true. | |
* | |
* @extends {Tone.SignalBase} | |
* @constructor | |
* @param {number} [inputCount=2] the input count | |
* @example | |
* var or = new Tone.OR(2); | |
* var sigA = new Tone.Signal(0)connect(or, 0, 0); | |
* var sigB = new Tone.Signal(1)connect(or, 0, 1); | |
* //output of or is 1 because at least | |
* //one of the inputs is equal to 1. | |
*/ | |
Tone.OR = function (inputCount) { | |
inputCount = this.defaultArg(inputCount, 2); | |
Tone.call(this, inputCount, 0); | |
/** | |
* a private summing node | |
* @type {GainNode} | |
* @private | |
*/ | |
this._sum = this.context.createGain(); | |
/** | |
* @type {Tone.Equal} | |
* @private | |
*/ | |
this._gtz = this.output = new Tone.GreaterThanZero(); | |
//make each of the inputs an alias | |
for (var i = 0; i < inputCount; i++) { | |
this.input[i] = this._sum; | |
} | |
this._sum.connect(this._gtz); | |
}; | |
Tone.extend(Tone.OR, Tone.SignalBase); | |
/** | |
* clean up | |
* @returns {Tone.OR} this | |
*/ | |
Tone.OR.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._gtz.dispose(); | |
this._gtz = null; | |
this._sum.disconnect(); | |
this._sum = null; | |
return this; | |
}; | |
return Tone.OR; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class [AND](https://en.wikipedia.org/wiki/Logical_conjunction) | |
* returns 1 when all the inputs are equal to 1 and returns 0 otherwise. | |
* | |
* @extends {Tone.SignalBase} | |
* @constructor | |
* @param {number} [inputCount=2] the number of inputs. NOTE: all inputs are | |
* connected to the single AND input node | |
* @example | |
* var and = new Tone.AND(2); | |
* var sigA = new Tone.Signal(0).connect(and, 0, 0); | |
* var sigB = new Tone.Signal(1).connect(and, 0, 1); | |
* //the output of and is 0. | |
*/ | |
Tone.AND = function (inputCount) { | |
inputCount = this.defaultArg(inputCount, 2); | |
Tone.call(this, inputCount, 0); | |
/** | |
* @type {Tone.Equal} | |
* @private | |
*/ | |
this._equals = this.output = new Tone.Equal(inputCount); | |
//make each of the inputs an alias | |
for (var i = 0; i < inputCount; i++) { | |
this.input[i] = this._equals; | |
} | |
}; | |
Tone.extend(Tone.AND, Tone.SignalBase); | |
/** | |
* clean up | |
* @returns {Tone.AND} this | |
*/ | |
Tone.AND.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._equals.dispose(); | |
this._equals = null; | |
return this; | |
}; | |
return Tone.AND; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Just an alias for Tone.EqualZero, but has the same effect as a NOT operator. | |
* Outputs 1 when input equals 0. | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @example | |
* var not = new Tone.NOT(); | |
* var sig = new Tone.Signal(1).connect(not); | |
* //output of not equals 0. | |
* sig.value = 0; | |
* //output of not equals 1. | |
*/ | |
Tone.NOT = Tone.EqualZero; | |
return Tone.NOT; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Output 1 if the signal is greater than the value, otherwise outputs 0. | |
* can compare two signals or a signal and a number. | |
* | |
* @constructor | |
* @extends {Tone.Signal} | |
* @param {number} [value=0] the value to compare to the incoming signal | |
* @example | |
* var gt = new Tone.GreaterThan(2); | |
* var sig = new Tone.Signal(4).connect(gt); | |
* //output of gt is equal 1. | |
*/ | |
Tone.GreaterThan = function (value) { | |
Tone.call(this, 2, 0); | |
/** | |
* subtract the amount from the incoming signal | |
* @type {Tone.Subtract} | |
* @private | |
*/ | |
this._param = this.input[0] = new Tone.Subtract(value); | |
this.input[1] = this._param.input[1]; | |
/** | |
* compare that amount to zero | |
* @type {Tone.GreaterThanZero} | |
* @private | |
*/ | |
this._gtz = this.output = new Tone.GreaterThanZero(); | |
//connect | |
this._param.connect(this._gtz); | |
}; | |
Tone.extend(Tone.GreaterThan, Tone.Signal); | |
/** | |
* dispose method | |
* @returns {Tone.GreaterThan} this | |
*/ | |
Tone.GreaterThan.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._param.dispose(); | |
this._param = null; | |
this._gtz.dispose(); | |
this._gtz = null; | |
return this; | |
}; | |
return Tone.GreaterThan; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Output 1 if the signal is less than the value, otherwise outputs 0. | |
* Can compare two signals or a signal and a number. | |
* | |
* @constructor | |
* @extends {Tone.Signal} | |
* @param {number=} value The value to compare to the incoming signal. | |
* If no value is provided, it will compare | |
* <code>input[0]</code> and <code>input[1]</code> | |
* @example | |
* var lt = new Tone.LessThan(2); | |
* var sig = new Tone.Signal(-1).connect(lt); | |
* //if (sig < 2) lt outputs 1 | |
*/ | |
Tone.LessThan = function (value) { | |
Tone.call(this, 2, 0); | |
/** | |
* negate the incoming signal | |
* @type {Tone.Negate} | |
* @private | |
*/ | |
this._neg = this.input[0] = new Tone.Negate(); | |
/** | |
* input < value === -input > -value | |
* @type {Tone.GreaterThan} | |
* @private | |
*/ | |
this._gt = this.output = new Tone.GreaterThan(); | |
/** | |
* negate the signal coming from the second input | |
* @private | |
* @type {Tone.Negate} | |
*/ | |
this._rhNeg = new Tone.Negate(); | |
/** | |
* the node where the value is set | |
* @private | |
* @type {Tone.Signal} | |
*/ | |
this._param = this.input[1] = new Tone.Signal(value); | |
//connect | |
this._neg.connect(this._gt); | |
this._param.connect(this._rhNeg); | |
this._rhNeg.connect(this._gt, 0, 1); | |
}; | |
Tone.extend(Tone.LessThan, Tone.Signal); | |
/** | |
* Clean up. | |
* @returns {Tone.LessThan} this | |
*/ | |
Tone.LessThan.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.LessThan; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Return the absolute value of an incoming signal. | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @example | |
* var signal = new Tone.Signal(-1); | |
* var abs = new Tone.Abs(); | |
* signal.connect(abs); | |
* //the output of abs is 1. | |
*/ | |
Tone.Abs = function () { | |
Tone.call(this, 1, 0); | |
/** | |
* @type {Tone.LessThan} | |
* @private | |
*/ | |
this._ltz = new Tone.LessThan(0); | |
/** | |
* @type {Tone.Select} | |
* @private | |
*/ | |
this._switch = this.output = new Tone.Select(2); | |
/** | |
* @type {Tone.Negate} | |
* @private | |
*/ | |
this._negate = new Tone.Negate(); | |
//two signal paths, positive and negative | |
this.input.connect(this._switch, 0, 0); | |
this.input.connect(this._negate); | |
this._negate.connect(this._switch, 0, 1); | |
//the control signal | |
this.input.chain(this._ltz, this._switch.gate); | |
}; | |
Tone.extend(Tone.Abs, Tone.SignalBase); | |
/** | |
* dispose method | |
* @returns {Tone.Abs} this | |
*/ | |
Tone.Abs.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._switch.dispose(); | |
this._switch = null; | |
this._ltz.dispose(); | |
this._ltz = null; | |
this._negate.dispose(); | |
this._negate = null; | |
return this; | |
}; | |
return Tone.Abs; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Outputs the greater of two signals. If a number is provided in the constructor | |
* it will use that instead of the signal. | |
* | |
* @constructor | |
* @extends {Tone.Signal} | |
* @param {number=} max Max value if provided. if not provided, it will use the | |
* signal value from input 1. | |
* @example | |
* var max = new Tone.Max(2); | |
* var sig = new Tone.Signal(3).connect(max); | |
* //max outputs 3 | |
* sig.value = 1; | |
* //max outputs 2 | |
* @example | |
* var max = new Tone.Max(); | |
* var sigA = new Tone.Signal(3); | |
* var sigB = new Tone.Signal(4); | |
* sigA.connect(max, 0, 0); | |
* sigB.connect(max, 0, 1); | |
* //output of max is 4. | |
*/ | |
Tone.Max = function (max) { | |
Tone.call(this, 2, 0); | |
this.input[0] = this.context.createGain(); | |
/** | |
* the max signal | |
* @type {Tone.Signal} | |
* @private | |
*/ | |
this._param = this.input[1] = new Tone.Signal(max); | |
/** | |
* @type {Tone.Select} | |
* @private | |
*/ | |
this._ifThenElse = this.output = new Tone.IfThenElse(); | |
/** | |
* @type {Tone.Select} | |
* @private | |
*/ | |
this._gt = new Tone.GreaterThan(); | |
//connections | |
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); | |
}; | |
Tone.extend(Tone.Max, Tone.Signal); | |
/** | |
* Clean up. | |
* @returns {Tone.Max} this | |
*/ | |
Tone.Max.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._param.dispose(); | |
this._ifThenElse.dispose(); | |
this._gt.dispose(); | |
this._param = null; | |
this._ifThenElse = null; | |
this._gt = null; | |
return this; | |
}; | |
return Tone.Max; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Outputs the lesser of two signals. If a number is given | |
* in the constructor, it will use a signal and a number. | |
* | |
* @constructor | |
* @extends {Tone.Signal} | |
* @param {number} min The minimum to compare to the incoming signal | |
* @example | |
* var min = new Tone.Min(2); | |
* var sig = new Tone.Signal(3).connect(min); | |
* //min outputs 2 | |
* sig.value = 1; | |
* //min outputs 1 | |
* @example | |
* var min = new Tone.Min(); | |
* var sigA = new Tone.Signal(3); | |
* var sigB = new Tone.Signal(4); | |
* sigA.connect(min, 0, 0); | |
* sigB.connect(min, 0, 1); | |
* //output of min is 3. | |
*/ | |
Tone.Min = function (min) { | |
Tone.call(this, 2, 0); | |
this.input[0] = this.context.createGain(); | |
/** | |
* @type {Tone.Select} | |
* @private | |
*/ | |
this._ifThenElse = this.output = new Tone.IfThenElse(); | |
/** | |
* @type {Tone.Select} | |
* @private | |
*/ | |
this._lt = new Tone.LessThan(); | |
/** | |
* the min signal | |
* @type {Tone.Signal} | |
* @private | |
*/ | |
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._param.connect(this._ifThenElse.else); | |
this._param.connect(this._lt, 0, 1); | |
}; | |
Tone.extend(Tone.Min, Tone.Signal); | |
/** | |
* clean up | |
* @returns {Tone.Min} this | |
*/ | |
Tone.Min.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._param.dispose(); | |
this._ifThenElse.dispose(); | |
this._lt.dispose(); | |
this._param = null; | |
this._ifThenElse = null; | |
this._lt = null; | |
return this; | |
}; | |
return Tone.Min; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Signal-rate modulo operator. Only works in AudioRange [-1, 1] and for modulus | |
* values in the NormalRange. | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @param {NormalRange} modulus The modulus to apply. | |
* @example | |
* var mod = new Tone.Modulo(0.2) | |
* var sig = new Tone.Signal(0.5).connect(mod); | |
* //mod outputs 0.1 | |
*/ | |
Tone.Modulo = function (modulus) { | |
Tone.call(this, 1, 1); | |
/** | |
* A waveshaper gets the integer multiple of | |
* the input signal and the modulus. | |
* @private | |
* @type {Tone.WaveShaper} | |
*/ | |
this._shaper = new Tone.WaveShaper(Math.pow(2, 16)); | |
/** | |
* the integer multiple is multiplied by the modulus | |
* @type {Tone.Multiply} | |
* @private | |
*/ | |
this._multiply = new Tone.Multiply(); | |
/** | |
* and subtracted from the input signal | |
* @type {Tone.Subtract} | |
* @private | |
*/ | |
this._subtract = this.output = new Tone.Subtract(); | |
/** | |
* the modulus signal | |
* @type {Tone.Signal} | |
* @private | |
*/ | |
this._modSignal = new Tone.Signal(modulus); | |
//connections | |
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(modulus); | |
}; | |
Tone.extend(Tone.Modulo, Tone.SignalBase); | |
/** | |
* @param {number} mod the modulus to apply | |
* @private | |
*/ | |
Tone.Modulo.prototype._setWaveShaper = function (mod) { | |
this._shaper.setMap(function (val) { | |
var multiple = Math.floor((val + 0.0001) / mod); | |
return multiple; | |
}); | |
}; | |
/** | |
* The modulus value. | |
* @memberOf Tone.Modulo# | |
* @type {NormalRange} | |
* @name value | |
*/ | |
Object.defineProperty(Tone.Modulo.prototype, 'value', { | |
get: function () { | |
return this._modSignal.value; | |
}, | |
set: function (mod) { | |
this._modSignal.value = mod; | |
this._setWaveShaper(mod); | |
} | |
}); | |
/** | |
* clean up | |
* @returns {Tone.Modulo} this | |
*/ | |
Tone.Modulo.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
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) { | |
/** | |
* @class Evaluate an expression at audio rate. <br><br> | |
* Parsing code modified from https://code.google.com/p/tapdigit/ | |
* Copyright 2011 2012 Ariya Hidayat, New BSD License | |
* | |
* @extends {Tone.SignalBase} | |
* @constructor | |
* @param {string} expr the expression to generate | |
* @example | |
* //adds the signals from input[0] and input[1]. | |
* var expr = new Tone.Expr("$0 + $1"); | |
*/ | |
Tone.Expr = function () { | |
var expr = this._replacements(Array.prototype.slice.call(arguments)); | |
var inputCount = this._parseInputs(expr); | |
/** | |
* hold onto all of the nodes for disposal | |
* @type {Array} | |
* @private | |
*/ | |
this._nodes = []; | |
/** | |
* The inputs. The length is determined by the expression. | |
* @type {Array} | |
*/ | |
this.input = new Array(inputCount); | |
//create a gain for each input | |
for (var i = 0; i < inputCount; i++) { | |
this.input[i] = this.context.createGain(); | |
} | |
//parse the syntax tree | |
var tree = this._parseTree(expr); | |
//evaluate the results | |
var result; | |
try { | |
result = this._eval(tree); | |
} catch (e) { | |
this._disposeNodes(); | |
throw new Error('Could evaluate expression: ' + expr); | |
} | |
/** | |
* The output node is the result of the expression | |
* @type {Tone} | |
*/ | |
this.output = result; | |
}; | |
Tone.extend(Tone.Expr, Tone.SignalBase); | |
//some helpers to cut down the amount of code | |
function applyBinary(Constructor, args, self) { | |
var op = new Constructor(); | |
self._eval(args[0]).connect(op, 0, 0); | |
self._eval(args[1]).connect(op, 0, 1); | |
return op; | |
} | |
function applyUnary(Constructor, args, self) { | |
var op = new Constructor(); | |
self._eval(args[0]).connect(op, 0, 0); | |
return op; | |
} | |
function getNumber(arg) { | |
return arg ? parseFloat(arg) : undefined; | |
} | |
function literalNumber(arg) { | |
return arg && arg.args ? parseFloat(arg.args) : undefined; | |
} | |
/* | |
* the Expressions that Tone.Expr can parse. | |
* | |
* each expression belongs to a group and contains a regexp | |
* for selecting the operator as well as that operators method | |
* | |
* @type {Object} | |
* @private | |
*/ | |
Tone.Expr._Expressions = { | |
//values | |
'value': { | |
'signal': { | |
regexp: /^\d+\.\d+|^\d+/, | |
method: function (arg) { | |
var sig = new Tone.Signal(getNumber(arg)); | |
return sig; | |
} | |
}, | |
'input': { | |
regexp: /^\$\d/, | |
method: function (arg, self) { | |
return self.input[getNumber(arg.substr(1))]; | |
} | |
} | |
}, | |
//syntactic glue | |
'glue': { | |
'(': { regexp: /^\(/ }, | |
')': { regexp: /^\)/ }, | |
',': { regexp: /^,/ } | |
}, | |
//functions | |
'func': { | |
'abs': { | |
regexp: /^abs/, | |
method: applyUnary.bind(this, Tone.Abs) | |
}, | |
'min': { | |
regexp: /^min/, | |
method: applyBinary.bind(this, Tone.Min) | |
}, | |
'max': { | |
regexp: /^max/, | |
method: applyBinary.bind(this, Tone.Max) | |
}, | |
'if': { | |
regexp: /^if/, | |
method: function (args, self) { | |
var op = new Tone.IfThenElse(); | |
self._eval(args[0]).connect(op.if); | |
self._eval(args[1]).connect(op.then); | |
self._eval(args[2]).connect(op.else); | |
return op; | |
} | |
}, | |
'gt0': { | |
regexp: /^gt0/, | |
method: applyUnary.bind(this, Tone.GreaterThanZero) | |
}, | |
'eq0': { | |
regexp: /^eq0/, | |
method: applyUnary.bind(this, Tone.EqualZero) | |
}, | |
'mod': { | |
regexp: /^mod/, | |
method: function (args, self) { | |
var modulus = literalNumber(args[1]); | |
var op = new Tone.Modulo(modulus); | |
self._eval(args[0]).connect(op); | |
return op; | |
} | |
}, | |
'pow': { | |
regexp: /^pow/, | |
method: function (args, self) { | |
var exp = literalNumber(args[1]); | |
var op = new Tone.Pow(exp); | |
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 | |
'binary': { | |
'+': { | |
regexp: /^\+/, | |
precedence: 1, | |
method: applyBinary.bind(this, Tone.Add) | |
}, | |
'-': { | |
regexp: /^\-/, | |
precedence: 1, | |
method: function (args, self) { | |
//both unary and binary op | |
if (args.length === 1) { | |
return applyUnary(Tone.Negate, args, self); | |
} else { | |
return applyBinary(Tone.Subtract, args, self); | |
} | |
} | |
}, | |
'*': { | |
regexp: /^\*/, | |
precedence: 0, | |
method: applyBinary.bind(this, Tone.Multiply) | |
}, | |
'>': { | |
regexp: /^\>/, | |
precedence: 2, | |
method: applyBinary.bind(this, Tone.GreaterThan) | |
}, | |
'<': { | |
regexp: /^</, | |
precedence: 2, | |
method: applyBinary.bind(this, Tone.LessThan) | |
}, | |
'==': { | |
regexp: /^==/, | |
precedence: 3, | |
method: applyBinary.bind(this, Tone.Equal) | |
}, | |
'&&': { | |
regexp: /^&&/, | |
precedence: 4, | |
method: applyBinary.bind(this, Tone.AND) | |
}, | |
'||': { | |
regexp: /^\|\|/, | |
precedence: 5, | |
method: applyBinary.bind(this, Tone.OR) | |
} | |
}, | |
//unary expressions | |
'unary': { | |
'-': { | |
regexp: /^\-/, | |
method: applyUnary.bind(this, Tone.Negate) | |
}, | |
'!': { | |
regexp: /^\!/, | |
method: applyUnary.bind(this, Tone.NOT) | |
} | |
} | |
}; | |
/** | |
* @param {string} expr the expression string | |
* @return {number} the input count | |
* @private | |
*/ | |
Tone.Expr.prototype._parseInputs = function (expr) { | |
var inputArray = expr.match(/\$\d/g); | |
var inputMax = 0; | |
if (inputArray !== null) { | |
for (var i = 0; i < inputArray.length; i++) { | |
var inputNum = parseInt(inputArray[i].substr(1)) + 1; | |
inputMax = Math.max(inputMax, inputNum); | |
} | |
} | |
return inputMax; | |
}; | |
/** | |
* @param {Array} args an array of arguments | |
* @return {string} the results of the replacements being replaced | |
* @private | |
*/ | |
Tone.Expr.prototype._replacements = function (args) { | |
var expr = args.shift(); | |
for (var i = 0; i < args.length; i++) { | |
expr = expr.replace(/\%/i, args[i]); | |
} | |
return expr; | |
}; | |
/** | |
* tokenize the expression based on the Expressions object | |
* @param {string} expr | |
* @return {Object} returns two methods on the tokenized list, next and peek | |
* @private | |
*/ | |
Tone.Expr.prototype._tokenize = function (expr) { | |
var position = -1; | |
var tokens = []; | |
while (expr.length > 0) { | |
expr = expr.trim(); | |
var token = getNextToken(expr); | |
tokens.push(token); | |
expr = expr.substr(token.value.length); | |
} | |
function getNextToken(expr) { | |
for (var type in Tone.Expr._Expressions) { | |
var group = Tone.Expr._Expressions[type]; | |
for (var opName in group) { | |
var op = group[opName]; | |
var reg = op.regexp; | |
var match = expr.match(reg); | |
if (match !== null) { | |
return { | |
type: type, | |
value: match[0], | |
method: op.method | |
}; | |
} | |
} | |
} | |
throw new SyntaxError('Unexpected token ' + expr); | |
} | |
return { | |
next: function () { | |
return tokens[++position]; | |
}, | |
peek: function () { | |
return tokens[position + 1]; | |
} | |
}; | |
}; | |
/** | |
* recursively parse the string expression into a syntax tree | |
* | |
* @param {string} expr | |
* @return {Object} | |
* @private | |
*/ | |
Tone.Expr.prototype._parseTree = function (expr) { | |
var lexer = this._tokenize(expr); | |
var isUndef = this.isUndef.bind(this); | |
function matchSyntax(token, syn) { | |
return !isUndef(token) && token.type === 'glue' && token.value === syn; | |
} | |
function matchGroup(token, groupName, prec) { | |
var ret = false; | |
var group = Tone.Expr._Expressions[groupName]; | |
if (!isUndef(token)) { | |
for (var opName in group) { | |
var op = group[opName]; | |
if (op.regexp.test(token.value)) { | |
if (!isUndef(prec)) { | |
if (op.precedence === prec) { | |
return true; | |
} | |
} else { | |
return true; | |
} | |
} | |
} | |
} | |
return ret; | |
} | |
function parseExpression(precedence) { | |
if (isUndef(precedence)) { | |
precedence = 5; | |
} | |
var expr; | |
if (precedence < 0) { | |
expr = parseUnary(); | |
} else { | |
expr = parseExpression(precedence - 1); | |
} | |
var token = lexer.peek(); | |
while (matchGroup(token, 'binary', precedence)) { | |
token = lexer.next(); | |
expr = { | |
operator: token.value, | |
method: token.method, | |
args: [ | |
expr, | |
parseExpression(precedence) | |
] | |
}; | |
token = lexer.peek(); | |
} | |
return expr; | |
} | |
function parseUnary() { | |
var token, expr; | |
token = lexer.peek(); | |
if (matchGroup(token, 'unary')) { | |
token = lexer.next(); | |
expr = parseUnary(); | |
return { | |
operator: token.value, | |
method: token.method, | |
args: [expr] | |
}; | |
} | |
return parsePrimary(); | |
} | |
function parsePrimary() { | |
var token, expr; | |
token = lexer.peek(); | |
if (isUndef(token)) { | |
throw new SyntaxError('Unexpected termination of expression'); | |
} | |
if (token.type === 'func') { | |
token = lexer.next(); | |
return parseFunctionCall(token); | |
} | |
if (token.type === 'value') { | |
token = lexer.next(); | |
return { | |
method: token.method, | |
args: token.value | |
}; | |
} | |
if (matchSyntax(token, '(')) { | |
lexer.next(); | |
expr = parseExpression(); | |
token = lexer.next(); | |
if (!matchSyntax(token, ')')) { | |
throw new SyntaxError('Expected )'); | |
} | |
return expr; | |
} | |
throw new SyntaxError('Parse error, cannot process token ' + token.value); | |
} | |
function parseFunctionCall(func) { | |
var token, args = []; | |
token = lexer.next(); | |
if (!matchSyntax(token, '(')) { | |
throw new SyntaxError('Expected ( in a function call "' + func.value + '"'); | |
} | |
token = lexer.peek(); | |
if (!matchSyntax(token, ')')) { | |
args = parseArgumentList(); | |
} | |
token = lexer.next(); | |
if (!matchSyntax(token, ')')) { | |
throw new SyntaxError('Expected ) in a function call "' + func.value + '"'); | |
} | |
return { | |
method: func.method, | |
args: args, | |
name: name | |
}; | |
} | |
function parseArgumentList() { | |
var token, expr, args = []; | |
while (true) { | |
expr = parseExpression(); | |
if (isUndef(expr)) { | |
// TODO maybe throw exception? | |
break; | |
} | |
args.push(expr); | |
token = lexer.peek(); | |
if (!matchSyntax(token, ',')) { | |
break; | |
} | |
lexer.next(); | |
} | |
return args; | |
} | |
return parseExpression(); | |
}; | |
/** | |
* recursively evaluate the expression tree | |
* @param {Object} tree | |
* @return {AudioNode} the resulting audio node from the expression | |
* @private | |
*/ | |
Tone.Expr.prototype._eval = function (tree) { | |
if (!this.isUndef(tree)) { | |
var node = tree.method(tree.args, this); | |
this._nodes.push(node); | |
return node; | |
} | |
}; | |
/** | |
* dispose all the nodes | |
* @private | |
*/ | |
Tone.Expr.prototype._disposeNodes = function () { | |
for (var i = 0; i < this._nodes.length; i++) { | |
var node = this._nodes[i]; | |
if (this.isFunction(node.dispose)) { | |
node.dispose(); | |
} else if (this.isFunction(node.disconnect)) { | |
node.disconnect(); | |
} | |
node = null; | |
this._nodes[i] = null; | |
} | |
this._nodes = null; | |
}; | |
/** | |
* clean up | |
*/ | |
Tone.Expr.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._disposeNodes(); | |
}; | |
return Tone.Expr; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Convert an incoming signal between 0, 1 to an equal power gain scale. | |
* | |
* @extends {Tone.SignalBase} | |
* @constructor | |
* @example | |
* var eqPowGain = new Tone.EqualPowerGain(); | |
*/ | |
Tone.EqualPowerGain = function () { | |
/** | |
* @type {Tone.WaveShaper} | |
* @private | |
*/ | |
this._eqPower = this.input = this.output = new Tone.WaveShaper(function (val) { | |
if (Math.abs(val) < 0.001) { | |
//should output 0 when input is 0 | |
return 0; | |
} else { | |
return this.equalPowerScale(val); | |
} | |
}.bind(this), 4096); | |
}; | |
Tone.extend(Tone.EqualPowerGain, Tone.SignalBase); | |
/** | |
* clean up | |
* @returns {Tone.EqualPowerGain} this | |
*/ | |
Tone.EqualPowerGain.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._eqPower.dispose(); | |
this._eqPower = null; | |
return this; | |
}; | |
return Tone.EqualPowerGain; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Crossfade provides equal power fading between two inputs. | |
* More on crossfading technique [here](https://en.wikipedia.org/wiki/Fade_(audio_engineering)#Crossfading). | |
* | |
* @constructor | |
* @extends {Tone} | |
* @param {NormalRange} [initialFade=0.5] | |
* @example | |
* var crossFade = new Tone.CrossFade(0.5); | |
* //connect effect A to crossfade from | |
* //effect output 0 to crossfade input 0 | |
* effectA.connect(crossFade, 0, 0); | |
* //connect effect B to crossfade from | |
* //effect output 0 to crossfade input 1 | |
* effectB.connect(crossFade, 0, 1); | |
* crossFade.fade.value = 0; | |
* // ^ only effectA is output | |
* crossFade.fade.value = 1; | |
* // ^ only effectB is output | |
* crossFade.fade.value = 0.5; | |
* // ^ the two signals are mixed equally. | |
*/ | |
Tone.CrossFade = function (initialFade) { | |
Tone.call(this, 2, 1); | |
/** | |
* Alias for <code>input[0]</code>. | |
* @type {GainNode} | |
*/ | |
this.a = this.input[0] = this.context.createGain(); | |
/** | |
* Alias for <code>input[1]</code>. | |
* @type {GainNode} | |
*/ | |
this.b = this.input[1] = this.context.createGain(); | |
/** | |
* The mix between the two inputs. A fade value of 0 | |
* will output 100% <code>input[0]</code> and | |
* a value of 1 will output 100% <code>input[1]</code>. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.fade = new Tone.Signal(this.defaultArg(initialFade, 0.5), Tone.Type.NormalRange); | |
/** | |
* equal power gain cross fade | |
* @private | |
* @type {Tone.EqualPowerGain} | |
*/ | |
this._equalPowerA = new Tone.EqualPowerGain(); | |
/** | |
* equal power gain cross fade | |
* @private | |
* @type {Tone.EqualPowerGain} | |
*/ | |
this._equalPowerB = new Tone.EqualPowerGain(); | |
/** | |
* invert the incoming signal | |
* @private | |
* @type {Tone} | |
*/ | |
this._invert = new Tone.Expr('1 - $0'); | |
//connections | |
this.a.connect(this.output); | |
this.b.connect(this.output); | |
this.fade.chain(this._equalPowerB, this.b.gain); | |
this.fade.chain(this._invert, this._equalPowerA, this.a.gain); | |
this._readOnly('fade'); | |
}; | |
Tone.extend(Tone.CrossFade); | |
/** | |
* clean up | |
* @returns {Tone.CrossFade} this | |
*/ | |
Tone.CrossFade.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._writable('fade'); | |
this._equalPowerA.dispose(); | |
this._equalPowerA = null; | |
this._equalPowerB.dispose(); | |
this._equalPowerB = null; | |
this.fade.dispose(); | |
this.fade = null; | |
this._invert.dispose(); | |
this._invert = null; | |
this.a.disconnect(); | |
this.a = null; | |
this.b.disconnect(); | |
this.b = null; | |
return this; | |
}; | |
return Tone.CrossFade; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Filter is a filter which allows for all of the same native methods | |
* as the [BiquadFilterNode](http://webaudio.github.io/web-audio-api/#the-biquadfilternode-interface). | |
* Tone.Filter has the added ability to set the filter rolloff at -12 | |
* (default), -24 and -48. | |
* | |
* @constructor | |
* @extends {Tone} | |
* @param {Frequency|Object} [frequency] The cutoff frequency of the filter. | |
* @param {string=} type The type of filter. | |
* @param {number=} rolloff The drop in decibels per octave after the cutoff frequency. | |
* 3 choices: -12, -24, and -48 | |
* @example | |
* var filter = new Tone.Filter(200, "highpass"); | |
*/ | |
Tone.Filter = function () { | |
Tone.call(this); | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'type', | |
'rolloff' | |
], Tone.Filter.defaults); | |
/** | |
* the filter(s) | |
* @type {Array} | |
* @private | |
*/ | |
this._filters = []; | |
/** | |
* The cutoff frequency of the filter. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency); | |
/** | |
* The detune parameter | |
* @type {Cents} | |
* @signal | |
*/ | |
this.detune = new Tone.Signal(0, Tone.Type.Cents); | |
/** | |
* The gain of the filter, only used in certain filter types | |
* @type {Number} | |
* @signal | |
*/ | |
this.gain = new Tone.Signal({ | |
'value': options.gain, | |
'convert': false | |
}); | |
/** | |
* The Q or Quality of the filter | |
* @type {Positive} | |
* @signal | |
*/ | |
this.Q = new Tone.Signal(options.Q); | |
/** | |
* the type of the filter | |
* @type {string} | |
* @private | |
*/ | |
this._type = options.type; | |
/** | |
* the rolloff value of the filter | |
* @type {number} | |
* @private | |
*/ | |
this._rolloff = options.rolloff; | |
//set the rolloff; | |
this.rolloff = options.rolloff; | |
this._readOnly([ | |
'detune', | |
'frequency', | |
'gain', | |
'Q' | |
]); | |
}; | |
Tone.extend(Tone.Filter); | |
/** | |
* the default parameters | |
* | |
* @static | |
* @type {Object} | |
*/ | |
Tone.Filter.defaults = { | |
'type': 'lowpass', | |
'frequency': 350, | |
'rolloff': -12, | |
'Q': 1, | |
'gain': 0 | |
}; | |
/** | |
* The type of the filter. Types: "lowpass", "highpass", | |
* "bandpass", "lowshelf", "highshelf", "notch", "allpass", or "peaking". | |
* @memberOf Tone.Filter# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.Filter.prototype, 'type', { | |
get: function () { | |
return this._type; | |
}, | |
set: function (type) { | |
var types = [ | |
'lowpass', | |
'highpass', | |
'bandpass', | |
'lowshelf', | |
'highshelf', | |
'notch', | |
'allpass', | |
'peaking' | |
]; | |
if (types.indexOf(type) === -1) { | |
throw new Error('Tone.Filter does not have filter type ' + type); | |
} | |
this._type = type; | |
for (var i = 0; i < this._filters.length; i++) { | |
this._filters[i].type = type; | |
} | |
} | |
}); | |
/** | |
* The rolloff of the filter which is the drop in db | |
* per octave. Implemented internally by cascading filters. | |
* Only accepts the values -12, -24, -48 and -96. | |
* @memberOf Tone.Filter# | |
* @type {number} | |
* @name rolloff | |
*/ | |
Object.defineProperty(Tone.Filter.prototype, 'rolloff', { | |
get: function () { | |
return this._rolloff; | |
}, | |
set: function (rolloff) { | |
rolloff = parseInt(rolloff, 10); | |
var possibilities = [ | |
-12, | |
-24, | |
-48, | |
-96 | |
]; | |
var cascadingCount = possibilities.indexOf(rolloff); | |
//check the rolloff is valid | |
if (cascadingCount === -1) { | |
throw new Error('Filter rolloff can only be -12, -24, -48 or -96'); | |
} | |
cascadingCount += 1; | |
this._rolloff = rolloff; | |
//first disconnect the filters and throw them away | |
this.input.disconnect(); | |
for (var i = 0; i < this._filters.length; i++) { | |
this._filters[i].disconnect(); | |
this._filters[i] = null; | |
} | |
this._filters = new Array(cascadingCount); | |
for (var count = 0; count < cascadingCount; count++) { | |
var filter = this.context.createBiquadFilter(); | |
filter.type = this._type; | |
this.frequency.connect(filter.frequency); | |
this.detune.connect(filter.detune); | |
this.Q.connect(filter.Q); | |
this.gain.connect(filter.gain); | |
this._filters[count] = filter; | |
} | |
//connect them up | |
var connectionChain = [this.input].concat(this._filters).concat([this.output]); | |
this.connectSeries.apply(this, connectionChain); | |
} | |
}); | |
/** | |
* Clean up. | |
* @return {Tone.Filter} this | |
*/ | |
Tone.Filter.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
for (var i = 0; i < this._filters.length; i++) { | |
this._filters[i].disconnect(); | |
this._filters[i] = null; | |
} | |
this._filters = null; | |
this._writable([ | |
'detune', | |
'frequency', | |
'gain', | |
'Q' | |
]); | |
this.frequency.dispose(); | |
this.Q.dispose(); | |
this.frequency = null; | |
this.Q = null; | |
this.detune.dispose(); | |
this.detune = null; | |
this.gain.dispose(); | |
this.gain = null; | |
return this; | |
}; | |
return Tone.Filter; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Split the incoming signal into three bands (low, mid, high) | |
* with two crossover frequency controls. | |
* | |
* @extends {Tone} | |
* @constructor | |
* @param {Frequency|Object} [lowFrequency] the low/mid crossover frequency | |
* @param {Frequency} [highFrequency] the mid/high crossover frequency | |
*/ | |
Tone.MultibandSplit = function () { | |
var options = this.optionsObject(arguments, [ | |
'lowFrequency', | |
'highFrequency' | |
], Tone.MultibandSplit.defaults); | |
/** | |
* the input | |
* @type {GainNode} | |
* @private | |
*/ | |
this.input = this.context.createGain(); | |
/** | |
* the outputs | |
* @type {Array} | |
* @private | |
*/ | |
this.output = new Array(3); | |
/** | |
* The low band. Alias for <code>output[0]</code> | |
* @type {Tone.Filter} | |
*/ | |
this.low = this.output[0] = new Tone.Filter(0, 'lowpass'); | |
/** | |
* the lower filter of the mid band | |
* @type {Tone.Filter} | |
* @private | |
*/ | |
this._lowMidFilter = new Tone.Filter(0, 'highpass'); | |
/** | |
* The mid band output. Alias for <code>output[1]</code> | |
* @type {Tone.Filter} | |
*/ | |
this.mid = this.output[1] = new Tone.Filter(0, 'lowpass'); | |
/** | |
* The high band output. Alias for <code>output[2]</code> | |
* @type {Tone.Filter} | |
*/ | |
this.high = this.output[2] = new Tone.Filter(0, 'highpass'); | |
/** | |
* The low/mid crossover frequency. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.lowFrequency = new Tone.Signal(options.lowFrequency, Tone.Type.Frequency); | |
/** | |
* The mid/high crossover frequency. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.highFrequency = new Tone.Signal(options.highFrequency, Tone.Type.Frequency); | |
/** | |
* The quality of all the filters | |
* @type {Number} | |
* @signal | |
*/ | |
this.Q = new Tone.Signal(options.Q); | |
this.input.fan(this.low, this.high); | |
this.input.chain(this._lowMidFilter, this.mid); | |
//the frequency control signal | |
this.lowFrequency.connect(this.low.frequency); | |
this.lowFrequency.connect(this._lowMidFilter.frequency); | |
this.highFrequency.connect(this.mid.frequency); | |
this.highFrequency.connect(this.high.frequency); | |
//the Q value | |
this.Q.connect(this.low.Q); | |
this.Q.connect(this._lowMidFilter.Q); | |
this.Q.connect(this.mid.Q); | |
this.Q.connect(this.high.Q); | |
this._readOnly([ | |
'high', | |
'mid', | |
'low', | |
'highFrequency', | |
'lowFrequency' | |
]); | |
}; | |
Tone.extend(Tone.MultibandSplit); | |
/** | |
* @private | |
* @static | |
* @type {Object} | |
*/ | |
Tone.MultibandSplit.defaults = { | |
'lowFrequency': 400, | |
'highFrequency': 2500, | |
'Q': 1 | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.MultibandSplit} this | |
*/ | |
Tone.MultibandSplit.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._writable([ | |
'high', | |
'mid', | |
'low', | |
'highFrequency', | |
'lowFrequency' | |
]); | |
this.low.dispose(); | |
this.low = null; | |
this._lowMidFilter.dispose(); | |
this._lowMidFilter = null; | |
this.mid.dispose(); | |
this.mid = null; | |
this.high.dispose(); | |
this.high = null; | |
this.lowFrequency.dispose(); | |
this.lowFrequency = null; | |
this.highFrequency.dispose(); | |
this.highFrequency = null; | |
this.Q.dispose(); | |
this.Q = null; | |
return this; | |
}; | |
return Tone.MultibandSplit; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.EQ3 is a three band EQ with control over low, mid, and high gain as | |
* well as the low and high crossover frequencies. | |
* | |
* @constructor | |
* @extends {Tone} | |
* | |
* @param {Decibels|Object} [lowLevel] The gain applied to the lows. | |
* @param {Decibels} [midLevel] The gain applied to the mid. | |
* @param {Decibels} [highLevel] The gain applied to the high. | |
* @example | |
* var eq = new Tone.EQ3(-10, 3, -20); | |
*/ | |
Tone.EQ3 = function () { | |
var options = this.optionsObject(arguments, [ | |
'low', | |
'mid', | |
'high' | |
], Tone.EQ3.defaults); | |
/** | |
* the output node | |
* @type {GainNode} | |
* @private | |
*/ | |
this.output = this.context.createGain(); | |
/** | |
* the multiband split | |
* @type {Tone.MultibandSplit} | |
* @private | |
*/ | |
this._multibandSplit = this.input = new Tone.MultibandSplit({ | |
'lowFrequency': options.lowFrequency, | |
'highFrequency': options.highFrequency | |
}); | |
/** | |
* The gain for the lower signals | |
* @type {Tone.Gain} | |
* @private | |
*/ | |
this._lowGain = new Tone.Gain(options.low, Tone.Type.Decibels); | |
/** | |
* The gain for the mid signals | |
* @type {Tone.Gain} | |
* @private | |
*/ | |
this._midGain = new Tone.Gain(options.mid, Tone.Type.Decibels); | |
/** | |
* The gain in decibels of the high part | |
* @type {Tone.Gain} | |
* @private | |
*/ | |
this._highGain = new Tone.Gain(options.high, Tone.Type.Decibels); | |
/** | |
* The gain in decibels of the low part | |
* @type {Decibels} | |
* @signal | |
*/ | |
this.low = this._lowGain.gain; | |
/** | |
* The gain in decibels of the mid part | |
* @type {Decibels} | |
* @signal | |
*/ | |
this.mid = this._midGain.gain; | |
/** | |
* The gain in decibels of the high part | |
* @type {Decibels} | |
* @signal | |
*/ | |
this.high = this._highGain.gain; | |
/** | |
* The Q value for all of the filters. | |
* @type {Positive} | |
* @signal | |
*/ | |
this.Q = this._multibandSplit.Q; | |
/** | |
* The low/mid crossover frequency. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.lowFrequency = this._multibandSplit.lowFrequency; | |
/** | |
* The mid/high crossover frequency. | |
* @type {Frequency} | |
* @signal | |
*/ | |
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); | |
this._readOnly([ | |
'low', | |
'mid', | |
'high', | |
'lowFrequency', | |
'highFrequency' | |
]); | |
}; | |
Tone.extend(Tone.EQ3); | |
/** | |
* the default values | |
*/ | |
Tone.EQ3.defaults = { | |
'low': 0, | |
'mid': 0, | |
'high': 0, | |
'lowFrequency': 400, | |
'highFrequency': 2500 | |
}; | |
/** | |
* clean up | |
* @returns {Tone.EQ3} this | |
*/ | |
Tone.EQ3.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._writable([ | |
'low', | |
'mid', | |
'high', | |
'lowFrequency', | |
'highFrequency' | |
]); | |
this._multibandSplit.dispose(); | |
this._multibandSplit = null; | |
this.lowFrequency = null; | |
this.highFrequency = null; | |
this._lowGain.dispose(); | |
this._lowGain = null; | |
this._midGain.dispose(); | |
this._midGain = null; | |
this._highGain.dispose(); | |
this._highGain = null; | |
this.low = null; | |
this.mid = null; | |
this.high = null; | |
this.Q = null; | |
return this; | |
}; | |
return Tone.EQ3; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Performs a linear scaling on an input signal. | |
* Scales a NormalRange input to between | |
* outputMin and outputMax. | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @param {number} [outputMin=0] The output value when the input is 0. | |
* @param {number} [outputMax=1] The output value when the input is 1. | |
* @example | |
* var scale = new Tone.Scale(50, 100); | |
* var signal = new Tone.Signal(0.5).connect(scale); | |
* //the output of scale equals 75 | |
*/ | |
Tone.Scale = function (outputMin, outputMax) { | |
/** | |
* @private | |
* @type {number} | |
*/ | |
this._outputMin = this.defaultArg(outputMin, 0); | |
/** | |
* @private | |
* @type {number} | |
*/ | |
this._outputMax = this.defaultArg(outputMax, 1); | |
/** | |
* @private | |
* @type {Tone.Multiply} | |
* @private | |
*/ | |
this._scale = this.input = new Tone.Multiply(1); | |
/** | |
* @private | |
* @type {Tone.Add} | |
* @private | |
*/ | |
this._add = this.output = new Tone.Add(0); | |
this._scale.connect(this._add); | |
this._setRange(); | |
}; | |
Tone.extend(Tone.Scale, Tone.SignalBase); | |
/** | |
* The minimum output value. This number is output when | |
* the value input value is 0. | |
* @memberOf Tone.Scale# | |
* @type {number} | |
* @name min | |
*/ | |
Object.defineProperty(Tone.Scale.prototype, 'min', { | |
get: function () { | |
return this._outputMin; | |
}, | |
set: function (min) { | |
this._outputMin = min; | |
this._setRange(); | |
} | |
}); | |
/** | |
* The maximum output value. This number is output when | |
* the value input value is 1. | |
* @memberOf Tone.Scale# | |
* @type {number} | |
* @name max | |
*/ | |
Object.defineProperty(Tone.Scale.prototype, 'max', { | |
get: function () { | |
return this._outputMax; | |
}, | |
set: function (max) { | |
this._outputMax = max; | |
this._setRange(); | |
} | |
}); | |
/** | |
* set the values | |
* @private | |
*/ | |
Tone.Scale.prototype._setRange = function () { | |
this._add.value = this._outputMin; | |
this._scale.value = this._outputMax - this._outputMin; | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.Scale} this | |
*/ | |
Tone.Scale.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._add.dispose(); | |
this._add = null; | |
this._scale.dispose(); | |
this._scale = null; | |
return this; | |
}; | |
return Tone.Scale; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Performs an exponential scaling on an input signal. | |
* Scales a NormalRange value [0,1] exponentially | |
* to the output range of outputMin to outputMax. | |
* | |
* @constructor | |
* @extends {Tone.SignalBase} | |
* @param {number} [outputMin=0] The output value when the input is 0. | |
* @param {number} [outputMax=1] The output value when the input is 1. | |
* @param {number} [exponent=2] The exponent which scales the incoming signal. | |
* @example | |
* var scaleExp = new Tone.ScaleExp(0, 100, 2); | |
* var signal = new Tone.Signal(0.5).connect(scaleExp); | |
*/ | |
Tone.ScaleExp = function (outputMin, outputMax, exponent) { | |
/** | |
* scale the input to the output range | |
* @type {Tone.Scale} | |
* @private | |
*/ | |
this._scale = this.output = new Tone.Scale(outputMin, outputMax); | |
/** | |
* @private | |
* @type {Tone.Pow} | |
* @private | |
*/ | |
this._exp = this.input = new Tone.Pow(this.defaultArg(exponent, 2)); | |
this._exp.connect(this._scale); | |
}; | |
Tone.extend(Tone.ScaleExp, Tone.SignalBase); | |
/** | |
* Instead of interpolating linearly between the <code>min</code> and | |
* <code>max</code> values, setting the exponent will interpolate between | |
* the two values with an exponential curve. | |
* @memberOf Tone.ScaleExp# | |
* @type {number} | |
* @name exponent | |
*/ | |
Object.defineProperty(Tone.ScaleExp.prototype, 'exponent', { | |
get: function () { | |
return this._exp.value; | |
}, | |
set: function (exp) { | |
this._exp.value = exp; | |
} | |
}); | |
/** | |
* The minimum output value. This number is output when | |
* the value input value is 0. | |
* @memberOf Tone.ScaleExp# | |
* @type {number} | |
* @name min | |
*/ | |
Object.defineProperty(Tone.ScaleExp.prototype, 'min', { | |
get: function () { | |
return this._scale.min; | |
}, | |
set: function (min) { | |
this._scale.min = min; | |
} | |
}); | |
/** | |
* The maximum output value. This number is output when | |
* the value input value is 1. | |
* @memberOf Tone.ScaleExp# | |
* @type {number} | |
* @name max | |
*/ | |
Object.defineProperty(Tone.ScaleExp.prototype, 'max', { | |
get: function () { | |
return this._scale.max; | |
}, | |
set: function (max) { | |
this._scale.max = max; | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.ScaleExp} this | |
*/ | |
Tone.ScaleExp.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._scale.dispose(); | |
this._scale = null; | |
this._exp.dispose(); | |
this._exp = null; | |
return this; | |
}; | |
return Tone.ScaleExp; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Comb filters are basic building blocks for physical modeling. Read more | |
* about comb filters on [CCRMA's website](https://ccrma.stanford.edu/~jos/pasp/Feedback_Comb_Filters.html). | |
* | |
* @extends {Tone} | |
* @constructor | |
* @param {Time|Object} [delayTime] The delay time of the filter. | |
* @param {NormalRange=} resonance The amount of feedback the filter has. | |
*/ | |
Tone.FeedbackCombFilter = function () { | |
Tone.call(this); | |
var options = this.optionsObject(arguments, [ | |
'delayTime', | |
'resonance' | |
], Tone.FeedbackCombFilter.defaults); | |
/** | |
* the delay node | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._delay = this.input = this.output = this.context.createDelay(1); | |
/** | |
* The amount of delay of the comb filter. | |
* @type {Time} | |
* @signal | |
*/ | |
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._readOnly([ | |
'resonance', | |
'delayTime' | |
]); | |
}; | |
Tone.extend(Tone.FeedbackCombFilter); | |
/** | |
* the default parameters | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.FeedbackCombFilter.defaults = { | |
'delayTime': 0.1, | |
'resonance': 0.5 | |
}; | |
/** | |
* clean up | |
* @returns {Tone.FeedbackCombFilter} this | |
*/ | |
Tone.FeedbackCombFilter.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._writable([ | |
'resonance', | |
'delayTime' | |
]); | |
this._delay.disconnect(); | |
this._delay = null; | |
this.delayTime.dispose(); | |
this.delayTime = null; | |
this.resonance.dispose(); | |
this.resonance = null; | |
this._feedback.disconnect(); | |
this._feedback = null; | |
return this; | |
}; | |
return Tone.FeedbackCombFilter; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Follower is a crude envelope follower which will follow | |
* the amplitude of an incoming signal. | |
* Take care with small (< 0.02) attack or decay values | |
* as follower has some ripple which is exaggerated | |
* at these values. Read more about envelope followers (also known | |
* as envelope detectors) on [Wikipedia](https://en.wikipedia.org/wiki/Envelope_detector). | |
* | |
* @constructor | |
* @extends {Tone} | |
* @param {Time|Object} [attack] The rate at which the follower rises. | |
* @param {Time=} release The rate at which the folower falls. | |
* @example | |
* var follower = new Tone.Follower(0.2, 0.4); | |
*/ | |
Tone.Follower = function () { | |
Tone.call(this); | |
var options = this.optionsObject(arguments, [ | |
'attack', | |
'release' | |
], Tone.Follower.defaults); | |
/** | |
* @type {Tone.Abs} | |
* @private | |
*/ | |
this._abs = new Tone.Abs(); | |
/** | |
* the lowpass filter which smooths the input | |
* @type {BiquadFilterNode} | |
* @private | |
*/ | |
this._filter = this.context.createBiquadFilter(); | |
this._filter.type = 'lowpass'; | |
this._filter.frequency.value = 0; | |
this._filter.Q.value = -100; | |
/** | |
* @type {WaveShaperNode} | |
* @private | |
*/ | |
this._frequencyValues = new Tone.WaveShaper(); | |
/** | |
* @type {Tone.Subtract} | |
* @private | |
*/ | |
this._sub = new Tone.Subtract(); | |
/** | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._delay = this.context.createDelay(); | |
this._delay.delayTime.value = this.blockTime; | |
/** | |
* this keeps it far from 0, even for very small differences | |
* @type {Tone.Multiply} | |
* @private | |
*/ | |
this._mult = new Tone.Multiply(10000); | |
/** | |
* @private | |
* @type {number} | |
*/ | |
this._attack = options.attack; | |
/** | |
* @private | |
* @type {number} | |
*/ | |
this._release = options.release; | |
//the smoothed signal to get the values | |
this.input.chain(this._abs, this._filter, this.output); | |
//the difference path | |
this._abs.connect(this._sub, 0, 1); | |
this._filter.chain(this._delay, this._sub); | |
//threshold the difference and use the thresh to set the frequency | |
this._sub.chain(this._mult, this._frequencyValues, this._filter.frequency); | |
//set the attack and release values in the table | |
this._setAttackRelease(this._attack, this._release); | |
}; | |
Tone.extend(Tone.Follower); | |
/** | |
* @static | |
* @type {Object} | |
*/ | |
Tone.Follower.defaults = { | |
'attack': 0.05, | |
'release': 0.5 | |
}; | |
/** | |
* sets the attack and release times in the wave shaper | |
* @param {Time} attack | |
* @param {Time} release | |
* @private | |
*/ | |
Tone.Follower.prototype._setAttackRelease = function (attack, release) { | |
var minTime = this.blockTime; | |
attack = this.secondsToFrequency(this.toSeconds(attack)); | |
release = this.secondsToFrequency(this.toSeconds(release)); | |
attack = Math.max(attack, minTime); | |
release = Math.max(release, minTime); | |
this._frequencyValues.setMap(function (val) { | |
if (val <= 0) { | |
return attack; | |
} else { | |
return release; | |
} | |
}); | |
}; | |
/** | |
* The attack time. | |
* @memberOf Tone.Follower# | |
* @type {Time} | |
* @name attack | |
*/ | |
Object.defineProperty(Tone.Follower.prototype, 'attack', { | |
get: function () { | |
return this._attack; | |
}, | |
set: function (attack) { | |
this._attack = attack; | |
this._setAttackRelease(this._attack, this._release); | |
} | |
}); | |
/** | |
* The release time. | |
* @memberOf Tone.Follower# | |
* @type {Time} | |
* @name release | |
*/ | |
Object.defineProperty(Tone.Follower.prototype, 'release', { | |
get: function () { | |
return this._release; | |
}, | |
set: function (release) { | |
this._release = release; | |
this._setAttackRelease(this._attack, this._release); | |
} | |
}); | |
/** | |
* Borrows the connect method from Signal so that the output can be used | |
* as a Tone.Signal control signal. | |
* @function | |
*/ | |
Tone.Follower.prototype.connect = Tone.Signal.prototype.connect; | |
/** | |
* dispose | |
* @returns {Tone.Follower} this | |
*/ | |
Tone.Follower.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.Follower; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.ScaledEnvelop is an envelope which can be scaled | |
* to any range. It's useful for applying an envelope | |
* to a frequency or any other non-NormalRange signal | |
* parameter. | |
* | |
* @extends {Tone.Envelope} | |
* @constructor | |
* @param {Time|Object} [attack] the attack time in seconds | |
* @param {Time} [decay] the decay time in seconds | |
* @param {number} [sustain] a percentage (0-1) of the full amplitude | |
* @param {Time} [release] the release time in seconds | |
* @example | |
* var scaledEnv = new Tone.ScaledEnvelope({ | |
* "attack" : 0.2, | |
* "min" : 200, | |
* "max" : 2000 | |
* }); | |
* scaledEnv.connect(oscillator.frequency); | |
*/ | |
Tone.ScaledEnvelope = function () { | |
//get all of the defaults | |
var options = this.optionsObject(arguments, [ | |
'attack', | |
'decay', | |
'sustain', | |
'release' | |
], Tone.Envelope.defaults); | |
Tone.Envelope.call(this, options); | |
options = this.defaultArg(options, Tone.ScaledEnvelope.defaults); | |
/** | |
* scale the incoming signal by an exponent | |
* @type {Tone.Pow} | |
* @private | |
*/ | |
this._exp = this.output = new Tone.Pow(options.exponent); | |
/** | |
* scale the signal to the desired range | |
* @type {Tone.Multiply} | |
* @private | |
*/ | |
this._scale = this.output = new Tone.Scale(options.min, options.max); | |
this._sig.chain(this._exp, this._scale); | |
}; | |
Tone.extend(Tone.ScaledEnvelope, Tone.Envelope); | |
/** | |
* the default parameters | |
* @static | |
*/ | |
Tone.ScaledEnvelope.defaults = { | |
'min': 0, | |
'max': 1, | |
'exponent': 1 | |
}; | |
/** | |
* The envelope's min output value. This is the value which it | |
* starts at. | |
* @memberOf Tone.ScaledEnvelope# | |
* @type {number} | |
* @name min | |
*/ | |
Object.defineProperty(Tone.ScaledEnvelope.prototype, 'min', { | |
get: function () { | |
return this._scale.min; | |
}, | |
set: function (min) { | |
this._scale.min = min; | |
} | |
}); | |
/** | |
* The envelope's max output value. In other words, the value | |
* at the peak of the attack portion of the envelope. | |
* @memberOf Tone.ScaledEnvelope# | |
* @type {number} | |
* @name max | |
*/ | |
Object.defineProperty(Tone.ScaledEnvelope.prototype, 'max', { | |
get: function () { | |
return this._scale.max; | |
}, | |
set: function (max) { | |
this._scale.max = max; | |
} | |
}); | |
/** | |
* The envelope's exponent value. | |
* @memberOf Tone.ScaledEnvelope# | |
* @type {number} | |
* @name exponent | |
*/ | |
Object.defineProperty(Tone.ScaledEnvelope.prototype, 'exponent', { | |
get: function () { | |
return this._exp.value; | |
}, | |
set: function (exp) { | |
this._exp.value = exp; | |
} | |
}); | |
/** | |
* clean up | |
* @returns {Tone.ScaledEnvelope} this | |
*/ | |
Tone.ScaledEnvelope.prototype.dispose = function () { | |
Tone.Envelope.prototype.dispose.call(this); | |
this._scale.dispose(); | |
this._scale = null; | |
this._exp.dispose(); | |
this._exp = null; | |
return this; | |
}; | |
return Tone.ScaledEnvelope; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.FrequencyEnvelope is a Tone.ScaledEnvelope, but instead of `min` and `max` | |
* it's got a `baseFrequency` and `octaves` parameter. | |
* | |
* @extends {Tone.Envelope} | |
* @constructor | |
* @param {Time|Object} [attack] the attack time in seconds | |
* @param {Time} [decay] the decay time in seconds | |
* @param {number} [sustain] a percentage (0-1) of the full amplitude | |
* @param {Time} [release] the release time in seconds | |
* @example | |
* var env = new Tone.FrequencyEnvelope({ | |
* "attack" : 0.2, | |
* "baseFrequency" : "C2", | |
* "octaves" : 4 | |
* }); | |
* scaledEnv.connect(oscillator.frequency); | |
*/ | |
Tone.FrequencyEnvelope = function () { | |
var options = this.optionsObject(arguments, [ | |
'attack', | |
'decay', | |
'sustain', | |
'release' | |
], Tone.Envelope.defaults); | |
Tone.ScaledEnvelope.call(this, options); | |
options = this.defaultArg(options, Tone.FrequencyEnvelope.defaults); | |
/** | |
* Stores the octave value | |
* @type {Positive} | |
* @private | |
*/ | |
this._octaves = options.octaves; | |
//setup | |
this.baseFrequency = options.baseFrequency; | |
this.octaves = options.octaves; | |
}; | |
Tone.extend(Tone.FrequencyEnvelope, Tone.Envelope); | |
/** | |
* the default parameters | |
* @static | |
*/ | |
Tone.FrequencyEnvelope.defaults = { | |
'baseFrequency': 200, | |
'octaves': 4, | |
'exponent': 2 | |
}; | |
/** | |
* The envelope's mininum output value. This is the value which it | |
* starts at. | |
* @memberOf Tone.FrequencyEnvelope# | |
* @type {Frequency} | |
* @name baseFrequency | |
*/ | |
Object.defineProperty(Tone.FrequencyEnvelope.prototype, 'baseFrequency', { | |
get: function () { | |
return this._scale.min; | |
}, | |
set: function (min) { | |
this._scale.min = this.toFrequency(min); | |
} | |
}); | |
/** | |
* The number of octaves above the baseFrequency that the | |
* envelope will scale to. | |
* @memberOf Tone.FrequencyEnvelope# | |
* @type {Positive} | |
* @name octaves | |
*/ | |
Object.defineProperty(Tone.FrequencyEnvelope.prototype, 'octaves', { | |
get: function () { | |
return this._octaves; | |
}, | |
set: function (octaves) { | |
this._octaves = octaves; | |
this._scale.max = this.baseFrequency * Math.pow(2, octaves); | |
} | |
}); | |
/** | |
* The envelope's exponent value. | |
* @memberOf Tone.FrequencyEnvelope# | |
* @type {number} | |
* @name exponent | |
*/ | |
Object.defineProperty(Tone.FrequencyEnvelope.prototype, 'exponent', { | |
get: function () { | |
return this._exp.value; | |
}, | |
set: function (exp) { | |
this._exp.value = exp; | |
} | |
}); | |
/** | |
* clean up | |
* @returns {Tone.FrequencyEnvelope} this | |
*/ | |
Tone.FrequencyEnvelope.prototype.dispose = function () { | |
Tone.ScaledEnvelope.prototype.dispose.call(this); | |
return this; | |
}; | |
return Tone.FrequencyEnvelope; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Gate only passes a signal through when the incoming | |
* signal exceeds a specified threshold. To do this, Gate uses | |
* a Tone.Follower to follow the amplitude of the incoming signal. | |
* A common implementation of this class is a [Noise Gate](https://en.wikipedia.org/wiki/Noise_gate). | |
* | |
* @constructor | |
* @extends {Tone} | |
* @param {Decibels|Object} [threshold] The threshold above which the gate will open. | |
* @param {Time=} attack The follower's attack time | |
* @param {Time=} release The follower's release time | |
* @example | |
* var gate = new Tone.Gate(-30, 0.2, 0.3).toMaster(); | |
* var mic = new Tone.Microphone().connect(gate); | |
* //the gate will only pass through the incoming | |
* //signal when it's louder than -30db | |
*/ | |
Tone.Gate = function () { | |
Tone.call(this); | |
var options = this.optionsObject(arguments, [ | |
'threshold', | |
'attack', | |
'release' | |
], Tone.Gate.defaults); | |
/** | |
* @type {Tone.Follower} | |
* @private | |
*/ | |
this._follower = new Tone.Follower(options.attack, options.release); | |
/** | |
* @type {Tone.GreaterThan} | |
* @private | |
*/ | |
this._gt = new Tone.GreaterThan(this.dbToGain(options.threshold)); | |
//the connections | |
this.input.connect(this.output); | |
//the control signal | |
this.input.chain(this._gt, this._follower, this.output.gain); | |
}; | |
Tone.extend(Tone.Gate); | |
/** | |
* @const | |
* @static | |
* @type {Object} | |
*/ | |
Tone.Gate.defaults = { | |
'attack': 0.1, | |
'release': 0.1, | |
'threshold': -40 | |
}; | |
/** | |
* The threshold of the gate in decibels | |
* @memberOf Tone.Gate# | |
* @type {Decibels} | |
* @name threshold | |
*/ | |
Object.defineProperty(Tone.Gate.prototype, 'threshold', { | |
get: function () { | |
return this.gainToDb(this._gt.value); | |
}, | |
set: function (thresh) { | |
this._gt.value = this.dbToGain(thresh); | |
} | |
}); | |
/** | |
* The attack speed of the gate | |
* @memberOf Tone.Gate# | |
* @type {Time} | |
* @name attack | |
*/ | |
Object.defineProperty(Tone.Gate.prototype, 'attack', { | |
get: function () { | |
return this._follower.attack; | |
}, | |
set: function (attackTime) { | |
this._follower.attack = attackTime; | |
} | |
}); | |
/** | |
* The release speed of the gate | |
* @memberOf Tone.Gate# | |
* @type {Time} | |
* @name release | |
*/ | |
Object.defineProperty(Tone.Gate.prototype, 'release', { | |
get: function () { | |
return this._follower.release; | |
}, | |
set: function (releaseTime) { | |
this._follower.release = releaseTime; | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.Gate} this | |
*/ | |
Tone.Gate.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._follower.dispose(); | |
this._gt.dispose(); | |
this._follower = null; | |
this._gt = null; | |
return this; | |
}; | |
return Tone.Gate; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class A Timeline State. Provides the methods: <code>setStateAtTime("state", time)</code> | |
* and <code>getStateAtTime(time)</code>. | |
* | |
* @extends {Tone.Timeline} | |
* @param {String} initial The initial state of the TimelineState. | |
* Defaults to <code>undefined</code> | |
*/ | |
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) { | |
/** | |
* @class A sample accurate clock which provides a callback at the given rate. | |
* 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 by itself since you can synchronize multiple callbacks. | |
* | |
* @constructor | |
* @extends {Tone} | |
* @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(function(time){ | |
* console.log(time); | |
* }, 1); | |
*/ | |
Tone.Clock = function () { | |
var options = this.optionsObject(arguments, [ | |
'callback', | |
'frequency' | |
], Tone.Clock.defaults); | |
/** | |
* 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._computedLookAhead = 1 / 60; | |
/** | |
* The value afterwhich events are thrown out | |
* @type {Number} | |
* @private | |
*/ | |
this._threshold = 0.5; | |
/** | |
* 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.TimelineSignal(options.frequency, Tone.Type.Frequency); | |
/** | |
* 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._state = new Tone.TimelineState(Tone.State.Stopped); | |
/** | |
* A pre-binded loop function to save a tiny bit of overhead | |
* of rebinding the function on every frame. | |
* @type {Function} | |
* @private | |
*/ | |
this._boundLoop = this._loop.bind(this); | |
this._readOnly('frequency'); | |
//start the loop | |
this._loop(); | |
}; | |
Tone.extend(Tone.Clock); | |
/** | |
* The defaults | |
* @const | |
* @type {Object} | |
*/ | |
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. 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) { | |
time = this.toSeconds(time); | |
if (this._state.getStateAtTime(time) !== Tone.State.Stopped) { | |
this._state.setStateAtTime(Tone.State.Stopped, time); | |
} | |
return this; | |
}; | |
/** | |
* 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.pause = function (time) { | |
time = this.toSeconds(time); | |
if (this._state.getStateAtTime(time) === Tone.State.Started) { | |
this._state.setStateAtTime(Tone.State.Paused, time); | |
} | |
return this; | |
}; | |
/** | |
* 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 () { | |
cancelAnimationFrame(this._loopID); | |
Tone.TimelineState.prototype.dispose.call(this); | |
this._writable('frequency'); | |
this.frequency.dispose(); | |
this.frequency = null; | |
this._boundLoop = Tone.noOp; | |
this._nextTick = Infinity; | |
this.callback = null; | |
this._state.dispose(); | |
this._state = null; | |
}; | |
return Tone.Clock; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Emitter 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.Emitter = function () { | |
/** | |
* Contains all of the events. | |
* @private | |
* @type {Object} | |
*/ | |
this._events = {}; | |
}; | |
Tone.extend(Tone.Emitter); | |
/** | |
* 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.Emitter} this | |
*/ | |
Tone.Emitter.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.Emitter.on. | |
* If no callback is given, all callbacks | |
* events are removed. | |
* @return {Tone.Emitter} this | |
*/ | |
Tone.Emitter.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 (Tone.prototype.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.Emitter} this | |
*/ | |
Tone.Emitter.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 Emitter functions (on/off/trigger) to the object | |
* @param {Object|Function} object The object or class to extend. | |
*/ | |
Tone.Emitter.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.Emitter.prototype[func]; | |
object[func] = emitterFunc; | |
} | |
}; | |
/** | |
* Clean up | |
* @return {Tone.Emitter} this | |
*/ | |
Tone.Emitter.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._events = null; | |
return this; | |
}; | |
return Tone.Emitter; | |
}); | |
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++) { | |
var ev = allNodes[i].event; | |
if (ev) { | |
callback(ev); | |
} | |
} | |
} | |
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) { | |
time = this.toSeconds(time); | |
if (this._root !== null) { | |
var results = []; | |
this._root.search(time, results); | |
for (var i = results.length - 1; i >= 0; i--) { | |
var ev = results[i].event; | |
if (ev) { | |
callback(ev); | |
} | |
} | |
} | |
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) { | |
time = this.toSeconds(time); | |
if (this._root !== null) { | |
var results = []; | |
this._root.searchAfter(time, results); | |
for (var i = results.length - 1; i >= 0; i--) { | |
var ev = results[i].event; | |
if (ev) { | |
callback(ev); | |
} | |
} | |
} | |
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. <br><br> | |
* A single transport is created for you when the library is initialized. | |
* <br><br> | |
* The transport emits the events: "start", "stop", "pause", and "loop" which are | |
* called with the time of that event as the argument. | |
* | |
* @extends {Tone.Emitter} | |
* @singleton | |
* @example | |
* //repeated event every 8th note | |
* Tone.Transport.setInterval(function(time){ | |
* //do something with the time | |
* }, "8n"); | |
* @example | |
* //one time event 1 second in the future | |
* Tone.Transport.setTimeout(function(time){ | |
* //do something with the time | |
* }, 1); | |
* @example | |
* //event fixed to the Transports timeline. | |
* Tone.Transport.setTimeline(function(time){ | |
* //do something with the time | |
* }, "16:0:0"); | |
*/ | |
Tone.Transport = function () { | |
Tone.Emitter.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} | |
* @signal | |
* @example | |
* Tone.Transport.bpm.value = 80; | |
* //ramp the bpm to 120 over 10 seconds | |
* Tone.Transport.bpm.rampTo(120, 10); | |
*/ | |
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 time signature, or more accurately the numerator | |
* of the time signature over a denominator of 4. | |
* @type {Number} | |
* @private | |
*/ | |
this._timeSignature = TransportConstructor.defaults.timeSignature; | |
/////////////////////////////////////////////////////////////////////// | |
// TIMELINE EVENTS | |
////////////////////////////////////////////////////////////////////// | |
/** | |
* All the events in an object to keep track by ID | |
* @type {Object} | |
* @private | |
*/ | |
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 | |
////////////////////////////////////////////////////////////////////// | |
var swingSeconds = this.notationToSeconds(TransportConstructor.defaults.swingSubdivision, TransportConstructor.defaults.bpm, TransportConstructor.defaults.timeSignature); | |
/** | |
* The subdivision of the swing | |
* @type {Ticks} | |
* @private | |
*/ | |
this._swingTicks = swingSeconds / (60 / TransportConstructor.defaults.bpm) * this._ppq; | |
/** | |
* The swing amount | |
* @type {NormalRange} | |
* @private | |
*/ | |
this._swingAmount = 0; | |
}; | |
Tone.extend(Tone.Transport, Tone.Emitter); | |
/** | |
* the defaults | |
* @type {Object} | |
* @const | |
* @static | |
*/ | |
Tone.Transport.defaults = { | |
'bpm': 120, | |
'swing': 0, | |
'swingSubdivision': '16n', | |
'timeSignature': 4, | |
'loopStart': 0, | |
'loopEnd': '4m', | |
'PPQ': 48 | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// TICKS | |
/////////////////////////////////////////////////////////////////////////////// | |
/** | |
* called on every tick | |
* @param {number} tickTime clock relative tick time | |
* @private | |
*/ | |
Tone.Transport.prototype._processTick = function (tickTime) { | |
//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); | |
} | |
} | |
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); | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// SCHEDULABLE EVENTS | |
/////////////////////////////////////////////////////////////////////////////// | |
/** | |
* Schedule an event along the timeline. | |
* @param {Function} callback The callback to be invoked at the time. | |
* @param {Time} time The time to invoke the callback at. | |
* @return {Number} The id of the event which can be used for canceling the event. | |
* @example | |
* //trigger the callback when the Transport reaches the desired time | |
* Tone.Transport.schedule(function(time){ | |
* envelope.triggerAttack(time); | |
* }, "128i"); | |
*/ | |
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; | |
}; | |
/** | |
* Schedule a repeated event along the timeline. The event will fire | |
* at the `interval` starting at the `startTime` and for the specified | |
* `duration`. | |
* @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. | |
* @example | |
* //a callback invoked every eighth note after the first measure | |
* Tone.Transport.scheduleRepeat(callback, "8n", "1m"); | |
*/ | |
Tone.Transport.prototype.scheduleRepeat = function (callback, interval, startTime, duration) { | |
if (interval <= 0) { | |
throw new Error('repeat events must have an interval larger than 0'); | |
} | |
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; | |
}; | |
/** | |
* 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.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; | |
}; | |
/** | |
* Clear the passed in event id from the timeline | |
* @param {Number} eventId The id of the event. | |
* @returns {Tone.Transport} this | |
*/ | |
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()]; | |
} | |
return this; | |
}; | |
/** | |
* 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.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; | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// QUANTIZATION | |
/////////////////////////////////////////////////////////////////////////////// | |
/** | |
* Returns the time closest time (equal to or after the given time) that aligns | |
* to the subidivision. | |
* @param {Time} time The time value to quantize to the given subdivision | |
* @param {String} [subdivision="4n"] The subdivision to quantize to. | |
* @return {Number} the time in seconds until the next subdivision. | |
* @example | |
* Tone.Transport.bpm.value = 120; | |
* Tone.Transport.quantize("3 * 4n", "1m"); //return 0.5 | |
* //if the clock is started, it will return a value less than 0.5 | |
*/ | |
Tone.Transport.prototype.quantize = function (time, subdivision) { | |
subdivision = this.defaultArg(subdivision, '4n'); | |
var tickTime = this.toTicks(time); | |
subdivision = this.toTicks(subdivision); | |
var remainingTicks = subdivision - tickTime % subdivision; | |
if (remainingTicks === subdivision) { | |
remainingTicks = 0; | |
} | |
var now = this.now(); | |
if (this.state === Tone.State.Started) { | |
now = this._clock._nextTick; | |
} | |
return this.toSeconds(time, now) + this.ticksToSeconds(remainingTicks); | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// START/STOP/PAUSE | |
/////////////////////////////////////////////////////////////////////////////// | |
/** | |
* Returns the playback state of the source, either "started", "stopped", or "paused" | |
* @type {Tone.State} | |
* @readOnly | |
* @memberOf Tone.Transport# | |
* @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. | |
* @param {Time=} offset The timeline offset to start the transport. | |
* @returns {Tone.Transport} this | |
* @example | |
* //start the transport in one second starting at beginning of the 5th measure. | |
* Tone.Transport.start("+1", "4:0:0"); | |
*/ | |
Tone.Transport.prototype.start = function (time, offset) { | |
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; | |
}; | |
/** | |
* Stop the transport and all sources synced to the transport. | |
* @param {Time} [time=now] The time when the transport should stop. | |
* @returns {Tone.Transport} this | |
* @example | |
* Tone.Transport.stop(); | |
*/ | |
Tone.Transport.prototype.stop = function (time) { | |
time = this.toSeconds(time); | |
this._clock.stop(time); | |
this.trigger('stop', time); | |
return this; | |
}; | |
/** | |
* Pause the transport and all sources synced to the transport. | |
* @param {Time} [time=now] | |
* @returns {Tone.Transport} this | |
*/ | |
Tone.Transport.prototype.pause = function (time) { | |
time = this.toSeconds(time); | |
this._clock.pause(time); | |
this.trigger('pause', time); | |
return this; | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// SETTERS/GETTERS | |
/////////////////////////////////////////////////////////////////////////////// | |
/** | |
* The time signature as just the numerator over 4. | |
* For example 4/4 would be just 4 and 6/8 would be 3. | |
* @memberOf Tone.Transport# | |
* @type {Number|Array} | |
* @name timeSignature | |
* @example | |
* //common time | |
* Tone.Transport.timeSignature = 4; | |
* // 7/8 | |
* Tone.Transport.timeSignature = [7, 8]; | |
* //this will be reduced to a single number | |
* Tone.Transport.timeSignature; //returns 3.5 | |
*/ | |
Object.defineProperty(Tone.Transport.prototype, 'timeSignature', { | |
get: function () { | |
return this._timeSignature; | |
}, | |
set: function (timeSig) { | |
if (this.isArray(timeSig)) { | |
timeSig = timeSig[0] / timeSig[1] * 4; | |
} | |
this._timeSignature = timeSig; | |
} | |
}); | |
/** | |
* When the Tone.Transport.loop = true, this is the starting position of the loop. | |
* @memberOf Tone.Transport# | |
* @type {Time} | |
* @name loopStart | |
*/ | |
Object.defineProperty(Tone.Transport.prototype, 'loopStart', { | |
get: function () { | |
return this.ticksToSeconds(this._loopStart); | |
}, | |
set: function (startPosition) { | |
this._loopStart = this.toTicks(startPosition); | |
} | |
}); | |
/** | |
* When the Tone.Transport.loop = true, this is the ending position of the loop. | |
* @memberOf Tone.Transport# | |
* @type {Time} | |
* @name loopEnd | |
*/ | |
Object.defineProperty(Tone.Transport.prototype, 'loopEnd', { | |
get: function () { | |
return this.ticksToSeconds(this._loopEnd); | |
}, | |
set: function (endPosition) { | |
this._loopEnd = this.toTicks(endPosition); | |
} | |
}); | |
/** | |
* Set the loop start and stop at the same time. | |
* @param {Time} startPosition | |
* @param {Time} endPosition | |
* @returns {Tone.Transport} this | |
* @example | |
* //loop over the first measure | |
* Tone.Transport.setLoopPoints(0, "1m"); | |
* Tone.Transport.loop = true; | |
*/ | |
Tone.Transport.prototype.setLoopPoints = function (startPosition, endPosition) { | |
this.loopStart = startPosition; | |
this.loopEnd = endPosition; | |
return this; | |
}; | |
/** | |
* The swing value. Between 0-1 where 1 equal to | |
* the note + half the subdivision. | |
* @memberOf Tone.Transport# | |
* @type {NormalRange} | |
* @name swing | |
*/ | |
Object.defineProperty(Tone.Transport.prototype, 'swing', { | |
get: function () { | |
return this._swingAmount * 2; | |
}, | |
set: function (amount) { | |
//scale the values to a normal range | |
this._swingAmount = amount * 0.5; | |
} | |
}); | |
/** | |
* Set the subdivision which the swing will be applied to. | |
* The default values is a 16th note. Value must be less | |
* than a quarter note. | |
* | |
* @memberOf Tone.Transport# | |
* @type {Time} | |
* @name swingSubdivision | |
*/ | |
Object.defineProperty(Tone.Transport.prototype, 'swingSubdivision', { | |
get: function () { | |
return this.toNotation(this._swingTicks + 'i'); | |
}, | |
set: function (subdivision) { | |
this._swingTicks = this.toTicks(subdivision); | |
} | |
}); | |
/** | |
* The Transport's position in MEASURES:BEATS:SIXTEENTHS. | |
* Setting the value will jump to that position right away. | |
* | |
* @memberOf Tone.Transport# | |
* @type {TransportTime} | |
* @name position | |
*/ | |
Object.defineProperty(Tone.Transport.prototype, 'position', { | |
get: function () { | |
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, | |
sixteenths | |
]; | |
return progress.join(':'); | |
}, | |
set: function (progress) { | |
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 | |
/////////////////////////////////////////////////////////////////////////////// | |
/** | |
* Attaches the signal to the tempo control signal so that | |
* any changes in the tempo will change the signal in the same | |
* ratio. | |
* | |
* @param {Tone.Signal} signal | |
* @param {number=} ratio Optionally pass in the ratio between | |
* the two signals. Otherwise it will be computed | |
* based on their current values. | |
* @returns {Tone.Transport} this | |
*/ | |
Tone.Transport.prototype.syncSignal = function (signal, ratio) { | |
if (!ratio) { | |
//get the sync ratio | |
if (signal._param.value !== 0) { | |
ratio = signal._param.value / this.bpm._param.value; | |
} else { | |
ratio = 0; | |
} | |
} | |
var ratioSignal = new Tone.Gain(ratio); | |
this.bpm.chain(ratioSignal, signal._param); | |
this._syncedSignals.push({ | |
'ratio': ratioSignal, | |
'signal': signal, | |
'initial': signal._param.value | |
}); | |
signal._param.value = 0; | |
return this; | |
}; | |
/** | |
* Unsyncs a previously synced signal from the transport's control. | |
* See Tone.Transport.syncSignal. | |
* @param {Tone.Signal} signal | |
* @returns {Tone.Transport} this | |
*/ | |
Tone.Transport.prototype.unsyncSignal = function (signal) { | |
for (var i = this._syncedSignals.length - 1; i >= 0; i--) { | |
var syncedSignal = this._syncedSignals[i]; | |
if (syncedSignal.signal === signal) { | |
syncedSignal.ratio.dispose(); | |
syncedSignal.signal._param.value = syncedSignal.initial; | |
this._syncedSignals.splice(i, 1); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.Transport} this | |
* @private | |
*/ | |
Tone.Transport.prototype.dispose = function () { | |
Tone.Emitter.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; | |
return this; | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// DEPRECATED FUNCTIONS | |
// (will be removed in r7) | |
/////////////////////////////////////////////////////////////////////////////// | |
/** | |
* @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"); | |
* @private | |
*/ | |
Tone.Transport.prototype.setInterval = function (callback, interval) { | |
console.warn('This method is deprecated. Use Tone.Transport.scheduleRepeat instead.'); | |
return Tone.Transport.scheduleRepeat(callback, interval); | |
}; | |
/** | |
* @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 | |
* @private | |
*/ | |
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. | |
* | |
* @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) | |
* @private | |
*/ | |
Tone.Transport.prototype.setTimeout = function (callback, timeout) { | |
console.warn('This method is deprecated. Use Tone.Transport.scheduleOnce instead.'); | |
return Tone.Transport.scheduleOnce(callback, timeout); | |
}; | |
/** | |
* @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 | |
* @private | |
*/ | |
Tone.Transport.prototype.clearTimeout = function (id) { | |
console.warn('This method is deprecated. Use Tone.Transport.clear instead.'); | |
return Tone.Transport.clear(id); | |
}; | |
/** | |
* @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"); | |
* @private | |
*/ | |
Tone.Transport.prototype.setTimeline = function (callback, time) { | |
console.warn('This method is deprecated. Use Tone.Transport.schedule instead.'); | |
return Tone.Transport.schedule(callback, time); | |
}; | |
/** | |
* @deprecated Use Tone.Note instead. | |
* Clear the timeline event. | |
* @param {number} id | |
* @return {boolean} true if it was removed | |
* @private | |
*/ | |
Tone.Transport.prototype.clearTimeline = function (id) { | |
console.warn('This method is deprecated. Use Tone.Transport.clear instead.'); | |
return Tone.Transport.clear(id); | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// INITIALIZATION | |
/////////////////////////////////////////////////////////////////////////////// | |
var TransportConstructor = Tone.Transport; | |
Tone._initAudioContext(function () { | |
if (typeof Tone.Transport === 'function') { | |
//a single transport object | |
Tone.Transport = new Tone.Transport(); | |
} else { | |
//stop the clock | |
Tone.Transport.stop(); | |
//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 previous config | |
Tone.Transport.set(prevSettings); | |
} | |
}); | |
return Tone.Transport; | |
}); | |
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 () { | |
var options = this.optionsObject(arguments, ['volume'], Tone.Volume.defaults); | |
/** | |
* the output node | |
* @type {GainNode} | |
* @private | |
*/ | |
this.output = this.input = new Tone.Gain(options.volume, Tone.Type.Decibels); | |
/** | |
* The volume control in decibels. | |
* @type {Decibels} | |
* @signal | |
*/ | |
this.volume = this.output.gain; | |
this._readOnly('volume'); | |
}; | |
Tone.extend(Tone.Volume); | |
/** | |
* Defaults | |
* @type {Object} | |
* @const | |
* @static | |
*/ | |
Tone.Volume.defaults = { 'volume': 0 }; | |
/** | |
* clean up | |
* @returns {Tone.Volume} this | |
*/ | |
Tone.Volume.prototype.dispose = function () { | |
this.input.dispose(); | |
Tone.prototype.dispose.call(this); | |
this._writable('volume'); | |
this.volume.dispose(); | |
this.volume = null; | |
return this; | |
}; | |
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. | |
* | |
* @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) { | |
//Sources only have an output and no input | |
Tone.call(this); | |
options = this.defaultArg(options, Tone.Source.defaults); | |
/** | |
* The output volume node | |
* @type {Tone.Volume} | |
* @private | |
*/ | |
this._volume = this.output = new Tone.Volume(options.volume); | |
/** | |
* The volume of the output in decibels. | |
* @type {Decibels} | |
* @signal | |
* @example | |
* source.volume.value = -6; | |
*/ | |
this.volume = this._volume.volume; | |
this._readOnly('volume'); | |
/** | |
* Keep track of the scheduled state. | |
* @type {Tone.TimelineState} | |
* @private | |
*/ | |
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._volume.output.output.channelCount = 2; | |
this._volume.output.output.channelCountMode = 'explicit'; | |
}; | |
Tone.extend(Tone.Source); | |
/** | |
* The default parameters | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Source.defaults = { 'volume': 0 }; | |
/** | |
* Returns the playback state of the source, either "started" or "stopped". | |
* @type {Tone.State} | |
* @readOnly | |
* @memberOf Tone.Source# | |
* @name state | |
*/ | |
Object.defineProperty(Tone.Source.prototype, 'state', { | |
get: function () { | |
return this._state.getStateAtTime(this.now()); | |
} | |
}); | |
/** | |
* Start the source at the specified time. If no time is given, | |
* start the source now. | |
* @param {Time} [time=now] When the source should be started. | |
* @returns {Tone.Source} this | |
* @example | |
* source.start("+0.5"); //starts the source 0.5 seconds from now | |
*/ | |
Tone.Source.prototype.start = function (time) { | |
time = this.toSeconds(time); | |
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; | |
}; | |
/** | |
* Stop the source at the specified time. If no time is given, | |
* stop the source now. | |
* @param {Time} [time=now] When the source should be stopped. | |
* @returns {Tone.Source} this | |
* @example | |
* source.stop(); // stops the source immediately | |
*/ | |
Tone.Source.prototype.stop = function (time) { | |
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); | |
} | |
} | |
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 | |
* or paused, so is the source. | |
* | |
* @param {Time} [delay=0] Delay time before starting the source after the | |
* Transport has started. | |
* @returns {Tone.Source} this | |
* @example | |
* //sync the source to start 1 measure after the transport starts | |
* source.sync("1m"); | |
* //start the transport. the source will start 1 measure later. | |
* Tone.Transport.start(); | |
*/ | |
Tone.Source.prototype.sync = function (delay) { | |
this._startDelay = this.defaultArg(delay, 0); | |
Tone.Transport.on('start', this._syncStart); | |
Tone.Transport.on('stop pause', this._syncStop); | |
return this; | |
}; | |
/** | |
* Unsync the source to the Transport. See Tone.Source.sync | |
* @returns {Tone.Source} this | |
*/ | |
Tone.Source.prototype.unsync = function () { | |
this._startDelay = 0; | |
Tone.Transport.off('start', this._syncStart); | |
Tone.Transport.off('stop pause', this._syncStop); | |
return this; | |
}; | |
/** | |
* Clean up. | |
* @return {Tone.Source} this | |
*/ | |
Tone.Source.prototype.dispose = function () { | |
this.stop(); | |
Tone.prototype.dispose.call(this); | |
this.unsync(); | |
this._writable('volume'); | |
this._volume.dispose(); | |
this._volume = null; | |
this.volume = null; | |
this._state.dispose(); | |
this._state = null; | |
this._syncStart = null; | |
this._syncStart = null; | |
}; | |
return Tone.Source; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Oscillator supports a number of features including | |
* phase rotation, multiple oscillator types (see Tone.Oscillator.type), | |
* and Transport syncing (see Tone.Oscillator.syncFrequency). | |
* | |
* @constructor | |
* @extends {Tone.Source} | |
* @param {Frequency} [frequency] Starting frequency | |
* @param {string} [type] The oscillator type. Read more about type below. | |
* @example | |
* //make and start a 440hz sine tone | |
* var osc = new Tone.Oscillator(440, "sine").toMaster().start(); | |
*/ | |
Tone.Oscillator = function () { | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'type' | |
], Tone.Oscillator.defaults); | |
Tone.Source.call(this, options); | |
/** | |
* the main oscillator | |
* @type {OscillatorNode} | |
* @private | |
*/ | |
this._oscillator = null; | |
/** | |
* The frequency control. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency); | |
/** | |
* The detune control signal. | |
* @type {Cents} | |
* @signal | |
*/ | |
this.detune = new Tone.Signal(options.detune, Tone.Type.Cents); | |
/** | |
* the periodic wave | |
* @type {PeriodicWave} | |
* @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 | |
* @type {number} | |
* @private | |
*/ | |
this._phase = options.phase; | |
/** | |
* the type of the oscillator | |
* @type {string} | |
* @private | |
*/ | |
this._type = null; | |
//setup | |
this.type = options.type; | |
this.phase = this._phase; | |
this._readOnly([ | |
'frequency', | |
'detune' | |
]); | |
}; | |
Tone.extend(Tone.Oscillator, Tone.Source); | |
/** | |
* the default parameters | |
* @type {Object} | |
*/ | |
Tone.Oscillator.defaults = { | |
'type': 'sine', | |
'frequency': 440, | |
'detune': 0, | |
'phase': 0, | |
'partials': [] | |
}; | |
/** | |
* 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] | |
* @private | |
*/ | |
Tone.Oscillator.prototype._start = function (time) { | |
//new oscillator with previous values | |
this._oscillator = this.context.createOscillator(); | |
this._oscillator.setPeriodicWave(this._wave); | |
//connect the control signal to the oscillator frequency & detune | |
this._oscillator.connect(this.output); | |
this.frequency.connect(this._oscillator.frequency); | |
this.detune.connect(this._oscillator.detune); | |
//start the oscillator | |
this._oscillator.start(this.toSeconds(time)); | |
}; | |
/** | |
* stop the oscillator | |
* @private | |
* @param {Time} [time=now] (optional) timing parameter | |
* @returns {Tone.Oscillator} this | |
*/ | |
Tone.Oscillator.prototype._stop = function (time) { | |
if (this._oscillator) { | |
this._oscillator.stop(this.toSeconds(time)); | |
this._oscillator = null; | |
} | |
return this; | |
}; | |
/** | |
* Sync the signal to the Transport's bpm. Any changes to the transports bpm, | |
* will also affect the oscillators frequency. | |
* @returns {Tone.Oscillator} this | |
* @example | |
* Tone.Transport.bpm.value = 120; | |
* osc.frequency.value = 440; | |
* //the ration between the bpm and the frequency will be maintained | |
* osc.syncFrequency(); | |
* Tone.Transport.bpm.value = 240; | |
* // the frequency of the oscillator is doubled to 880 | |
*/ | |
Tone.Oscillator.prototype.syncFrequency = function () { | |
Tone.Transport.syncSignal(this.frequency); | |
return this; | |
}; | |
/** | |
* Unsync the oscillator's frequency from the Transport. | |
* See Tone.Oscillator.syncFrequency | |
* @returns {Tone.Oscillator} this | |
*/ | |
Tone.Oscillator.prototype.unsyncFrequency = function () { | |
Tone.Transport.unsyncSignal(this.frequency); | |
return this; | |
}; | |
/** | |
* The type of the oscillator: either sine, square, triangle, or sawtooth. Also capable of | |
* setting the first x number of partials of the oscillator. For example: "sine4" would | |
* set be the first 4 partials of the sine wave and "triangle8" would set the first | |
* 8 partials of the triangle wave. | |
* <br><br> | |
* Uses PeriodicWave internally even for native types so that it can set the phase. | |
* PeriodicWave equations are from the | |
* [Webkit Web Audio implementation](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/modules/webaudio/PeriodicWave.cpp&sq=package:chromium). | |
* | |
* @memberOf Tone.Oscillator# | |
* @type {string} | |
* @name type | |
* @example | |
* //set it to a square wave | |
* osc.type = "square"; | |
* @example | |
* //set the first 6 partials of a sawtooth wave | |
* osc.type = "sawtooth6"; | |
*/ | |
Object.defineProperty(Tone.Oscillator.prototype, 'type', { | |
get: function () { | |
return this._type; | |
}, | |
set: function (type) { | |
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 = 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; | |
} | |
}); | |
/** | |
* The phase of the oscillator in degrees. | |
* @memberOf Tone.Oscillator# | |
* @type {Degrees} | |
* @name phase | |
* @example | |
* osc.phase = 180; //flips the phase of the oscillator | |
*/ | |
Object.defineProperty(Tone.Oscillator.prototype, 'phase', { | |
get: function () { | |
return this._phase * (180 / Math.PI); | |
}, | |
set: function (phase) { | |
this._phase = phase * Math.PI / 180; | |
//reset the type | |
this.type = this._type; | |
} | |
}); | |
/** | |
* Dispose and disconnect. | |
* @return {Tone.Oscillator} this | |
*/ | |
Tone.Oscillator.prototype.dispose = function () { | |
Tone.Source.prototype.dispose.call(this); | |
if (this._oscillator !== null) { | |
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; | |
return this; | |
}; | |
return Tone.Oscillator; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class LFO stands for low frequency oscillator. Tone.LFO produces an output signal | |
* which can be attached to an AudioParam or Tone.Signal | |
* in order to modulate that parameter with an oscillator. The LFO can | |
* also be synced to the transport to start/stop and change when the tempo changes. | |
* | |
* @constructor | |
* @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. | |
* @param {number=} max The maximum value of the LFO. | |
* @example | |
* var lfo = new Tone.LFO("4n", 400, 4000); | |
* lfo.connect(filter.frequency); | |
*/ | |
Tone.LFO = function () { | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'min', | |
'max' | |
], Tone.LFO.defaults); | |
/** | |
* The oscillator. | |
* @type {Tone.Oscillator} | |
* @private | |
*/ | |
this._oscillator = new Tone.Oscillator({ | |
'frequency': options.frequency, | |
'type': options.type | |
}); | |
/** | |
* the lfo's frequency | |
* @type {Frequency} | |
* @signal | |
*/ | |
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 | |
* is 10, setting the amplitude to 0.5 would make the LFO modulate | |
* between -5 and 5. | |
* @type {Number} | |
* @signal | |
*/ | |
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 | |
*/ | |
this._a2g = new Tone.AudioToGain(); | |
/** | |
* @type {Tone.Scale} | |
* @private | |
*/ | |
this._scaler = this.output = new Tone.Scale(options.min, options.max); | |
/** | |
* the units of the LFO (used for converting) | |
* @type {Tone.Type} | |
* @private | |
*/ | |
this._units = Tone.Type.Default; | |
this.units = options.units; | |
//connect it up | |
this._oscillator.chain(this._a2g, this._scaler); | |
this._stoppedSignal.connect(this._a2g); | |
this._readOnly([ | |
'amplitude', | |
'frequency' | |
]); | |
this.phase = options.phase; | |
}; | |
Tone.extend(Tone.LFO, Tone.Oscillator); | |
/** | |
* the default parameters | |
* | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.LFO.defaults = { | |
'type': 'sine', | |
'min': 0, | |
'max': 1, | |
'phase': 0, | |
'frequency': '4n', | |
'amplitude': 1, | |
'units': Tone.Type.Default | |
}; | |
/** | |
* Start the LFO. | |
* @param {Time} [time=now] the time the LFO will start | |
* @returns {Tone.LFO} this | |
*/ | |
Tone.LFO.prototype.start = function (time) { | |
time = this.toSeconds(time); | |
this._stoppedSignal.setValueAtTime(0, time); | |
this._oscillator.start(time); | |
return this; | |
}; | |
/** | |
* Stop the LFO. | |
* @param {Time} [time=now] the time the LFO will stop | |
* @returns {Tone.LFO} this | |
*/ | |
Tone.LFO.prototype.stop = function (time) { | |
time = this.toSeconds(time); | |
this._stoppedSignal.setValueAtTime(this._stoppedValue, time); | |
this._oscillator.stop(time); | |
return this; | |
}; | |
/** | |
* Sync the start/stop/pause to the transport | |
* and the frequency to the bpm of the transport | |
* | |
* @param {Time} [delay=0] the time to delay the start of the | |
* LFO from the start of the transport | |
* @returns {Tone.LFO} this | |
* @example | |
* lfo.frequency.value = "8n"; | |
* lfo.sync(); | |
* //the rate of the LFO will always be an eighth note, | |
* //even as the tempo changes | |
*/ | |
Tone.LFO.prototype.sync = function (delay) { | |
this._oscillator.sync(delay); | |
this._oscillator.syncFrequency(); | |
return this; | |
}; | |
/** | |
* unsync the LFO from transport control | |
* @returns {Tone.LFO} this | |
*/ | |
Tone.LFO.prototype.unsync = function () { | |
this._oscillator.unsync(); | |
this._oscillator.unsyncFrequency(); | |
return this; | |
}; | |
/** | |
* The miniumum output of the LFO. | |
* @memberOf Tone.LFO# | |
* @type {number} | |
* @name min | |
*/ | |
Object.defineProperty(Tone.LFO.prototype, 'min', { | |
get: function () { | |
return this._toUnits(this._scaler.min); | |
}, | |
set: function (min) { | |
min = this._fromUnits(min); | |
this._scaler.min = min; | |
} | |
}); | |
/** | |
* The maximum output of the LFO. | |
* @memberOf Tone.LFO# | |
* @type {number} | |
* @name max | |
*/ | |
Object.defineProperty(Tone.LFO.prototype, 'max', { | |
get: function () { | |
return this._toUnits(this._scaler.max); | |
}, | |
set: function (max) { | |
max = this._fromUnits(max); | |
this._scaler.max = max; | |
} | |
}); | |
/** | |
* The type of the oscillator: sine, square, sawtooth, triangle. | |
* @memberOf Tone.LFO# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.LFO.prototype, 'type', { | |
get: function () { | |
return this._oscillator.type; | |
}, | |
set: function (type) { | |
this._oscillator.type = type; | |
this._stoppedValue = this._oscillator._getInitialValue(); | |
this._stoppedSignal.value = this._stoppedValue; | |
} | |
}); | |
/** | |
* The phase of the LFO. | |
* @memberOf Tone.LFO# | |
* @type {number} | |
* @name phase | |
*/ | |
Object.defineProperty(Tone.LFO.prototype, 'phase', { | |
get: function () { | |
return this._oscillator.phase; | |
}, | |
set: function (phase) { | |
this._oscillator.phase = phase; | |
this._stoppedValue = this._oscillator._getInitialValue(); | |
this._stoppedSignal.value = this._stoppedValue; | |
} | |
}); | |
/** | |
* The output units of the LFO. | |
* @memberOf Tone.LFO# | |
* @type {Tone.Type} | |
* @name units | |
*/ | |
Object.defineProperty(Tone.LFO.prototype, 'units', { | |
get: function () { | |
return this._units; | |
}, | |
set: function (val) { | |
var currentMin = this.min; | |
var currentMax = this.max; | |
//convert the min and the max | |
this._units = val; | |
this.min = currentMin; | |
this.max = currentMax; | |
} | |
}); | |
/** | |
* 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 | |
* @param {number} [inputNum=0] optionally which input to connect to | |
* @returns {Tone.LFO} this | |
* @private | |
*/ | |
Tone.LFO.prototype.connect = function (node) { | |
if (node.constructor === Tone.Signal || node.constructor === Tone.Param || node.constructor === Tone.TimelineSignal) { | |
this.convert = node.convert; | |
this.units = node.units; | |
} | |
Tone.Signal.prototype.connect.apply(this, arguments); | |
return this; | |
}; | |
/** | |
* private method borrowed from Param converts | |
* units from their destination value | |
* @function | |
* @private | |
*/ | |
Tone.LFO.prototype._fromUnits = Tone.Param.prototype._fromUnits; | |
/** | |
* private method borrowed from Param converts | |
* units to their destination value | |
* @function | |
* @private | |
*/ | |
Tone.LFO.prototype._toUnits = Tone.Param.prototype._toUnits; | |
/** | |
* disconnect and dispose | |
* @returns {Tone.LFO} this | |
*/ | |
Tone.LFO.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.LFO; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Limiter will limit the loudness of an incoming signal. | |
* It is composed of a Tone.Compressor with a fast attack | |
* and release. Limiters are commonly used to safeguard against | |
* signal clipping. Unlike a compressor, limiters do not provide | |
* smooth gain reduction and almost completely prevent | |
* additional gain above the threshold. | |
* | |
* @extends {Tone} | |
* @constructor | |
* @param {number} threshold The theshold above which the limiting is applied. | |
* @example | |
* var limiter = new Tone.Limiter(-6); | |
*/ | |
Tone.Limiter = function () { | |
var options = this.optionsObject(arguments, ['threshold'], Tone.Limiter.defaults); | |
/** | |
* the compressor | |
* @private | |
* @type {Tone.Compressor} | |
*/ | |
this._compressor = this.input = this.output = new Tone.Compressor({ | |
'attack': 0.001, | |
'decay': 0.001, | |
'threshold': options.threshold | |
}); | |
/** | |
* The threshold of of the limiter | |
* @type {Decibel} | |
* @signal | |
*/ | |
this.threshold = this._compressor.threshold; | |
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 | |
*/ | |
Tone.Limiter.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._compressor.dispose(); | |
this._compressor = null; | |
this._writable('threshold'); | |
this.threshold = null; | |
return this; | |
}; | |
return Tone.Limiter; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Lowpass is a lowpass feedback comb filter. It is similar to | |
* Tone.FeedbackCombFilter, but includes a lowpass filter. | |
* | |
* @extends {Tone} | |
* @constructor | |
* @param {Time|Object} [delayTime] The delay time of the comb filter | |
* @param {NormalRange=} resonance The resonance (feedback) of the comb filter | |
* @param {Frequency=} dampening The cutoff of the lowpass filter dampens the | |
* signal as it is fedback. | |
*/ | |
Tone.LowpassCombFilter = function () { | |
Tone.call(this); | |
var options = this.optionsObject(arguments, [ | |
'delayTime', | |
'resonance', | |
'dampening' | |
], Tone.LowpassCombFilter.defaults); | |
/** | |
* the delay node | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._delay = this.input = this.context.createDelay(1); | |
/** | |
* The delayTime of the comb filter. | |
* @type {Time} | |
* @signal | |
*/ | |
this.delayTime = new Tone.Signal(options.delayTime, Tone.Type.Time); | |
/** | |
* the lowpass filter | |
* @type {BiquadFilterNode} | |
* @private | |
*/ | |
this._lowpass = this.output = this.context.createBiquadFilter(); | |
this._lowpass.Q.value = 0; | |
this._lowpass.type = 'lowpass'; | |
/** | |
* The dampening control of the feedback | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.dampening = new Tone.Param({ | |
'param': this._lowpass.frequency, | |
'units': Tone.Type.Frequency, | |
'value': options.dampening | |
}); | |
/** | |
* the feedback gain | |
* @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, | |
'units': Tone.Type.NormalRange, | |
'value': options.resonance | |
}); | |
//connections | |
this._delay.chain(this._lowpass, this._feedback, this._delay); | |
this.delayTime.connect(this._delay.delayTime); | |
this._readOnly([ | |
'dampening', | |
'resonance', | |
'delayTime' | |
]); | |
}; | |
Tone.extend(Tone.LowpassCombFilter); | |
/** | |
* the default parameters | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.LowpassCombFilter.defaults = { | |
'delayTime': 0.1, | |
'resonance': 0.5, | |
'dampening': 3000 | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.LowpassCombFilter} this | |
*/ | |
Tone.LowpassCombFilter.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.LowpassCombFilter; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Merge brings two signals into the left and right | |
* channels of a single stereo channel. | |
* | |
* @constructor | |
* @extends {Tone} | |
* @example | |
* var merge = new Tone.Merge().toMaster(); | |
* //routing a sine tone in the left channel | |
* //and noise in the right channel | |
* var osc = new Tone.Oscillator().connect(merge.left); | |
* var noise = new Tone.Noise().connect(merge.right); | |
* //starting our oscillators | |
* noise.start(); | |
* osc.start(); | |
*/ | |
Tone.Merge = function () { | |
Tone.call(this, 2, 0); | |
/** | |
* The left input channel. | |
* Alias for <code>input[0]</code> | |
* @type {GainNode} | |
*/ | |
this.left = this.input[0] = this.context.createGain(); | |
/** | |
* The right input channel. | |
* Alias for <code>input[1]</code>. | |
* @type {GainNode} | |
*/ | |
this.right = this.input[1] = this.context.createGain(); | |
/** | |
* the merger node for the two channels | |
* @type {ChannelMergerNode} | |
* @private | |
*/ | |
this._merger = this.output = this.context.createChannelMerger(2); | |
//connections | |
this.left.connect(this._merger, 0, 0); | |
this.right.connect(this._merger, 0, 1); | |
this.left.channelCount = 1; | |
this.right.channelCount = 1; | |
this.left.channelCountMode = 'explicit'; | |
this.right.channelCountMode = 'explicit'; | |
}; | |
Tone.extend(Tone.Merge); | |
/** | |
* Clean up. | |
* @returns {Tone.Merge} this | |
*/ | |
Tone.Merge.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this.left.disconnect(); | |
this.left = null; | |
this.right.disconnect(); | |
this.right = null; | |
this._merger.disconnect(); | |
this._merger = null; | |
return this; | |
}; | |
return Tone.Merge; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Meter gets the [RMS](https://en.wikipedia.org/wiki/Root_mean_square) | |
* of an input signal with some averaging applied. | |
* It can also get the raw value of the signal or the value in dB. For signal | |
* processing, it's better to use Tone.Follower which will produce an audio-rate | |
* envelope follower instead of needing to poll the Meter to get the output. | |
* <br><br> | |
* Meter was inspired by [Chris Wilsons Volume Meter](https://github.com/cwilso/volume-meter/blob/master/volume-meter.js). | |
* | |
* @constructor | |
* @extends {Tone} | |
* @param {number} [channels=1] number of channels being metered | |
* @param {number} [smoothing=0.8] amount of smoothing applied to the volume | |
* @param {number} [clipMemory=0.5] number in seconds that a "clip" should be remembered | |
* @example | |
* var meter = new Tone.Meter(); | |
* var mic = new Tone.Microphone().start(); | |
* //connect mic to the meter | |
* mic.connect(meter); | |
* //use getLevel or getDb | |
* //to access meter level | |
* meter.getLevel(); | |
*/ | |
Tone.Meter = function () { | |
var options = this.optionsObject(arguments, [ | |
'channels', | |
'smoothing' | |
], Tone.Meter.defaults); | |
//extends Unit | |
Tone.call(this); | |
/** | |
* The channel count | |
* @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 = options.smoothing; | |
/** | |
* The amount of time a clip is remember for. | |
* @type {Time} | |
*/ | |
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 | |
* @type {Array} | |
*/ | |
this._volume = new Array(this._channels); | |
/** | |
* the raw values for each of the channels | |
* @private | |
* @type {Array} | |
*/ | |
this._values = new Array(this._channels); | |
//zero out the volume array | |
for (var i = 0; i < this._channels; i++) { | |
this._volume[i] = 0; | |
this._values[i] = 0; | |
} | |
/** | |
* last time the values clipped | |
* @private | |
* @type {Array} | |
*/ | |
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(options.bufferSize, this._channels, 1); | |
this._jsNode.onaudioprocess = this._onprocess.bind(this); | |
//so it doesn't get garbage collected | |
this._jsNode.noGC(); | |
//signal just passes | |
this.input.connect(this.output); | |
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 | |
* @param {AudioProcessingEvent} event | |
*/ | |
Tone.Meter.prototype._onprocess = function (event) { | |
var bufferSize = this._jsNode.bufferSize; | |
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; | |
for (var i = 0; i < bufferSize; i++) { | |
x = input[i]; | |
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; | |
} | |
}; | |
/** | |
* Get the rms of the signal. | |
* @param {number} [channel=0] which channel | |
* @return {number} the value | |
*/ | |
Tone.Meter.prototype.getLevel = function (channel) { | |
channel = this.defaultArg(channel, 0); | |
var vol = this._volume[channel]; | |
if (vol < 0.00001) { | |
return 0; | |
} else { | |
return vol; | |
} | |
}; | |
/** | |
* Get the raw value of the signal. | |
* @param {number=} channel | |
* @return {number} | |
*/ | |
Tone.Meter.prototype.getValue = function (channel) { | |
channel = this.defaultArg(channel, 0); | |
return this._values[channel]; | |
}; | |
/** | |
* Get the volume of the signal in dB | |
* @param {number=} channel | |
* @return {Decibels} | |
*/ | |
Tone.Meter.prototype.getDb = function (channel) { | |
return this.gainToDb(this.getLevel(channel)); | |
}; | |
/** | |
* @returns {boolean} if the audio has clipped. The value resets | |
* based on the clipMemory defined. | |
*/ | |
Tone.Meter.prototype.isClipped = function (channel) { | |
channel = this.defaultArg(channel, 0); | |
return Date.now() - this._lastClip[channel] < this._clipMemory * 1000; | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.Meter} this | |
*/ | |
Tone.Meter.prototype.dispose = function () { | |
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; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Split splits an incoming signal into left and right channels. | |
* | |
* @constructor | |
* @extends {Tone} | |
* @example | |
* var split = new Tone.Split(); | |
* stereoSignal.connect(split); | |
*/ | |
Tone.Split = function () { | |
Tone.call(this, 0, 2); | |
/** | |
* @type {ChannelSplitterNode} | |
* @private | |
*/ | |
this._splitter = this.input = this.context.createChannelSplitter(2); | |
/** | |
* Left channel output. | |
* Alias for <code>output[0]</code> | |
* @type {GainNode} | |
*/ | |
this.left = this.output[0] = this.context.createGain(); | |
/** | |
* Right channel output. | |
* Alias for <code>output[1]</code> | |
* @type {GainNode} | |
*/ | |
this.right = this.output[1] = this.context.createGain(); | |
//connections | |
this._splitter.connect(this.left, 0, 0); | |
this._splitter.connect(this.right, 1, 0); | |
}; | |
Tone.extend(Tone.Split); | |
/** | |
* Clean up. | |
* @returns {Tone.Split} this | |
*/ | |
Tone.Split.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._splitter.disconnect(); | |
this.left.disconnect(); | |
this.right.disconnect(); | |
this.left = null; | |
this.right = null; | |
this._splitter = null; | |
return this; | |
}; | |
return Tone.Split; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Mid/Side processing separates the the 'mid' signal | |
* (which comes out of both the left and the right channel) | |
* and the 'side' (which only comes out of the the side channels). <br><br> | |
* <code> | |
* Mid = (Left+Right)/sqrt(2); // obtain mid-signal from left and right<br> | |
* Side = (Left-Right)/sqrt(2); // obtain side-signal from left and righ<br> | |
* </code> | |
* | |
* @extends {Tone} | |
* @constructor | |
*/ | |
Tone.MidSideSplit = function () { | |
Tone.call(this, 0, 2); | |
/** | |
* split the incoming signal into left and right channels | |
* @type {Tone.Split} | |
* @private | |
*/ | |
this._split = this.input = new Tone.Split(); | |
/** | |
* The mid send. Connect to mid processing. Alias for | |
* <code>output[0]</code> | |
* @type {Tone.Expr} | |
*/ | |
this.mid = this.output[0] = new Tone.Expr('($0 + $1) * $2'); | |
/** | |
* The side output. Connect to side processing. Alias for | |
* <code>output[1]</code> | |
* @type {Tone.Expr} | |
*/ | |
this.side = this.output[1] = new Tone.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); | |
sqrtTwo.connect(this.mid, 0, 2); | |
sqrtTwo.connect(this.side, 0, 2); | |
}; | |
Tone.extend(Tone.MidSideSplit); | |
/** | |
* a constant signal equal to 1 / sqrt(2) | |
* @type {Number} | |
* @signal | |
* @private | |
* @static | |
*/ | |
var sqrtTwo = null; | |
Tone._initAudioContext(function () { | |
sqrtTwo = new Tone.Signal(1 / Math.sqrt(2)); | |
}); | |
/** | |
* clean up | |
* @returns {Tone.MidSideSplit} this | |
*/ | |
Tone.MidSideSplit.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this.mid.dispose(); | |
this.mid = null; | |
this.side.dispose(); | |
this.side = null; | |
this._split.dispose(); | |
this._split = null; | |
return this; | |
}; | |
return Tone.MidSideSplit; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Mid/Side processing separates the the 'mid' signal | |
* (which comes out of both the left and the right channel) | |
* and the 'side' (which only comes out of the the side channels). | |
* MidSideMerge merges the mid and side signal after they've been seperated | |
* by Tone.MidSideSplit.<br><br> | |
* <code> | |
* Left = (Mid+Side)/sqrt(2); // obtain left signal from mid and side<br> | |
* Right = (Mid-Side)/sqrt(2); // obtain right signal from mid and side<br> | |
* </code> | |
* | |
* @extends {Tone.StereoEffect} | |
* @constructor | |
*/ | |
Tone.MidSideMerge = function () { | |
Tone.call(this, 2, 0); | |
/** | |
* The mid signal input. Alias for | |
* <code>input[0]</code> | |
* @type {GainNode} | |
*/ | |
this.mid = this.input[0] = this.context.createGain(); | |
/** | |
* recombine the mid/side into Left | |
* @type {Tone.Expr} | |
* @private | |
*/ | |
this._left = new Tone.Expr('($0 + $1) * $2'); | |
/** | |
* The side signal input. Alias for | |
* <code>input[1]</code> | |
* @type {GainNode} | |
*/ | |
this.side = this.input[1] = this.context.createGain(); | |
/** | |
* recombine the mid/side into Right | |
* @type {Tone.Expr} | |
* @private | |
*/ | |
this._right = new Tone.Expr('($0 - $1) * $2'); | |
/** | |
* Merge the left/right signal back into a stereo signal. | |
* @type {Tone.Merge} | |
* @private | |
*/ | |
this._merge = this.output = new Tone.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); | |
sqrtTwo.connect(this._left, 0, 2); | |
sqrtTwo.connect(this._right, 0, 2); | |
}; | |
Tone.extend(Tone.MidSideMerge); | |
/** | |
* A constant signal equal to 1 / sqrt(2). | |
* @type {Number} | |
* @signal | |
* @private | |
* @static | |
*/ | |
var sqrtTwo = null; | |
Tone._initAudioContext(function () { | |
sqrtTwo = new Tone.Signal(1 / Math.sqrt(2)); | |
}); | |
/** | |
* clean up | |
* @returns {Tone.MidSideMerge} this | |
*/ | |
Tone.MidSideMerge.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.MidSideMerge; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.MidSideCompressor applies two different compressors to the mid | |
* and side signal components. See Tone.MidSideSplit. | |
* | |
* @extends {Tone} | |
* @param {Object} options The options that are passed to the mid and side | |
* compressors. | |
* @constructor | |
*/ | |
Tone.MidSideCompressor = function (options) { | |
options = this.defaultArg(options, Tone.MidSideCompressor.defaults); | |
/** | |
* the mid/side split | |
* @type {Tone.MidSideSplit} | |
* @private | |
*/ | |
this._midSideSplit = this.input = new Tone.MidSideSplit(); | |
/** | |
* the mid/side recombination | |
* @type {Tone.MidSideMerge} | |
* @private | |
*/ | |
this._midSideMerge = this.output = new Tone.MidSideMerge(); | |
/** | |
* The compressor applied to the mid signal | |
* @type {Tone.Compressor} | |
*/ | |
this.mid = new Tone.Compressor(options.mid); | |
/** | |
* The compressor applied to the side signal | |
* @type {Tone.Compressor} | |
*/ | |
this.side = new Tone.Compressor(options.side); | |
this._midSideSplit.mid.chain(this.mid, this._midSideMerge.mid); | |
this._midSideSplit.side.chain(this.side, this._midSideMerge.side); | |
this._readOnly([ | |
'mid', | |
'side' | |
]); | |
}; | |
Tone.extend(Tone.MidSideCompressor); | |
/** | |
* @const | |
* @static | |
* @type {Object} | |
*/ | |
Tone.MidSideCompressor.defaults = { | |
'mid': { | |
'ratio': 3, | |
'threshold': -24, | |
'release': 0.03, | |
'attack': 0.02, | |
'knee': 16 | |
}, | |
'side': { | |
'ratio': 6, | |
'threshold': -30, | |
'release': 0.25, | |
'attack': 0.03, | |
'knee': 10 | |
} | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.MidSideCompressor} this | |
*/ | |
Tone.MidSideCompressor.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.MidSideCompressor; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Mono coerces the incoming mono or stereo signal into a mono signal | |
* where both left and right channels have the same value. This can be useful | |
* for [stereo imaging](https://en.wikipedia.org/wiki/Stereo_imaging). | |
* | |
* @extends {Tone} | |
* @constructor | |
*/ | |
Tone.Mono = function () { | |
Tone.call(this, 1, 0); | |
/** | |
* merge the signal | |
* @type {Tone.Merge} | |
* @private | |
*/ | |
this._merge = this.output = new Tone.Merge(); | |
this.input.connect(this._merge, 0, 0); | |
this.input.connect(this._merge, 0, 1); | |
this.input.gain.value = this.dbToGain(-10); | |
}; | |
Tone.extend(Tone.Mono); | |
/** | |
* clean up | |
* @returns {Tone.Mono} this | |
*/ | |
Tone.Mono.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._merge.dispose(); | |
this._merge = null; | |
return this; | |
}; | |
return Tone.Mono; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class A compressor with seperate controls over low/mid/high dynamics | |
* | |
* @extends {Tone} | |
* @constructor | |
* @param {Object} options The low/mid/high compressor settings. | |
* @example | |
* var multiband = new Tone.MultibandCompressor({ | |
* "lowFrequency" : 200, | |
* "highFrequency" : 1300 | |
* "low" : { | |
* "threshold" : -12 | |
* } | |
* }) | |
*/ | |
Tone.MultibandCompressor = function (options) { | |
options = this.defaultArg(arguments, Tone.MultibandCompressor.defaults); | |
/** | |
* split the incoming signal into high/mid/low | |
* @type {Tone.MultibandSplit} | |
* @private | |
*/ | |
this._splitter = this.input = new Tone.MultibandSplit({ | |
'lowFrequency': options.lowFrequency, | |
'highFrequency': options.highFrequency | |
}); | |
/** | |
* low/mid crossover frequency. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.lowFrequency = this._splitter.lowFrequency; | |
/** | |
* mid/high crossover frequency. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.highFrequency = this._splitter.highFrequency; | |
/** | |
* the output | |
* @type {GainNode} | |
* @private | |
*/ | |
this.output = this.context.createGain(); | |
/** | |
* The compressor applied to the low frequencies. | |
* @type {Tone.Compressor} | |
*/ | |
this.low = new Tone.Compressor(options.low); | |
/** | |
* The compressor applied to the mid frequencies. | |
* @type {Tone.Compressor} | |
*/ | |
this.mid = new Tone.Compressor(options.mid); | |
/** | |
* The compressor applied to the high frequencies. | |
* @type {Tone.Compressor} | |
*/ | |
this.high = new Tone.Compressor(options.high); | |
//connect the compressor | |
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' | |
]); | |
}; | |
Tone.extend(Tone.MultibandCompressor); | |
/** | |
* @const | |
* @static | |
* @type {Object} | |
*/ | |
Tone.MultibandCompressor.defaults = { | |
'low': Tone.Compressor.defaults, | |
'mid': Tone.Compressor.defaults, | |
'high': Tone.Compressor.defaults, | |
'lowFrequency': 250, | |
'highFrequency': 2000 | |
}; | |
/** | |
* clean up | |
* @returns {Tone.MultibandCompressor} this | |
*/ | |
Tone.MultibandCompressor.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.MultibandCompressor; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Maps a NormalRange [0, 1] to an AudioRange [-1, 1]. | |
* See also Tone.AudioToGain. | |
* | |
* @extends {Tone.SignalBase} | |
* @constructor | |
* @example | |
* var g2a = new Tone.GainToAudio(); | |
*/ | |
Tone.GainToAudio = function () { | |
/** | |
* @type {WaveShaperNode} | |
* @private | |
*/ | |
this._norm = this.input = this.output = new Tone.WaveShaper(function (x) { | |
return Math.abs(x) * 2 - 1; | |
}); | |
}; | |
Tone.extend(Tone.GainToAudio, Tone.SignalBase); | |
/** | |
* clean up | |
* @returns {Tone.GainToAudio} this | |
*/ | |
Tone.GainToAudio.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._norm.dispose(); | |
this._norm = null; | |
return this; | |
}; | |
return Tone.GainToAudio; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Panner is an equal power Left/Right Panner and does not | |
* support 3D. Panner uses the StereoPannerNode when available. | |
* | |
* @constructor | |
* @extends {Tone} | |
* @param {NormalRange} [initialPan=0.5] The initail panner value (defaults to 0.5 = center) | |
* @example | |
* //pan the input signal hard right. | |
* var panner = new Tone.Panner(1); | |
*/ | |
Tone.Panner = function (initialPan) { | |
Tone.call(this); | |
/** | |
* indicates if the panner is using the new StereoPannerNode internally | |
* @type {boolean} | |
* @private | |
*/ | |
this._hasStereoPanner = this.isFunction(this.context.createStereoPanner); | |
if (this._hasStereoPanner) { | |
/** | |
* the panner node | |
* @type {StereoPannerNode} | |
* @private | |
*/ | |
this._panner = this.input = this.output = this.context.createStereoPanner(); | |
/** | |
* The pan control. 0 = hard left, 1 = hard right. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.pan = new Tone.Signal(0, Tone.Type.NormalRange); | |
/** | |
* scale the pan signal to between -1 and 1 | |
* @type {Tone.WaveShaper} | |
* @private | |
*/ | |
this._scalePan = new Tone.GainToAudio(); | |
//connections | |
this.pan.chain(this._scalePan, this._panner.pan); | |
} else { | |
/** | |
* the dry/wet knob | |
* @type {Tone.CrossFade} | |
* @private | |
*/ | |
this._crossFade = new Tone.CrossFade(); | |
/** | |
* @type {Tone.Merge} | |
* @private | |
*/ | |
this._merger = this.output = new Tone.Merge(); | |
/** | |
* @type {Tone.Split} | |
* @private | |
*/ | |
this._splitter = this.input = new Tone.Split(); | |
/** | |
* The pan control. 0 = hard left, 1 = hard right. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.pan = this._crossFade.fade; | |
//CONNECTIONS: | |
//left channel is a, right channel is b | |
this._splitter.connect(this._crossFade, 0, 0); | |
this._splitter.connect(this._crossFade, 1, 1); | |
//merge it back together | |
this._crossFade.a.connect(this._merger, 0, 0); | |
this._crossFade.b.connect(this._merger, 0, 1); | |
} | |
//initial value | |
this.pan.value = this.defaultArg(initialPan, 0.5); | |
this._readOnly('pan'); | |
}; | |
Tone.extend(Tone.Panner); | |
/** | |
* Clean up. | |
* @returns {Tone.Panner} this | |
*/ | |
Tone.Panner.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._writable('pan'); | |
if (this._hasStereoPanner) { | |
this._panner.disconnect(); | |
this._panner = null; | |
this.pan.dispose(); | |
this.pan = null; | |
this._scalePan.dispose(); | |
this._scalePan = null; | |
} else { | |
this._crossFade.dispose(); | |
this._crossFade = null; | |
this._splitter.dispose(); | |
this._splitter = null; | |
this._merger.dispose(); | |
this._merger = null; | |
this.pan = null; | |
} | |
return this; | |
}; | |
return Tone.Panner; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.PanVol is a Tone.Panner and Tone.Volume in one. | |
* | |
* @extends {Tone} | |
* @constructor | |
* @param {NormalRange} pan the initial pan | |
* @param {number} volume The output volume. | |
* @example | |
* //pan the incoming signal left and drop the volume | |
* var panVol = new Tone.PanVol(0.25, -12); | |
*/ | |
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(options.pan); | |
/** | |
* The L/R panning control. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.pan = this._panner.pan; | |
/** | |
* The volume node | |
* @type {Tone.Volume} | |
*/ | |
this._volume = this.output = new Tone.Volume(options.volume); | |
/** | |
* The volume control in decibels. | |
* @type {Decibels} | |
* @signal | |
*/ | |
this.volume = this._volume.volume; | |
//connections | |
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 | |
*/ | |
Tone.PanVol.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._writable([ | |
'pan', | |
'volume' | |
]); | |
this._panner.dispose(); | |
this._panner = null; | |
this.pan = null; | |
this._volume.dispose(); | |
this._volume = null; | |
this.volume = null; | |
return this; | |
}; | |
return Tone.PanVol; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.CtrlInterpolate will interpolate between given values based | |
* on the "index" property. Passing in an array or object literal | |
* will interpolate each of the parameters. Note (i.e. "C3") | |
* and Time (i.e. "4n + 2") can be interpolated. All other values are | |
* assumed to be numbers. | |
* @example | |
* var interp = new Tone.CtrlInterpolate([0, 2, 9, 4]); | |
* interp.index = 0.75; | |
* interp.value; //returns 1.5 | |
* | |
* @example | |
* var interp = new Tone.CtrlInterpolate([ | |
* ["C3", "G4", "E5"], | |
* ["D4", "F#4", "E5"], | |
* ]); | |
* @param {Array} values The array of values to interpolate over | |
* @param {Positive} index The initial interpolation index. | |
* @extends {Tone} | |
*/ | |
Tone.CtrlInterpolate = function () { | |
var options = this.optionsObject(arguments, [ | |
'values', | |
'index' | |
], Tone.CtrlInterpolate.defaults); | |
/** | |
* The values to interpolate between | |
* @type {Array} | |
*/ | |
this.values = options.values; | |
/** | |
* The interpolated index between values. For example: a value of 1.5 | |
* would interpolate equally between the value at index 1 | |
* and the value at index 2. | |
* @example | |
* interp.index = 0; | |
* interp.value; //returns the value at 0 | |
* interp.index = 0.5; | |
* interp.value; //returns the value between indices 0 and 1. | |
* @type {Positive} | |
*/ | |
this.index = options.index; | |
}; | |
Tone.extend(Tone.CtrlInterpolate); | |
/** | |
* The defaults | |
* @const | |
* @type {Object} | |
*/ | |
Tone.CtrlInterpolate.defaults = { | |
'index': 0, | |
'values': [] | |
}; | |
/** | |
* The current interpolated value based on the index | |
* @readOnly | |
* @memberOf Tone.CtrlInterpolate# | |
* @type {*} | |
* @name value | |
*/ | |
Object.defineProperty(Tone.CtrlInterpolate.prototype, 'value', { | |
get: function () { | |
var index = this.index; | |
index = Math.min(index, this.values.length - 1); | |
var lowerPosition = Math.floor(index); | |
var lower = this.values[lowerPosition]; | |
var upper = this.values[Math.ceil(index)]; | |
return this._interpolate(index - lowerPosition, lower, upper); | |
} | |
}); | |
/** | |
* Internal interpolation routine | |
* @param {NormalRange} index The index between the lower and upper | |
* @param {*} lower | |
* @param {*} upper | |
* @return {*} The interpolated value | |
* @private | |
*/ | |
Tone.CtrlInterpolate.prototype._interpolate = function (index, lower, upper) { | |
if (this.isArray(lower)) { | |
var retArray = []; | |
for (var i = 0; i < lower.length; i++) { | |
retArray[i] = this._interpolate(index, lower[i], upper[i]); | |
} | |
return retArray; | |
} else if (this.isObject(lower)) { | |
var retObj = {}; | |
for (var attr in lower) { | |
retObj[attr] = this._interpolate(index, lower[attr], upper[attr]); | |
} | |
return retObj; | |
} else { | |
lower = this._toNumber(lower); | |
upper = this._toNumber(upper); | |
return (1 - index) * lower + index * upper; | |
} | |
}; | |
/** | |
* Convert from the given type into a number | |
* @param {Number|String} value | |
* @return {Number} | |
* @private | |
*/ | |
Tone.CtrlInterpolate.prototype._toNumber = function (val) { | |
if (this.isNumber(val)) { | |
return val; | |
} else if (this.isNote(val)) { | |
return this.toFrequency(val); | |
} else { | |
//otherwise assume that it's Time... | |
return this.toSeconds(val); | |
} | |
}; | |
/** | |
* Clean up | |
* @return {Tone.CtrlInterpolate} this | |
*/ | |
Tone.CtrlInterpolate.prototype.dispose = function () { | |
this.values = null; | |
}; | |
return Tone.CtrlInterpolate; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.CtrlMarkov represents a Markov Chain where each call | |
* to Tone.CtrlMarkov.next will move to the next state. If the next | |
* state choice is an array, the next state is chosen randomly with | |
* even probability for all of the choices. For a weighted probability | |
* of the next choices, pass in an object with "state" and "probability" attributes. | |
* The probabilities will be normalized and then chosen. If no next options | |
* are given for the current state, the state will stay there. | |
* @extends {Tone} | |
* @example | |
* var chain = new Tone.CtrlMarkov({ | |
* "beginning" : ["end", "middle"], | |
* "middle" : "end" | |
* }); | |
* chain.value = "beginning"; | |
* chain.next(); //returns "end" or "middle" with 50% probability | |
* | |
* @example | |
* var chain = new Tone.CtrlMarkov({ | |
* "beginning" : [{"value" : "end", "probability" : 0.8}, | |
* {"value" : "middle", "probability" : 0.2}], | |
* "middle" : "end" | |
* }); | |
* chain.value = "beginning"; | |
* chain.next(); //returns "end" with 80% probability or "middle" with 20%. | |
* @param {Object} values An object with the state names as the keys | |
* and the next state(s) as the values. | |
*/ | |
Tone.CtrlMarkov = function (values, initial) { | |
/** | |
* The Markov values with states as the keys | |
* and next state(s) as the values. | |
* @type {Object} | |
*/ | |
this.values = this.defaultArg(values, {}); | |
/** | |
* The current state of the Markov values. The next | |
* state will be evaluated and returned when Tone.CtrlMarkov.next | |
* is invoked. | |
* @type {String} | |
*/ | |
this.value = this.defaultArg(initial, Object.keys(this.values)[0]); | |
}; | |
Tone.extend(Tone.CtrlMarkov); | |
/** | |
* Returns the next state of the Markov values. | |
* @return {String} | |
*/ | |
Tone.CtrlMarkov.prototype.next = function () { | |
if (this.values.hasOwnProperty(this.value)) { | |
var next = this.values[this.value]; | |
if (this.isArray(next)) { | |
var distribution = this._getProbDistribution(next); | |
var rand = Math.random(); | |
var total = 0; | |
for (var i = 0; i < distribution.length; i++) { | |
var dist = distribution[i]; | |
if (rand > total && rand < total + dist) { | |
var chosen = next[i]; | |
if (this.isObject(chosen)) { | |
this.value = chosen.value; | |
} else { | |
this.value = chosen; | |
} | |
} | |
total += dist; | |
} | |
} else { | |
this.value = next; | |
} | |
} | |
return this.value; | |
}; | |
/** | |
* Choose randomly from an array weighted options in the form | |
* {"state" : string, "probability" : number} or an array of values | |
* @param {Array} options | |
* @return {Array} The randomly selected choice | |
* @private | |
*/ | |
Tone.CtrlMarkov.prototype._getProbDistribution = function (options) { | |
var distribution = []; | |
var total = 0; | |
var needsNormalizing = false; | |
for (var i = 0; i < options.length; i++) { | |
var option = options[i]; | |
if (this.isObject(option)) { | |
needsNormalizing = true; | |
distribution[i] = option.probability; | |
} else { | |
distribution[i] = 1 / options.length; | |
} | |
total += distribution[i]; | |
} | |
if (needsNormalizing) { | |
//normalize the values | |
for (var j = 0; j < distribution.length; j++) { | |
distribution[j] = distribution[j] / total; | |
} | |
} | |
return distribution; | |
}; | |
/** | |
* Clean up | |
* @return {Tone.CtrlMarkov} this | |
*/ | |
Tone.CtrlMarkov.prototype.dispose = function () { | |
this.values = null; | |
}; | |
return Tone.CtrlMarkov; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Generate patterns from an array of values. | |
* Has a number of arpeggiation and randomized | |
* selection patterns. | |
* <ul> | |
* <li>"up" - cycles upward</li> | |
* <li>"down" - cycles downward</li> | |
* <li>"upDown" - up then and down</li> | |
* <li>"downUp" - cycles down then and up</li> | |
* <li>"alternateUp" - jump up two and down one</li> | |
* <li>"alternateDown" - jump down two and up one</li> | |
* <li>"random" - randomly select an index</li> | |
* <li>"randomWalk" - randomly moves one index away from the current position</li> | |
* <li>"randomOnce" - randomly select an index without repeating until all values have been chosen.</li> | |
* </ul> | |
* @param {Array} values An array of options to choose from. | |
* @param {Tone.CtrlPattern.Type=} type The name of the pattern. | |
* @extends {Tone} | |
*/ | |
Tone.CtrlPattern = function () { | |
var options = this.optionsObject(arguments, [ | |
'values', | |
'type' | |
], Tone.CtrlPattern.defaults); | |
/** | |
* The array of values to arpeggiate over | |
* @type {Array} | |
*/ | |
this.values = options.values; | |
/** | |
* The current position in the values array | |
* @type {Number} | |
*/ | |
this.index = 0; | |
/** | |
* The type placeholder | |
* @type {Tone.CtrlPattern.Type} | |
* @private | |
*/ | |
this._type = null; | |
/** | |
* Shuffled values for the RandomOnce type | |
* @type {Array} | |
* @private | |
*/ | |
this._shuffled = null; | |
/** | |
* The direction of the movement | |
* @type {String} | |
* @private | |
*/ | |
this._direction = null; | |
this.type = options.type; | |
}; | |
Tone.extend(Tone.CtrlPattern); | |
/** | |
* The Control Patterns | |
* @type {Object} | |
* @static | |
*/ | |
Tone.CtrlPattern.Type = { | |
Up: 'up', | |
Down: 'down', | |
UpDown: 'upDown', | |
DownUp: 'downUp', | |
AlternateUp: 'alternateUp', | |
AlternateDown: 'alternateDown', | |
Random: 'random', | |
RandomWalk: 'randomWalk', | |
RandomOnce: 'randomOnce' | |
}; | |
/** | |
* The default values. | |
* @type {Object} | |
*/ | |
Tone.CtrlPattern.defaults = { | |
'type': Tone.CtrlPattern.Type.Up, | |
'values': [] | |
}; | |
/** | |
* The value at the current index of the pattern. | |
* @readOnly | |
* @memberOf Tone.CtrlPattern# | |
* @type {*} | |
* @name value | |
*/ | |
Object.defineProperty(Tone.CtrlPattern.prototype, 'value', { | |
get: function () { | |
//some safeguards | |
if (this.values.length === 0) { | |
return; | |
} else if (this.values.length === 1) { | |
return this.values[0]; | |
} | |
this.index = Math.min(this.index, this.values.length - 1); | |
var val = this.values[this.index]; | |
if (this.type === Tone.CtrlPattern.Type.RandomOnce) { | |
if (this.values.length !== this._shuffled.length) { | |
this._shuffleValues(); | |
} | |
val = this.values[this._shuffled[this.index]]; | |
} | |
return val; | |
} | |
}); | |
/** | |
* The pattern used to select the next | |
* item from the values array | |
* @memberOf Tone.CtrlPattern# | |
* @type {Tone.CtrlPattern.Type} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.CtrlPattern.prototype, 'type', { | |
get: function () { | |
return this._type; | |
}, | |
set: function (type) { | |
this._type = type; | |
this._shuffled = null; | |
//the first index | |
if (this._type === Tone.CtrlPattern.Type.Up || this._type === Tone.CtrlPattern.Type.UpDown || this._type === Tone.CtrlPattern.Type.RandomOnce || this._type === Tone.CtrlPattern.Type.AlternateUp) { | |
this.index = 0; | |
} else if (this._type === Tone.CtrlPattern.Type.Down || this._type === Tone.CtrlPattern.Type.DownUp || this._type === Tone.CtrlPattern.Type.AlternateDown) { | |
this.index = this.values.length - 1; | |
} | |
//the direction | |
if (this._type === Tone.CtrlPattern.Type.UpDown || this._type === Tone.CtrlPattern.Type.AlternateUp) { | |
this._direction = Tone.CtrlPattern.Type.Up; | |
} else if (this._type === Tone.CtrlPattern.Type.DownUp || this._type === Tone.CtrlPattern.Type.AlternateDown) { | |
this._direction = Tone.CtrlPattern.Type.Down; | |
} | |
//randoms | |
if (this._type === Tone.CtrlPattern.Type.RandomOnce) { | |
this._shuffleValues(); | |
} else if (this._type === Tone.CtrlPattern.Random) { | |
this.index = Math.floor(Math.random() * this.values.length); | |
} | |
} | |
}); | |
/** | |
* Return the next value given the current position | |
* and pattern. | |
* @return {*} The next value | |
*/ | |
Tone.CtrlPattern.prototype.next = function () { | |
var type = this.type; | |
//choose the next index | |
if (type === Tone.CtrlPattern.Type.Up) { | |
this.index++; | |
if (this.index >= this.values.length) { | |
this.index = 0; | |
} | |
} else if (type === Tone.CtrlPattern.Type.Down) { | |
this.index--; | |
if (this.index < 0) { | |
this.index = this.values.length - 1; | |
} | |
} else if (type === Tone.CtrlPattern.Type.UpDown || type === Tone.CtrlPattern.Type.DownUp) { | |
if (this._direction === Tone.CtrlPattern.Type.Up) { | |
this.index++; | |
} else { | |
this.index--; | |
} | |
if (this.index < 0) { | |
this.index = 1; | |
this._direction = Tone.CtrlPattern.Type.Up; | |
} else if (this.index >= this.values.length) { | |
this.index = this.values.length - 2; | |
this._direction = Tone.CtrlPattern.Type.Down; | |
} | |
} else if (type === Tone.CtrlPattern.Type.Random) { | |
this.index = Math.floor(Math.random() * this.values.length); | |
} else if (type === Tone.CtrlPattern.Type.RandomWalk) { | |
if (Math.random() < 0.5) { | |
this.index--; | |
this.index = Math.max(this.index, 0); | |
} else { | |
this.index++; | |
this.index = Math.min(this.index, this.values.length - 1); | |
} | |
} else if (type === Tone.CtrlPattern.Type.RandomOnce) { | |
this.index++; | |
if (this.index >= this.values.length) { | |
this.index = 0; | |
//reshuffle the values for next time | |
this._shuffleValues(); | |
} | |
} else if (type === Tone.CtrlPattern.Type.AlternateUp) { | |
if (this._direction === Tone.CtrlPattern.Type.Up) { | |
this.index += 2; | |
this._direction = Tone.CtrlPattern.Type.Down; | |
} else { | |
this.index -= 1; | |
this._direction = Tone.CtrlPattern.Type.Up; | |
} | |
if (this.index >= this.values.length) { | |
this.index = 0; | |
this._direction = Tone.CtrlPattern.Type.Up; | |
} | |
} else if (type === Tone.CtrlPattern.Type.AlternateDown) { | |
if (this._direction === Tone.CtrlPattern.Type.Up) { | |
this.index += 1; | |
this._direction = Tone.CtrlPattern.Type.Down; | |
} else { | |
this.index -= 2; | |
this._direction = Tone.CtrlPattern.Type.Up; | |
} | |
if (this.index < 0) { | |
this.index = this.values.length - 1; | |
this._direction = Tone.CtrlPattern.Type.Down; | |
} | |
} | |
return this.value; | |
}; | |
/** | |
* Shuffles the values and places the results into the _shuffled | |
* @private | |
*/ | |
Tone.CtrlPattern.prototype._shuffleValues = function () { | |
var copy = []; | |
this._shuffled = []; | |
for (var i = 0; i < this.values.length; i++) { | |
copy[i] = i; | |
} | |
while (copy.length > 0) { | |
var randVal = copy.splice(Math.floor(copy.length * Math.random()), 1); | |
this._shuffled.push(randVal[0]); | |
} | |
}; | |
/** | |
* Clean up | |
* @returns {Tone.CtrlPattern} this | |
*/ | |
Tone.CtrlPattern.prototype.dispose = function () { | |
this._shuffled = null; | |
this.values = null; | |
}; | |
return Tone.CtrlPattern; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Choose a random value. | |
* @extends {Tone} | |
* @example | |
* var randomWalk = new Tone.CtrlRandom({ | |
* "min" : 0, | |
* "max" : 10, | |
* "integer" : true | |
* }); | |
* randomWalk.eval(); | |
* | |
* @param {Number|Time=} min The minimum return value. | |
* @param {Number|Time=} max The maximum return value. | |
*/ | |
Tone.CtrlRandom = function () { | |
var options = this.optionsObject(arguments, [ | |
'min', | |
'max' | |
], Tone.CtrlRandom.defaults); | |
/** | |
* The minimum return value | |
* @type {Number|Time} | |
*/ | |
this.min = options.min; | |
/** | |
* The maximum return value | |
* @type {Number|Time} | |
*/ | |
this.max = options.max; | |
/** | |
* If the return value should be an integer | |
* @type {Boolean} | |
*/ | |
this.integer = options.integer; | |
}; | |
Tone.extend(Tone.CtrlRandom); | |
/** | |
* The defaults | |
* @const | |
* @type {Object} | |
*/ | |
Tone.CtrlRandom.defaults = { | |
'min': 0, | |
'max': 1, | |
'integer': false | |
}; | |
/** | |
* Return a random value between min and max. | |
* @readOnly | |
* @memberOf Tone.CtrlRandom# | |
* @type {*} | |
* @name value | |
*/ | |
Object.defineProperty(Tone.CtrlRandom.prototype, 'value', { | |
get: function () { | |
var min = this.toSeconds(this.min); | |
var max = this.toSeconds(this.max); | |
var rand = Math.random(); | |
var val = rand * min + (1 - rand) * max; | |
if (this.integer) { | |
val = Math.floor(val); | |
} | |
return val; | |
} | |
}); | |
return Tone.CtrlRandom; | |
}); | |
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. | |
* <br><br> | |
* 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. <code>undefined</code> 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 || options.url instanceof Tone.Buffer) { | |
this.set(options.url); | |
this.onload(this); | |
} else if (this.isString(options.url)) { | |
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 | |
/////////////////////////////////////////////////////////////////////////// | |
//statically inherits Emitter methods | |
Tone.Emitter.mixin(Tone.Buffer); | |
/** | |
* 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 = function (e) { | |
Tone.Buffer.trigger('error', e); | |
}; | |
} | |
} else if (Tone.Buffer._currentDownloads.length === 0) { | |
Tone.Buffer.trigger('load'); | |
//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.trigger('progress', 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; | |
}; | |
/** | |
* @deprecated us on([event]) instead | |
*/ | |
Object.defineProperty(Tone.Buffer, 'onload', { | |
set: function (cb) { | |
console.warn('Tone.Buffer.onload is deprecated, use Tone.Buffer.on(\'load\', callback)'); | |
Tone.Buffer.on('load', cb); | |
} | |
}); | |
Object.defineProperty(Tone.Buffer, 'onprogress', { | |
set: function (cb) { | |
console.warn('Tone.Buffer.onprogress is deprecated, use Tone.Buffer.on(\'progress\', callback)'); | |
Tone.Buffer.on('progress', cb); | |
} | |
}); | |
Object.defineProperty(Tone.Buffer, 'onerror', { | |
set: function (cb) { | |
console.warn('Tone.Buffer.onerror is deprecated, use Tone.Buffer.on(\'error\', callback)'); | |
Tone.Buffer.on('error', cb); | |
} | |
}); | |
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 Wrapper around Web Audio's native [DelayNode](http://webaudio.github.io/web-audio-api/#the-delaynode-interface). | |
* @extends {Tone} | |
* @param {Time=} delayTime The delay applied to the incoming signal. | |
* @param {Time=} maxDelay The maximum delay time. | |
*/ | |
Tone.Delay = function () { | |
var options = this.optionsObject(arguments, [ | |
'delayTime', | |
'maxDelay' | |
], Tone.Delay.defaults); | |
/** | |
* The native delay node | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._delayNode = this.input = this.output = this.context.createDelay(this.toSeconds(options.maxDelay)); | |
/** | |
* The amount of time the incoming signal is | |
* delayed. | |
* @type {Tone.Param} | |
* @signal | |
*/ | |
this.delayTime = new Tone.Param({ | |
'param': this._delayNode.delayTime, | |
'units': Tone.Type.Time, | |
'value': options.delayTime | |
}); | |
this._readOnly('delayTime'); | |
}; | |
Tone.extend(Tone.Delay); | |
/** | |
* The defaults | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Delay.defaults = { | |
'maxDelay': 1, | |
'delayTime': 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 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. | |
* <br><br> | |
* 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 private volume node | |
* @type {Tone.Volume} | |
* @private | |
*/ | |
this._volume = this.output = new Tone.Volume(); | |
/** | |
* The volume of the master output. | |
* @type {Decibels} | |
* @signal | |
*/ | |
this.volume = this._volume.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; | |
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) { | |
/** | |
* @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. | |
* <br><br> | |
* 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. | |
* <br><br> | |
* 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)`. | |
* <br><br> | |
* 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) { | |
/** | |
* @class Tone.Effect is the base class for effects. Connect the effect between | |
* the effectSend and effectReturn GainNodes, then control the amount of | |
* effect which goes to the output using the wet control. | |
* | |
* @constructor | |
* @extends {Tone} | |
* @param {NormalRange|Object} [wet] The starting wet value. | |
*/ | |
Tone.Effect = function () { | |
Tone.call(this); | |
//get all of the defaults | |
var options = this.optionsObject(arguments, ['wet'], Tone.Effect.defaults); | |
/** | |
* the drywet knob to control the amount of effect | |
* @type {Tone.CrossFade} | |
* @private | |
*/ | |
this._dryWet = new Tone.CrossFade(options.wet); | |
/** | |
* The wet control is how much of the effected | |
* will pass through to the output. 1 = 100% effected | |
* signal, 0 = 100% dry signal. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.wet = this._dryWet.fade; | |
/** | |
* connect the effectSend to the input of hte effect | |
* @type {GainNode} | |
* @private | |
*/ | |
this.effectSend = this.context.createGain(); | |
/** | |
* connect the output of the effect to the effectReturn | |
* @type {GainNode} | |
* @private | |
*/ | |
this.effectReturn = this.context.createGain(); | |
//connections | |
this.input.connect(this._dryWet.a); | |
this.input.connect(this.effectSend); | |
this.effectReturn.connect(this._dryWet.b); | |
this._dryWet.connect(this.output); | |
this._readOnly(['wet']); | |
}; | |
Tone.extend(Tone.Effect); | |
/** | |
* @static | |
* @type {Object} | |
*/ | |
Tone.Effect.defaults = { 'wet': 1 }; | |
/** | |
* chains the effect in between the effectSend and effectReturn | |
* @param {Tone} effect | |
* @private | |
* @returns {Tone.Effect} this | |
*/ | |
Tone.Effect.prototype.connectEffect = function (effect) { | |
this.effectSend.chain(effect, this.effectReturn); | |
return this; | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.Effect} this | |
*/ | |
Tone.Effect.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._dryWet.dispose(); | |
this._dryWet = null; | |
this.effectSend.disconnect(); | |
this.effectSend = null; | |
this.effectReturn.disconnect(); | |
this.effectReturn = null; | |
this._writable(['wet']); | |
this.wet = null; | |
return this; | |
}; | |
return Tone.Effect; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.AutoFilter is a Tone.Filter with a Tone.LFO connected to the filter cutoff frequency. | |
* Setting the LFO rate and depth allows for control over the filter modulation rate | |
* and depth. | |
* | |
* @constructor | |
* @extends {Tone.Effect} | |
* @param {Time|Object} [frequency] The rate of the LFO. | |
* @param {Frequency=} baseFrequency The lower value of the LFOs oscillation | |
* @param {Frequency=} octaves The number of octaves above the baseFrequency | |
* @example | |
* //create an autofilter and start it's LFO | |
* var autoFilter = new Tone.AutoFilter("4n").toMaster().start(); | |
* //route an oscillator through the filter and start it | |
* var oscillator = new Tone.Oscillator().connect(autoFilter).start(); | |
*/ | |
Tone.AutoFilter = function () { | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'baseFrequency', | |
'octaves' | |
], Tone.AutoFilter.defaults); | |
Tone.Effect.call(this, options); | |
/** | |
* the lfo which drives the filter cutoff | |
* @type {Tone.LFO} | |
* @private | |
*/ | |
this._lfo = new Tone.LFO({ | |
'frequency': options.frequency, | |
'amplitude': options.depth | |
}); | |
/** | |
* The range of the filter modulating between the min and max frequency. | |
* 0 = no modulation. 1 = full modulation. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.depth = this._lfo.amplitude; | |
/** | |
* How fast the filter modulates between min and max. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = this._lfo.frequency; | |
/** | |
* The filter node | |
* @type {Tone.Filter} | |
*/ | |
this.filter = new Tone.Filter(options.filter); | |
/** | |
* The octaves placeholder | |
* @type {Positive} | |
* @private | |
*/ | |
this._octaves = 0; | |
//connections | |
this.connectEffect(this.filter); | |
this._lfo.connect(this.filter.frequency); | |
this.type = options.type; | |
this._readOnly([ | |
'frequency', | |
'depth' | |
]); | |
this.octaves = options.octaves; | |
this.baseFrequency = options.baseFrequency; | |
}; | |
//extend Effect | |
Tone.extend(Tone.AutoFilter, Tone.Effect); | |
/** | |
* defaults | |
* @static | |
* @type {Object} | |
*/ | |
Tone.AutoFilter.defaults = { | |
'frequency': 1, | |
'type': 'sine', | |
'depth': 1, | |
'baseFrequency': 200, | |
'octaves': 2.6, | |
'filter': { | |
'type': 'lowpass', | |
'rolloff': -12, | |
'Q': 1 | |
} | |
}; | |
/** | |
* Start the effect. | |
* @param {Time} [time=now] When the LFO will start. | |
* @returns {Tone.AutoFilter} this | |
*/ | |
Tone.AutoFilter.prototype.start = function (time) { | |
this._lfo.start(time); | |
return this; | |
}; | |
/** | |
* Stop the effect. | |
* @param {Time} [time=now] When the LFO will stop. | |
* @returns {Tone.AutoFilter} this | |
*/ | |
Tone.AutoFilter.prototype.stop = function (time) { | |
this._lfo.stop(time); | |
return this; | |
}; | |
/** | |
* Sync the filter to the transport. | |
* @param {Time} [delay=0] Delay time before starting the effect after the | |
* Transport has started. | |
* @returns {Tone.AutoFilter} this | |
*/ | |
Tone.AutoFilter.prototype.sync = function (delay) { | |
this._lfo.sync(delay); | |
return this; | |
}; | |
/** | |
* Unsync the filter from the transport. | |
* @returns {Tone.AutoFilter} this | |
*/ | |
Tone.AutoFilter.prototype.unsync = function () { | |
this._lfo.unsync(); | |
return this; | |
}; | |
/** | |
* Type of oscillator attached to the AutoFilter. | |
* Possible values: "sine", "square", "triangle", "sawtooth". | |
* @memberOf Tone.AutoFilter# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.AutoFilter.prototype, 'type', { | |
get: function () { | |
return this._lfo.type; | |
}, | |
set: function (type) { | |
this._lfo.type = type; | |
} | |
}); | |
/** | |
* The minimum value of the filter's cutoff frequency. | |
* @memberOf Tone.AutoFilter# | |
* @type {Frequency} | |
* @name min | |
*/ | |
Object.defineProperty(Tone.AutoFilter.prototype, 'baseFrequency', { | |
get: function () { | |
return this._lfo.min; | |
}, | |
set: function (freq) { | |
this._lfo.min = this.toFrequency(freq); | |
} | |
}); | |
/** | |
* The maximum value of the filter's cutoff frequency. | |
* @memberOf Tone.AutoFilter# | |
* @type {Positive} | |
* @name octaves | |
*/ | |
Object.defineProperty(Tone.AutoFilter.prototype, 'octaves', { | |
get: function () { | |
return this._octaves; | |
}, | |
set: function (oct) { | |
this._octaves = oct; | |
this._lfo.max = this.baseFrequency * Math.pow(2, oct); | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.AutoFilter} this | |
*/ | |
Tone.AutoFilter.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this._lfo.dispose(); | |
this._lfo = null; | |
this.filter.dispose(); | |
this.filter = null; | |
this._writable([ | |
'frequency', | |
'depth' | |
]); | |
this.frequency = null; | |
this.depth = null; | |
return this; | |
}; | |
return Tone.AutoFilter; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.AutoPanner is a Tone.Panner with an LFO connected to the pan amount. | |
* More on using autopanners [here](https://www.ableton.com/en/blog/autopan-chopper-effect-and-more-liveschool/). | |
* | |
* @constructor | |
* @extends {Tone.Effect} | |
* @param {Frequency|Object} [frequency] Rate of left-right oscillation. | |
* @example | |
* //create an autopanner and start it's LFO | |
* var autoPanner = new Tone.AutoPanner("4n").toMaster().start(); | |
* //route an oscillator through the panner and start it | |
* var oscillator = new Tone.Oscillator().connect(autoPanner).start(); | |
*/ | |
Tone.AutoPanner = function () { | |
var options = this.optionsObject(arguments, ['frequency'], Tone.AutoPanner.defaults); | |
Tone.Effect.call(this, options); | |
/** | |
* the lfo which drives the panning | |
* @type {Tone.LFO} | |
* @private | |
*/ | |
this._lfo = new Tone.LFO({ | |
'frequency': options.frequency, | |
'amplitude': options.depth, | |
'min': 0, | |
'max': 1 | |
}); | |
/** | |
* The amount of panning between left and right. | |
* 0 = always center. 1 = full range between left and right. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.depth = this._lfo.amplitude; | |
/** | |
* the panner node which does the panning | |
* @type {Tone.Panner} | |
* @private | |
*/ | |
this._panner = new Tone.Panner(); | |
/** | |
* How fast the panner modulates between left and right. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = this._lfo.frequency; | |
//connections | |
this.connectEffect(this._panner); | |
this._lfo.connect(this._panner.pan); | |
this.type = options.type; | |
this._readOnly([ | |
'depth', | |
'frequency' | |
]); | |
}; | |
//extend Effect | |
Tone.extend(Tone.AutoPanner, Tone.Effect); | |
/** | |
* defaults | |
* @static | |
* @type {Object} | |
*/ | |
Tone.AutoPanner.defaults = { | |
'frequency': 1, | |
'type': 'sine', | |
'depth': 1 | |
}; | |
/** | |
* Start the effect. | |
* @param {Time} [time=now] When the LFO will start. | |
* @returns {Tone.AutoPanner} this | |
*/ | |
Tone.AutoPanner.prototype.start = function (time) { | |
this._lfo.start(time); | |
return this; | |
}; | |
/** | |
* Stop the effect. | |
* @param {Time} [time=now] When the LFO will stop. | |
* @returns {Tone.AutoPanner} this | |
*/ | |
Tone.AutoPanner.prototype.stop = function (time) { | |
this._lfo.stop(time); | |
return this; | |
}; | |
/** | |
* Sync the panner to the transport. | |
* @param {Time} [delay=0] Delay time before starting the effect after the | |
* Transport has started. | |
* @returns {Tone.AutoPanner} this | |
*/ | |
Tone.AutoPanner.prototype.sync = function (delay) { | |
this._lfo.sync(delay); | |
return this; | |
}; | |
/** | |
* Unsync the panner from the transport | |
* @returns {Tone.AutoPanner} this | |
*/ | |
Tone.AutoPanner.prototype.unsync = function () { | |
this._lfo.unsync(); | |
return this; | |
}; | |
/** | |
* Type of oscillator attached to the AutoFilter. | |
* Possible values: "sine", "square", "triangle", "sawtooth". | |
* @memberOf Tone.AutoFilter# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.AutoPanner.prototype, 'type', { | |
get: function () { | |
return this._lfo.type; | |
}, | |
set: function (type) { | |
this._lfo.type = type; | |
} | |
}); | |
/** | |
* clean up | |
* @returns {Tone.AutoPanner} this | |
*/ | |
Tone.AutoPanner.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this._lfo.dispose(); | |
this._lfo = null; | |
this._panner.dispose(); | |
this._panner = null; | |
this._writable([ | |
'depth', | |
'frequency' | |
]); | |
this.frequency = null; | |
this.depth = null; | |
return this; | |
}; | |
return Tone.AutoPanner; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.AutoWah connects a Tone.Follower to a bandpass filter (Tone.Filter). | |
* The frequency of the filter is adjusted proportionally to the | |
* incoming signal's amplitude. Inspiration from [Tuna.js](https://github.com/Dinahmoe/tuna). | |
* | |
* @constructor | |
* @extends {Tone.Effect} | |
* @param {Frequency|Object} [baseFrequency] The frequency the filter is set | |
* to at the low point of the wah | |
* @param {Positive} [octaves] The number of octaves above the baseFrequency | |
* the filter will sweep to when fully open | |
* @param {Decibels} [sensitivity] The decibel threshold sensitivity for | |
* the incoming signal. Normal range of -40 to 0. | |
* @example | |
* var autoWah = new Tone.AutoWah(50, 6, -30).toMaster(); | |
* //initialize the synth and connect to autowah | |
* var synth = new SimpleSynth.connect(autoWah); | |
* //Q value influences the effect of the wah - default is 2 | |
* autoWah.Q.value = 6; | |
* //more audible on higher notes | |
* synth.triggerAttackRelease("C4", "8n") | |
*/ | |
Tone.AutoWah = function () { | |
var options = this.optionsObject(arguments, [ | |
'baseFrequency', | |
'octaves', | |
'sensitivity' | |
], Tone.AutoWah.defaults); | |
Tone.Effect.call(this, options); | |
/** | |
* The envelope follower. Set the attack/release | |
* timing to adjust how the envelope is followed. | |
* @type {Tone.Follower} | |
* @private | |
*/ | |
this.follower = new Tone.Follower(options.follower); | |
/** | |
* scales the follower value to the frequency domain | |
* @type {Tone} | |
* @private | |
*/ | |
this._sweepRange = new Tone.ScaleExp(0, 1, 0.5); | |
/** | |
* @type {number} | |
* @private | |
*/ | |
this._baseFrequency = options.baseFrequency; | |
/** | |
* @type {number} | |
* @private | |
*/ | |
this._octaves = options.octaves; | |
/** | |
* the input gain to adjust the sensitivity | |
* @type {GainNode} | |
* @private | |
*/ | |
this._inputBoost = this.context.createGain(); | |
/** | |
* @type {BiquadFilterNode} | |
* @private | |
*/ | |
this._bandpass = new Tone.Filter({ | |
'rolloff': -48, | |
'frequency': 0, | |
'Q': options.Q | |
}); | |
/** | |
* @type {Tone.Filter} | |
* @private | |
*/ | |
this._peaking = new Tone.Filter(0, 'peaking'); | |
this._peaking.gain.value = options.gain; | |
/** | |
* The gain of the filter. | |
* @type {Number} | |
* @signal | |
*/ | |
this.gain = this._peaking.gain; | |
/** | |
* The quality of the filter. | |
* @type {Positive} | |
* @signal | |
*/ | |
this.Q = this._bandpass.Q; | |
//the control signal path | |
this.effectSend.chain(this._inputBoost, this.follower, this._sweepRange); | |
this._sweepRange.connect(this._bandpass.frequency); | |
this._sweepRange.connect(this._peaking.frequency); | |
//the filtered path | |
this.effectSend.chain(this._bandpass, this._peaking, this.effectReturn); | |
//set the initial value | |
this._setSweepRange(); | |
this.sensitivity = options.sensitivity; | |
this._readOnly([ | |
'gain', | |
'Q' | |
]); | |
}; | |
Tone.extend(Tone.AutoWah, Tone.Effect); | |
/** | |
* @static | |
* @type {Object} | |
*/ | |
Tone.AutoWah.defaults = { | |
'baseFrequency': 100, | |
'octaves': 6, | |
'sensitivity': 0, | |
'Q': 2, | |
'gain': 2, | |
'follower': { | |
'attack': 0.3, | |
'release': 0.5 | |
} | |
}; | |
/** | |
* The number of octaves that the filter will sweep above the | |
* baseFrequency. | |
* @memberOf Tone.AutoWah# | |
* @type {Number} | |
* @name octaves | |
*/ | |
Object.defineProperty(Tone.AutoWah.prototype, 'octaves', { | |
get: function () { | |
return this._octaves; | |
}, | |
set: function (octaves) { | |
this._octaves = octaves; | |
this._setSweepRange(); | |
} | |
}); | |
/** | |
* The base frequency from which the sweep will start from. | |
* @memberOf Tone.AutoWah# | |
* @type {Frequency} | |
* @name baseFrequency | |
*/ | |
Object.defineProperty(Tone.AutoWah.prototype, 'baseFrequency', { | |
get: function () { | |
return this._baseFrequency; | |
}, | |
set: function (baseFreq) { | |
this._baseFrequency = baseFreq; | |
this._setSweepRange(); | |
} | |
}); | |
/** | |
* The sensitivity to control how responsive to the input signal the filter is. | |
* @memberOf Tone.AutoWah# | |
* @type {Decibels} | |
* @name sensitivity | |
*/ | |
Object.defineProperty(Tone.AutoWah.prototype, 'sensitivity', { | |
get: function () { | |
return this.gainToDb(1 / this._inputBoost.gain.value); | |
}, | |
set: function (sensitivy) { | |
this._inputBoost.gain.value = 1 / this.dbToGain(sensitivy); | |
} | |
}); | |
/** | |
* sets the sweep range of the scaler | |
* @private | |
*/ | |
Tone.AutoWah.prototype._setSweepRange = function () { | |
this._sweepRange.min = this._baseFrequency; | |
this._sweepRange.max = Math.min(this._baseFrequency * Math.pow(2, this._octaves), this.context.sampleRate / 2); | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.AutoWah} this | |
*/ | |
Tone.AutoWah.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this.follower.dispose(); | |
this.follower = null; | |
this._sweepRange.dispose(); | |
this._sweepRange = null; | |
this._bandpass.dispose(); | |
this._bandpass = null; | |
this._peaking.dispose(); | |
this._peaking = null; | |
this._inputBoost.disconnect(); | |
this._inputBoost = null; | |
this._writable([ | |
'gain', | |
'Q' | |
]); | |
this.gain = null; | |
this.Q = null; | |
return this; | |
}; | |
return Tone.AutoWah; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Bitcrusher downsamples the incoming signal to a different bitdepth. | |
* Lowering the bitdepth of the signal creates distortion. Read more about Bitcrushing | |
* on [Wikipedia](https://en.wikipedia.org/wiki/Bitcrusher). | |
* | |
* @constructor | |
* @extends {Tone.Effect} | |
* @param {Number} bits The number of bits to downsample the signal. Nominal range | |
* of 1 to 8. | |
* @example | |
* //initialize crusher and route a synth through it | |
* var crusher = new Tone.BitCrusher(4).toMaster(); | |
* var synth = new Tone.MonoSynth().connect(crusher); | |
*/ | |
Tone.BitCrusher = function () { | |
var options = this.optionsObject(arguments, ['bits'], Tone.BitCrusher.defaults); | |
Tone.Effect.call(this, options); | |
var invStepSize = 1 / Math.pow(2, options.bits - 1); | |
/** | |
* Subtract the input signal and the modulus of the input signal | |
* @type {Tone.Subtract} | |
* @private | |
*/ | |
this._subtract = new Tone.Subtract(); | |
/** | |
* The mod function | |
* @type {Tone.Modulo} | |
* @private | |
*/ | |
this._modulo = new Tone.Modulo(invStepSize); | |
/** | |
* keeps track of the bits | |
* @type {number} | |
* @private | |
*/ | |
this._bits = options.bits; | |
//connect it up | |
this.effectSend.fan(this._subtract, this._modulo); | |
this._modulo.connect(this._subtract, 0, 1); | |
this._subtract.connect(this.effectReturn); | |
}; | |
Tone.extend(Tone.BitCrusher, Tone.Effect); | |
/** | |
* the default values | |
* @static | |
* @type {Object} | |
*/ | |
Tone.BitCrusher.defaults = { 'bits': 4 }; | |
/** | |
* The bit depth of the effect. Nominal range of 1-8. | |
* @memberOf Tone.BitCrusher# | |
* @type {number} | |
* @name bits | |
*/ | |
Object.defineProperty(Tone.BitCrusher.prototype, 'bits', { | |
get: function () { | |
return this._bits; | |
}, | |
set: function (bits) { | |
this._bits = bits; | |
var invStepSize = 1 / Math.pow(2, bits - 1); | |
this._modulo.value = invStepSize; | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.BitCrusher} this | |
*/ | |
Tone.BitCrusher.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this._subtract.dispose(); | |
this._subtract = null; | |
this._modulo.dispose(); | |
this._modulo = null; | |
return this; | |
}; | |
return Tone.BitCrusher; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.ChebyShev is a Chebyshev waveshaper, an effect which is good | |
* for making different types of distortion sounds. | |
* Note that odd orders sound very different from even ones, | |
* and order = 1 is no change. | |
* Read more at [music.columbia.edu](http://music.columbia.edu/cmc/musicandcomputers/chapter4/04_06.php). | |
* | |
* @extends {Tone.Effect} | |
* @constructor | |
* @param {Positive|Object} [order] The order of the chebyshev polynomial. Normal range between 1-100. | |
* @example | |
* //create a new cheby | |
* var cheby = new Tone.Chebyshev(50); | |
* //create a monosynth connected to our cheby | |
* synth = new Tone.MonoSynth().connect(cheby); | |
*/ | |
Tone.Chebyshev = function () { | |
var options = this.optionsObject(arguments, ['order'], Tone.Chebyshev.defaults); | |
Tone.Effect.call(this, options); | |
/** | |
* @type {WaveShaperNode} | |
* @private | |
*/ | |
this._shaper = new Tone.WaveShaper(4096); | |
/** | |
* holds onto the order of the filter | |
* @type {number} | |
* @private | |
*/ | |
this._order = options.order; | |
this.connectEffect(this._shaper); | |
this.order = options.order; | |
this.oversample = options.oversample; | |
}; | |
Tone.extend(Tone.Chebyshev, Tone.Effect); | |
/** | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Chebyshev.defaults = { | |
'order': 1, | |
'oversample': 'none' | |
}; | |
/** | |
* get the coefficient for that degree | |
* @param {number} x the x value | |
* @param {number} degree | |
* @param {Object} memo memoize the computed value. | |
* this speeds up computation greatly. | |
* @return {number} the coefficient | |
* @private | |
*/ | |
Tone.Chebyshev.prototype._getCoefficient = function (x, degree, memo) { | |
if (memo.hasOwnProperty(degree)) { | |
return memo[degree]; | |
} else if (degree === 0) { | |
memo[degree] = 0; | |
} else if (degree === 1) { | |
memo[degree] = x; | |
} else { | |
memo[degree] = 2 * x * this._getCoefficient(x, degree - 1, memo) - this._getCoefficient(x, degree - 2, memo); | |
} | |
return memo[degree]; | |
}; | |
/** | |
* The order of the Chebyshev polynomial which creates | |
* the equation which is applied to the incoming | |
* signal through a Tone.WaveShaper. The equations | |
* are in the form:<br> | |
* order 2: 2x^2 + 1<br> | |
* order 3: 4x^3 + 3x <br> | |
* @memberOf Tone.Chebyshev# | |
* @type {Positive} | |
* @name order | |
*/ | |
Object.defineProperty(Tone.Chebyshev.prototype, 'order', { | |
get: function () { | |
return this._order; | |
}, | |
set: function (order) { | |
this._order = order; | |
var curve = new Array(4096); | |
var len = curve.length; | |
for (var i = 0; i < len; ++i) { | |
var x = i * 2 / len - 1; | |
if (x === 0) { | |
//should output 0 when input is 0 | |
curve[i] = 0; | |
} else { | |
curve[i] = this._getCoefficient(x, order, {}); | |
} | |
} | |
this._shaper.curve = curve; | |
} | |
}); | |
/** | |
* The oversampling of the effect. Can either be "none", "2x" or "4x". | |
* @memberOf Tone.Chebyshev# | |
* @type {string} | |
* @name oversample | |
*/ | |
Object.defineProperty(Tone.Chebyshev.prototype, 'oversample', { | |
get: function () { | |
return this._shaper.oversample; | |
}, | |
set: function (oversampling) { | |
this._shaper.oversample = oversampling; | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.Chebyshev} this | |
*/ | |
Tone.Chebyshev.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this._shaper.dispose(); | |
this._shaper = null; | |
return this; | |
}; | |
return Tone.Chebyshev; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Base class for Stereo effects. Provides effectSendL/R and effectReturnL/R. | |
* | |
* @constructor | |
* @extends {Tone.Effect} | |
*/ | |
Tone.StereoEffect = function () { | |
Tone.call(this); | |
//get the defaults | |
var options = this.optionsObject(arguments, ['wet'], Tone.Effect.defaults); | |
/** | |
* the drywet knob to control the amount of effect | |
* @type {Tone.CrossFade} | |
* @private | |
*/ | |
this._dryWet = new Tone.CrossFade(options.wet); | |
/** | |
* The wet control, i.e. how much of the effected | |
* will pass through to the output. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.wet = this._dryWet.fade; | |
/** | |
* then split it | |
* @type {Tone.Split} | |
* @private | |
*/ | |
this._split = new Tone.Split(); | |
/** | |
* the effects send LEFT | |
* @type {GainNode} | |
* @private | |
*/ | |
this.effectSendL = this._split.left; | |
/** | |
* the effects send RIGHT | |
* @type {GainNode} | |
* @private | |
*/ | |
this.effectSendR = this._split.right; | |
/** | |
* the stereo effect merger | |
* @type {Tone.Merge} | |
* @private | |
*/ | |
this._merge = new Tone.Merge(); | |
/** | |
* the effect return LEFT | |
* @type {GainNode} | |
* @private | |
*/ | |
this.effectReturnL = this._merge.left; | |
/** | |
* the effect return RIGHT | |
* @type {GainNode} | |
* @private | |
*/ | |
this.effectReturnR = this._merge.right; | |
//connections | |
this.input.connect(this._split); | |
//dry wet connections | |
this.input.connect(this._dryWet, 0, 0); | |
this._merge.connect(this._dryWet, 0, 1); | |
this._dryWet.connect(this.output); | |
this._readOnly(['wet']); | |
}; | |
Tone.extend(Tone.StereoEffect, Tone.Effect); | |
/** | |
* Clean up. | |
* @returns {Tone.StereoEffect} this | |
*/ | |
Tone.StereoEffect.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.StereoEffect; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.FeedbackEffect provides a loop between an | |
* audio source and its own output. This is a base-class | |
* for feedback effects. | |
* | |
* @constructor | |
* @extends {Tone.Effect} | |
* @param {NormalRange|Object} [feedback] The initial feedback value. | |
*/ | |
Tone.FeedbackEffect = function () { | |
var options = this.optionsObject(arguments, ['feedback']); | |
options = this.defaultArg(options, Tone.FeedbackEffect.defaults); | |
Tone.Effect.call(this, options); | |
/** | |
* The amount of signal which is fed back into the effect input. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.feedback = new Tone.Signal(options.feedback, Tone.Type.NormalRange); | |
/** | |
* the gain which controls the feedback | |
* @type {GainNode} | |
* @private | |
*/ | |
this._feedbackGain = this.context.createGain(); | |
//the feedback loop | |
this.effectReturn.chain(this._feedbackGain, this.effectSend); | |
this.feedback.connect(this._feedbackGain.gain); | |
this._readOnly(['feedback']); | |
}; | |
Tone.extend(Tone.FeedbackEffect, Tone.Effect); | |
/** | |
* @static | |
* @type {Object} | |
*/ | |
Tone.FeedbackEffect.defaults = { 'feedback': 0.125 }; | |
/** | |
* Clean up. | |
* @returns {Tone.FeedbackEffect} this | |
*/ | |
Tone.FeedbackEffect.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this._writable(['feedback']); | |
this.feedback.dispose(); | |
this.feedback = null; | |
this._feedbackGain.disconnect(); | |
this._feedbackGain = null; | |
return this; | |
}; | |
return Tone.FeedbackEffect; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Just like a stereo feedback effect, but the feedback is routed from left to right | |
* and right to left instead of on the same channel. | |
* | |
* @constructor | |
* @extends {Tone.FeedbackEffect} | |
*/ | |
Tone.StereoXFeedbackEffect = function () { | |
var options = this.optionsObject(arguments, ['feedback'], Tone.FeedbackEffect.defaults); | |
Tone.StereoEffect.call(this, options); | |
/** | |
* The amount of feedback from the output | |
* back into the input of the effect (routed | |
* across left and right channels). | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.feedback = new Tone.Signal(options.feedback, Tone.Type.NormalRange); | |
/** | |
* the left side feeback | |
* @type {GainNode} | |
* @private | |
*/ | |
this._feedbackLR = this.context.createGain(); | |
/** | |
* the right side feeback | |
* @type {GainNode} | |
* @private | |
*/ | |
this._feedbackRL = this.context.createGain(); | |
//connect it up | |
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']); | |
}; | |
Tone.extend(Tone.StereoXFeedbackEffect, Tone.FeedbackEffect); | |
/** | |
* clean up | |
* @returns {Tone.StereoXFeedbackEffect} this | |
*/ | |
Tone.StereoXFeedbackEffect.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.StereoXFeedbackEffect; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Chorus is a stereo chorus effect with feedback composed of | |
* a left and right delay with a Tone.LFO applied to the delayTime of each channel. | |
* Inspiration from [Tuna.js](https://github.com/Dinahmoe/tuna/blob/master/tuna.js). | |
* Read more on the chorus effect on [SoundOnSound](http://www.soundonsound.com/sos/jun04/articles/synthsecrets.htm). | |
* | |
* @constructor | |
* @extends {Tone.StereoXFeedbackEffect} | |
* @param {Frequency|Object} [frequency] The frequency of the LFO. | |
* @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); | |
* var synth = new Tone.PolySynth(4, Tone.MonoSynth).connect(chorus); | |
* synth.triggerAttackRelease(["C3","E3","G3"], "8n"); | |
*/ | |
Tone.Chorus = function () { | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'delayTime', | |
'depth' | |
], Tone.Chorus.defaults); | |
Tone.StereoXFeedbackEffect.call(this, options); | |
/** | |
* the depth of the chorus | |
* @type {number} | |
* @private | |
*/ | |
this._depth = options.depth; | |
/** | |
* the delayTime | |
* @type {number} | |
* @private | |
*/ | |
this._delayTime = options.delayTime / 1000; | |
/** | |
* the lfo which controls the delayTime | |
* @type {Tone.LFO} | |
* @private | |
*/ | |
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({ | |
'frequency': options.frequency, | |
'min': 0, | |
'max': 1, | |
'phase': 180 | |
}); | |
/** | |
* delay for left | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._delayNodeL = this.context.createDelay(); | |
/** | |
* delay for right | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._delayNodeR = this.context.createDelay(); | |
/** | |
* The frequency of the LFO which modulates the delayTime. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = this._lfoL.frequency; | |
//connections | |
this.effectSendL.chain(this._delayNodeL, this.effectReturnL); | |
this.effectSendR.chain(this._delayNodeR, this.effectReturnR); | |
//and pass through to make the detune apparent | |
this.effectSendL.connect(this.effectReturnL); | |
this.effectSendR.connect(this.effectReturnR); | |
//lfo setup | |
this._lfoL.connect(this._delayNodeL.delayTime); | |
this._lfoR.connect(this._delayNodeR.delayTime); | |
//start the lfo | |
this._lfoL.start(); | |
this._lfoR.start(); | |
//have one LFO frequency control the other | |
this._lfoL.frequency.connect(this._lfoR.frequency); | |
//set the initial values | |
this.depth = this._depth; | |
this.frequency.value = options.frequency; | |
this.type = options.type; | |
this._readOnly(['frequency']); | |
this.spread = options.spread; | |
}; | |
Tone.extend(Tone.Chorus, Tone.StereoXFeedbackEffect); | |
/** | |
* @static | |
* @type {Object} | |
*/ | |
Tone.Chorus.defaults = { | |
'frequency': 1.5, | |
'delayTime': 3.5, | |
'depth': 0.7, | |
'feedback': 0.1, | |
'type': 'sine', | |
'spread': 180 | |
}; | |
/** | |
* The depth of the effect. A depth of 1 makes the delayTime | |
* modulate between 0 and 2*delayTime (centered around the delayTime). | |
* @memberOf Tone.Chorus# | |
* @type {NormalRange} | |
* @name depth | |
*/ | |
Object.defineProperty(Tone.Chorus.prototype, 'depth', { | |
get: function () { | |
return this._depth; | |
}, | |
set: function (depth) { | |
this._depth = depth; | |
var deviation = this._delayTime * depth; | |
this._lfoL.min = Math.max(this._delayTime - deviation, 0); | |
this._lfoL.max = this._delayTime + deviation; | |
this._lfoR.min = Math.max(this._delayTime - deviation, 0); | |
this._lfoR.max = this._delayTime + deviation; | |
} | |
}); | |
/** | |
* The delayTime in milliseconds of the chorus. A larger delayTime | |
* will give a more pronounced effect. Nominal range a delayTime | |
* is between 2 and 20ms. | |
* @memberOf Tone.Chorus# | |
* @type {Milliseconds} | |
* @name delayTime | |
*/ | |
Object.defineProperty(Tone.Chorus.prototype, 'delayTime', { | |
get: function () { | |
return this._delayTime * 1000; | |
}, | |
set: function (delayTime) { | |
this._delayTime = delayTime / 1000; | |
this.depth = this._depth; | |
} | |
}); | |
/** | |
* The oscillator type of the LFO. | |
* @memberOf Tone.Chorus# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.Chorus.prototype, 'type', { | |
get: function () { | |
return this._lfoL.type; | |
}, | |
set: function (type) { | |
this._lfoL.type = type; | |
this._lfoR.type = type; | |
} | |
}); | |
/** | |
* Amount of stereo spread. When set to 0, both LFO's will be panned centrally. | |
* When set to 180, LFO's will be panned hard left and right respectively. | |
* @memberOf Tone.Chorus# | |
* @type {Degrees} | |
* @name spread | |
*/ | |
Object.defineProperty(Tone.Chorus.prototype, 'spread', { | |
get: function () { | |
return this._lfoR.phase - this._lfoL.phase; //180 | |
}, | |
set: function (spread) { | |
this._lfoL.phase = 90 - spread / 2; | |
this._lfoR.phase = spread / 2 + 90; | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.Chorus} this | |
*/ | |
Tone.Chorus.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.Chorus; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Convolver is a wrapper around the Native Web Audio | |
* [ConvolverNode](http://webaudio.github.io/web-audio-api/#the-convolvernode-interface). | |
* Convolution is useful for reverb and filter emulation. Read more about convolution reverb on | |
* [Wikipedia](https://en.wikipedia.org/wiki/Convolution_reverb). | |
* | |
* @constructor | |
* @extends {Tone.Effect} | |
* @param {string|Tone.Buffer|Object} [url] The URL of the impulse response or the Tone.Buffer | |
* contianing the impulse response. | |
* @example | |
* //initializing the convolver with an impulse response | |
* var convolver = new Tone.Convolver("./path/to/ir.wav"); | |
* convolver.toMaster(); | |
* //after the buffer has loaded | |
* Tone.Buffer.onload = function(){ | |
* //testing out convolution with a noise burst | |
* var burst = new Tone.NoiseSynth().connect(convolver); | |
* burst.triggerAttackRelease("16n"); | |
* }; | |
*/ | |
Tone.Convolver = function () { | |
var options = this.optionsObject(arguments, ['url'], Tone.Convolver.defaults); | |
Tone.Effect.call(this, options); | |
/** | |
* convolver node | |
* @type {ConvolverNode} | |
* @private | |
*/ | |
this._convolver = this.context.createConvolver(); | |
/** | |
* the convolution buffer | |
* @type {Tone.Buffer} | |
* @private | |
*/ | |
this._buffer = new Tone.Buffer(options.url, function (buffer) { | |
this.buffer = buffer; | |
options.onload(); | |
}.bind(this)); | |
this.connectEffect(this._convolver); | |
}; | |
Tone.extend(Tone.Convolver, Tone.Effect); | |
/** | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Convolver.defaults = { | |
'url': '', | |
'onload': Tone.noOp | |
}; | |
/** | |
* The convolver's buffer | |
* @memberOf Tone.Convolver# | |
* @type {AudioBuffer} | |
* @name buffer | |
*/ | |
Object.defineProperty(Tone.Convolver.prototype, 'buffer', { | |
get: function () { | |
return this._buffer.get(); | |
}, | |
set: function (buffer) { | |
this._buffer.set(buffer); | |
this._convolver.buffer = this._buffer.get(); | |
} | |
}); | |
/** | |
* Load an impulse response url as an audio buffer. | |
* Decodes the audio asynchronously and 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 | |
* @returns {Tone.Convolver} this | |
*/ | |
Tone.Convolver.prototype.load = function (url, callback) { | |
this._buffer.load(url, function (buff) { | |
this.buffer = buff; | |
if (callback) { | |
callback(); | |
} | |
}.bind(this)); | |
return this; | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.Convolver} this | |
*/ | |
Tone.Convolver.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this._convolver.disconnect(); | |
this._convolver = null; | |
this._buffer.dispose(); | |
this._buffer = null; | |
return this; | |
}; | |
return Tone.Convolver; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Distortion is a simple distortion effect using Tone.WaveShaper. | |
* Algorithm from [a stackoverflow answer](http://stackoverflow.com/a/22313408). | |
* | |
* @extends {Tone.Effect} | |
* @constructor | |
* @param {Number|Object} [distortion] The amount of distortion (nominal range of 0-1) | |
* @example | |
* var dist = new Tone.Distortion(0.8).toMaster(); | |
* var fm = new Tone.SimpleFM().connect(dist); | |
* //this sounds good on bass notes | |
* fm.triggerAttackRelease("A1", "8n"); | |
*/ | |
Tone.Distortion = function () { | |
var options = this.optionsObject(arguments, ['distortion'], Tone.Distortion.defaults); | |
Tone.Effect.call(this, options); | |
/** | |
* @type {Tone.WaveShaper} | |
* @private | |
*/ | |
this._shaper = new Tone.WaveShaper(4096); | |
/** | |
* holds the distortion amount | |
* @type {number} | |
* @private | |
*/ | |
this._distortion = options.distortion; | |
this.connectEffect(this._shaper); | |
this.distortion = options.distortion; | |
this.oversample = options.oversample; | |
}; | |
Tone.extend(Tone.Distortion, Tone.Effect); | |
/** | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Distortion.defaults = { | |
'distortion': 0.4, | |
'oversample': 'none' | |
}; | |
/** | |
* The amount of distortion. | |
* @memberOf Tone.Distortion# | |
* @type {NormalRange} | |
* @name distortion | |
*/ | |
Object.defineProperty(Tone.Distortion.prototype, 'distortion', { | |
get: function () { | |
return this._distortion; | |
}, | |
set: function (amount) { | |
this._distortion = amount; | |
var k = amount * 100; | |
var deg = Math.PI / 180; | |
this._shaper.setMap(function (x) { | |
if (Math.abs(x) < 0.001) { | |
//should output 0 when input is 0 | |
return 0; | |
} else { | |
return (3 + k) * x * 20 * deg / (Math.PI + k * Math.abs(x)); | |
} | |
}); | |
} | |
}); | |
/** | |
* The oversampling of the effect. Can either be "none", "2x" or "4x". | |
* @memberOf Tone.Distortion# | |
* @type {string} | |
* @name oversample | |
*/ | |
Object.defineProperty(Tone.Distortion.prototype, 'oversample', { | |
get: function () { | |
return this._shaper.oversample; | |
}, | |
set: function (oversampling) { | |
this._shaper.oversample = oversampling; | |
} | |
}); | |
/** | |
* Clean up. | |
* @returns {Tone.Distortion} this | |
*/ | |
Tone.Distortion.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this._shaper.dispose(); | |
this._shaper = null; | |
return this; | |
}; | |
return Tone.Distortion; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.FeedbackDelay is a DelayNode in which part of output | |
* signal is fed back into the delay. | |
* | |
* @constructor | |
* @extends {Tone.FeedbackEffect} | |
* @param {Time|Object} [delayTime] The delay applied to the incoming signal. | |
* @param {NormalRange=} feedback The amount of the effected signal which | |
* is fed back through the delay. | |
* @example | |
* var feedbackDelay = new Tone.FeedbackDelay("8n", 0.5).toMaster(); | |
* var tom = new Tone.DrumSynth({ | |
* "octaves" : 4, | |
* "pitchDecay" : 0.1 | |
* }).connect(feedbackDelay); | |
* tom.triggerAttackRelease("A2","32n"); | |
*/ | |
Tone.FeedbackDelay = function () { | |
var options = this.optionsObject(arguments, [ | |
'delayTime', | |
'feedback' | |
], Tone.FeedbackDelay.defaults); | |
Tone.FeedbackEffect.call(this, options); | |
/** | |
* The delayTime of the DelayNode. | |
* @type {Time} | |
* @signal | |
*/ | |
this.delayTime = new Tone.Signal(options.delayTime, Tone.Type.Time); | |
/** | |
* the delay node | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._delayNode = this.context.createDelay(4); | |
// connect it up | |
this.connectEffect(this._delayNode); | |
this.delayTime.connect(this._delayNode.delayTime); | |
this._readOnly(['delayTime']); | |
}; | |
Tone.extend(Tone.FeedbackDelay, Tone.FeedbackEffect); | |
/** | |
* The default values. | |
* @const | |
* @static | |
* @type {Object} | |
*/ | |
Tone.FeedbackDelay.defaults = { 'delayTime': 0.25 }; | |
/** | |
* clean up | |
* @returns {Tone.FeedbackDelay} this | |
*/ | |
Tone.FeedbackDelay.prototype.dispose = function () { | |
Tone.FeedbackEffect.prototype.dispose.call(this); | |
this.delayTime.dispose(); | |
this._delayNode.disconnect(); | |
this._delayNode = null; | |
this._writable(['delayTime']); | |
this.delayTime = null; | |
return this; | |
}; | |
return Tone.FeedbackDelay; | |
}); | |
Module(function (Tone) { | |
/** | |
* an array of comb filter delay values from Freeverb implementation | |
* @static | |
* @private | |
* @type {Array} | |
*/ | |
var combFilterTunings = [ | |
1557 / 44100, | |
1617 / 44100, | |
1491 / 44100, | |
1422 / 44100, | |
1277 / 44100, | |
1356 / 44100, | |
1188 / 44100, | |
1116 / 44100 | |
]; | |
/** | |
* an array of allpass filter frequency values from Freeverb implementation | |
* @private | |
* @static | |
* @type {Array} | |
*/ | |
var allpassFilterFrequencies = [ | |
225, | |
556, | |
441, | |
341 | |
]; | |
/** | |
* @class Tone.Freeverb is a reverb based on [Freeverb](https://ccrma.stanford.edu/~jos/pasp/Freeverb.html). | |
* Read more on reverb on [SoundOnSound](http://www.soundonsound.com/sos/may00/articles/reverb.htm). | |
* | |
* @extends {Tone.Effect} | |
* @constructor | |
* @param {NormalRange|Object} [roomSize] Correlated to the decay time. | |
* @param {Frequency} [dampening] The cutoff frequency of a lowpass filter as part | |
* of the reverb. | |
* @example | |
* var freeverb = new Tone.Freeverb().toMaster(); | |
* freeverb.dampening.value = 1000; | |
* //routing synth through the reverb | |
* var synth = new Tone.AMSynth().connect(freeverb); | |
*/ | |
Tone.Freeverb = function () { | |
var options = this.optionsObject(arguments, [ | |
'roomSize', | |
'dampening' | |
], Tone.Freeverb.defaults); | |
Tone.StereoEffect.call(this, options); | |
/** | |
* The roomSize value between. A larger roomSize | |
* will result in a longer decay. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.roomSize = new Tone.Signal(options.roomSize, Tone.Type.NormalRange); | |
/** | |
* The amount of dampening of the reverberant signal. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.dampening = new Tone.Signal(options.dampening, Tone.Type.Frequency); | |
/** | |
* the comb filters | |
* @type {Array} | |
* @private | |
*/ | |
this._combFilters = []; | |
/** | |
* the allpass filters on the left | |
* @type {Array} | |
* @private | |
*/ | |
this._allpassFiltersL = []; | |
/** | |
* the allpass filters on the right | |
* @type {Array} | |
* @private | |
*/ | |
this._allpassFiltersR = []; | |
//make the allpass filters on teh right | |
for (var l = 0; l < allpassFilterFrequencies.length; l++) { | |
var allpassL = this.context.createBiquadFilter(); | |
allpassL.type = 'allpass'; | |
allpassL.frequency.value = allpassFilterFrequencies[l]; | |
this._allpassFiltersL.push(allpassL); | |
} | |
//make the allpass filters on the left | |
for (var r = 0; r < allpassFilterFrequencies.length; r++) { | |
var allpassR = this.context.createBiquadFilter(); | |
allpassR.type = 'allpass'; | |
allpassR.frequency.value = allpassFilterFrequencies[r]; | |
this._allpassFiltersR.push(allpassR); | |
} | |
//make the comb filters | |
for (var c = 0; c < combFilterTunings.length; c++) { | |
var lfpf = new Tone.LowpassCombFilter(combFilterTunings[c]); | |
if (c < combFilterTunings.length / 2) { | |
this.effectSendL.chain(lfpf, this._allpassFiltersL[0]); | |
} else { | |
this.effectSendR.chain(lfpf, this._allpassFiltersR[0]); | |
} | |
this.roomSize.connect(lfpf.resonance); | |
this.dampening.connect(lfpf.dampening); | |
this._combFilters.push(lfpf); | |
} | |
//chain the allpass filters togetehr | |
this.connectSeries.apply(this, this._allpassFiltersL); | |
this.connectSeries.apply(this, this._allpassFiltersR); | |
this._allpassFiltersL[this._allpassFiltersL.length - 1].connect(this.effectReturnL); | |
this._allpassFiltersR[this._allpassFiltersR.length - 1].connect(this.effectReturnR); | |
this._readOnly([ | |
'roomSize', | |
'dampening' | |
]); | |
}; | |
Tone.extend(Tone.Freeverb, Tone.StereoEffect); | |
/** | |
* @static | |
* @type {Object} | |
*/ | |
Tone.Freeverb.defaults = { | |
'roomSize': 0.7, | |
'dampening': 3000 | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.Freeverb} this | |
*/ | |
Tone.Freeverb.prototype.dispose = function () { | |
Tone.StereoEffect.prototype.dispose.call(this); | |
for (var al = 0; al < this._allpassFiltersL.length; al++) { | |
this._allpassFiltersL[al].disconnect(); | |
this._allpassFiltersL[al] = null; | |
} | |
this._allpassFiltersL = null; | |
for (var ar = 0; ar < this._allpassFiltersR.length; ar++) { | |
this._allpassFiltersR[ar].disconnect(); | |
this._allpassFiltersR[ar] = null; | |
} | |
this._allpassFiltersR = null; | |
for (var cf = 0; cf < this._combFilters.length; cf++) { | |
this._combFilters[cf].dispose(); | |
this._combFilters[cf] = null; | |
} | |
this._combFilters = null; | |
this._writable([ | |
'roomSize', | |
'dampening' | |
]); | |
this.roomSize.dispose(); | |
this.roomSize = null; | |
this.dampening.dispose(); | |
this.dampening = null; | |
return this; | |
}; | |
return Tone.Freeverb; | |
}); | |
Module(function (Tone) { | |
/** | |
* an array of the comb filter delay time values | |
* @private | |
* @static | |
* @type {Array} | |
*/ | |
var combFilterDelayTimes = [ | |
1687 / 25000, | |
1601 / 25000, | |
2053 / 25000, | |
2251 / 25000 | |
]; | |
/** | |
* the resonances of each of the comb filters | |
* @private | |
* @static | |
* @type {Array} | |
*/ | |
var combFilterResonances = [ | |
0.773, | |
0.802, | |
0.753, | |
0.733 | |
]; | |
/** | |
* the allpass filter frequencies | |
* @private | |
* @static | |
* @type {Array} | |
*/ | |
var allpassFilterFreqs = [ | |
347, | |
113, | |
37 | |
]; | |
/** | |
* @class Tone.JCReverb is a simple [Schroeder Reverberator](https://ccrma.stanford.edu/~jos/pasp/Schroeder_Reverberators.html) | |
* tuned by John Chowning in 1970. | |
* It is made up of three allpass filters and four Tone.FeedbackCombFilter. | |
* | |
* | |
* @extends {Tone.Effect} | |
* @constructor | |
* @param {NormalRange|Object} [roomSize] Coorelates to the decay time. | |
* @example | |
* var reverb = new Tone.JCReverb(0.4).connect(Tone.Master); | |
* var delay = new Tone.FeedbackDelay(0.5); | |
* //connecting the synth to reverb through delay | |
* var synth = new Tone.DuoSynth().chain(delay, reverb); | |
* synth.triggerAttackRelease("A4","8n"); | |
*/ | |
Tone.JCReverb = function () { | |
var options = this.optionsObject(arguments, ['roomSize'], Tone.JCReverb.defaults); | |
Tone.StereoEffect.call(this, options); | |
/** | |
* room size control values between [0,1] | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.roomSize = new Tone.Signal(options.roomSize, Tone.Type.NormalRange); | |
/** | |
* scale the room size | |
* @type {Tone.Scale} | |
* @private | |
*/ | |
this._scaleRoomSize = new Tone.Scale(-0.733, 0.197); | |
/** | |
* a series of allpass filters | |
* @type {Array} | |
* @private | |
*/ | |
this._allpassFilters = []; | |
/** | |
* parallel feedback comb filters | |
* @type {Array} | |
* @private | |
*/ | |
this._feedbackCombFilters = []; | |
//make the allpass filters | |
for (var af = 0; af < allpassFilterFreqs.length; af++) { | |
var allpass = this.context.createBiquadFilter(); | |
allpass.type = 'allpass'; | |
allpass.frequency.value = allpassFilterFreqs[af]; | |
this._allpassFilters.push(allpass); | |
} | |
//and the comb filters | |
for (var cf = 0; cf < combFilterDelayTimes.length; cf++) { | |
var fbcf = new Tone.FeedbackCombFilter(combFilterDelayTimes[cf], 0.1); | |
this._scaleRoomSize.connect(fbcf.resonance); | |
fbcf.resonance.value = combFilterResonances[cf]; | |
this._allpassFilters[this._allpassFilters.length - 1].connect(fbcf); | |
if (cf < combFilterDelayTimes.length / 2) { | |
fbcf.connect(this.effectReturnL); | |
} else { | |
fbcf.connect(this.effectReturnR); | |
} | |
this._feedbackCombFilters.push(fbcf); | |
} | |
//chain the allpass filters together | |
this.roomSize.connect(this._scaleRoomSize); | |
this.connectSeries.apply(this, this._allpassFilters); | |
this.effectSendL.connect(this._allpassFilters[0]); | |
this.effectSendR.connect(this._allpassFilters[0]); | |
this._readOnly(['roomSize']); | |
}; | |
Tone.extend(Tone.JCReverb, Tone.StereoEffect); | |
/** | |
* the default values | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.JCReverb.defaults = { 'roomSize': 0.5 }; | |
/** | |
* Clean up. | |
* @returns {Tone.JCReverb} this | |
*/ | |
Tone.JCReverb.prototype.dispose = function () { | |
Tone.StereoEffect.prototype.dispose.call(this); | |
for (var apf = 0; apf < this._allpassFilters.length; apf++) { | |
this._allpassFilters[apf].disconnect(); | |
this._allpassFilters[apf] = null; | |
} | |
this._allpassFilters = null; | |
for (var fbcf = 0; fbcf < this._feedbackCombFilters.length; fbcf++) { | |
this._feedbackCombFilters[fbcf].dispose(); | |
this._feedbackCombFilters[fbcf] = null; | |
} | |
this._feedbackCombFilters = null; | |
this._writable(['roomSize']); | |
this.roomSize.dispose(); | |
this.roomSize = null; | |
this._scaleRoomSize.dispose(); | |
this._scaleRoomSize = null; | |
return this; | |
}; | |
return Tone.JCReverb; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Mid/Side processing separates the the 'mid' signal | |
* (which comes out of both the left and the right channel) | |
* and the 'side' (which only comes out of the the side channels) | |
* and effects them separately before being recombined. | |
* Applies a Mid/Side seperation and recombination. | |
* Algorithm found in [kvraudio forums](http://www.kvraudio.com/forum/viewtopic.php?t=212587). | |
* <br><br> | |
* This is a base-class for Mid/Side Effects. | |
* | |
* @extends {Tone.Effect} | |
* @constructor | |
*/ | |
Tone.MidSideEffect = function () { | |
Tone.Effect.apply(this, arguments); | |
/** | |
* The mid/side split | |
* @type {Tone.MidSideSplit} | |
* @private | |
*/ | |
this._midSideSplit = new Tone.MidSideSplit(); | |
/** | |
* The mid/side merge | |
* @type {Tone.MidSideMerge} | |
* @private | |
*/ | |
this._midSideMerge = new Tone.MidSideMerge(); | |
/** | |
* The mid send. Connect to mid processing | |
* @type {Tone.Expr} | |
* @private | |
*/ | |
this.midSend = this._midSideSplit.mid; | |
/** | |
* The side send. Connect to side processing | |
* @type {Tone.Expr} | |
* @private | |
*/ | |
this.sideSend = this._midSideSplit.side; | |
/** | |
* The mid return connection | |
* @type {GainNode} | |
* @private | |
*/ | |
this.midReturn = this._midSideMerge.mid; | |
/** | |
* The side return connection | |
* @type {GainNode} | |
* @private | |
*/ | |
this.sideReturn = this._midSideMerge.side; | |
//the connections | |
this.effectSend.connect(this._midSideSplit); | |
this._midSideMerge.connect(this.effectReturn); | |
}; | |
Tone.extend(Tone.MidSideEffect, Tone.Effect); | |
/** | |
* Clean up. | |
* @returns {Tone.MidSideEffect} this | |
*/ | |
Tone.MidSideEffect.prototype.dispose = function () { | |
Tone.Effect.prototype.dispose.call(this); | |
this._midSideSplit.dispose(); | |
this._midSideSplit = null; | |
this._midSideMerge.dispose(); | |
this._midSideMerge = null; | |
this.midSend = null; | |
this.sideSend = null; | |
this.midReturn = null; | |
this.sideReturn = null; | |
return this; | |
}; | |
return Tone.MidSideEffect; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Phaser is a phaser effect. Phasers work by changing the phase | |
* of different frequency components of an incoming signal. Read more on | |
* [Wikipedia](https://en.wikipedia.org/wiki/Phaser_(effect)). | |
* Inspiration for this phaser comes from [Tuna.js](https://github.com/Dinahmoe/tuna/). | |
* | |
* @extends {Tone.StereoEffect} | |
* @constructor | |
* @param {Frequency|Object} [frequency] The speed of the phasing. | |
* @param {number} [octaves] The octaves of the effect. | |
* @param {Frequency} [baseFrequency] The base frequency of the filters. | |
* @example | |
* var phaser = new Tone.Phaser({ | |
* "frequency" : 15, | |
* "octaves" : 5, | |
* "baseFrequency" : 1000 | |
* }).toMaster(); | |
* var synth = new Tone.FMSynth().connect(phaser); | |
* synth.triggerAttackRelease("E3", "2n"); | |
*/ | |
Tone.Phaser = function () { | |
//set the defaults | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'octaves', | |
'baseFrequency' | |
], Tone.Phaser.defaults); | |
Tone.StereoEffect.call(this, options); | |
/** | |
* the lfo which controls the frequency on the left side | |
* @type {Tone.LFO} | |
* @private | |
*/ | |
this._lfoL = new Tone.LFO(options.frequency, 0, 1); | |
/** | |
* the lfo which controls the frequency on the right side | |
* @type {Tone.LFO} | |
* @private | |
*/ | |
this._lfoR = new Tone.LFO(options.frequency, 0, 1); | |
this._lfoR.phase = 180; | |
/** | |
* the base modulation frequency | |
* @type {number} | |
* @private | |
*/ | |
this._baseFrequency = options.baseFrequency; | |
/** | |
* the octaves of the phasing | |
* @type {number} | |
* @private | |
*/ | |
this._octaves = options.octaves; | |
/** | |
* The quality factor of the filters | |
* @type {Positive} | |
* @signal | |
*/ | |
this.Q = new Tone.Signal(options.Q, Tone.Type.Positive); | |
/** | |
* the array of filters for the left side | |
* @type {Array} | |
* @private | |
*/ | |
this._filtersL = this._makeFilters(options.stages, this._lfoL, this.Q); | |
/** | |
* the array of filters for the left side | |
* @type {Array} | |
* @private | |
*/ | |
this._filtersR = this._makeFilters(options.stages, this._lfoR, this.Q); | |
/** | |
* the frequency of the effect | |
* @type {Tone.Signal} | |
*/ | |
this.frequency = this._lfoL.frequency; | |
this.frequency.value = options.frequency; | |
//connect them up | |
this.effectSendL.connect(this._filtersL[0]); | |
this.effectSendR.connect(this._filtersR[0]); | |
this._filtersL[options.stages - 1].connect(this.effectReturnL); | |
this._filtersR[options.stages - 1].connect(this.effectReturnR); | |
//control the frequency with one LFO | |
this._lfoL.frequency.connect(this._lfoR.frequency); | |
//set the options | |
this.baseFrequency = options.baseFrequency; | |
this.octaves = options.octaves; | |
//start the lfo | |
this._lfoL.start(); | |
this._lfoR.start(); | |
this._readOnly([ | |
'frequency', | |
'Q' | |
]); | |
}; | |
Tone.extend(Tone.Phaser, Tone.StereoEffect); | |
/** | |
* defaults | |
* @static | |
* @type {object} | |
*/ | |
Tone.Phaser.defaults = { | |
'frequency': 0.5, | |
'octaves': 3, | |
'stages': 10, | |
'Q': 10, | |
'baseFrequency': 350 | |
}; | |
/** | |
* @param {number} stages | |
* @returns {Array} the number of filters all connected together | |
* @private | |
*/ | |
Tone.Phaser.prototype._makeFilters = function (stages, connectToFreq, Q) { | |
var filters = new Array(stages); | |
//make all the filters | |
for (var i = 0; i < stages; i++) { | |
var filter = this.context.createBiquadFilter(); | |
filter.type = 'allpass'; | |
Q.connect(filter.Q); | |
connectToFreq.connect(filter.frequency); | |
filters[i] = filter; | |
} | |
this.connectSeries.apply(this, filters); | |
return filters; | |
}; | |
/** | |
* The number of octaves the phase goes above | |
* the baseFrequency | |
* @memberOf Tone.Phaser# | |
* @type {Positive} | |
* @name octaves | |
*/ | |
Object.defineProperty(Tone.Phaser.prototype, 'octaves', { | |
get: function () { | |
return this._octaves; | |
}, | |
set: function (octaves) { | |
this._octaves = octaves; | |
var max = this._baseFrequency * Math.pow(2, octaves); | |
this._lfoL.max = max; | |
this._lfoR.max = max; | |
} | |
}); | |
/** | |
* The the base frequency of the filters. | |
* @memberOf Tone.Phaser# | |
* @type {number} | |
* @name baseFrequency | |
*/ | |
Object.defineProperty(Tone.Phaser.prototype, 'baseFrequency', { | |
get: function () { | |
return this._baseFrequency; | |
}, | |
set: function (freq) { | |
this._baseFrequency = freq; | |
this._lfoL.min = freq; | |
this._lfoR.min = freq; | |
this.octaves = this._octaves; | |
} | |
}); | |
/** | |
* clean up | |
* @returns {Tone.Phaser} this | |
*/ | |
Tone.Phaser.prototype.dispose = function () { | |
Tone.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; | |
for (var i = 0; i < this._filtersL.length; i++) { | |
this._filtersL[i].disconnect(); | |
this._filtersL[i] = null; | |
} | |
this._filtersL = null; | |
for (var j = 0; j < this._filtersR.length; j++) { | |
this._filtersR[j].disconnect(); | |
this._filtersR[j] = null; | |
} | |
this._filtersR = null; | |
this.frequency = null; | |
return this; | |
}; | |
return Tone.Phaser; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.PingPongDelay is a feedback delay effect where the echo is heard | |
* first in one channel and next in the opposite channel. In a stereo | |
* system these are the right and left channels. | |
* PingPongDelay in more simplified terms is two Tone.FeedbackDelays | |
* with independent delay values. Each delay is routed to one channel | |
* (left or right), and the channel triggered second will always | |
* trigger at the same interval after the first. | |
* | |
* @constructor | |
* @extends {Tone.StereoXFeedbackEffect} | |
* @param {Time|Object} [delayTime] The delayTime between consecutive echos. | |
* @param {NormalRange=} feedback The amount of the effected signal which | |
* is fed back through the delay. | |
* @example | |
* var pingPong = new Tone.PingPongDelay("4n", 0.2).toMaster(); | |
* var drum = new Tone.DrumSynth().connect(pingPong); | |
* drum.triggerAttackRelease("C4", "32n"); | |
*/ | |
Tone.PingPongDelay = function () { | |
var options = this.optionsObject(arguments, [ | |
'delayTime', | |
'feedback' | |
], Tone.PingPongDelay.defaults); | |
Tone.StereoXFeedbackEffect.call(this, options); | |
/** | |
* the delay node on the left side | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._leftDelay = this.context.createDelay(options.maxDelayTime); | |
/** | |
* the delay node on the right side | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._rightDelay = this.context.createDelay(options.maxDelayTime); | |
/** | |
* the predelay on the right side | |
* @type {DelayNode} | |
* @private | |
*/ | |
this._rightPreDelay = this.context.createDelay(options.maxDelayTime); | |
/** | |
* the delay time signal | |
* @type {Time} | |
* @signal | |
*/ | |
this.delayTime = new Tone.Signal(options.delayTime, Tone.Type.Time); | |
//connect it up | |
this.effectSendL.chain(this._leftDelay, this.effectReturnL); | |
this.effectSendR.chain(this._rightPreDelay, this._rightDelay, this.effectReturnR); | |
this.delayTime.fan(this._leftDelay.delayTime, this._rightDelay.delayTime, this._rightPreDelay.delayTime); | |
//rearranged the feedback to be after the rightPreDelay | |
this._feedbackLR.disconnect(); | |
this._feedbackLR.connect(this._rightDelay); | |
this._readOnly(['delayTime']); | |
}; | |
Tone.extend(Tone.PingPongDelay, Tone.StereoXFeedbackEffect); | |
/** | |
* @static | |
* @type {Object} | |
*/ | |
Tone.PingPongDelay.defaults = { | |
'delayTime': 0.25, | |
'maxDelayTime': 1 | |
}; | |
/** | |
* Clean up. | |
* @returns {Tone.PingPongDelay} this | |
*/ | |
Tone.PingPongDelay.prototype.dispose = function () { | |
Tone.StereoXFeedbackEffect.prototype.dispose.call(this); | |
this._leftDelay.disconnect(); | |
this._leftDelay = null; | |
this._rightDelay.disconnect(); | |
this._rightDelay = null; | |
this._rightPreDelay.disconnect(); | |
this._rightPreDelay = null; | |
this._writable(['delayTime']); | |
this.delayTime.dispose(); | |
this.delayTime = null; | |
return this; | |
}; | |
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} | |
* @private | |
*/ | |
this._crossFadeLFO = new Tone.LFO({ | |
'min': 0, | |
'max': 1, | |
'type': 'triangle', | |
'phase': 90 | |
}).connect(this._crossFade.fade); | |
/** | |
* The delay node | |
* @type {Tone.Delay} | |
* @private | |
*/ | |
this._feedbackDelay = new Tone.Delay(options.delayTime); | |
/** | |
* The amount of delay on the input signal | |
* @type {Time} | |
* @signal | |
*/ | |
this.delayTime = this._feedbackDelay.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._feedbackDelay, 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._feedbackDelay.dispose(); | |
this._feedbackDelay = null; | |
this.delayTime = null; | |
return this; | |
}; | |
return Tone.PitchShift; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Base class for stereo feedback effects where the effectReturn | |
* is fed back into the same channel. | |
* | |
* @constructor | |
* @extends {Tone.FeedbackEffect} | |
*/ | |
Tone.StereoFeedbackEffect = function () { | |
var options = this.optionsObject(arguments, ['feedback'], Tone.FeedbackEffect.defaults); | |
Tone.StereoEffect.call(this, options); | |
/** | |
* controls the amount of feedback | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.feedback = new Tone.Signal(options.feedback, Tone.Type.NormalRange); | |
/** | |
* the left side feeback | |
* @type {GainNode} | |
* @private | |
*/ | |
this._feedbackL = this.context.createGain(); | |
/** | |
* the right side feeback | |
* @type {GainNode} | |
* @private | |
*/ | |
this._feedbackR = this.context.createGain(); | |
//connect it up | |
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']); | |
}; | |
Tone.extend(Tone.StereoFeedbackEffect, Tone.FeedbackEffect); | |
/** | |
* clean up | |
* @returns {Tone.StereoFeedbackEffect} this | |
*/ | |
Tone.StereoFeedbackEffect.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.StereoFeedbackEffect; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Applies a width factor to the mid/side seperation. | |
* 0 is all mid and 1 is all side. | |
* Algorithm found in [kvraudio forums](http://www.kvraudio.com/forum/viewtopic.php?t=212587). | |
* <br><br> | |
* <code> | |
* Mid *= 2*(1-width)<br> | |
* Side *= 2*width | |
* </code> | |
* | |
* @extends {Tone.MidSideEffect} | |
* @constructor | |
* @param {NormalRange|Object} [width] The stereo width. A width of 0 is mono and 1 is stereo. 0.5 is no change. | |
*/ | |
Tone.StereoWidener = function () { | |
var options = this.optionsObject(arguments, ['width'], Tone.StereoWidener.defaults); | |
Tone.MidSideEffect.call(this, options); | |
/** | |
* The width control. 0 = 100% mid. 1 = 100% side. 0.5 = no change. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.width = new Tone.Signal(options.width, Tone.Type.NormalRange); | |
/** | |
* Mid multiplier | |
* @type {Tone.Expr} | |
* @private | |
*/ | |
this._midMult = new Tone.Expr('$0 * ($1 * (1 - $2))'); | |
/** | |
* Side multiplier | |
* @type {Tone.Expr} | |
* @private | |
*/ | |
this._sideMult = new Tone.Expr('$0 * ($1 * $2)'); | |
/** | |
* constant output of 2 | |
* @type {Tone} | |
* @private | |
*/ | |
this._two = new Tone.Signal(2); | |
//the mid chain | |
this._two.connect(this._midMult, 0, 1); | |
this.width.connect(this._midMult, 0, 2); | |
//the side chain | |
this._two.connect(this._sideMult, 0, 1); | |
this.width.connect(this._sideMult, 0, 2); | |
//connect it to the effect send/return | |
this.midSend.chain(this._midMult, this.midReturn); | |
this.sideSend.chain(this._sideMult, this.sideReturn); | |
this._readOnly(['width']); | |
}; | |
Tone.extend(Tone.StereoWidener, Tone.MidSideEffect); | |
/** | |
* the default values | |
* @static | |
* @type {Object} | |
*/ | |
Tone.StereoWidener.defaults = { 'width': 0.5 }; | |
/** | |
* Clean up. | |
* @returns {Tone.StereoWidener} this | |
*/ | |
Tone.StereoWidener.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.StereoWidener; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Tremolo modulates the amplitude of an incoming signal using a Tone.LFO. | |
* The type, frequency, and depth of the LFO is controllable. | |
* | |
* @extends {Tone.StereoEffect} | |
* @constructor | |
* @param {Frequency} [frequency] The rate of the effect. | |
* @param {NormalRange} [depth] The depth of the effect. | |
* @example | |
* //create a tremolo and start it's LFO | |
* var tremolo = new Tone.Tremolo(9, 0.75).toMaster().start(); | |
* //route an oscillator through the tremolo and start it | |
* var oscillator = new Tone.Oscillator().connect(tremolo).start(); | |
*/ | |
Tone.Tremolo = function () { | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'depth' | |
], Tone.Tremolo.defaults); | |
Tone.StereoEffect.call(this, options); | |
/** | |
* The tremelo LFO in the left channel | |
* @type {Tone.LFO} | |
* @private | |
*/ | |
this._lfoL = new Tone.LFO({ | |
'phase': options.spread, | |
'min': 1, | |
'max': 0 | |
}); | |
/** | |
* The tremelo LFO in the left channel | |
* @type {Tone.LFO} | |
* @private | |
*/ | |
this._lfoR = new Tone.LFO({ | |
'phase': options.spread, | |
'min': 1, | |
'max': 0 | |
}); | |
/** | |
* Where the gain is multiplied | |
* @type {Tone.Gain} | |
* @private | |
*/ | |
this._amplitudeL = new Tone.Gain(); | |
/** | |
* Where the gain is multiplied | |
* @type {Tone.Gain} | |
* @private | |
*/ | |
this._amplitudeR = new Tone.Gain(); | |
/** | |
* The frequency of the tremolo. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency); | |
/** | |
* The depth of the effect. A depth of 0, has no effect | |
* on the amplitude, and a depth of 1 makes the amplitude | |
* modulate fully between 0 and 1. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.depth = new Tone.Signal(options.depth, Tone.Type.NormalRange); | |
this._readOnly([ | |
'frequency', | |
'depth' | |
]); | |
this.effectSendL.chain(this._amplitudeL, this.effectReturnL); | |
this.effectSendR.chain(this._amplitudeR, this.effectReturnR); | |
this._lfoL.connect(this._amplitudeL.gain); | |
this._lfoR.connect(this._amplitudeR.gain); | |
this.frequency.fan(this._lfoL.frequency, this._lfoR.frequency); | |
this.depth.fan(this._lfoR.amplitude, this._lfoL.amplitude); | |
this.type = options.type; | |
this.spread = options.spread; | |
}; | |
Tone.extend(Tone.Tremolo, Tone.StereoEffect); | |
/** | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Tremolo.defaults = { | |
'frequency': 10, | |
'type': 'sine', | |
'depth': 0.5, | |
'spread': 180 | |
}; | |
/** | |
* Start the tremolo. | |
* @param {Time} [time=now] When the tremolo begins. | |
* @returns {Tone.Tremolo} this | |
*/ | |
Tone.Tremolo.prototype.start = function (time) { | |
this._lfoL.start(time); | |
this._lfoR.start(time); | |
return this; | |
}; | |
/** | |
* Stop the tremolo. | |
* @param {Time} [time=now] When the tremolo stops. | |
* @returns {Tone.Tremolo} this | |
*/ | |
Tone.Tremolo.prototype.stop = function (time) { | |
this._lfoL.stop(time); | |
this._lfoR.stop(time); | |
return this; | |
}; | |
/** | |
* Sync the effect to the transport. | |
* @param {Time} [delay=0] Delay time before starting the effect after the | |
* Transport has started. | |
* @returns {Tone.AutoFilter} this | |
*/ | |
Tone.Tremolo.prototype.sync = function (delay) { | |
this._lfoL.sync(delay); | |
this._lfoR.sync(delay); | |
return this; | |
}; | |
/** | |
* Unsync the filter from the transport | |
* @returns {Tone.Tremolo} this | |
*/ | |
Tone.Tremolo.prototype.unsync = function () { | |
this._lfoL.unsync(); | |
this._lfoR.unsync(); | |
return this; | |
}; | |
/** | |
* The Tremolo's oscillator type. | |
* @memberOf Tone.Tremolo# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.Tremolo.prototype, 'type', { | |
get: function () { | |
return this._lfoL.type; | |
}, | |
set: function (type) { | |
this._lfoL.type = type; | |
this._lfoR.type = type; | |
} | |
}); | |
/** | |
* Amount of stereo spread. When set to 0, both LFO's will be panned centrally. | |
* When set to 180, LFO's will be panned hard left and right respectively. | |
* @memberOf Tone.Tremolo# | |
* @type {Degrees} | |
* @name spread | |
*/ | |
Object.defineProperty(Tone.Tremolo.prototype, 'spread', { | |
get: function () { | |
return this._lfoR.phase - this._lfoL.phase; //180 | |
}, | |
set: function (spread) { | |
this._lfoL.phase = 90 - spread / 2; | |
this._lfoR.phase = spread / 2 + 90; | |
} | |
}); | |
/** | |
* clean up | |
* @returns {Tone.Tremolo} this | |
*/ | |
Tone.Tremolo.prototype.dispose = function () { | |
Tone.StereoEffect.prototype.dispose.call(this); | |
this._writable([ | |
'frequency', | |
'depth' | |
]); | |
this._lfoL.dispose(); | |
this._lfoL = null; | |
this._lfoR.dispose(); | |
this._lfoR = null; | |
this._amplitudeL.dispose(); | |
this._amplitudeL = null; | |
this._amplitudeR.dispose(); | |
this._amplitudeR = null; | |
this.frequency = null; | |
this.depth = null; | |
return this; | |
}; | |
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(0, 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) { | |
/** | |
* @class Tone.Event abstracts away Tone.Transport.schedule and provides a schedulable | |
* callback for a single or repeatable events along the timeline. | |
* | |
* @extends {Tone} | |
* @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.Event(function(time, chord){ | |
* //the chord as well as the exact time of the event | |
* //are passed in as arguments to the callback function | |
* }, ["D4", "E4", "F4"]); | |
* //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.Event = function () { | |
var options = this.optionsObject(arguments, [ | |
'callback', | |
'value' | |
], Tone.Event.defaults); | |
/** | |
* 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 = this.toTicks(options.loopStart); | |
/** | |
* When the note is scheduled to start. | |
* @type {Number} | |
* @private | |
*/ | |
this._loopEnd = this.toTicks(options.loopEnd); | |
/** | |
* Tracks the scheduled events | |
* @type {Tone.TimelineState} | |
* @private | |
*/ | |
this._state = 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; | |
/** | |
* A delay time from when the event is scheduled to start | |
* @type {Ticks} | |
* @private | |
*/ | |
this._startOffset = 0; | |
/** | |
* The probability that the callback will be invoked | |
* at the scheduled time. | |
* @type {NormalRange} | |
* @example | |
* //the callback will be invoked 50% of the time | |
* event.probability = 0.5; | |
*/ | |
this.probability = options.probability; | |
/** | |
* If set to true, will apply small (+/-0.02 seconds) random variation | |
* to the callback time. If the value is given as a time, it will randomize | |
* by that amount. | |
* @example | |
* event.humanize = true; | |
* @type {Boolean|Time} | |
*/ | |
this.humanize = options.humanize; | |
/** | |
* If mute is true, the callback won't be | |
* invoked. | |
* @type {Boolean} | |
*/ | |
this.mute = options.mute; | |
//set the initial values | |
this.playbackRate = options.playbackRate; | |
}; | |
Tone.extend(Tone.Event); | |
/** | |
* The default values | |
* @type {Object} | |
* @const | |
*/ | |
Tone.Event.defaults = { | |
'callback': Tone.noOp, | |
'loop': false, | |
'loopEnd': '1m', | |
'loopStart': 0, | |
'playbackRate': 1, | |
'value': null, | |
'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.Event} this | |
* @private | |
*/ | |
Tone.Event.prototype._rescheduleEvents = function (after) { | |
//if no argument is given, schedules all of the events | |
after = this.defaultArg(after, -1); | |
this._state.forEachFrom(after, function (event) { | |
var duration; | |
if (event.state === Tone.State.Started) { | |
if (!this.isUndef(event.id)) { | |
Tone.Transport.clear(event.id); | |
} | |
var startTick = event.time + Math.round(this.startOffset / this._playbackRate); | |
if (this._loop) { | |
duration = Infinity; | |
if (this.isNumber(this._loop)) { | |
duration = (this._loop - 1) * this._getLoopDuration(); | |
} | |
var nextEvent = this._state.getEventAfter(startTick); | |
if (nextEvent !== null) { | |
duration = Math.min(duration, nextEvent.time - startTick); | |
} | |
if (duration !== Infinity) { | |
//schedule a stop since it's finite duration | |
this._state.setStateAtTime(Tone.State.Stopped, startTick + duration + 1); | |
duration += 'i'; | |
} | |
event.id = Tone.Transport.scheduleRepeat(this._tick.bind(this), this._getLoopDuration().toString() + 'i', startTick + 'i', duration); | |
} else { | |
event.id = Tone.Transport.schedule(this._tick.bind(this), startTick + 'i'); | |
} | |
} | |
}.bind(this)); | |
return this; | |
}; | |
/** | |
* Returns the playback state of the note, either "started" or "stopped". | |
* @type {String} | |
* @readOnly | |
* @memberOf Tone.Event# | |
* @name state | |
*/ | |
Object.defineProperty(Tone.Event.prototype, 'state', { | |
get: function () { | |
return this._state.getStateAtTime(Tone.Transport.ticks); | |
} | |
}); | |
/** | |
* The start from the scheduled start time | |
* @type {Ticks} | |
* @memberOf Tone.Event# | |
* @name startOffset | |
* @private | |
*/ | |
Object.defineProperty(Tone.Event.prototype, 'startOffset', { | |
get: function () { | |
return this._startOffset; | |
}, | |
set: function (offset) { | |
this._startOffset = offset; | |
} | |
}); | |
/** | |
* Start the note at the given time. | |
* @param {Time} time When the note should start. | |
* @return {Tone.Event} this | |
*/ | |
Tone.Event.prototype.start = function (time) { | |
time = this.toTicks(time); | |
if (this._state.getStateAtTime(time) === Tone.State.Stopped) { | |
this._state.addEvent({ | |
'state': Tone.State.Started, | |
'time': time, | |
'id': undefined | |
}); | |
this._rescheduleEvents(time); | |
} | |
return this; | |
}; | |
/** | |
* Stop the Event at the given time. | |
* @param {Time} time When the note should stop. | |
* @return {Tone.Event} this | |
*/ | |
Tone.Event.prototype.stop = function (time) { | |
this.cancel(time); | |
time = this.toTicks(time); | |
if (this._state.getStateAtTime(time) === Tone.State.Started) { | |
this._state.setStateAtTime(Tone.State.Stopped, time); | |
var previousEvent = this._state.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.Event} this | |
*/ | |
Tone.Event.prototype.cancel = function (time) { | |
time = this.defaultArg(time, -Infinity); | |
time = this.toTicks(time); | |
this._state.forEachFrom(time, function (event) { | |
Tone.Transport.clear(event.id); | |
}); | |
this._state.cancel(time); | |
return this; | |
}; | |
/** | |
* The callback function invoker. Also | |
* checks if the Event is done playing | |
* @param {Number} time The time of the event in seconds | |
* @private | |
*/ | |
Tone.Event.prototype._tick = function (time) { | |
if (!this.mute && this._state.getStateAtTime(Tone.Transport.ticks) === Tone.State.Started) { | |
if (this.probability < 1 && Math.random() > this.probability) { | |
return; | |
} | |
if (this.humanize) { | |
var variation = 0.02; | |
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.Event.prototype._getLoopDuration = function () { | |
return Math.round((this._loopEnd - this._loopStart) / this._playbackRate); | |
}; | |
/** | |
* If the note should loop or not | |
* between Tone.Event.loopStart and | |
* Tone.Event.loopEnd. An integer | |
* value corresponds to the number of | |
* loops the Event does after it starts. | |
* @memberOf Tone.Event# | |
* @type {Boolean|Positive} | |
* @name loop | |
*/ | |
Object.defineProperty(Tone.Event.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.Event# | |
* @type {Positive} | |
* @name playbackRate | |
* @example | |
* note.loop = true; | |
* //repeat the note twice as fast | |
* note.playbackRate = 2; | |
*/ | |
Object.defineProperty(Tone.Event.prototype, 'playbackRate', { | |
get: function () { | |
return this._playbackRate; | |
}, | |
set: function (rate) { | |
this._playbackRate = rate; | |
this._rescheduleEvents(); | |
} | |
}); | |
/** | |
* The loopEnd point is the time the event will loop. | |
* Note: only loops if Tone.Event.loop is true. | |
* @memberOf Tone.Event# | |
* @type {Boolean|Positive} | |
* @name loopEnd | |
*/ | |
Object.defineProperty(Tone.Event.prototype, 'loopEnd', { | |
get: function () { | |
return this.toNotation(this._loopEnd + 'i'); | |
}, | |
set: function (loopEnd) { | |
this._loopEnd = this.toTicks(loopEnd); | |
if (this._loop) { | |
this._rescheduleEvents(); | |
} | |
} | |
}); | |
/** | |
* The time when the loop should start. | |
* @memberOf Tone.Event# | |
* @type {Boolean|Positive} | |
* @name loopStart | |
*/ | |
Object.defineProperty(Tone.Event.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 event is not started yet or | |
* it is not set to loop. | |
* @memberOf Tone.Event# | |
* @type {NormalRange} | |
* @name progress | |
* @readOnly | |
*/ | |
Object.defineProperty(Tone.Event.prototype, 'progress', { | |
get: function () { | |
if (this._loop) { | |
var ticks = Tone.Transport.ticks; | |
var lastEvent = this._state.getEvent(ticks); | |
if (lastEvent !== null && lastEvent.state === Tone.State.Started) { | |
var loopDuration = this._getLoopDuration(); | |
var progress = (ticks - lastEvent.time) % loopDuration; | |
return progress / loopDuration; | |
} else { | |
return 0; | |
} | |
} else { | |
return 0; | |
} | |
} | |
}); | |
/** | |
* Clean up | |
* @return {Tone.Event} this | |
*/ | |
Tone.Event.prototype.dispose = function () { | |
this.cancel(); | |
this._state.dispose(); | |
this._state = null; | |
this.callback = null; | |
this.value = null; | |
}; | |
return Tone.Event; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Loop creates a looped callback at the | |
* specified interval. The callback can be | |
* started, stopped and scheduled along | |
* the Transport's timeline. | |
* @example | |
* var loop = new Tone.Loop(function(time){ | |
* //triggered every eighth note. | |
* console.log(time); | |
* }, "8n").start(0); | |
* Tone.Transport.start(); | |
* @extends {Tone} | |
* @param {Function} callback The callback to invoke with the | |
* event. | |
* @param {Array} events The events to arpeggiate over. | |
*/ | |
Tone.Loop = function () { | |
var options = this.optionsObject(arguments, [ | |
'callback', | |
'interval' | |
], Tone.Loop.defaults); | |
/** | |
* The event which produces the callbacks | |
*/ | |
this._event = new Tone.Event({ | |
'callback': this._tick.bind(this), | |
'loop': true, | |
'loopEnd': options.interval, | |
'playbackRate': options.playbackRate, | |
'probability': options.probability | |
}); | |
/** | |
* The callback to invoke with the next event in the pattern | |
* @type {Function} | |
*/ | |
this.callback = options.callback; | |
//set the iterations | |
this.iterations = options.iterations; | |
}; | |
Tone.extend(Tone.Loop); | |
/** | |
* The defaults | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Loop.defaults = { | |
'interval': '4n', | |
'callback': Tone.noOp, | |
'playbackRate': 1, | |
'iterations': Infinity, | |
'probability': true, | |
'mute': false | |
}; | |
/** | |
* Start the loop at the specified time along the Transport's | |
* timeline. | |
* @param {Time=} time When to start the Loop. | |
* @return {Tone.Loop} this | |
*/ | |
Tone.Loop.prototype.start = function (time) { | |
this._event.start(time); | |
return this; | |
}; | |
/** | |
* Stop the loop at the given time. | |
* @param {Time=} time When to stop the Arpeggio | |
* @return {Tone.Loop} this | |
*/ | |
Tone.Loop.prototype.stop = function (time) { | |
this._event.stop(time); | |
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.Loop} this | |
*/ | |
Tone.Loop.prototype.cancel = function (time) { | |
this._event.cancel(time); | |
return this; | |
}; | |
/** | |
* Internal function called when the notes should be called | |
* @param {Number} time The time the event occurs | |
* @private | |
*/ | |
Tone.Loop.prototype._tick = function (time) { | |
this.callback(time); | |
}; | |
/** | |
* The state of the Loop, either started or stopped. | |
* @memberOf Tone.Loop# | |
* @type {String} | |
* @name state | |
* @readOnly | |
*/ | |
Object.defineProperty(Tone.Loop.prototype, 'state', { | |
get: function () { | |
return this._event.state; | |
} | |
}); | |
/** | |
* The progress of the loop as a value between 0-1. 0, when | |
* the loop is stopped or done iterating. | |
* @memberOf Tone.Loop# | |
* @type {NormalRange} | |
* @name progress | |
* @readOnly | |
*/ | |
Object.defineProperty(Tone.Loop.prototype, 'progress', { | |
get: function () { | |
return this._event.progress; | |
} | |
}); | |
/** | |
* The time between successive callbacks. | |
* @example | |
* loop.interval = "8n"; //loop every 8n | |
* @memberOf Tone.Loop# | |
* @type {Time} | |
* @name interval | |
*/ | |
Object.defineProperty(Tone.Loop.prototype, 'interval', { | |
get: function () { | |
return this._event.loopEnd; | |
}, | |
set: function (interval) { | |
this._event.loopEnd = interval; | |
} | |
}); | |
/** | |
* The playback rate of the loop. The normal playback rate is 1 (no change). | |
* A `playbackRate` of 2 would be twice as fast. | |
* @memberOf Tone.Loop# | |
* @type {Time} | |
* @name playbackRate | |
*/ | |
Object.defineProperty(Tone.Loop.prototype, 'playbackRate', { | |
get: function () { | |
return this._event.playbackRate; | |
}, | |
set: function (rate) { | |
this._event.playbackRate = rate; | |
} | |
}); | |
/** | |
* Random variation +/-0.01s to the scheduled time. | |
* Or give it a time value which it will randomize by. | |
* @type {Boolean|Time} | |
* @memberOf Tone.Loop# | |
* @name humanize | |
*/ | |
Object.defineProperty(Tone.Loop.prototype, 'humanize', { | |
get: function () { | |
return this._event.humanize; | |
}, | |
set: function (variation) { | |
this._event.humanize = variation; | |
} | |
}); | |
/** | |
* The probably of the callback being invoked. | |
* @memberOf Tone.Loop# | |
* @type {NormalRange} | |
* @name probability | |
*/ | |
Object.defineProperty(Tone.Loop.prototype, 'probability', { | |
get: function () { | |
return this._event.probability; | |
}, | |
set: function (prob) { | |
this._event.probability = prob; | |
} | |
}); | |
/** | |
* Muting the Loop means that no callbacks are invoked. | |
* @memberOf Tone.Loop# | |
* @type {Boolean} | |
* @name mute | |
*/ | |
Object.defineProperty(Tone.Loop.prototype, 'mute', { | |
get: function () { | |
return this._event.mute; | |
}, | |
set: function (mute) { | |
this._event.mute = mute; | |
} | |
}); | |
/** | |
* The number of iterations of the loop. The default | |
* value is Infinity (loop forever). | |
* @memberOf Tone.Loop# | |
* @type {Positive} | |
* @name iterations | |
*/ | |
Object.defineProperty(Tone.Loop.prototype, 'iterations', { | |
get: function () { | |
if (this._event.loop === true) { | |
return Infinity; | |
} else { | |
return this._event.loop; | |
} | |
return this._pattern.index; | |
}, | |
set: function (iters) { | |
if (iters === Infinity) { | |
this._event.loop = true; | |
} else { | |
this._event.loop = iters; | |
} | |
} | |
}); | |
/** | |
* Clean up | |
* @return {Tone.Loop} this | |
*/ | |
Tone.Loop.prototype.dispose = function () { | |
this._event.dispose(); | |
this._event = null; | |
this.callback = null; | |
}; | |
return Tone.Loop; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Part is a collection Tone.Events which can be | |
* started/stoped and looped as a single unit. | |
* | |
* @extends {Tone.Event} | |
* @param {Function} callback The callback to invoke on each event | |
* @param {Array} events the array of events | |
* @example | |
* var part = new Tone.Part(function(time, note){ | |
* //the notes given as the second element in the array | |
* //will be passed in as the second argument | |
* synth.triggerAttackRelease(note, "8n", time); | |
* }, [[0, "C2"], ["0:2", "C3"], ["0:3:2", "G2"]]); | |
* @example | |
* //use an array of objects as long as the object has a "time" attribute | |
* var part = new Tone.Part(function(time, value){ | |
* //the value is an object which contains both the note and the velocity | |
* synth.triggerAttackRelease(value.note, "8n", time, value.velocity); | |
* }, [{"time" : 0, "note" : "C3", "velocity": 0.9}, | |
* {"time" : "0:2", "note" : "C4", "velocity": 0.5} | |
* ]).start(0); | |
*/ | |
Tone.Part = function () { | |
var options = this.optionsObject(arguments, [ | |
'callback', | |
'events' | |
], Tone.Part.defaults); | |
/** | |
* If the part is looping or not | |
* @type {Boolean|Positive} | |
* @private | |
*/ | |
this._loop = options.loop; | |
/** | |
* When the note is scheduled to start. | |
* @type {Ticks} | |
* @private | |
*/ | |
this._loopStart = this.toTicks(options.loopStart); | |
/** | |
* When the note is scheduled to start. | |
* @type {Ticks} | |
* @private | |
*/ | |
this._loopEnd = this.toTicks(options.loopEnd); | |
/** | |
* The playback rate of the part | |
* @type {Positive} | |
* @private | |
*/ | |
this._playbackRate = options.playbackRate; | |
/** | |
* private holder of probability value | |
* @type {NormalRange} | |
* @private | |
*/ | |
this._probability = options.probability; | |
/** | |
* the amount of variation from the | |
* given time. | |
* @type {Boolean|Time} | |
* @private | |
*/ | |
this._humanize = options.humanize; | |
/** | |
* The start offset | |
* @type {Ticks} | |
* @private | |
*/ | |
this._startOffset = 0; | |
/** | |
* Keeps track of the current state | |
* @type {Tone.TimelineState} | |
* @private | |
*/ | |
this._state = new Tone.TimelineState(Tone.State.Stopped); | |
/** | |
* An array of Objects. | |
* @type {Array} | |
* @private | |
*/ | |
this._events = []; | |
/** | |
* The callback to invoke at all the scheduled events. | |
* @type {Function} | |
*/ | |
this.callback = options.callback; | |
/** | |
* If mute is true, the callback won't be | |
* invoked. | |
* @type {Boolean} | |
*/ | |
this.mute = options.mute; | |
//add the events | |
var events = this.defaultArg(options.events, []); | |
if (!this.isUndef(options.events)) { | |
for (var i = 0; i < events.length; i++) { | |
if (Array.isArray(events[i])) { | |
this.add(events[i][0], events[i][1]); | |
} else { | |
this.add(events[i]); | |
} | |
} | |
} | |
}; | |
Tone.extend(Tone.Part, Tone.Event); | |
/** | |
* The default values | |
* @type {Object} | |
* @const | |
*/ | |
Tone.Part.defaults = { | |
'callback': Tone.noOp, | |
'loop': false, | |
'loopEnd': '1m', | |
'loopStart': 0, | |
'playbackRate': 1, | |
'probability': 1, | |
'humanize': false, | |
'mute': false | |
}; | |
/** | |
* Start the part at the given 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._state.getStateAtTime(ticks) !== Tone.State.Started) { | |
offset = this.defaultArg(offset, 0); | |
offset = this.toTicks(offset); | |
this._state.addEvent({ | |
'state': Tone.State.Started, | |
'time': ticks, | |
'offset': offset | |
}); | |
this._forEach(function (event) { | |
this._startNote(event, ticks, offset); | |
}); | |
} | |
return this; | |
}; | |
/** | |
* Start the event in the given event at the correct time given | |
* the ticks and offset and looping. | |
* @param {Tone.Event} event | |
* @param {Ticks} ticks | |
* @param {Ticks} offset | |
* @private | |
*/ | |
Tone.Part.prototype._startNote = function (event, ticks, offset) { | |
ticks -= offset; | |
if (this._loop) { | |
if (event.startOffset >= this._loopStart && event.startOffset < this._loopEnd) { | |
if (event.startOffset < offset) { | |
//start it on the next loop | |
ticks += this._getLoopDuration(); | |
} | |
event.start(ticks + 'i'); | |
} | |
} else { | |
if (event.startOffset >= offset) { | |
event.start(ticks + 'i'); | |
} | |
} | |
}; | |
/** | |
* The start from the scheduled start time | |
* @type {Ticks} | |
* @memberOf Tone.Part# | |
* @name startOffset | |
* @private | |
*/ | |
Object.defineProperty(Tone.Part.prototype, 'startOffset', { | |
get: function () { | |
return this._startOffset; | |
}, | |
set: function (offset) { | |
this._startOffset = offset; | |
this._forEach(function (event) { | |
event.startOffset += this._startOffset; | |
}); | |
} | |
}); | |
/** | |
* 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._state.getStateAtTime(ticks) === Tone.State.Started) { | |
this._state.setStateAtTime(Tone.State.Stopped, ticks); | |
this._forEach(function (event) { | |
event.stop(time); | |
}); | |
} | |
return this; | |
}; | |
/** | |
* Get/Set an Event's value at the given time. | |
* If a value is passed in and no event exists at | |
* the given time, one will be created with that value. | |
* If two events are at the same time, the first one will | |
* be returned. | |
* @example | |
* part.at("1m"); //returns the part at the first measure | |
* | |
* part.at("2m", "C2"); //set the value at "2m" to C2. | |
* //if an event didn't exist at that time, it will be created. | |
* @param {Time} time the time of the event to get or set | |
* @param {*=} value If a value is passed in, the value of the | |
* event at the given time will be set to it. | |
* @return {Tone.Event} the event at the time | |
*/ | |
Tone.Part.prototype.at = function (time, value) { | |
time = this.toTicks(time); | |
var tickTime = this.ticksToSeconds(1); | |
for (var i = 0; i < this._events.length; i++) { | |
var event = this._events[i]; | |
if (Math.abs(time - event.startOffset) < tickTime) { | |
if (!this.isUndef(value)) { | |
event.value = value; | |
} | |
return event; | |
} | |
} | |
//if there was no event at that time, create one | |
if (!this.isUndef(value)) { | |
this.add(time + 'i', value); | |
//return the new event | |
return this._events[this._events.length - 1]; | |
} else { | |
return null; | |
} | |
}; | |
/** | |
* Add a an event 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.Event|*} value | |
* @returns {Tone.Part} this | |
* @example | |
* part.add("1m", "C#+11"); | |
*/ | |
Tone.Part.prototype.add = function (time, value) { | |
//extract the parameters | |
if (this.isObject(time) && time.hasOwnProperty('time')) { | |
value = time; | |
time = value.time; | |
delete value.time; | |
} | |
time = this.toTicks(time); | |
var event; | |
if (value instanceof Tone.Event) { | |
event = value; | |
event.callback = this._tick.bind(this); | |
} else { | |
event = new Tone.Event({ | |
'callback': this._tick.bind(this), | |
'value': value | |
}); | |
} | |
//the start offset | |
event.startOffset = time; | |
//initialize the values | |
event.set({ | |
'loopEnd': this.loopEnd, | |
'loopStart': this.loopStart, | |
'loop': this.loop, | |
'humanize': this.humanize, | |
'playbackRate': this.playbackRate, | |
'probability': this.probability | |
}); | |
this._events.push(event); | |
//start the note if it should be played right now | |
this._restartEvent(event); | |
return this; | |
}; | |
/** | |
* Restart the given event | |
* @param {Tone.Event} event | |
* @private | |
*/ | |
Tone.Part.prototype._restartEvent = function (event) { | |
var stateEvent = this._state.getEvent(this.now()); | |
if (stateEvent && stateEvent.state === Tone.State.Started) { | |
this._startNote(event, stateEvent.time, stateEvent.offset); | |
} | |
}; | |
/** | |
* Remove an event from the part. Will recursively iterate | |
* into nested parts to find the event. | |
* @param {Time} time The time of the event | |
* @param {*} value Optionally select only a specific event value | |
*/ | |
Tone.Part.prototype.remove = function (time, value) { | |
//extract the parameters | |
if (this.isObject(time) && time.hasOwnProperty('time')) { | |
value = time; | |
time = value.time; | |
} | |
time = this.toTicks(time); | |
for (var i = this._events.length - 1; i >= 0; i--) { | |
var event = this._events[i]; | |
if (event instanceof Tone.Part) { | |
event.remove(time, value); | |
} else { | |
if (event.startOffset === time) { | |
if (this.isUndef(value) || !this.isUndef(value) && event.value === value) { | |
this._events.splice(i, 1); | |
event.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.dispose(); | |
}); | |
this._events = []; | |
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.cancel(after); | |
}); | |
this._state.cancel(after); | |
return this; | |
}; | |
/** | |
* Iterate over all of the events | |
* @param {Function} callback | |
* @param {Object} ctx The context | |
* @private | |
*/ | |
Tone.Part.prototype._forEach = function (callback, ctx) { | |
ctx = this.defaultArg(ctx, this); | |
for (var i = this._events.length - 1; i >= 0; i--) { | |
var e = this._events[i]; | |
if (e instanceof Tone.Part) { | |
e._forEach(callback, ctx); | |
} else { | |
callback.call(ctx, e); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Set the attribute of all of the events | |
* @param {String} attr the attribute to set | |
* @param {*} value The value to set it to | |
* @private | |
*/ | |
Tone.Part.prototype._setAll = function (attr, value) { | |
this._forEach(function (event) { | |
event[attr] = value; | |
}); | |
}; | |
/** | |
* 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.callback(time, value); | |
} | |
}; | |
/** | |
* Determine if the event should be currently looping | |
* given the loop boundries of this Part. | |
* @param {Tone.Event} event The event to test | |
* @private | |
*/ | |
Tone.Part.prototype._testLoopBoundries = function (event) { | |
if (event.startOffset < this._loopStart || event.startOffset >= this._loopEnd) { | |
event.cancel(); | |
} else { | |
//reschedule it if it's stopped | |
if (event.state === Tone.State.Stopped) { | |
this._restartEvent(event); | |
} | |
} | |
}; | |
/** | |
* 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._setAll('probability', prob); | |
} | |
}); | |
/** | |
* If set to true, will apply small random variation | |
* to the callback time. If the value is given as a time, it will randomize | |
* by that amount. | |
* @example | |
* event.humanize = true; | |
* @type {Boolean|Time} | |
* @name humanize | |
*/ | |
Object.defineProperty(Tone.Part.prototype, 'humanize', { | |
get: function () { | |
return this._humanize; | |
}, | |
set: function (variation) { | |
this._humanize = variation; | |
this._setAll('humanize', variation); | |
} | |
}); | |
/** | |
* If the part 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 | |
* @example | |
* //loop the part 8 times | |
* part.loop = 8; | |
*/ | |
Object.defineProperty(Tone.Part.prototype, 'loop', { | |
get: function () { | |
return this._loop; | |
}, | |
set: function (loop) { | |
this._loop = loop; | |
this._forEach(function (event) { | |
event._loopStart = this._loopStart; | |
event._loopEnd = this._loopEnd; | |
event.loop = loop; | |
this._testLoopBoundries(event); | |
}); | |
} | |
}); | |
/** | |
* 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.loopEnd = this.loopEnd; | |
this._testLoopBoundries(event); | |
}); | |
} | |
} | |
}); | |
/** | |
* 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.loopStart = this.loopStart; | |
this._testLoopBoundries(event); | |
}); | |
} | |
} | |
}); | |
/** | |
* 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._playbackRate = rate; | |
this._setAll('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._events.length; | |
} | |
}); | |
/** | |
* Clean up | |
* @return {Tone.Part} this | |
*/ | |
Tone.Part.prototype.dispose = function () { | |
this.removeAll(); | |
this._state.dispose(); | |
this._state = null; | |
this.callback = null; | |
this._events = null; | |
return this; | |
}; | |
return Tone.Part; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.Pattern arpeggiates between the given notes | |
* in a number of patterns. See Tone.CtrlPattern for | |
* a full list of patterns. | |
* @example | |
* var pattern = new Tone.Pattern(function(time, note){ | |
* //the order of the notes passed in depends on the pattern | |
* }, ["C2", "D4", "E5", "A6"], "upDown"); | |
* @extends {Tone.Loop} | |
* @param {Function} callback The callback to invoke with the | |
* event. | |
* @param {Array} events The events to arpeggiate over. | |
*/ | |
Tone.Pattern = function () { | |
var options = this.optionsObject(arguments, [ | |
'callback', | |
'events', | |
'pattern' | |
], Tone.Pattern.defaults); | |
Tone.Loop.call(this, options); | |
/** | |
* The pattern manager | |
* @type {Tone.CtrlPattern} | |
* @private | |
*/ | |
this._pattern = new Tone.CtrlPattern({ | |
'values': options.events, | |
'type': options.pattern, | |
'index': options.index | |
}); | |
}; | |
Tone.extend(Tone.Pattern, Tone.Loop); | |
/** | |
* The defaults | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Pattern.defaults = { | |
'pattern': Tone.CtrlPattern.Type.Up, | |
'events': [] | |
}; | |
/** | |
* Internal function called when the notes should be called | |
* @param {Number} time The time the event occurs | |
* @private | |
*/ | |
Tone.Pattern.prototype._tick = function (time) { | |
this.callback(time, this._pattern.value); | |
this._pattern.next(); | |
}; | |
/** | |
* The current index in the events array. | |
* @memberOf Tone.Pattern# | |
* @type {Positive} | |
* @name index | |
*/ | |
Object.defineProperty(Tone.Pattern.prototype, 'index', { | |
get: function () { | |
return this._pattern.index; | |
}, | |
set: function (i) { | |
this._pattern.index = i; | |
} | |
}); | |
/** | |
* The array of events. | |
* @memberOf Tone.Pattern# | |
* @type {Array} | |
* @name events | |
*/ | |
Object.defineProperty(Tone.Pattern.prototype, 'events', { | |
get: function () { | |
return this._pattern.values; | |
}, | |
set: function (vals) { | |
this._pattern.values = vals; | |
} | |
}); | |
/** | |
* The current value of the pattern. | |
* @memberOf Tone.Pattern# | |
* @type {*} | |
* @name value | |
* @readOnly | |
*/ | |
Object.defineProperty(Tone.Pattern.prototype, 'value', { | |
get: function () { | |
return this._pattern.value; | |
} | |
}); | |
/** | |
* The pattern type. See Tone.CtrlPattern for the full list of patterns. | |
* @memberOf Tone.Pattern# | |
* @type {String} | |
* @name pattern | |
*/ | |
Object.defineProperty(Tone.Pattern.prototype, 'pattern', { | |
get: function () { | |
return this._pattern.type; | |
}, | |
set: function (pattern) { | |
this._pattern.type = pattern; | |
} | |
}); | |
/** | |
* Clean up | |
* @return {Tone.Pattern} this | |
*/ | |
Tone.Pattern.prototype.dispose = function () { | |
Tone.Loop.prototype.dispose.call(this); | |
this._pattern.dispose(); | |
this._pattern = null; | |
}; | |
return Tone.Pattern; | |
}); | |
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 spaced at the | |
* given subdivision. Sub-arrays will subdivide that beat | |
* by the number of items are in the array. | |
* Sequence notation inspiration from [Tidal](http://yaxu.org/tidal/) | |
* @param {Function} callback The callback to invoke with every note | |
* @param {Array} events The sequence | |
* @param {Time} subdivision The subdivision between which events are placed. | |
* @extends {Tone.Part} | |
* @example | |
* var seq = new Tone.Sequence(function(time, note){ | |
* console.log(note); | |
* //straight quater notes | |
* }, ["C4", "E4", "G4", "A4"], "4n"); | |
* @example | |
* var seq = new Tone.Sequence(function(time, note){ | |
* console.log(note); | |
* //subdivisions are given as subarrays | |
* }, ["C4", ["E4", "D4", "E4"], "G4", ["A4", "G4"]]); | |
*/ | |
Tone.Sequence = function () { | |
var options = this.optionsObject(arguments, [ | |
'callback', | |
'events', | |
'subdivision' | |
], Tone.Sequence.defaults); | |
//remove the events | |
var events = options.events; | |
delete options.events; | |
Tone.Part.call(this, options); | |
/** | |
* The subdivison of each note | |
* @type {Ticks} | |
* @private | |
*/ | |
this._subdivision = this.toTicks(options.subdivision); | |
//if no time was passed in, the loop end is the end of the cycle | |
if (this.isUndef(options.loopEnd) && !this.isUndef(events)) { | |
this._loopEnd = events.length * this._subdivision; | |
} | |
//defaults to looping | |
this._loop = true; | |
//add all of the events | |
if (!this.isUndef(events)) { | |
for (var i = 0; i < events.length; i++) { | |
this.add(i, events[i]); | |
} | |
} | |
}; | |
Tone.extend(Tone.Sequence, Tone.Part); | |
/** | |
* The default values. | |
* @type {Object} | |
*/ | |
Tone.Sequence.defaults = { 'subdivision': '4n' }; | |
/** | |
* The subdivision of the sequence. This can only be | |
* set in the constructor. The subdivision is the | |
* interval between successive steps. | |
* @type {Time} | |
* @memberOf Tone.Sequence# | |
* @name subdivision | |
* @readOnly | |
*/ | |
Object.defineProperty(Tone.Sequence.prototype, 'subdivision', { | |
get: function () { | |
return this.toNotation(this._subdivision + 'i'); | |
} | |
}); | |
/** | |
* Get/Set an index of the sequence. If the index contains a subarray, | |
* a Tone.Sequence representing that sub-array will be returned. | |
* @example | |
* var sequence = new Tone.Sequence(playNote, ["E4", "C4", "F#4", ["A4", "Bb3"]]) | |
* sequence.at(0)// => returns "E4" | |
* //set a value | |
* sequence.at(0, "G3"); | |
* //get a nested sequence | |
* sequence.at(3).at(1)// => returns "Bb3" | |
* @param {Positive} index The index to get or set | |
* @param {*} value Optionally pass in the value to set at the given index. | |
*/ | |
Tone.Sequence.prototype.at = function (index, value) { | |
//if the value is an array, | |
if (this.isArray(value)) { | |
//remove the current event at that index | |
this.remove(index); | |
} | |
//call the parent's method | |
return Tone.Part.prototype.at.call(this, this._indexTime(index), value); | |
}; | |
/** | |
* Add an event at an index, if there's already something | |
* at that index, overwrite it. If `value` is an array, | |
* it will be parsed as a subsequence. | |
* @param {Number} index The index to add the event to | |
* @param {*} value The value to add at that index | |
* @returns {Tone.Sequence} this | |
*/ | |
Tone.Sequence.prototype.add = function (index, value) { | |
if (value === null) { | |
return this; | |
} | |
if (this.isArray(value)) { | |
//make a subsequence and add that to the sequence | |
var subSubdivision = Math.round(this._subdivision / value.length) + 'i'; | |
value = new Tone.Sequence(this._tick.bind(this), value, subSubdivision); | |
} | |
Tone.Part.prototype.add.call(this, this._indexTime(index), value); | |
return this; | |
}; | |
/** | |
* Remove a value from the sequence by index | |
* @param {Number} index The index of the event to remove | |
* @returns {Tone.Sequence} this | |
*/ | |
Tone.Sequence.prototype.remove = function (index, value) { | |
Tone.Part.prototype.remove.call(this, this._indexTime(index), value); | |
return this; | |
}; | |
/** | |
* Get the time of the index given the Sequence's subdivision | |
* @param {Number} index | |
* @return {Time} The time of that index | |
* @private | |
*/ | |
Tone.Sequence.prototype._indexTime = function (index) { | |
if (this.isTicks(index)) { | |
return index; | |
} else { | |
return index * this._subdivision + this.startOffset + 'i'; | |
} | |
}; | |
/** | |
* Clean up. | |
* @return {Tone.Sequence} this | |
*/ | |
Tone.Sequence.prototype.dispose = function () { | |
Tone.Part.prototype.dispose.call(this); | |
return this; | |
}; | |
return Tone.Sequence; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.PulseOscillator is a pulse oscillator with control over pulse width, | |
* also known as the duty cycle. At 50% duty cycle (width = 0.5) the wave is | |
* a square and only odd-numbered harmonics are present. At all other widths | |
* even-numbered harmonics are present. Read more | |
* [here](https://wigglewave.wordpress.com/2014/08/16/pulse-waveforms-and-harmonics/). | |
* | |
* @constructor | |
* @extends {Tone.Oscillator} | |
* @param {Frequency} [frequency] The frequency of the oscillator | |
* @param {NormalRange} [width] The width of the pulse | |
* @example | |
* var pulse = new Tone.PulseOscillator("E5", 0.4).toMaster().start(); | |
*/ | |
Tone.PulseOscillator = function () { | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'width' | |
], Tone.Oscillator.defaults); | |
Tone.Source.call(this, options); | |
/** | |
* The width of the pulse. | |
* @type {NormalRange} | |
* @signal | |
*/ | |
this.width = new Tone.Signal(options.width, Tone.Type.NormalRange); | |
/** | |
* gate the width amount | |
* @type {GainNode} | |
* @private | |
*/ | |
this._widthGate = this.context.createGain(); | |
/** | |
* the sawtooth oscillator | |
* @type {Tone.Oscillator} | |
* @private | |
*/ | |
this._sawtooth = new Tone.Oscillator({ | |
frequency: options.frequency, | |
detune: options.detune, | |
type: 'sawtooth', | |
phase: options.phase | |
}); | |
/** | |
* The frequency control. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = this._sawtooth.frequency; | |
/** | |
* The detune in cents. | |
* @type {Cents} | |
* @signal | |
*/ | |
this.detune = this._sawtooth.detune; | |
/** | |
* Threshold the signal to turn it into a square | |
* @type {Tone.WaveShaper} | |
* @private | |
*/ | |
this._thresh = new Tone.WaveShaper(function (val) { | |
if (val < 0) { | |
return -1; | |
} else { | |
return 1; | |
} | |
}); | |
//connections | |
this._sawtooth.chain(this._thresh, this.output); | |
this.width.chain(this._widthGate, this._thresh); | |
this._readOnly([ | |
'width', | |
'frequency', | |
'detune' | |
]); | |
}; | |
Tone.extend(Tone.PulseOscillator, Tone.Oscillator); | |
/** | |
* The default parameters. | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.PulseOscillator.defaults = { | |
'frequency': 440, | |
'detune': 0, | |
'phase': 0, | |
'width': 0.2 | |
}; | |
/** | |
* start the oscillator | |
* @param {Time} time | |
* @private | |
*/ | |
Tone.PulseOscillator.prototype._start = function (time) { | |
time = this.toSeconds(time); | |
this._sawtooth.start(time); | |
this._widthGate.gain.setValueAtTime(1, time); | |
}; | |
/** | |
* stop the oscillator | |
* @param {Time} time | |
* @private | |
*/ | |
Tone.PulseOscillator.prototype._stop = function (time) { | |
time = this.toSeconds(time); | |
this._sawtooth.stop(time); | |
//the width is still connected to the output. | |
//that needs to be stopped also | |
this._widthGate.gain.setValueAtTime(0, time); | |
}; | |
/** | |
* The phase of the oscillator in degrees. | |
* @memberOf Tone.PulseOscillator# | |
* @type {Degrees} | |
* @name phase | |
*/ | |
Object.defineProperty(Tone.PulseOscillator.prototype, 'phase', { | |
get: function () { | |
return this._sawtooth.phase; | |
}, | |
set: function (phase) { | |
this._sawtooth.phase = phase; | |
} | |
}); | |
/** | |
* The type of the oscillator. Always returns "pulse". | |
* @readOnly | |
* @memberOf Tone.PulseOscillator# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.PulseOscillator.prototype, 'type', { | |
get: function () { | |
return 'pulse'; | |
} | |
}); | |
/** | |
* The partials of the waveform. Cannot set partials for this waveform type | |
* @memberOf Tone.PulseOscillator# | |
* @type {Array} | |
* @name partials | |
* @private | |
*/ | |
Object.defineProperty(Tone.PulseOscillator.prototype, 'partials', { | |
get: function () { | |
return []; | |
} | |
}); | |
/** | |
* Clean up method. | |
* @return {Tone.PulseOscillator} this | |
*/ | |
Tone.PulseOscillator.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.PulseOscillator; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.PWMOscillator modulates the width of a Tone.PulseOscillator | |
* at the modulationFrequency. This has the effect of continuously | |
* changing the timbre of the oscillator by altering the harmonics | |
* generated. | |
* | |
* @extends {Tone.Oscillator} | |
* @constructor | |
* @param {Frequency} frequency The starting frequency of the oscillator. | |
* @param {Frequency} modulationFrequency The modulation frequency of the width of the pulse. | |
* @example | |
* var pwm = new Tone.PWMOscillator("Ab3", 0.3).toMaster().start(); | |
*/ | |
Tone.PWMOscillator = function () { | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'modulationFrequency' | |
], Tone.PWMOscillator.defaults); | |
Tone.Source.call(this, options); | |
/** | |
* the pulse oscillator | |
* @type {Tone.PulseOscillator} | |
* @private | |
*/ | |
this._pulse = new Tone.PulseOscillator(options.modulationFrequency); | |
//change the pulse oscillator type | |
this._pulse._sawtooth.type = 'sine'; | |
/** | |
* the modulator | |
* @type {Tone.Oscillator} | |
* @private | |
*/ | |
this._modulator = new Tone.Oscillator({ | |
'frequency': options.frequency, | |
'detune': options.detune, | |
'phase': options.phase | |
}); | |
/** | |
* Scale the oscillator so it doesn't go silent | |
* at the extreme values. | |
* @type {Tone.Multiply} | |
* @private | |
*/ | |
this._scale = new Tone.Multiply(1.01); | |
/** | |
* The frequency control. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = this._modulator.frequency; | |
/** | |
* The detune of the oscillator. | |
* @type {Cents} | |
* @signal | |
*/ | |
this.detune = this._modulator.detune; | |
/** | |
* The modulation rate of the oscillator. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.modulationFrequency = this._pulse.frequency; | |
//connections | |
this._modulator.chain(this._scale, this._pulse.width); | |
this._pulse.connect(this.output); | |
this._readOnly([ | |
'modulationFrequency', | |
'frequency', | |
'detune' | |
]); | |
}; | |
Tone.extend(Tone.PWMOscillator, Tone.Oscillator); | |
/** | |
* default values | |
* @static | |
* @type {Object} | |
* @const | |
*/ | |
Tone.PWMOscillator.defaults = { | |
'frequency': 440, | |
'detune': 0, | |
'phase': 0, | |
'modulationFrequency': 0.4 | |
}; | |
/** | |
* start the oscillator | |
* @param {Time} [time=now] | |
* @private | |
*/ | |
Tone.PWMOscillator.prototype._start = function (time) { | |
time = this.toSeconds(time); | |
this._modulator.start(time); | |
this._pulse.start(time); | |
}; | |
/** | |
* stop the oscillator | |
* @param {Time} time (optional) timing parameter | |
* @private | |
*/ | |
Tone.PWMOscillator.prototype._stop = function (time) { | |
time = this.toSeconds(time); | |
this._modulator.stop(time); | |
this._pulse.stop(time); | |
}; | |
/** | |
* The type of the oscillator. Always returns "pwm". | |
* @readOnly | |
* @memberOf Tone.PWMOscillator# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.PWMOscillator.prototype, 'type', { | |
get: function () { | |
return 'pwm'; | |
} | |
}); | |
/** | |
* The partials of the waveform. Cannot set partials for this waveform type | |
* @memberOf Tone.PWMOscillator# | |
* @type {Array} | |
* @name partials | |
* @private | |
*/ | |
Object.defineProperty(Tone.PWMOscillator.prototype, 'partials', { | |
get: function () { | |
return []; | |
} | |
}); | |
/** | |
* The phase of the oscillator in degrees. | |
* @memberOf Tone.PWMOscillator# | |
* @type {number} | |
* @name phase | |
*/ | |
Object.defineProperty(Tone.PWMOscillator.prototype, 'phase', { | |
get: function () { | |
return this._modulator.phase; | |
}, | |
set: function (phase) { | |
this._modulator.phase = phase; | |
} | |
}); | |
/** | |
* Clean up. | |
* @return {Tone.PWMOscillator} this | |
*/ | |
Tone.PWMOscillator.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.PWMOscillator; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.OmniOscillator aggregates Tone.Oscillator, Tone.PulseOscillator, | |
* and Tone.PWMOscillator into one class, allowing it to have the | |
* types: sine, square, triangle, sawtooth, pulse or pwm. Additionally, | |
* OmniOscillator is capable of setting the first x number of partials | |
* of the oscillator. For example: "sine4" would set be the first 4 | |
* partials of the sine wave and "triangle8" would set the first | |
* 8 partials of the triangle wave. | |
* | |
* @extends {Tone.Oscillator} | |
* @constructor | |
* @param {Frequency} frequency The initial frequency of the oscillator. | |
* @param {string} type The type of the oscillator. | |
* @example | |
* var omniOsc = new Tone.OmniOscillator("C#4", "pwm"); | |
*/ | |
Tone.OmniOscillator = function () { | |
var options = this.optionsObject(arguments, [ | |
'frequency', | |
'type' | |
], Tone.OmniOscillator.defaults); | |
Tone.Source.call(this, options); | |
/** | |
* The frequency control. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency); | |
/** | |
* The detune control | |
* @type {Cents} | |
* @signal | |
*/ | |
this.detune = new Tone.Signal(options.detune, Tone.Type.Cents); | |
/** | |
* the type of the oscillator source | |
* @type {string} | |
* @private | |
*/ | |
this._sourceType = undefined; | |
/** | |
* the oscillator | |
* @type {Tone.Oscillator|Tone.PWMOscillator|Tone.PulseOscillator} | |
* @private | |
*/ | |
this._oscillator = null; | |
//set the oscillator | |
this.type = options.type; | |
this.phase = options.phase; | |
this._readOnly([ | |
'frequency', | |
'detune' | |
]); | |
if (this.isArray(options.partials)) { | |
this.partials = options.partials; | |
} | |
}; | |
Tone.extend(Tone.OmniOscillator, Tone.Oscillator); | |
/** | |
* default values | |
* @static | |
* @type {Object} | |
* @const | |
*/ | |
Tone.OmniOscillator.defaults = { | |
'frequency': 440, | |
'detune': 0, | |
'type': 'sine', | |
'phase': 0, | |
'width': 0.4, | |
//only applies if the oscillator is set to "pulse", | |
'modulationFrequency': 0.4 | |
}; | |
/** | |
* @enum {string} | |
* @private | |
*/ | |
var OmniOscType = { | |
PulseOscillator: 'PulseOscillator', | |
PWMOscillator: 'PWMOscillator', | |
Oscillator: 'Oscillator' | |
}; | |
/** | |
* start the oscillator | |
* @param {Time} [time=now] the time to start the oscillator | |
* @private | |
*/ | |
Tone.OmniOscillator.prototype._start = function (time) { | |
this._oscillator.start(time); | |
}; | |
/** | |
* start the oscillator | |
* @param {Time} [time=now] the time to start the oscillator | |
* @private | |
*/ | |
Tone.OmniOscillator.prototype._stop = function (time) { | |
this._oscillator.stop(time); | |
}; | |
/** | |
* The type of the oscillator. sine, square, triangle, sawtooth, pwm, or pulse. | |
* @memberOf Tone.OmniOscillator# | |
* @type {string} | |
* @name type | |
*/ | |
Object.defineProperty(Tone.OmniOscillator.prototype, 'type', { | |
get: function () { | |
return this._oscillator.type; | |
}, | |
set: function (type) { | |
if (type.indexOf('sine') === 0 || type.indexOf('square') === 0 || type.indexOf('triangle') === 0 || type.indexOf('sawtooth') === 0 || type === Tone.Oscillator.Type.Custom) { | |
if (this._sourceType !== OmniOscType.Oscillator) { | |
this._sourceType = OmniOscType.Oscillator; | |
this._createNewOscillator(Tone.Oscillator); | |
} | |
this._oscillator.type = type; | |
} else if (type === 'pwm') { | |
if (this._sourceType !== OmniOscType.PWMOscillator) { | |
this._sourceType = OmniOscType.PWMOscillator; | |
this._createNewOscillator(Tone.PWMOscillator); | |
} | |
} else if (type === 'pulse') { | |
if (this._sourceType !== OmniOscType.PulseOscillator) { | |
this._sourceType = OmniOscType.PulseOscillator; | |
this._createNewOscillator(Tone.PulseOscillator); | |
} | |
} else { | |
throw new Error('Tone.OmniOscillator does not support type ' + type); | |
} | |
} | |
}); | |
/** | |
* 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.OmniOscillator# | |
* @type {Array} | |
* @name partials | |
* @example | |
* osc.partials = [1, 0.2, 0.01]; | |
*/ | |
Object.defineProperty(Tone.OmniOscillator.prototype, 'partials', { | |
get: function () { | |
return this._oscillator.partials; | |
}, | |
set: function (partials) { | |
if (this._sourceType !== OmniOscType.Oscillator) { | |
this.type = Tone.Oscillator.Type.Custom; | |
} | |
this._oscillator.partials = partials; | |
} | |
}); | |
/** | |
* connect the oscillator to the frequency and detune signals | |
* @private | |
*/ | |
Tone.OmniOscillator.prototype._createNewOscillator = function (OscillatorConstructor) { | |
//short delay to avoid clicks on the change | |
var now = this.now() + this.blockTime; | |
if (this._oscillator !== null) { | |
var oldOsc = this._oscillator; | |
oldOsc.stop(now); | |
//dispose the old one | |
setTimeout(function () { | |
oldOsc.dispose(); | |
oldOsc = null; | |
}, this.blockTime * 1000); | |
} | |
this._oscillator = new OscillatorConstructor(); | |
this.frequency.connect(this._oscillator.frequency); | |
this.detune.connect(this._oscillator.detune); | |
this._oscillator.connect(this.output); | |
if (this.state === Tone.State.Started) { | |
this._oscillator.start(now); | |
} | |
}; | |
/** | |
* The phase of the oscillator in degrees. | |
* @memberOf Tone.OmniOscillator# | |
* @type {Degrees} | |
* @name phase | |
*/ | |
Object.defineProperty(Tone.OmniOscillator.prototype, 'phase', { | |
get: function () { | |
return this._oscillator.phase; | |
}, | |
set: function (phase) { | |
this._oscillator.phase = phase; | |
} | |
}); | |
/** | |
* The width of the oscillator (only if the oscillator is set to pulse) | |
* @memberOf Tone.OmniOscillator# | |
* @type {NormalRange} | |
* @signal | |
* @name width | |
* @example | |
* var omniOsc = new Tone.OmniOscillator(440, "pulse"); | |
* //can access the width attribute only if type === "pulse" | |
* omniOsc.width.value = 0.2; | |
*/ | |
Object.defineProperty(Tone.OmniOscillator.prototype, 'width', { | |
get: function () { | |
if (this._sourceType === OmniOscType.PulseOscillator) { | |
return this._oscillator.width; | |
} | |
} | |
}); | |
/** | |
* The modulationFrequency Signal of the oscillator | |
* (only if the oscillator type is set to pwm). | |
* @memberOf Tone.OmniOscillator# | |
* @type {Frequency} | |
* @signal | |
* @name modulationFrequency | |
* @example | |
* var omniOsc = new Tone.OmniOscillator(440, "pwm"); | |
* //can access the modulationFrequency attribute only if type === "pwm" | |
* omniOsc.modulationFrequency.value = 0.2; | |
*/ | |
Object.defineProperty(Tone.OmniOscillator.prototype, 'modulationFrequency', { | |
get: function () { | |
if (this._sourceType === OmniOscType.PWMOscillator) { | |
return this._oscillator.modulationFrequency; | |
} | |
} | |
}); | |
/** | |
* Clean up. | |
* @return {Tone.OmniOscillator} this | |
*/ | |
Tone.OmniOscillator.prototype.dispose = function () { | |
Tone.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; | |
return this; | |
}; | |
return Tone.OmniOscillator; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Base-class for all instruments | |
* | |
* @constructor | |
* @extends {Tone} | |
*/ | |
Tone.Instrument = function (options) { | |
//get the defaults | |
options = this.defaultArg(options, Tone.Instrument.defaults); | |
/** | |
* The output and volume triming node | |
* @type {Tone.Volume} | |
* @private | |
*/ | |
this._volume = this.output = new Tone.Volume(options.volume); | |
/** | |
* The volume of the output in decibels. | |
* @type {Decibels} | |
* @signal | |
* @example | |
* source.volume.value = -6; | |
*/ | |
this.volume = this._volume.volume; | |
this._readOnly('volume'); | |
}; | |
Tone.extend(Tone.Instrument); | |
/** | |
* the default attributes | |
* @type {object} | |
*/ | |
Tone.Instrument.defaults = { | |
/** the volume of the output in decibels */ | |
'volume': 0 | |
}; | |
/** | |
* @abstract | |
* @param {string|number} note the note to trigger | |
* @param {Time} [time=now] the time to trigger the ntoe | |
* @param {number} [velocity=1] the velocity to trigger the note | |
*/ | |
Tone.Instrument.prototype.triggerAttack = Tone.noOp; | |
/** | |
* @abstract | |
* @param {Time} [time=now] when to trigger the release | |
*/ | |
Tone.Instrument.prototype.triggerRelease = Tone.noOp; | |
/** | |
* Trigger the attack and then the release after the duration. | |
* @param {Frequency} note The note to trigger. | |
* @param {Time} duration How long the note should be held for before | |
* triggering the release. | |
* @param {Time} [time=now] When the note should be triggered. | |
* @param {NormalRange} [velocity=1] The velocity the note should be triggered at. | |
* @returns {Tone.Instrument} this | |
* @example | |
* //trigger "C4" for the duration of an 8th note | |
* synth.triggerAttackRelease("C4", "8n"); | |
*/ | |
Tone.Instrument.prototype.triggerAttackRelease = function (note, duration, time, velocity) { | |
time = this.toSeconds(time); | |
duration = this.toSeconds(duration); | |
this.triggerAttack(note, time, velocity); | |
this.triggerRelease(time + duration); | |
return this; | |
}; | |
/** | |
* clean up | |
* @returns {Tone.Instrument} this | |
*/ | |
Tone.Instrument.prototype.dispose = function () { | |
Tone.prototype.dispose.call(this); | |
this._volume.dispose(); | |
this._volume = null; | |
this._writable(['volume']); | |
this.volume = null; | |
return this; | |
}; | |
return Tone.Instrument; | |
}); | |
Module(function (Tone) { | |
/** | |
* @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 | |
* @extends {Tone.Instrument} | |
*/ | |
Tone.Monophonic = function (options) { | |
//get the defaults | |
options = this.defaultArg(options, Tone.Monophonic.defaults); | |
Tone.Instrument.call(this, options); | |
/** | |
* The glide time between notes. | |
* @type {Time} | |
*/ | |
this.portamento = options.portamento; | |
}; | |
Tone.extend(Tone.Monophonic, Tone.Instrument); | |
/** | |
* @static | |
* @const | |
* @type {Object} | |
*/ | |
Tone.Monophonic.defaults = { 'portamento': 0 }; | |
/** | |
* Trigger the attack of the note optionally with a given velocity. | |
* | |
* | |
* @param {Frequency} note The note to trigger. | |
* @param {Time} [time=now] When the note should start. | |
* @param {number} [velocity=1] velocity The velocity scaler | |
* determines how "loud" the note | |
* will be triggered. | |
* @returns {Tone.Monophonic} this | |
* @example | |
* synth.triggerAttack("C4"); | |
* @example | |
* //trigger the note a half second from now at half velocity | |
* synth.triggerAttack("C4", "+0.5", 0.5); | |
*/ | |
Tone.Monophonic.prototype.triggerAttack = function (note, time, velocity) { | |
time = this.toSeconds(time); | |
this._triggerEnvelopeAttack(time, velocity); | |
this.setNote(note, time); | |
return this; | |
}; | |
/** | |
* Trigger the release portion of the envelope | |
* @param {Time} [time=now] If no time is given, the release happens immediatly | |
* @returns {Tone.Monophonic} this | |
* @example | |
* synth.triggerRelease(); | |
*/ | |
Tone.Monophonic.prototype.triggerRelease = function (time) { | |
this._triggerEnvelopeRelease(time); | |
return this; | |
}; | |
/** | |
* override this method with the actual method | |
* @abstract | |
* @private | |
*/ | |
Tone.Monophonic.prototype._triggerEnvelopeAttack = function () { | |
}; | |
/** | |
* override this method with the actual method | |
* @abstract | |
* @private | |
*/ | |
Tone.Monophonic.prototype._triggerEnvelopeRelease = function () { | |
}; | |
/** | |
* Set the note at the given time. If no time is given, the note | |
* will set immediately. | |
* @param {Frequency} note The note to change to. | |
* @param {Time} [time=now] The time when the note should be set. | |
* @returns {Tone.Monophonic} this | |
* @example | |
* //change to F#6 in one quarter note from now. | |
* synth.setNote("F#6", "+4n"); | |
* @example | |
* //change to Bb4 right now | |
* synth.setNote("Bb4"); | |
*/ | |
Tone.Monophonic.prototype.setNote = function (note, time) { | |
time = this.toSeconds(time); | |
if (this.portamento > 0) { | |
var currentNote = this.frequency.value; | |
this.frequency.setValueAtTime(currentNote, time); | |
var portTime = this.toSeconds(this.portamento); | |
this.frequency.exponentialRampToValueAtTime(note, time + portTime); | |
} else { | |
this.frequency.setValueAtTime(note, time); | |
} | |
return this; | |
}; | |
return Tone.Monophonic; | |
}); | |
Module(function (Tone) { | |
/** | |
* @class Tone.MonoSynth is composed of one oscillator, one filter, and two envelopes. | |
* The amplitude of the Tone.Oscillator and the cutoff frequency of the | |
* Tone.Filter are controlled by Tone.Envelopes. | |
* <img src="https://docs.google.com/drawings/d/1gaY1DF9_Hzkodqf8JI1Cg2VZfwSElpFQfI94IQwad38/pub?w=924&h=240"> | |
* | |
* @constructor | |
* @extends {Tone.Monophonic} | |
* @param {Object} [options] the options available for the synth | |
* see defaults below | |
* @example | |
* var synth = new Tone.MonoSynth({ | |
* "oscillator" : { | |
* "type" : "square" | |
* }, | |
* "envelope" : { | |
* "attack" : 0.1 | |
* } | |
* }).toMaster(); | |
* synth.triggerAttackRelease("C4", "8n"); | |
*/ | |
Tone.MonoSynth = function (options) { | |
//get the defaults | |
options = this.defaultArg(options, Tone.MonoSynth.defaults); | |
Tone.Monophonic.call(this, options); | |
/** | |
* The oscillator. | |
* @type {Tone.OmniOscillator} | |
*/ | |
this.oscillator = new Tone.OmniOscillator(options.oscillator); | |
/** | |
* The frequency control. | |
* @type {Frequency} | |
* @signal | |
*/ | |
this.frequency = this.oscillator.frequency; | |
/** | |
* The detune control. | |
* @type {Cents} | |
* @signal | |
*/ | |
this.detune = this.oscillator.detune; | |
/** | |
* The filter. | |
* @type {Tone.Filter} | |
*/ | |
this.filter = new Tone.Filter(options.filter); | |
/** | |
* The filter envelope. | |
* @type {Tone.FrequencyEnvelope} | |
*/ | |
this.filterEnvelope = new Tone.FrequencyEnvelope(options.filterEnvelope); | |
/** | |
* The amplitude envelope. | |
* @type {Tone.AmplitudeEnvelope} | |
*/ | |
this.envelope = new Tone.AmplitudeEnvelope(options.envelope); | |
//connect the oscillators to the output | |
this.oscillator.chain(this.filter, this.envelope, this.output); | |
//start the oscillators | |
this.oscillator.start(); | |
//connect the filter envelope | |
this.filterEnvelope.connect(this.filter.frequency); | |
this._readOnly([ | |
'oscillator', | |
'frequency', | |
'detune', | |
'filter', | |
'filterEnvelope', | |
'envelope' | |
]); | |
}; | |
Tone.extend(Tone.MonoSynth, Tone.Monophonic); | |
/** | |
* @const | |
* @static | |
* @type {Object} | |
*/ | |
Tone.MonoSynth.defaults = { | |
'frequency': 'C4', | |
'detune': 0, | |
'oscillator': { 'type': 'square' }, | |
'filter': { | |
'Q': 6, | |
'type': 'lowpass', | |
'rolloff': -24 | |
}, | |
'envelope': { | |
'attack': 0.005, | |
'decay': 0.1, | |
'sustain': 0.9, | |
'release': 1 | |
}, | |
'filterEnvelope': { | |
'attack': 0.06, | |
'decay': 0.2, | |
'sustain': 0.5, | |
'release': 2, | |
'baseFrequency': 200, | |
'octaves': 7, | |
'exponent': 2 | |
} | |
}; | |
/** | |
* start the attack portion of the envelope | |
* @param {Time} [time=now] the time the attack should start |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment