+
+
+ 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, @@ -54,7 +54,7 @@ * function setup() { * createCanvas(100,100); * //translates canvas 50px down - * select('canvas').translate(0,50); + * select('canvas').position(100, 100); * } *
@@ -566,7 +566,7 @@
* Creates a radio button <input></input> element in the DOM.
*
* @method createRadio
- * @param {String} [divId] the id and name of the created div and input field respectively
+ * @param {String} [divId] the id and name of the created div and input field respectively
* @return {Object/p5.Element} pointer to p5.Element holding created node
*/
p5.prototype.createRadio = function() {
@@ -646,7 +646,7 @@
};
return self
};
-
+
/**
* Creates an <input></input> element in the DOM for text input.
* Use .size() to set the display length of the box.
@@ -729,7 +729,7 @@
}
}
}
-
+
// Now let's handle when a file was selected
elt.addEventListener('change', handleFileSelect, false);
return addElement(elt, this);
@@ -809,7 +809,7 @@
* 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 this
- * page for further information about supported formats.
+ * page for further information about supported formats.
*
* @method createAudio
* @param {String|Array} src path to an audio file, or array of paths for
@@ -849,7 +849,7 @@
* @method createCapture
* @param {String|Constant|Object} type type of capture, either VIDEO or
* AUDIO if none specified, default both,
- * or a Constraints boject
+ * or a Constraints object
* @param {Function} callback function to be called once
* stream has loaded
* @return {Object/p5.Element} capture video p5.Element
@@ -1160,91 +1160,51 @@
}
};
- /**
- * 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
- *
- * function setup() {
- * var cnv = createCanvas(100,100);
- * //translates canvas 50px down
- * cnv.translate(0,50);
- * }
- *
- */
- p5.Element.prototype.translate = function(){
+ /* Helper method called by p5.Element.style() */
+ 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, '');
+ // save out initial non-translate transform styling
+ var transform = '';
+ if (this.elt.style.transform) {
+ transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
+ transform = transform.replace(/translate[X-Z]?\(.*\)/g, '');
+ }
+ if (arguments.length === 2) {
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, '');
+ } else if (arguments.length > 2) {
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';
+ if (arguments.length === 3) {
+ this.elt.parentElement.style.perspective = '1000px';
+ } else {
+ this.elt.parentElement.style.perspective = arguments[3]+'px';
+ }
}
- return this;
+ // add any extra transform styling back on end
+ this.elt.style.transform += transform;
+ 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
- *
- * var x = 0,
- * y = 0,
- * z = 0;
- *
- * function draw(){
- * x+=.5 % 360;
- * y+=.5 % 360;
- * z+=.5 % 360;
- * //rotates p5.js logo .5 degrees on every axis each frame.
- * select('canvas').rotate(x,y,z);
- * }
- *
- */
- p5.Element.prototype.rotate = function(){
+ /* Helper method called by p5.Element.style() */
+ p5.Element.prototype._rotate = function(){
+ // save out initial non-rotate transform styling
+ var transform = '';
+ if (this.elt.style.transform) {
+ var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
+ transform = transform.replace(/rotate[X-Z]?\(.*\)/g, '');
+ }
+
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;
+ // add remaining transform back on
+ this.elt.style.transform += transform;
+ return this;
};
/**
@@ -1269,15 +1229,37 @@
* var myDiv = createDiv("I like pandas.");
* myDiv.style("font-size", "18px");
* myDiv.style("color", "#ff0000");
+ *
* var col = color(25,23,200,50);
- * createButton('button').style("background-color", col);
+ * var button = createButton("button");
+ * button.style("background-color", col);
+ * button.position(10, 10);
+ *
+ * var myDiv = createDiv("I like lizards.");
+ * myDiv.style("position", 20, 20);
+ * myDiv.style("rotate", 45);
+ *
+ * var myDiv;
+ * function setup() {
+ * background(200);
+ * myDiv = createDiv("I like gray.");
+ * myDiv.position(20, 20);
+ * }
+ *
+ * function draw() {
+ * myDiv.style("font-size", mouseX+"px");
+ * }
*
* var myDiv = createDiv("I like pandas.");
- *myDiv.attribute("align", "center");
+ * myDiv.attribute("align", "center");
*
+ * // gets the value
+ * var inp;
+ * function setup() {
+ * inp = createInput('');
+ * }
+ *
+ * function mousePressed() {
+ * print(inp.value());
+ * }
+ *
+ * // sets the value
+ * var inp;
+ * function setup() {
+ * inp = createInput('myValue');
+ * }
+ *
+ * function mousePressed() {
+ * inp.value("myValue");
+ * }
+ *
+ * var div = createDiv('div');
+ * div.attribute("display", "none");
+ * div.show(); // turns display to block
+ *
+ * var div = createDiv('this is a div');
+ * div.hide();
+ *
+ * var div = createDiv('this is a div');
+ * div.size(100, 100);
+ *
diff --git a/public/mode_assets/p5/empty_project/libraries/p5.js b/public/mode_assets/p5/empty_project/libraries/p5.js
index 42a670d..43d7136 100644
--- a/public/mode_assets/p5/empty_project/libraries/p5.js
+++ b/public/mode_assets/p5/empty_project/libraries/p5.js
@@ -1,4 +1,4 @@
-/*! p5.js v0.4.20 December 11, 2015 */
+/*! p5.js v0.4.23 March 04, 2016 */
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.p5 = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o
*
- * // draw an ellipsoid with radius 200, 300 and 400 .
+ * // draw an ellipsoid with radius 200, 300 and 400
* function setup(){
* createCanvas(100, 100, WEBGL);
* }
@@ -5722,7 +5723,7 @@ p5.prototype.ellipsoid = function(radiusx, radiusy, radiusz, detail){
* @example
*
*
- * //draw a spining sylinder with radius 200 and height 200
+ * //draw a spinning cylinder with radius 200 and height 200
* function setup(){
* createCanvas(100, 100, WEBGL);
* }
@@ -5818,7 +5819,7 @@ p5.prototype.cylinder = function(radius, height, detail){
* @example
*
*
- * //draw a spining cone with radius 200 and height 200
+ * //draw a spinning cone with radius 200 and height 200
* function setup(){
* createCanvas(100, 100, WEBGL);
* }
@@ -5891,7 +5892,7 @@ p5.prototype.cone = function(radius, height, detail){
* @example
*
*
- * //draw a spining torus with radius 200 and tube radius 60
+ * //draw a spinning torus with radius 200 and tube radius 60
* function setup(){
* createCanvas(100, 100, WEBGL);
* }
@@ -5950,7 +5951,7 @@ p5.prototype.torus = function(radius, tubeRadius, detail){
* @example
*
*
- * //draw a spining box with width, height and depth 200
+ * //draw a spinning box with width, height and depth 200
* function setup(){
* createCanvas(100, 100, WEBGL);
* }
@@ -6327,7 +6328,7 @@ p5.Renderer3D.prototype.strokeWeight = function() {
//////////////////////////////////////////////
p5.Renderer3D.prototype.fill = function(r, g, b, a) {
- var color = this._pInst.color.apply(this._pInst, arguments);
+ var color = this._pInst.color.apply(this, arguments);
var colorNormalized = color._array;
this.curColor = colorNormalized;
this.drawMode = 'fill';
@@ -6335,7 +6336,7 @@ p5.Renderer3D.prototype.fill = function(r, g, b, a) {
};
p5.Renderer3D.prototype.stroke = function(r, g, b, a) {
- var color = this._pInst.color.apply(this._pInst, arguments);
+ var color = this._pInst.color.apply(this, arguments);
var colorNormalized = color._array;
this.curColor = colorNormalized;
this.drawMode = 'stroke';
@@ -8845,7 +8846,7 @@ p5.prototype.brightness = function(c) {
* current colorMode(). The default mode is RGB values from 0 to 255
* and, therefore, the function call color(255, 204, 0) will return a
* bright yellow color.
- *
+ *
* Note that if only one value is provided to color(), it will be interpreted
* as a grayscale value. Add a second value, and it will be used for alpha
* transparency. When three values are specified, they are interpreted as
@@ -8993,9 +8994,17 @@ p5.prototype.color = function() {
if (arguments[0] instanceof p5.Color) {
return arguments[0]; // Do nothing if argument is already a color object.
} else if (arguments[0] instanceof Array) {
- return new p5.Color(this, arguments[0]);
+ if (this instanceof p5.Renderer) {
+ return new p5.Color(this, arguments[0]);
+ } else {
+ return new p5.Color(this._renderer, arguments[0]);
+ }
} else {
- return new p5.Color(this, arguments);
+ if (this instanceof p5.Renderer) {
+ return new p5.Color(this, arguments);
+ } else {
+ return new p5.Color(this._renderer, arguments);
+ }
}
};
@@ -9067,7 +9076,7 @@ p5.prototype.hue = function(c) {
* above 1 will be capped at 1. This is different from the behavior of lerp(),
* but necessary because otherwise numbers outside the range will produce
* strange and unexpected colors.
- *
+ *
* The way that colours are interpolated depends on the current color mode.
*
* @method lerpColor
@@ -9271,11 +9280,11 @@ var color_conversion = _dereq_('./color_conversion');
* @class p5.Color
* @constructor
*/
-p5.Color = function(pInst, vals) {
+p5.Color = function(renderer, vals) {
// Record color mode and maxes at time of construction.
- this.mode = pInst._renderer._colorMode;
- this.maxes = pInst._renderer._colorMaxes;
+ this.mode = renderer._colorMode;
+ this.maxes = renderer._colorMaxes;
// Calculate normalized RGBA values.
if (this.mode !== constants.RGB &&
@@ -9283,7 +9292,7 @@ p5.Color = function(pInst, vals) {
this.mode !== constants.HSB) {
throw new Error(this.mode + ' is an invalid colorMode.');
} else {
- this._array = p5.Color._parseInputs.apply(pInst, vals);
+ this._array = p5.Color._parseInputs.apply(renderer, vals);
}
// Expose closest screen color.
@@ -9668,8 +9677,8 @@ var colorPatterns = {
*/
p5.Color._parseInputs = function() {
var numArgs = arguments.length;
- var mode = this._renderer._colorMode;
- var maxes = this._renderer._colorMaxes;
+ var mode = this._colorMode;
+ var maxes = this._colorMaxes;
var results = [];
if (numArgs >= 3) { // Argument is a list of component values.
@@ -9977,6 +9986,18 @@ p5.prototype.background = function() {
* @example
*
*
+ * // Clear the screen on mouse press.
+ * function setup() {
+ * createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * ellipse(mouseX, mouseY, 20, 20);
+ * }
+ *
+ * function mousePressed() {
+ * clear();
+ * }
*
*
*/
@@ -9992,7 +10013,7 @@ p5.prototype.clear = function() {
* setting colorMode(RGB, 255). Setting colorMode(HSB) lets you use the HSB
* system instead. By default, this is colorMode(HSB, 360, 100, 100, 1). You
* can also use HSL.
- *
+ *
* Note: existing color objects remember the mode that they were created in,
* so you can change modes as you like without affecting their appearance.
*
@@ -10093,9 +10114,11 @@ p5.prototype.colorMode = function() {
* fill(204, 102, 0), all subsequent shapes will be filled with orange. This
* color is either specified in terms of the RGB or HSB color depending on
* the current colorMode(). (The default color space is RGB, with each value
- * in the range from 0 to 255.) If a single string argument is provided, RGB,
- * RGBA and Hex CSS color strings and all named color strings are supported.
- * A p5 Color object can also be provided to set the fill color.
+ * in the range from 0 to 255).
+ *
+ * If a single string argument is provided, RGB, RGBA and Hex CSS color strings
+ * and all named color strings are supported. A p5 Color object can also be
+ * provided to set the fill color.
*
* @method fill
* @param {Number|Array|String|p5.Color} v1 gray value, red or hue value
@@ -10248,9 +10271,11 @@ p5.prototype.noStroke = function() {
* Sets the color used to draw lines and borders around shapes. This color
* is either specified in terms of the RGB or HSB color depending on the
* current colorMode() (the default color space is RGB, with each value in
- * the range from 0 to 255). If a single string argument is provided, RGB,
- * RGBA and Hex CSS color strings and all named color strings are supported.
- * A p5 Color object can also be provided to set the stroke color.
+ * the range from 0 to 255).
+ *
+ * If a single string argument is provided, RGB, RGBA and Hex CSS color
+ * strings and all named color strings are supported. A p5 Color object
+ * can also be provided to set the stroke color.
*
* @method stroke
* @param {Number|Array|String|p5.Color} v1 gray value, red or hue value
@@ -10693,14 +10718,14 @@ p5.prototype.point = function() {
* clockwise or counter-clockwise around the defined shape.
*
* @method quad
- * @param {type} x1 the x-coordinate of the first point
- * @param {type} y1 the y-coordinate of the first point
- * @param {type} x2 the x-coordinate of the second point
- * @param {type} y2 the y-coordinate of the second point
- * @param {type} x3 the x-coordinate of the third point
- * @param {type} y3 the y-coordinate of the third point
- * @param {type} x4 the x-coordinate of the fourth point
- * @param {type} y4 the y-coordinate of the fourth point
+ * @param {Number} x1 the x-coordinate of the first point
+ * @param {Number} y1 the y-coordinate of the first point
+ * @param {Number} x2 the x-coordinate of the second point
+ * @param {Number} y2 the y-coordinate of the second point
+ * @param {Number} x3 the x-coordinate of the third point
+ * @param {Number} y3 the y-coordinate of the third point
+ * @param {Number} x4 the x-coordinate of the fourth point
+ * @param {Number} y4 the y-coordinate of the fourth point
* @return {p5} the p5 object
* @example
*
@@ -10770,11 +10795,12 @@ p5.prototype.quad = function() {
* every angle at ninety degrees. By default, the first two parameters set
* the location of the upper-left corner, the third sets the width, and the
* fourth sets the height. The way these parameters are interpreted, however,
-* may be changed with the rectMode() function. If provided, the fifth, sixth
-* seventh and eighth parameters, if specified, determine corner radius for
-* the top-right, top-left, lower-right and lower-left corners, respectively.
-* An omitted corner radius parameter is set to the value of the previously
-* specified radius value in the parameter list.
+* may be changed with the rectMode() function.
+*
+* The fifth, sixth, seventh and eighth parameters, if specified,
+* determine corner radius for the top-right, top-left, lower-right and
+* lower-left corners, respectively. An omitted corner radius parameter is set
+* to the value of the previously specified radius value in the parameter list.
*
* @method rect
* @param {Number} x x-coordinate of the rectangle.
@@ -10920,24 +10946,24 @@ var constants = _dereq_('./constants');
/**
* Modifies the location from which ellipses are drawn by changing the way
* in which parameters given to ellipse() are interpreted.
- *
+ *
* The default mode is ellipseMode(CENTER), which interprets the first two
* parameters of ellipse() as the shape's center point, while the third and
* fourth parameters are its width and height.
- *
+ *
* ellipseMode(RADIUS) also uses the first two parameters of ellipse() as
* the shape's center point, but uses the third and fourth parameters to
* specify half of the shapes's width and height.
- *
+ *
* ellipseMode(CORNER) interprets the first two parameters of ellipse() as
* the upper-left corner of the shape, while the third and fourth parameters
* are its width and height.
- *
+ *
* ellipseMode(CORNERS) interprets the first two parameters of ellipse() as
* the location of one corner of the ellipse's bounding box, and the third
* and fourth parameters as the location of the opposite corner.
- *
- * The parameter must be written in ALL CAPS because Processing is a
+ *
+ * The parameter must be written in ALL CAPS because Javascript is a
* case-sensitive language.
*
* @method ellipseMode
@@ -11005,24 +11031,24 @@ p5.prototype.noSmooth = function() {
/**
* Modifies the location from which rectangles are drawn by changing the way
* in which parameters given to rect() are interpreted.
- *
+ *
* The default mode is rectMode(CORNER), which interprets the first two
* parameters of rect() as the upper-left corner of the shape, while the
* third and fourth parameters are its width and height.
- *
+ *
* rectMode(CORNERS) interprets the first two parameters of rect() as the
* location of one corner, and the third and fourth parameters as the
* location of the opposite corner.
- *
+ *
* rectMode(CENTER) interprets the first two parameters of rect() as the
* shape's center point, while the third and fourth parameters are its
* width and height.
- *
+ *
* rectMode(RADIUS) also uses the first two parameters of rect() as the
* shape's center point, but uses the third and fourth parameters to specify
* half of the shapes's width and height.
- *
- * The parameter must be written in ALL CAPS because Processing is a
+ *
+ * The parameter must be written in ALL CAPS because Javascript is a
* case-sensitive language.
*
* @method rectMode
@@ -11524,9 +11550,10 @@ var p5 = function(sketch, node, sync) {
* define initial environment properties such as screen size and background
* color and to load media such as images and fonts as the program starts.
* There can only be one setup() function for each program and it shouldn't
- * be called again after its initial execution. Note: Variables declared
- * within setup() are not accessible within other functions, including
- * draw().
+ * be called again after its initial execution.
+ *
+ * Note: Variables declared within setup() are not accessible within other
+ * functions, including draw().
*
* @method setup
* @example
@@ -11550,15 +11577,15 @@ var p5 = function(sketch, node, sync) {
* the lines of code contained inside its block until the program is stopped
* or noLoop() is called. draw() is called automatically and should never be
* called explicitly.
- *
+ *
* It should always be controlled with noLoop(), redraw() and loop(). After
* noLoop() stops the code in draw() from executing, redraw() causes the
* code inside draw() to execute once, and loop() will cause the code
* inside draw() to resume executing continuously.
- *
+ *
* The number of times draw() executes in each second may be controlled with
* the frameRate() function.
- *
+ *
* There can only be one draw() function for each sketch, and draw() must
* exist if you want the code to run continuously, or to process events such
* as mousePressed(). Sometimes, you might have an empty call to draw() in
@@ -11730,6 +11757,9 @@ var p5 = function(sketch, node, sync) {
if (typeof context.preload === 'function') {
for (var f in this._preloadMethods) {
context[f] = this._preloadMethods[f][f];
+ if (context[f] && this) {
+ context[f] = context[f].bind(this);
+ }
}
}
@@ -11778,8 +11808,8 @@ var p5 = function(sketch, node, sync) {
}
this._setProperty('frameCount', this.frameCount + 1);
- this._updatePMouseCoords();
- this._updatePTouchCoords();
+ this._updateMouseCoords();
+ this._updateTouchCoords();
this.redraw();
this._frameRate = 1000.0/(now - this._lastFrameTime);
this._lastFrameTime = now;
@@ -12362,7 +12392,7 @@ p5.prototype.curvePoint = function(a, b, c, d, t) {
/**
* Evaluates the tangent to the curve at position t for points a, b, c, d.
* The parameter t varies between 0 and 1, a and d are points on the curve,
- * and b and c are the control points
+ * and b and c are the control points.
*
* @method curveTangent
* @param {Number} a coordinate of first point on the curve
@@ -12430,7 +12460,7 @@ if (window.console && console.log) {
* producing. This function creates a new line of text for each call to
* the function. Individual elements can be
* separated with quotes ("") and joined with the addition operator (+).
- *
+ *
* While print() is similar to console.log(), it does not directly map to
* it in order to simulate easier to understand behavior than
* console.log(). Due to this, it is slower. For fastest results, use
@@ -12442,7 +12472,7 @@ if (window.console && console.log) {
* @example
*
* var x = 10;
- * print("The value of x is "+x);
+ * print("The value of x is " + x);
* // prints "The value of x is 10"
*
*/
@@ -12574,12 +12604,13 @@ p5.prototype.cursor = function(type, x, y) {
* frame rate will not be achieved. Setting the frame rate within setup() is
* recommended. The default rate is 60 frames per second. This is the same as
* setFrameRate(val).
- *
+ *
* Calling frameRate() with no arguments returns the current framerate. This
* is the same as getFrameRate().
- *
+ *
* Calling frameRate() with arguments that are not of the type numbers
* or are non positive also returns current framerate.
+ *
* @method frameRate
* @param {Number} [fps] number of frames to be displayed every second
* @return {Number} current frameRate
@@ -12782,8 +12813,9 @@ p5.prototype.height = 0;
* be called on user input, for example, on mouse press like the example
* below.
*
- * @method fullScreen
- * @param {Boolean} [val] whether the sketch should be fullscreened or not
+ * @method fullscreen
+ * @param {Boolean} [val] whether the sketch should be in fullscreen mode
+ * or not
* @return {Boolean} current fullscreen state
* @example
*
@@ -12794,14 +12826,14 @@ p5.prototype.height = 0;
* }
* function mousePressed() {
* if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
- * var fs = fullScreen();
- * fullScreen(!fs);
+ * var fs = fullscreen();
+ * fullscreen(!fs);
* }
* }
*
*
*/
-p5.prototype.fullScreen = function(val) {
+p5.prototype.fullscreen = function(val) {
// no arguments, return fullscreen or not
if (typeof val === 'undefined') {
return document.fullscreenElement ||
@@ -13257,28 +13289,111 @@ function friendlyWelcome() {
report(str, 'println', '#4DB200'); // auto dark green
} */
-// This is a list of p5 functions/variables that are commonly misused
-// by beginners at top-level code, outside of setup/draw. We'd like to
-// detect these errors and help the user by suggesting they move them
+// This is a lazily-defined list of p5 symbols that may be
+// misused by beginners at top-level code, outside of setup/draw. We'd like
+// to detect these errors and help the user by suggesting they move them
// into setup/draw.
//
// For more details, see https://github.com/processing/p5.js/issues/1121.
-var misusedAtTopLevelCode = [
- 'color',
- 'random'
-];
+var misusedAtTopLevelCode = null;
+var FAQ_URL = 'https://github.com/processing/p5.js/wiki/' +
+ 'Frequently-Asked-Questions' +
+ '#why-cant-i-assign-variables-using-p5-functions-and-' +
+ 'variables-before-setup';
+
+function defineMisusedAtTopLevelCode() {
+ var uniqueNamesFound = {};
+
+ var getSymbols = function(obj) {
+ return Object.getOwnPropertyNames(obj).filter(function(name) {
+ if (name[0] === '_') {
+ return false;
+ }
+ if (name in uniqueNamesFound) {
+ return false;
+ }
+
+ uniqueNamesFound[name] = true;
+
+ return true;
+ }).map(function(name) {
+ var type;
+
+ if (typeof(obj[name]) === 'function') {
+ type = 'function';
+ } else if (name === name.toUpperCase()) {
+ type = 'constant';
+ } else {
+ type = 'variable';
+ }
+
+ return {name: name, type: type};
+ });
+ };
+
+ misusedAtTopLevelCode = [].concat(
+ getSymbols(p5.prototype),
+ // At present, p5 only adds its constants to p5.prototype during
+ // construction, which may not have happened at the time a
+ // ReferenceError is thrown, so we'll manually add them to our list.
+ getSymbols(_dereq_('./constants'))
+ );
+
+ // This will ultimately ensure that we report the most specific error
+ // possible to the user, e.g. advising them about HALF_PI instead of PI
+ // when their code misuses the former.
+ misusedAtTopLevelCode.sort(function(a, b) {
+ return b.name.length - a.name.length;
+ });
+}
+
+function helpForMisusedAtTopLevelCode(e, log) {
+ if (!log) {
+ log = console.log.bind(console);
+ }
+
+ if (!misusedAtTopLevelCode) {
+ defineMisusedAtTopLevelCode();
+ }
-function helpForMisusedAtTopLevelCode(e) {
- misusedAtTopLevelCode.forEach(function(name) {
- if (e.message && e.message.indexOf(name) !== -1) {
- console.log('%c Did you just try to use p5.js\'s \'' + name + '\' ' +
- 'function or variable? If so, you may want to ' +
- 'move it into your sketch\'s setup() function.',
- 'color: #B40033' /* Dark magenta */);
+ // If we find that we're logging lots of false positives, we can
+ // uncomment the following code to avoid displaying anything if the
+ // user's code isn't likely to be using p5's global mode. (Note that
+ // setup/draw are more likely to be defined due to JS function hoisting.)
+ //
+ //if (!('setup' in window || 'draw' in window)) {
+ // return;
+ //}
+
+ misusedAtTopLevelCode.some(function(symbol) {
+ // Note that while just checking for the occurrence of the
+ // symbol name in the error message could result in false positives,
+ // a more rigorous test is difficult because different browsers
+ // log different messages, and the format of those messages may
+ // change over time.
+ //
+ // For example, if the user uses 'PI' in their code, it may result
+ // in any one of the following messages:
+ //
+ // * 'PI' is undefined (Microsoft Edge)
+ // * ReferenceError: PI is undefined (Firefox)
+ // * Uncaught ReferenceError: PI is not defined (Chrome)
+
+ if (e.message && e.message.indexOf(symbol.name) !== -1) {
+ log('%cDid you just try to use p5.js\'s ' + symbol.name +
+ (symbol.type === 'function' ? '() ' : ' ') + symbol.type +
+ '? If so, you may want to ' +
+ 'move it into your sketch\'s setup() function.\n\n' +
+ 'For more details, see: ' + FAQ_URL,
+ 'color: #B40033' /* Dark magenta */);
+ return true;
}
});
}
+// Exposing this primarily for unit testing.
+p5.prototype._helpForMisusedAtTopLevelCode = helpForMisusedAtTopLevelCode;
+
if (document.readyState !== 'complete') {
window.addEventListener('error', helpForMisusedAtTopLevelCode, false);
@@ -13293,7 +13408,7 @@ if (document.readyState !== 'complete') {
module.exports = p5;
-},{"./core":48}],52:[function(_dereq_,module,exports){
+},{"./constants":47,"./core":48}],52:[function(_dereq_,module,exports){
/**
* @module DOM
* @submodule DOM
@@ -13477,6 +13592,41 @@ p5.Element.prototype.mousePressed = function (fxn) {
* @param {Function} fxn function to be fired when mouse wheel is
* scrolled over the element.
* @return {p5.Element}
+ * @example
+ *
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseWheel(changeSize); // attach listener for
+ * // activity on canvas only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with mousewheel movement
+ * // anywhere on screen
+ * function mouseWheel() {
+ * g = g + 10;
+ * }
+ *
+ * // this function fires with mousewheel movement
+ * // over canvas only
+ * function changeSize() {
+ * if (event.wheelDelta > 0) {
+ * d = d + 10;
+ * } else {
+ * d = d - 10;
+ * }
+ * }
+ *
+ *
*/
p5.Element.prototype.mouseWheel = function (fxn) {
attachListener('wheel', fxn, this);
@@ -13492,6 +13642,37 @@ p5.Element.prototype.mouseWheel = function (fxn) {
* @param {Function} fxn function to be fired when mouse is
* released over the element.
* @return {p5.Element}
+ * @example
+ *
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseReleased(changeGray); // attach listener for
+ * // activity on canvas only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // released
+ * function mouseReleased() {
+ * d = d + 10;
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // released while on canvas
+ * function changeGray() {
+ * g = random(0, 255);
+ * }
+ *
+ *
*/
p5.Element.prototype.mouseReleased = function (fxn) {
attachListener('mouseup', fxn, this);
@@ -13509,6 +13690,36 @@ p5.Element.prototype.mouseReleased = function (fxn) {
* @param {Function} fxn function to be fired when mouse is
* clicked over the element.
* @return {p5.Element}
+ * @example
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseClicked(changeGray); // attach listener for
+ * // activity on canvas only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // clicked anywhere
+ * function mouseClicked() {
+ * d = d + 10;
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // clicked on canvas
+ * function changeGray() {
+ * g = random(0, 255);
+ * }
+ *
+ *
*/
p5.Element.prototype.mouseClicked = function (fxn) {
attachListener('click', fxn, this);
@@ -13524,6 +13735,43 @@ p5.Element.prototype.mouseClicked = function (fxn) {
* @param {Function} fxn function to be fired when mouse is
* moved over the element.
* @return {p5.Element}
+ * @example
+ *
+ * var cnv;
+ * var d = 30;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseMoved(changeSize); // attach listener for
+ * // activity on canvas only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * fill(200);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires when mouse moves anywhere on
+ * // page
+ * function mouseMoved() {
+ * g = g + 5;
+ * if (g > 255) {
+ * g = 0;
+ * }
+ * }
+ *
+ * // this function fires when mouse moves over canvas
+ * function changeSize() {
+ * d = d + 2;
+ * if (d > 100) {
+ * d = 0;
+ * }
+ * }
+ *
+ *
*/
p5.Element.prototype.mouseMoved = function (fxn) {
attachListener('mousemove', fxn, this);
@@ -13540,6 +13788,29 @@ p5.Element.prototype.mouseMoved = function (fxn) {
* @param {Function} fxn function to be fired when mouse is
* moved over the element.
* @return {p5.Element}
+ * @example
+ *
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseOver(changeGray);
+ * d = 10;
+ * }
+ *
+ * function draw() {
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * function changeGray() {
+ * d = d + 10;
+ * if (d > 100) {
+ * d = 0;
+ * }
+ * }
+ *
+ *
*/
p5.Element.prototype.mouseOver = function (fxn) {
attachListener('mouseover', fxn, this);
@@ -13556,6 +13827,52 @@ p5.Element.prototype.mouseOver = function (fxn) {
* @param {Function} fxn function to be fired when the value of an
* element changes.
* @return {p5.Element}
+ * @example
+ *
+ * var sel;
+ *
+ * function setup() {
+ * textAlign(CENTER);
+ * background(200);
+ * sel = createSelect();
+ * sel.position(10, 10);
+ * sel.option('pear');
+ * sel.option('kiwi');
+ * sel.option('grape');
+ * sel.changed(mySelectEvent);
+ * }
+ *
+ * function mySelectEvent() {
+ * var item = sel.value();
+ * background(200);
+ * text("it's a "+item+"!", 50, 50);
+ * }
+ *
+ *
+ * var checkbox;
+ * var cnv;
+ *
+ * function setup() {
+ * checkbox = createCheckbox(" fill");
+ * checkbox.changed(changeFill);
+ * cnv = createCanvas(100, 100);
+ * cnv.position(0, 30);
+ * noFill();
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * ellipse(50, 50, 50, 50);
+ * }
+ *
+ * function changeFill() {
+ * if (checkbox.checked()) {
+ * fill(0);
+ * } else {
+ * noFill();
+ * }
+ * }
+ *
*/
p5.Element.prototype.changed = function (fxn) {
attachListener('change', fxn, this);
@@ -13572,6 +13889,19 @@ p5.Element.prototype.changed = function (fxn) {
* @method input
* @param {Function} fxn function to be fired on user input.
* @return {p5.Element}
+ * @example
+ *
+ * // Open your console to see the output
+ * function setup() {
+ * var inp = createInput('');
+ * inp.input(myInputEvent);
+ * }
+ *
+ * function myInputEvent() {
+ * console.log('you are typing: ', this.value());
+ * }
+ *
+ *
*/
p5.Element.prototype.input = function (fxn) {
attachListener('input', fxn, this);
@@ -13587,20 +13917,6 @@ p5.Element.prototype.input = function (fxn) {
* @param {Function} fxn function to be fired when mouse is
* moved off the element.
* @return {p5.Element}
- */
-p5.Element.prototype.mouseOut = function (fxn) {
- attachListener('mouseout', fxn, this);
- return this;
-};
-
-/**
- * The .touchStarted() function is called once after every time a touch is
- * registered. This can be used to attach element specific event listeners.
- *
- * @method touchStarted
- * @param {Function} fxn function to be fired when touch is
- * started over the element.
- * @return {p5.Element}
* @example
*
* var cnv;
@@ -13608,18 +13924,55 @@ p5.Element.prototype.mouseOut = function (fxn) {
* var g;
* function setup() {
* cnv = createCanvas(100, 100);
- * cnv.touchStarted(changeGray); // attach listener for
- * // canvas click only
+ * cnv.mouseOut(changeGray);
* d = 10;
- * g = 100;
* }
*
* function draw() {
- * background(g);
* ellipse(width/2, height/2, d, d);
* }
*
- * // this function fires with any touch anywhere
+ * function changeGray() {
+ * d = d + 10;
+ * if (d > 100) {
+ * d = 0;
+ * }
+ * }
+ *
+ *
+ */
+p5.Element.prototype.mouseOut = function (fxn) {
+ attachListener('mouseout', fxn, this);
+ return this;
+};
+
+/**
+ * The .touchStarted() function is called once after every time a touch is
+ * registered. This can be used to attach element specific event listeners.
+ *
+ * @method touchStarted
+ * @param {Function} fxn function to be fired when touch is
+ * started over the element.
+ * @return {p5.Element}
+ * @example
+ *
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.touchStarted(changeGray); // attach listener for
+ * // canvas click only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with any touch anywhere
* function touchStarted() {
* d = d + 10;
* }
@@ -14143,34 +14496,9 @@ var filters = _dereq_('../image/filters');
_dereq_('./p5.Renderer');
/**
- * 2D graphics renderer class. Can also be used as an off-screen
- * graphics buffer. A p5.Renderer2D object can be constructed
- * with the createRenderer2D() function. The fields and methods
- * for this class are extensive, but mirror the normal drawing API for p5.
- *
- * @class p5.Renderer2D
- * @constructor
- * @extends p5.Renderer
- * @param {String} elt DOM node that is wrapped
- * @param {Object} [pInst] pointer to p5 instance
- * @example
- *
- *
- * var pg;
- * function setup() {
- * createCanvas(100, 100);
- * pg = createRenderer2D(40, 40);
- * }
- * function draw() {
- * background(200);
- * pg.background(100);
- * pg.noStroke();
- * pg.ellipse(pg.width/2, pg.height/2, 50, 50);
- * image(pg, 9, 30);
- * image(pg, 51, 30);
- * }
- *
- *
+ * p5.Renderer2D
+ * The 2D graphics canvas renderer class.
+ * extends p5.Renderer
*/
var styleEmpty = 'rgba(0,0,0,0)';
// var alphaThreshold = 0.00125; // minimum visible
@@ -14212,7 +14540,7 @@ p5.Renderer2D.prototype.background = function() {
} else {
var curFill = this.drawingContext.fillStyle;
// create background rect
- var color = this._pInst.color.apply(this._pInst, arguments);
+ var color = this._pInst.color.apply(this, arguments);
var newFill = color.toString();
this.drawingContext.fillStyle = newFill;
this.drawingContext.fillRect(0, 0, this.width, this.height);
@@ -14229,13 +14557,13 @@ p5.Renderer2D.prototype.clear = function() {
p5.Renderer2D.prototype.fill = function() {
var ctx = this.drawingContext;
- var color = this._pInst.color.apply(this._pInst, arguments);
+ var color = this._pInst.color.apply(this, arguments);
ctx.fillStyle = color.toString();
};
p5.Renderer2D.prototype.stroke = function() {
var ctx = this.drawingContext;
- var color = this._pInst.color.apply(this._pInst, arguments);
+ var color = this._pInst.color.apply(this, arguments);
ctx.strokeStyle = color.toString();
};
@@ -14248,7 +14576,7 @@ p5.Renderer2D.prototype.image =
var cnv;
try {
if (this._tint) {
- if (img instanceof p5.MediaElement) {
+ if (p5.MediaElement && img instanceof p5.MediaElement) {
img.loadPixels();
}
if (img.canvas) {
@@ -14420,6 +14748,9 @@ p5.Renderer2D.prototype.loadPixels = function () {
};
p5.Renderer2D.prototype.set = function (x, y, imgOrCol) {
+ // round down to get integer numbers
+ x = Math.floor(x);
+ y = Math.floor(y);
if (imgOrCol instanceof p5.Image) {
this.drawingContext.save();
this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
@@ -15440,7 +15771,8 @@ var defaultId = 'defaultCanvas0'; // this gets set again in createCanvas
* in pixels. This method should be called only once at the start of setup.
* Calling createCanvas more than once in a sketch will result in very
* unpredicable behavior. If you want more than one drawing canvas
- * you could use createGraphics (hidden by default but it can be shown).
+ * you could use createGraphics (hidden by default but it can be shown).
+ *
* The system variables width and height are set by the parameters passed
* to this function. If createCanvas() is not used, the window will be
* given a default size of 100x100 pixels.
@@ -15448,7 +15780,7 @@ var defaultId = 'defaultCanvas0'; // this gets set again in createCanvas
* @method createCanvas
* @param {Number} w width of the canvas
* @param {Number} h height of the canvas
- * @param optional:{String} renderer 'p2d' | 'webgl'
+ * @param {String} [renderer] 'p2d' | 'webgl'
* @return {Object} canvas generated
* @example
*
@@ -15771,17 +16103,20 @@ window.performance.now = (function(){
* shim for Uint8ClampedArray.slice
* (allows arrayCopy to work with pixels[])
* with thanks to http://halfpapstudios.com/blog/tag/html5-canvas/
+ * Enumerable set to false to protect for...in from
+ * Uint8ClampedArray.prototype pollution.
*/
(function () {
'use strict';
-
- if (typeof Uint8ClampedArray !== 'undefined') {
- //Firefox and Chrome
- Uint8ClampedArray.prototype.slice = Array.prototype.slice;
+ if (typeof Uint8ClampedArray !== 'undefined' &&
+ !Uint8ClampedArray.prototype.slice) {
+ Object.defineProperty(Uint8ClampedArray.prototype, 'slice', {
+ value: Array.prototype.slice,
+ writable: true, configurable: true, enumerable: false
+ });
}
}());
-
},{}],58:[function(_dereq_,module,exports){
/**
* @module Structure
@@ -15798,21 +16133,20 @@ p5.prototype.exit = function() {
throw 'exit() not implemented, see remove()';
};
/**
- * Stops p5.js from continuously executing the code within draw().
+ * Stops p5.js from continuously executing the code within draw().
* If loop() is called, the code in draw() begins to run continuously again.
* If using noLoop() in setup(), it should be the last line inside the block.
- *
- *
- * When noLoop() is used, it's not possible to manipulate or access the
+ *
+ * When noLoop() is used, it's not possible to manipulate or access the
* screen inside event handling functions such as mousePressed() or
* keyPressed(). Instead, use those functions to call redraw() or loop(),
* which will run draw(), which can update the screen properly. This means
* that when noLoop() has been called, no drawing can happen, and functions
- * like saveFrame() or loadPixels() may not be used.
- *
- * Note that if the sketch is resized, redraw() will be called to update
+ * like saveFrame() or loadPixels() may not be used.
+ *
+ * Note that if the sketch is resized, redraw() will be called to update
* the sketch, even after noLoop() has been specified. Otherwise, the sketch
- * would enter an odd state until loop() was called.
+ * would enter an odd state until loop() was called.
*
* @method noLoop
* @example
@@ -16033,12 +16367,12 @@ p5.prototype.popStyle = function() {
* Executes the code within draw() one time. This functions allows the
* program to update the display window only when necessary, for example
* when an event registered by mousePressed() or keyPressed() occurs.
- *
+ *
* In structuring a program, it only makes sense to call redraw() within
* events such as mousePressed(). This is because redraw() does not run
* draw() immediately (it only sets a flag that indicates an update is
* needed).
- *
+ *
* The redraw() function does not work properly when called inside draw().
* To enable/disable animations, use loop() and noLoop().
*
@@ -16075,11 +16409,11 @@ p5.prototype.redraw = function () {
this._registeredMethods.pre.forEach(function (f) {
f.call(self);
});
- this.pop();
userDraw();
this._registeredMethods.post.forEach(function (f) {
f.call(self);
});
+ this.pop();
}
};
@@ -16165,14 +16499,14 @@ p5.prototype.resetMatrix = function() {
* Rotates a shape the amount specified by the angle parameter. This
* function accounts for angleMode, so angles can be entered in either
* RADIANS or DEGREES.
- *
+ *
* Objects are always rotated around their relative position to the
* origin and positive numbers rotate objects in a clockwise direction.
* Transformations apply to everything that happens after and subsequent
* calls to the function accumulates the effect. For example, calling
* rotate(HALF_PI) and then rotate(HALF_PI) is the same as rotate(PI).
* All tranformations are reset when draw() begins again.
- *
+ *
* Technically, rotate() multiplies the current transformation matrix
* by a rotation matrix. This function can be further controlled by
* the push() and pop().
@@ -16286,12 +16620,12 @@ p5.prototype.rotateZ = function(rad) {
* coordinate system. Scale values are specified as decimal percentages.
* For example, the function call scale(2.0) increases the dimension of a
* shape by 200%.
- *
+ *
* Transformations apply to everything that happens after and subsequent
* calls to the function multiply the effect. For example, calling scale(2.0)
* and then scale(1.5) is the same as scale(3.0). If scale() is called
* within draw(), the transformation is reset when the loop begins again.
- *
+ *
* Using this fuction with the z parameter requires using P3D as a
* parameter for size(), as shown in the third example above. This function
* can be further controlled with push() and pop().
@@ -16362,13 +16696,13 @@ p5.prototype.scale = function() {
* parameter. Angles should be specified in the current angleMode.
* Objects are always sheared around their relative position to the origin
* and positive numbers shear objects in a clockwise direction.
- *
+ *
* Transformations apply to everything that happens after and subsequent
* calls to the function accumulates the effect. For example, calling
* shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI).
* If shearX() is called within the draw(), the transformation is reset when
* the loop begins again.
- *
+ *
* Technically, shearX() multiplies the current transformation matrix by a
* rotation matrix. This function can be further controlled by the
* push() and pop() functions.
@@ -16399,13 +16733,13 @@ p5.prototype.shearX = function(angle) {
* parameter. Angles should be specified in the current angleMode. Objects
* are always sheared around their relative position to the origin and
* positive numbers shear objects in a clockwise direction.
- *
+ *
* Transformations apply to everything that happens after and subsequent
* calls to the function accumulates the effect. For example, calling
* shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI). If
* shearY() is called within the draw(), the transformation is reset when
* the loop begins again.
- *
+ *
* Technically, shearY() multiplies the current transformation matrix by a
* rotation matrix. This function can be further controlled by the
* push() and pop() functions.
@@ -16435,7 +16769,7 @@ p5.prototype.shearY = function(angle) {
* Specifies an amount to displace objects within the display window.
* The x parameter specifies left/right translation, the y parameter
* specifies up/down translation.
- *
+ *
* Transformations are cumulative and apply to everything that happens after
* and subsequent calls to the function accumulates the effect. For example,
* calling translate(50, 0) and then translate(20, 0) is the same as
@@ -16567,13 +16901,14 @@ p5.prototype.beginContour = function() {
* complex forms. beginShape() begins recording vertices for a shape and
* endShape() stops recording. The value of the kind parameter tells it which
* types of shapes to create from the provided vertices. With no mode
- * specified, the shape can be any irregular polygon. The parameters
- * available for beginShape() are POINTS, LINES, TRIANGLES, TRIANGLE_FAN,
- * TRIANGLE_STRIP, QUADS, and QUAD_STRIP. After calling the beginShape()
- * function, a series of vertex() commands must follow. To stop drawing the
- * shape, call endShape(). Each shape will be outlined with the current
- * stroke color and filled with the fill color.
- *
+ * specified, the shape can be any irregular polygon.
+ *
+ * The parameters available for beginShape() are POINTS, LINES, TRIANGLES,
+ * TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP. After calling the
+ * beginShape() function, a series of vertex() commands must follow. To stop
+ * drawing the shape, call endShape(). Each shape will be outlined with the
+ * current stroke color and filled with the fill color.
+ *
* Transformations such as translate(), rotate(), and scale() do not work
* within beginShape(). It is also not possible to use other shapes, such as
* ellipse() or rect() within beginShape().
@@ -16750,7 +17085,9 @@ p5.prototype.beginShape = function(kind) {
* Specifies vertex coordinates for Bezier curves. Each call to
* bezierVertex() defines the position of two control points and
* one anchor point of a Bezier curve, adding a new segment to a
- * line or shape. The first time bezierVertex() is used within a
+ * line or shape.
+ *
+ * The first time bezierVertex() is used within a
* beginShape() call, it must be prefaced with a call to vertex()
* to set the first anchor point. This function must be used between
* beginShape() and endShape() and only when there is no MODE
@@ -16807,8 +17144,9 @@ p5.prototype.bezierVertex = function(x2, y2, x3, y3, x4, y4) {
/**
* Specifies vertex coordinates for curves. This function may only
* be used between beginShape() and endShape() and only when there
- * is no MODE parameter specified to beginShape(). The first and
- * last points in a series of curveVertex() lines will be used to
+ * is no MODE parameter specified to beginShape().
+ *
+ * The first and last points in a series of curveVertex() lines will be used to
* guide the beginning and end of a the curve. A minimum of four
* points is required to draw a tiny curve between the second and
* third points. Adding a fifth point with curveVertex() will draw
@@ -17185,6 +17523,27 @@ p5.prototype._updatePAccelerations = function(){
/**
* The system variable rotationX always contains the rotation of the
* device along the x axis. Value is represented as 0 to +/-180 degrees.
+ *
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ *
+ *
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * //rotateZ(radians(rotationZ));
+ * rotateX(radians(rotationX));
+ * //rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ *
+ *
*
* @property rotationX
*/
@@ -17192,7 +17551,28 @@ p5.prototype.rotationX = 0;
/**
* The system variable rotationY always contains the rotation of the
- * device along the y axis. Value is represented as 0 to +/-180 degrees.
+ * device along the y axis. Value is represented as 0 to +/-90 degrees.
+ *
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ *
+ *
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * //rotateZ(radians(rotationZ));
+ * //rotateX(radians(rotationX));
+ * rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ *
+ *
*
* @property rotationY
*/
@@ -17204,6 +17584,27 @@ p5.prototype.rotationY = 0;
*
* Unlike rotationX and rotationY, this variable is available for devices
* with a built-in compass only.
+ *
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ *
+ *
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * rotateZ(radians(rotationZ));
+ * //rotateX(radians(rotationX));
+ * //rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ *
+ *
*
* @property rotationZ
*/
@@ -17250,7 +17651,7 @@ p5.prototype.pRotationX = 0;
/**
* The system variable pRotationY always contains the rotation of the
* device along the y axis in the frame previous to the current frame. Value
- * is represented as 0 to +/-180 degrees.
+ * is represented as 0 to +/-90 degrees.
*
* pRotationY can also be used with rotationY to determine the rotate
* direction of the device along the Y-axis.
@@ -17750,6 +18151,9 @@ p5.prototype.keyCode = 0;
*
*/
p5.prototype._onkeydown = function (e) {
+ if (downKeys[e.which]) { // prevent multiple firings
+ return;
+ }
this._setProperty('isKeyPressed', true);
this._setProperty('keyIsPressed', true);
this._setProperty('keyCode', e.which);
@@ -17798,6 +18202,7 @@ p5.prototype._onkeyup = function (e) {
var keyReleased = this.keyReleased || window.keyReleased;
this._setProperty('isKeyPressed', false);
this._setProperty('keyIsPressed', false);
+ this._setProperty('_lastKeyCodeTyped', null);
downKeys[e.which] = false;
//delete this._downKeys[e.which];
var key = String.fromCharCode(e.which);
@@ -17820,11 +18225,12 @@ p5.prototype._onkeyup = function (e) {
* key pressed will be stored in the key variable.
*
* Because of how operating systems handle key repeats, holding down a key
- * will cause multiple calls to keyTyped(), the rate is set by the operating
- * system and how each computer is configured.
- * Browsers may have different default
- * behaviors attached to various key events. To prevent any default
- * behavior for this event, add "return false" to the end of the method.
+ * will cause multiple calls to keyTyped() (and keyReleased() as well). The
+ * rate of repeat is set by the operating system and how each computer is
+ * configured.
+ * Browsers may have different default behaviors attached to various key
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
*
* @method keyTyped
* @example
@@ -17847,7 +18253,11 @@ p5.prototype._onkeyup = function (e) {
*
*/
p5.prototype._onkeypress = function (e) {
+ if (e.which === this._lastKeyCodeTyped) { // prevent multiple firings
+ return;
+ }
this._setProperty('keyCode', e.which);
+ this._setProperty('_lastKeyCodeTyped', e.which); // track last keyCode
this._setProperty('key', String.fromCharCode(e.which));
var keyTyped = this.keyTyped || window.keyTyped;
if (typeof keyTyped === 'function') {
@@ -17868,7 +18278,7 @@ p5.prototype._onblur = function (e) {
};
/**
- * The keyIsDown function checks if the key is currently down, i.e. pressed.
+ * The keyIsDown() function checks if the key is currently down, i.e. pressed.
* It can be used if you have an object that moves, and you want several keys
* to be able to affect its behaviour simultaneously, such as moving a
* sprite diagonally. You can put in any number representing the keyCode of
@@ -17927,6 +18337,17 @@ module.exports = p5;
var p5 = _dereq_('../core/core');
var constants = _dereq_('../core/constants');
+/*
+ * These are helper vars that store the mouseX and mouseY vals
+ * between the time that a mouse event happens and the next frame
+ * of draw. This is done to deal with the asynchronicity of event
+ * calls interacting with the draw loop. When a mouse event occurs
+ * the _nextMouseX/Y vars are updated, then on each call of draw, mouseX/Y
+ * and pmouseX/Y are updated using the _nextMouseX/Y vals.
+ */
+p5.prototype._nextMouseX = 0;
+p5.prototype._nextMouseY = 0;
+
/**
* The system variable mouseX always contains the current horizontal
* position of the mouse, relative to (0, 0) of the canvas.
@@ -18215,26 +18636,28 @@ p5.prototype.mouseButton = 0;
p5.prototype.mouseIsPressed = false;
p5.prototype.isMousePressed = false; // both are supported
-p5.prototype._updateMouseCoords = function(e) {
+p5.prototype._updateNextMouseCoords = function(e) {
if(e.type === 'touchstart' ||
e.type === 'touchmove' ||
e.type === 'touchend') {
- this._setProperty('mouseX', this.touchX);
- this._setProperty('mouseY', this.touchY);
+ this._setProperty('_nextMouseX', this._nextTouchX);
+ this._setProperty('_nextMouseY', this._nextTouchY);
} else {
if(this._curElement !== null) {
var mousePos = getMousePos(this._curElement.elt, e);
- this._setProperty('mouseX', mousePos.x);
- this._setProperty('mouseY', mousePos.y);
+ this._setProperty('_nextMouseX', mousePos.x);
+ this._setProperty('_nextMouseY', mousePos.y);
}
}
this._setProperty('winMouseX', e.pageX);
this._setProperty('winMouseY', e.pageY);
};
-p5.prototype._updatePMouseCoords = function() {
+p5.prototype._updateMouseCoords = function() {
this._setProperty('pmouseX', this.mouseX);
this._setProperty('pmouseY', this.mouseY);
+ this._setProperty('mouseX', this._nextMouseX);
+ this._setProperty('mouseY', this._nextMouseY);
this._setProperty('pwinMouseX', this.winMouseX);
this._setProperty('pwinMouseY', this.winMouseY);
};
@@ -18254,10 +18677,6 @@ p5.prototype._setMouseButton = function(e) {
this._setProperty('mouseButton', constants.RIGHT);
} else {
this._setProperty('mouseButton', constants.LEFT);
- if(e.type === 'touchstart' || e.type === 'touchmove') {
- this._setProperty('mouseX', this.touchX);
- this._setProperty('mouseY', this.touchY);
- }
}
};
@@ -18266,7 +18685,7 @@ p5.prototype._setMouseButton = function(e) {
* button is not pressed.
* Browsers may have different default
* behaviors attached to various mouse events. To prevent any default
- * behavior for this event, add `return false` to the end of the method.
+ * behavior for this event, add "return false" to the end of the method.
*
* @method mouseMoved
* @example
@@ -18306,7 +18725,7 @@ p5.prototype._setMouseButton = function(e) {
* touchMoved() function will be called instead if it is defined.
* Browsers may have different default
* behaviors attached to various mouse events. To prevent any default
- * behavior for this event, add `return false` to the end of the method.
+ * behavior for this event, add "return false" to the end of the method.
*
* @method mouseDragged
* @example
@@ -18342,8 +18761,8 @@ p5.prototype._setMouseButton = function(e) {
p5.prototype._onmousemove = function(e){
var context = this._isGlobal ? window : this;
var executeDefault;
- this._updateMouseCoords(e);
- this._updateTouchCoords(e);
+ this._updateNextMouseCoords(e);
+ this._updateNextTouchCoords(e);
if (!this.isMousePressed) {
if (typeof context.mouseMoved === 'function') {
executeDefault = context.mouseMoved(e);
@@ -18375,7 +18794,7 @@ p5.prototype._onmousemove = function(e){
* called instead if it is defined.
* Browsers may have different default
* behaviors attached to various mouse events. To prevent any default
- * behavior for this event, add `return false` to the end of the method.
+ * behavior for this event, add "return false" to the end of the method.
*
* @method mousePressed
* @example
@@ -18415,8 +18834,8 @@ p5.prototype._onmousedown = function(e) {
this._setProperty('isMousePressed', true);
this._setProperty('mouseIsPressed', true);
this._setMouseButton(e);
- this._updateMouseCoords(e);
- this._updateTouchCoords(e);
+ this._updateNextMouseCoords(e);
+ this._updateNextTouchCoords(e);
if (typeof context.mousePressed === 'function') {
executeDefault = context.mousePressed(e);
if(executeDefault === false) {
@@ -18436,7 +18855,7 @@ p5.prototype._onmousedown = function(e) {
* function will be called instead if it is defined.
* Browsers may have different default
* behaviors attached to various mouse events. To prevent any default
- * behavior for this event, add `return false` to the end of the method.
+ * behavior for this event, add "return false" to the end of the method.
*
*
* @method mouseReleased
@@ -18498,7 +18917,7 @@ p5.prototype._ondragover = p5.prototype._onmousemove;
* pressed and then released.
* Browsers may have different default
* behaviors attached to various mouse events. To prevent any default
- * behavior for this event, add `return false` to the end of the method.
+ * behavior for this event, add "return false" to the end of the method.
*
* @method mouseClicked
* @example
@@ -18544,7 +18963,7 @@ p5.prototype._onclick = function(e) {
};
/**
- * The function mouseWheel is executed every time a vertical mouse wheel
+ * The function mouseWheel() is executed every time a vertical mouse wheel
* event is detected either triggered by an actual mouse wheel or by a
* touchpad.
* The event.delta property returns the amount the mouse wheel
@@ -18553,32 +18972,32 @@ p5.prototype._onclick = function(e) {
* are inverted).
* Browsers may have different default behaviors attached to various
* mouse events. To prevent any default behavior for this event, add
- * `return false` to the end of the method.
- * Due to the current support of the `wheel` event on Safari, the function
- * may only work as expected if `return false` is included while using Safari.
+ * "return false" to the end of the method.
+ * Due to the current support of the "wheel" event on Safari, the function
+ * may only work as expected if "return false" is included while using Safari.
*
* @method mouseWheel
*
- * @example
- *
- *
- * var pos = 25;
- *
- * function draw() {
- * background(237, 34, 93);
- * fill(0);
- * rect(25, pos, 50, 50);
- * }
- *
- * function mouseWheel(event) {
- * print(event.delta);
- * //move the square according to the vertical scroll amount
- * pos += event.delta;
- * //uncomment to block page scrolling
- * //return false;
- * }
- *
- *
+ * @example
+ *
+ *
+ * var pos = 25;
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ * rect(25, pos, 50, 50);
+ * }
+ *
+ * function mouseWheel(event) {
+ * print(event.delta);
+ * //move the square according to the vertical scroll amount
+ * pos += event.delta;
+ * //uncomment to block page scrolling
+ * //return false;
+ * }
+ *
+ *
*/
p5.prototype._onwheel = function(e) {
var context = this._isGlobal ? window : this;
@@ -18605,6 +19024,17 @@ module.exports = p5;
var p5 = _dereq_('../core/core');
+/*
+ * These are helper vars that store the touchX and touchY vals
+ * between the time that a mouse event happens and the next frame
+ * of draw. This is done to deal with the asynchronicity of event
+ * calls interacting with the draw loop. When a touch event occurs
+ * the _nextTouchX/Y vars are updated, then on each call of draw, touchX/Y
+ * and ptouchX/Y are updated using the _nextMouseX/Y vals.
+ */
+p5.prototype._nextTouchX = 0;
+p5.prototype._nextTouchY = 0;
+
/**
* The system variable touchX always contains the horizontal position of
* one finger, relative to (0, 0) of the canvas. This is best used for
@@ -18661,17 +19091,17 @@ p5.prototype.touches = [];
*/
p5.prototype.touchIsDown = false;
-p5.prototype._updateTouchCoords = function(e) {
+p5.prototype._updateNextTouchCoords = function(e) {
if(e.type === 'mousedown' ||
e.type === 'mousemove' ||
e.type === 'mouseup'){
- this._setProperty('touchX', this.mouseX);
- this._setProperty('touchY', this.mouseY);
+ this._setProperty('_nextTouchX', this._nextMouseX);
+ this._setProperty('_nextTouchY', this._nextMouseY);
} else {
if(this._curElement !== null) {
var touchInfo = getTouchInfo(this._curElement.elt, e, 0);
- this._setProperty('touchX', touchInfo.x);
- this._setProperty('touchY', touchInfo.y);
+ this._setProperty('_nextTouchX', touchInfo.x);
+ this._setProperty('_nextTouchY', touchInfo.y);
var touches = [];
for(var i = 0; i < e.touches.length; i++){
@@ -18682,9 +19112,11 @@ p5.prototype._updateTouchCoords = function(e) {
}
};
-p5.prototype._updatePTouchCoords = function() {
+p5.prototype._updateTouchCoords = function() {
this._setProperty('ptouchX', this.touchX);
this._setProperty('ptouchY', this.touchY);
+ this._setProperty('touchX', this._nextTouchX);
+ this._setProperty('touchY', this._nextTouchY);
};
function getTouchInfo(canvas, e, i) {
@@ -18701,10 +19133,10 @@ function getTouchInfo(canvas, e, i) {
/**
* The touchStarted() function is called once after every time a touch is
* registered. If no touchStarted() function is defined, the mousePressed()
- * function will be called instead if it is defined. Browsers may have
- * different default
- * behaviors attached to various touch events. To prevent any default
- * behavior for this event, add `return false` to the end of the method.
+ * function will be called instead if it is defined.
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
*
* @method touchStarted
* @example
@@ -18741,8 +19173,8 @@ function getTouchInfo(canvas, e, i) {
p5.prototype._ontouchstart = function(e) {
var context = this._isGlobal ? window : this;
var executeDefault;
- this._updateTouchCoords(e);
- this._updateMouseCoords(e);
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
this._setProperty('touchIsDown', true);
if(typeof context.touchStarted === 'function') {
executeDefault = context.touchStarted(e);
@@ -18760,10 +19192,11 @@ p5.prototype._ontouchstart = function(e) {
/**
* The touchMoved() function is called every time a touch move is registered.
- * If no touchStarted() function is defined, the mouseDragged() function will
- * be called instead if it is defined. Browsers may have different default
- * behaviors attached to various touch events. To prevent any default
- * behavior for this event, add `return false` to the end of the method.
+ * If no touchMoved() function is defined, the mouseDragged() function will
+ * be called instead if it is defined.
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
*
* @method touchMoved
* @example
@@ -18799,8 +19232,8 @@ p5.prototype._ontouchstart = function(e) {
p5.prototype._ontouchmove = function(e) {
var context = this._isGlobal ? window : this;
var executeDefault;
- this._updateTouchCoords(e);
- this._updateMouseCoords(e);
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
if (typeof context.touchMoved === 'function') {
executeDefault = context.touchMoved(e);
if(executeDefault === false) {
@@ -18816,10 +19249,11 @@ p5.prototype._ontouchmove = function(e) {
/**
* The touchEnded() function is called every time a touch ends. If no
- * touchStarted() function is defined, the mouseReleased() function will be
- * called instead if it is defined. Browsers may have different default
- * behaviors attached to various touch events. To prevent any default
- * behavior for this event, add `return false` to the end of the method.
+ * touchEnded() function is defined, the mouseReleased() function will be
+ * called instead if it is defined.
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
*
* @method touchEnded
* @example
@@ -18854,8 +19288,8 @@ p5.prototype._ontouchmove = function(e) {
*
*/
p5.prototype._ontouchend = function(e) {
- this._updateTouchCoords(e);
- this._updateMouseCoords(e);
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
if (this.touches.length === 0) {
this._setProperty('touchIsDown', false);
}
@@ -19507,7 +19941,7 @@ var frames = [];
* Creates a new p5.Image (the datatype for storing images). This provides a
* fresh buffer of pixels to play with. Set the size of the buffer with the
* width and height parameters.
- *
+ *
* .pixels gives access to an array containing the values for all the pixels
* in the display window.
* These values are numbers. This array is the size (including an appropriate
@@ -19517,9 +19951,8 @@ var frames = [];
* more info. It may also be simpler to use set() or get().
*
* Before accessing the pixels of an image, the data must loaded with the
- * loadPixels()
- * function. After the array data has been modified, the updatePixels()
- * function must be run to update the changes.
+ * loadPixels() function. After the array data has been modified, the
+ * updatePixels() function must be run to update the changes.
*
* @method createImage
* @param {Integer} width width in pixels
@@ -19578,7 +20011,7 @@ p5.prototype.createImage = function(width, height) {
};
/**
- * Save the current canvas as an image. In Safari, will open the
+ * Save the current canvas as an image. In Safari, this will open the
* image in the window and the user must provide their own
* filename on save-as. Other browsers will either save the
* file immediately, or prompt the user with a dialogue window.
@@ -19703,12 +20136,17 @@ p5.prototype.saveCanvas = function() {
* all of the images that have just been created.
*
* @method saveFrames
- * @param {[type]} filename [description]
- * @param {[type]} extension [description]
- * @param {[type]} _duration [description]
- * @param {[type]} _fps [description]
- * @param {[Function]} callback [description]
- * @return {[type]} [description]
+ * @param {String} filename
+ * @param {String} extension 'jpg' or 'png'
+ * @param {Number} duration Duration in seconds to save the frames for.
+ * @param {Number} framerate Framerate to save the frames in.
+ * @param {Function} [callback] A callback function that will be executed
+ to handle the image data. This function
+ should accept an array as argument. The
+ array will contain the spcecified number of
+ frames of objects. Each object have three
+ properties: imageData - an
+ image/octet-stream, filename and extension.
*/
p5.prototype.saveFrames = function(fName, ext, _duration, _fps, callback) {
var duration = _duration || 3;
@@ -19980,8 +20418,8 @@ p5.prototype.image =
if (img.elt && img.elt.videoWidth && !img.canvas) { // video no canvas
var actualW = img.elt.videoWidth;
var actualH = img.elt.videoHeight;
- dWidth = sWidth || img.width;
- dHeight = sHeight || img.width*actualH/actualW;
+ dWidth = sWidth || img.elt.width;
+ dHeight = sHeight || img.elt.width*actualH/actualW;
sWidth = actualW;
sHeight = actualH;
} else {
@@ -20015,12 +20453,12 @@ p5.prototype.image =
/**
* Sets the fill value for displaying images. Images can be tinted to
* specified colors or made transparent by including an alpha value.
- *
+ *
* To apply transparency to an image without affecting its color, use
* white as the tint color and specify an alpha value. For instance,
* tint(255, 128) will make an image 50% transparent (assuming the default
* alpha range of 0-255, which can be changed with colorMode()).
- *
+ *
* The value for the gray parameter must be less than or equal to the current
* maximum value as specified by colorMode(). The default maximum value is
* 255.
@@ -20149,10 +20587,11 @@ p5.prototype._getTintedImageCanvas = function(img) {
* third parameters of image() as the upper-left corner of the image. If
* two additional parameters are specified, they are used to set the image's
* width and height.
- *
+ *
* imageMode(CORNERS) interprets the second and third parameters of image()
* as the location of one corner, and the fourth and fifth parameters as the
* opposite corner.
+ *
* imageMode(CENTER) interprets the second and third parameters of image()
* as the image's center point. If two additional parameters are specified,
* they are used to set the image's width and height.
@@ -20236,14 +20675,17 @@ var Filters = _dereq_('./filters');
/**
* Creates a new p5.Image. A p5.Image is a canvas backed representation of an
- * image. p5 can display .gif, .jpg and .png images. Images may be displayed
+ * image.
+ *
+ * p5 can display .gif, .jpg and .png images. Images may be displayed
* in 2D and 3D space. Before an image is used, it must be loaded with the
* loadImage() function. The p5.Image class contains fields for the width and
* height of the image, as well as an array called pixels[] that contains the
- * values for every pixel in the image. The methods described below allow
- * easy access to the image's pixels and alpha channel and simplify the
- * process of compositing.
- *
+ * values for every pixel in the image.
+ *
+ * The methods described below allow easy access to the image's pixels and
+ * alpha channel and simplify the process of compositing.
+ *
* Before using the pixels[] array, be sure to use the loadPixels() method on
* the image to make sure that the pixel data is properly loaded.
*
@@ -20381,6 +20823,28 @@ p5.Image.prototype._setProperty = function (prop, value) {
* Loads the pixels data for this image into the [pixels] attribute.
*
* @method loadPixels
+ * @example
+ *
+ * var myImage;
+ * var halfImage;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * myImage.loadPixels();
+ * halfImage = 4 * width * height/2;
+ * for(var i = 0; i < halfImage; i++){
+ * myImage.pixels[i+halfImage] = myImage.pixels[i];
+ * }
+ * myImage.updatePixels();
+ * }
+ *
+ * function draw() {
+ * image(myImage, 0, 0);
+ * }
+ *
*/
p5.Image.prototype.loadPixels = function(){
p5.Renderer2D.prototype.loadPixels.call(this);
@@ -20399,6 +20863,28 @@ p5.Image.prototype.loadPixels = function(){
* underlying canvas
* @param {Integer|undefined} h height of the target update area for the
* underlying canvas
+ * @example
+ *
+ * var myImage;
+ * var halfImage;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * myImage.loadPixels();
+ * halfImage = 4 * width * height/2;
+ * for(var i = 0; i < halfImage; i++){
+ * myImage.pixels[i+halfImage] = myImage.pixels[i];
+ * }
+ * myImage.updatePixels();
+ * }
+ *
+ * function draw() {
+ * image(myImage, 0, 0);
+ * }
+ *
*/
p5.Image.prototype.updatePixels = function(x, y, w, h){
p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
@@ -20421,6 +20907,25 @@ p5.Image.prototype.updatePixels = function(x, y, w, h){
* @param {Number} [h] height
* @return {Array/Color | p5.Image} color of pixel at x,y in array format
* [R, G, B, A] or p5.Image
+ * @example
+ *
+ * var myImage;
+ * var c;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * background(myImage);
+ * noStroke();
+ * c = myImage.get(60, 90);
+ * fill(c);
+ * rect(25, 25, 50, 50);
+ * }
+ *
+ * //get() returns color here
+ *
*/
p5.Image.prototype.get = function(x, y, w, h){
return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
@@ -20496,8 +21001,16 @@ p5.Image.prototype.resize = function(width, height){
// reference to the backing canvas of a p5.Image. But since we do not
// enforce that at the moment, I am leaving in the slower, but safer
// implementation.
- width = width || this.canvas.width;
- height = height || this.canvas.height;
+
+ // auto-resize
+ if (width === 0 && height === 0) {
+ width = this.canvas.width;
+ height = this.canvas.height;
+ } else if (width === 0) {
+ width = this.canvas.width * height / this.canvas.height;
+ } else if (height === 0) {
+ height = this.canvas.height * width / this.canvas.width;
+ }
var tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
@@ -20541,6 +21054,25 @@ p5.Image.prototype.resize = function(width, height){
* @param {Integer} dy Y coordinate of the destination's upper left corner
* @param {Integer} dw destination image width
* @param {Integer} dh destination image height
+ * @example
+ *
+ * var photo;
+ * var bricks;
+ * var x;
+ * var y;
+ *
+ * function preload() {
+ * photo = loadImage("assets/rockies.jpg");
+ * bricks = loadImage("assets/bricks.jpg");
+ * }
+ *
+ * function setup() {
+ * x = bricks.width/2;
+ * y = bricks.height/2;
+ * photo.copy(bricks, 0, 0, x, y, 0, 0, x, y);
+ * image(photo, 0, 0);
+ * }
+ *
*/
p5.Image.prototype.copy = function () {
p5.prototype.copy.apply(this, arguments);
@@ -20612,6 +21144,22 @@ p5.Image.prototype.mask = function(p5Image) {
* opaque see Filters.js for docs on each available
* filter
* @param {Number|undefined} value
+ * @example
+ *
+ * var photo1;
+ * var photo2;
+ *
+ * function preload() {
+ * photo1 = loadImage("assets/rockies.jpg");
+ * photo2 = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * photo2.filter("gray");
+ * image(photo1, 0, 0);
+ * image(photo2, width/2, 0);
+ * }
+ *
*/
p5.Image.prototype.filter = function(operation, value) {
Filters.apply(this.canvas, Filters[operation.toLowerCase()], value);
@@ -20651,9 +21199,27 @@ p5.Image.prototype.blend = function() {
* Accepts two strings for filename and file extension
* Supports png (default) and jpg.
*
- * @method save
- * @param {String} filename give your file a name
- * @param {String} extension 'png' or 'jpg'
+ * @method save
+ * @param {String} filename give your file a name
+ * @param {String} extension 'png' or 'jpg'
+ * @example
+ *
+ * var photo;
+ *
+ * function preload() {
+ * photo = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function draw() {
+ * image(photo, 0, 0);
+ * }
+ *
+ * function keyTyped() {
+ * if (key == 's') {
+ * photo.save("photo", "png");
+ * }
+ * }
+ *
*/
p5.Image.prototype.save = function(filename, extension) {
var mimeType;
@@ -20724,16 +21290,18 @@ _dereq_('../color/p5.Color');
* high denisty displays will have more pixels[] (by a factor of
* pixelDensity^2).
* For example, if the image is 100x100 pixels, there will be 40,000. On a
- * retina display, there will be 160,000. The first four values
- * (indices 0-3) in the array will be the R, G, B, A values of the pixel at
- * (0, 0). The second four values (indices 4-7) will contain the R, G, B, A
- * values of the pixel at (1, 0). More generally, to set values for a pixel
- * at (x, y):
- * var d = pixelDensity;
+ * retina display, there will be 160,000.
+ *
+ * The first four values (indices 0-3) in the array will be the R, G, B, A
+ * values of the pixel at (0, 0). The second four values (indices 4-7) will
+ * contain the R, G, B, A values of the pixel at (1, 0). More generally, to
+ * set values for a pixel at (x, y):
+ *
+ * var d = pixelDensity;
* for (var i = 0; i < d; i++) {
* for (var j = 0; j < d; j++) {
* // loop over
- * idx = 4*((y * d + j) * width * d + (x * d + i));
+ * idx = 4 * ((y * d + j) * width * d + (x * d + i));
* pixels[idx] = r;
* pixels[idx+1] = g;
* pixels[idx+2] = b;
@@ -20741,7 +21309,8 @@ _dereq_('../color/p5.Color');
* }
* }
*
- * While the above method is complex, it is flexible enough to work with
+ *
+ * While the above method is complex, it is flexible enough to work with
* any pixelDensity. Note that set() will automatically take care of
* setting all the appropriate values in pixels[] for a given (x, y) at
* any pixelDensity, but the performance may not be as fast when lots of
@@ -20754,7 +21323,7 @@ _dereq_('../color/p5.Color');
* Note that this is not a standard javascript array. This means that
* standard javascript functions such as slice() or
* arrayCopy() do not
- * work.
+ * work.
*
* @property pixels[]
* @example
@@ -20762,7 +21331,7 @@ _dereq_('../color/p5.Color');
*
* var pink = color(255, 102, 204);
* loadPixels();
- * var d = pixelDensity;
+ * var d = pixelDensity();
* var halfImage = 4 * (width * d) * (height/2 * d);
* for (var i = 0; i < halfImage; i+=4) {
* pixels[i] = red(pink);
@@ -21055,18 +21624,19 @@ p5.prototype.filter = function(operation, value) {
* the display window by specifying additional w and h parameters. When
* getting an image, the x and y parameters define the coordinates for the
* upper-left corner of the image, regardless of the current imageMode().
- *
+ *
* If the pixel requested is outside of the image window, [0,0,0,255] is
* returned. To get the numbers scaled according to the current color ranges
* and taking into account colorMode, use getColor instead of get.
- *
+ *
* Getting the color of a single pixel with get(x, y) is easy, but not as fast
* as grabbing the data directly from pixels[]. The equivalent statement to
* get(x, y) using pixels[] with pixel density d is
- * [pixels[(y*width*d+x)*d],
+ * [pixels[(y*width*d+x)*d],
* pixels[(y*width*d+x)*d+1],
* pixels[(y*width*d+x)*d+2],
- * pixels[(y*width*d+x)*d+3] ].
+ * pixels[(y*width*d+x)*d+3]].
+ *
* See the reference for pixels[] for more information.
*
* @method get
@@ -21126,7 +21696,7 @@ p5.prototype.get = function(x, y, w, h){
*
* function setup() {
* image(img, 0, 0);
- * var d = pixelDensity;
+ * var d = pixelDensity();
* var halfImage = 4 * (img.width * d) *
(img.height/2 * d);
* loadPixels();
@@ -21234,8 +21804,8 @@ p5.prototype.set = function (x, y, imgOrCol) {
*
* function setup() {
* image(img, 0, 0);
- * var halfImage = 4 * (img.width * pixelDensity) *
- * (img.height * pixelDensity/2);
+ * var halfImage = 4 * (img.width * pixelDensity()) *
+ * (img.height * pixelDensity()/2);
* loadPixels();
* for (var i = 0; i < halfImage; i++) {
* pixels[i+halfImage] = pixels[i];
@@ -21540,12 +22110,12 @@ p5.prototype.loadJSON = function () {
* Reads the contents of a file and creates a String array of its individual
* lines. If the name of the file is used as the parameter, as in the above
* example, the file must be located in the sketch directory/folder.
- *
+ *
* Alternatively, the file maybe be loaded from anywhere on the local
* computer using an absolute path (something that starts with / on Unix and
* Linux, or a drive letter on Windows), or the filename parameter can be a
* URL for a file found on a network.
- *
+ *
* This method is asynchronous, meaning it may not finish before the next
* line in your sketch is executed.
*
@@ -21860,8 +22430,8 @@ p5.prototype.loadTable = function (path) {
if (header) {
t.columns = records.shift();
} else {
- for (i = 0; i < records.length; i++) {
- t.columns[i] = i.toString();
+ for (i = 0; i < records[0].length; i++) {
+ t.columns[i] = 'null';
}
}
var row;
@@ -21917,18 +22487,18 @@ function makeObject(row, headers) {
* Reads the contents of a file and creates an XML object with its values.
* If the name of the file is used as the parameter, as in the above example,
* the file must be located in the sketch directory/folder.
- *
+ *
* Alternatively, the file maybe be loaded from anywhere on the local
* computer using an absolute path (something that starts with / on Unix and
* Linux, or a drive letter on Windows), or the filename parameter can be a
* URL for a file found on a network.
- *
+ *
* This method is asynchronous, meaning it may not finish before the next
* line in your sketch is executed. Calling loadXML() inside preload()
* guarantees to complete the operation before setup() and draw() are called.
- *
- * Outside of preload(), you may supply a callback function to handle the
- * object:
+ *
+ * Outside of preload(), you may supply a callback function to handle the
+ * object:
*
* @method loadXML
* @param {String} filename name of the file or URL to load
@@ -22471,7 +23041,7 @@ function escapeHelper(content) {
* @method saveTable
* @param {p5.Table} Table the Table object to save to a file
* @param {String} filename the filename to which the Table should be saved
- * @param {[String]} options can be one of "tsv", "csv", or "html"
+ * @param {String} [options] can be one of "tsv", "csv", or "html"
* @example
*
* var table;
@@ -24292,6 +24862,7 @@ p5.prototype.mag = function(x, y) {
/**
* Re-maps a number from one range to another.
+ *
* In the first example above, the number 25 is converted from a value in the
* range of 0 to 100 into a value that ranges from the left edge of the
* window (0) to the right edge (width).
@@ -24305,24 +24876,22 @@ p5.prototype.mag = function(x, y) {
* @return {Number} remapped number
* @example
*
- * createCanvas(200, 200);
* var value = 25;
* var m = map(value, 0, 100, 0, width);
- * ellipse(m, 200, 10, 10);
+ * ellipse(m, 50, 10, 10);
*
*
*
* function setup() {
- * createCanvas(200, 200);
* noStroke();
* }
*
* function draw() {
* background(204);
- * var x1 = map(mouseX, 0, width, 50, 150);
- * ellipse(x1, 75, 50, 50);
- * var x2 = map(mouseX, 0, width, 0, 200);
- * ellipse(x2, 125, 50, 50);
+ * var x1 = map(mouseX, 0, width, 25, 75);
+ * ellipse(x1, 25, 25, 25);
+ * var x2 = map(mouseX, 0, width, 0, 100);
+ * ellipse(x2, 75, 25, 25);
* }
*
*/
@@ -24556,7 +25125,7 @@ p5.prototype.round = Math.round;
* noStroke();
* fill(0);
* text("x = " + x1, 0, y1 + spacing);
- * text("sqrt(x) = " + x2, 0, y2 + spacing);
+ * text("sq(x) = " + x2, 0, y2 + spacing);
* }
*
*/
@@ -24811,15 +25380,17 @@ p5.prototype.noise = function(x,y,z) {
* function. Similar to harmonics in physics, noise is computed over
* several octaves. Lower octaves contribute more to the output signal and
* as such define the overall intensity of the noise, whereas higher octaves
- * create finer grained details in the noise sequence. By default, noise is
- * computed over 4 octaves with each octave contributing exactly half than
- * its predecessor, starting at 50% strength for the 1st octave. This
- * falloff amount can be changed by adding an additional function
+ * create finer grained details in the noise sequence.
+ *
+ * By default, noise is computed over 4 octaves with each octave contributing
+ * exactly half than its predecessor, starting at 50% strength for the 1st
+ * octave. This falloff amount can be changed by adding an additional function
* parameter. Eg. a falloff factor of 0.75 means each octave will now have
* 75% impact (25% less) of the previous lower octave. Any value between
* 0.0 and 1.0 is valid, however note that values greater than 0.5 might
- * result in greater than 1.0 values returned by noise().
By changing these parameters, the signal created by the noise()
+ * result in greater than 1.0 values returned by noise().
+ *
+ * By changing these parameters, the signal created by the noise()
* function can be adapted to fit very specific needs and characteristics.
*
* @method noiseDetail
@@ -24944,18 +25515,20 @@ var constants = _dereq_('../core/constants');
* A class to describe a two or three dimensional vector, specifically
* a Euclidean (also known as geometric) vector. A vector is an entity
* that has both magnitude and direction. The datatype, however, stores
- * the components of the vector (x,y for 2D, and x,y,z for 3D). The magnitude
- * and direction can be accessed via the methods mag() and heading(). In many
- * of the p5.js examples, you will see p5.Vector used to describe a position,
- * velocity, or acceleration. For example, if you consider a rectangle moving
- * across the screen, at any given instant it has a position (a vector that
- * points from the origin to its location), a velocity (the rate at which the
- * object's position changes per time unit, expressed as a vector), and
+ * the components of the vector (x, y for 2D, and x, y, z for 3D). The magnitude
+ * and direction can be accessed via the methods mag() and heading().
+ *
+ * In many of the p5.js examples, you will see p5.Vector used to describe a
+ * position, velocity, or acceleration. For example, if you consider a rectangle
+ * moving across the screen, at any given instant it has a position (a vector
+ * that points from the origin to its location), a velocity (the rate at which
+ * the object's position changes per time unit, expressed as a vector), and
* acceleration (the rate at which the object's velocity changes per time
- * unit, expressed as a vector). Since vectors represent groupings of values,
- * we cannot simply use traditional addition/multiplication/etc. Instead,
- * we'll need to do some "vector" math, which is made easy by the methods
- * inside the p5.Vector class.
+ * unit, expressed as a vector).
+ *
+ * Since vectors represent groupings of values, we cannot simply use
+ * traditional addition/multiplication/etc. Instead, we'll need to do some
+ * "vector" math, which is made easy by the methods inside the p5.Vector class.
*
* @class p5.Vector
* @constructor
@@ -26121,14 +26694,15 @@ p5.prototype.random = function (min, max) {
*
* Returns a random number fitting a Gaussian, or
* normal, distribution. There is theoretically no minimum or maximum
- * value that randomGaussian() might return. Rather, there is
+ * value that randomGaussian() might return. Rather, there is
* just a very low probability that values far from the mean will be
* returned; and a higher probability that numbers near the mean will
* be returned.
- * Takes either 0, 1 or 2 arguments.
- * If no args, returns a mean of 0 and standard deviation of 1
- * If one arg, that arg is the mean (standard deviation is 1)
- * If two args, first is mean, second is standard deviation
+ *
+ * Takes either 0, 1 or 2 arguments.
+ * If no args, returns a mean of 0 and standard deviation of 1.
+ * If one arg, that arg is the mean (standard deviation is 1).
+ * If two args, first is mean, second is standard deviation.
*
* @method randomGaussian
* @param {Number} mean the mean
@@ -26332,9 +26906,11 @@ p5.prototype.atan = function(ratio) {
* Calculates the angle (in radians) from a specified point to the coordinate
* origin as measured from the positive x-axis. Values are returned as a
* float in the range from PI to -PI. The atan2() function is most often used
- * for orienting geometry to the position of the cursor. Note: The
- * y-coordinate of the point is the first parameter, and the x-coordinate is
- * the second parameter, due the the structure of calculating the tangent.
+ * for orienting geometry to the position of the cursor.
+ *
+ * Note: The y-coordinate of the point is the first parameter, and the
+ * x-coordinate is the second parameter, due the the structure of calculating
+ * the tangent.
*
* @method atan2
* @param {Number} y y-coordinate of the point
@@ -26768,10 +27344,10 @@ _dereq_('../core/error_helpers');
* with textSize(). Change the color of the text with the fill() function.
* Change the outline of the text with the stroke() and strokeWeight()
* functions.
- *
+ *
* The text displays in relation to the textAlign() function, which gives the
* option to draw to the left, right, and center of the coordinates.
- *
+ *
* The x2 and y2 parameters define a rectangular area to display within and
* may only be used with string data. When these parameters are specified,
* they are interpreted based on the current rectMode() setting. Text that
@@ -27037,6 +27613,53 @@ p5.Font.prototype.textBounds = function(str, x, y, fontSize, options) {
return result;
};
+
+/**
+ * Computes an array of points following the path for specified text
+ *
+ * @param {String} txt a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Number} fontSize font size to use (optional)
+ * @param {Object} options an (optional) object that can contain:
+ *
+ *
sampleFactor - the ratio of path-length to number of samples
+ * (default=.25); higher values yield more points and are therefore
+ * more precise
+ *
+ *
simplifyThreshold - if set to a non-zero value, collinear points will be
+ * be removed from the polygon; the value represents the threshold angle to use
+ * when determining whether two edges are collinear
+ *
+ * @return {Array} an array of points, each with x, y, alpha (the path angle)
+ */
+p5.Font.prototype.textToPoints = function(txt, x, y, fontSize, options) {
+
+ var xoff = 0, result = [], glyphs = this._getGlyphs(txt);
+
+ fontSize = fontSize || this.parent._renderer._textSize;
+
+ for (var i = 0; i < glyphs.length; i++) {
+
+ var gpath = glyphs[i].getPath(x, y, fontSize),
+ paths = splitPaths(gpath.commands);
+
+ for (var j = 0; j < paths.length; j++) {
+
+ var pts = pathToPoints(paths[j], options);
+
+ for (var k = 0; k < pts.length; k++) {
+ pts[k].x += xoff;
+ result.push(pts[k]);
+ }
+ }
+
+ xoff += glyphs[i].advanceWidth * this._scale(fontSize);
+ }
+
+ return result;
+};
+
// ----------------------------- End API ------------------------------
/**
@@ -27273,6 +27896,602 @@ p5.Font.prototype._handleAlignment = function(p, ctx, line, x, y) {
return { x: x, y: y };
};
+// path-utils
+
+function pathToPoints(cmds, options) {
+
+ var opts = parseOpts(options, {
+ sampleFactor: 0.1,
+ simplifyThreshold: 0,
+ });
+
+ var len = pointAtLength(cmds,0,1), // total-length
+ t = len / (len * opts.sampleFactor),
+ pts = [];
+
+ for (var i = 0; i < len; i += t) {
+ pts.push(pointAtLength(cmds, i));
+ }
+
+ if (opts.simplifyThreshold) {
+ /*var count = */simplify(pts, opts.simplifyThreshold);
+ //console.log('Simplify: removed ' + count + ' pts');
+ }
+
+ return pts;
+}
+
+function simplify(pts, angle) {
+
+ angle = (typeof angle === 'undefined') ? 0 : angle;
+
+ var num = 0;
+ for (var i = pts.length - 1; pts.length > 3 && i >= 0; --i) {
+
+ if (collinear(at(pts, i - 1), at(pts, i), at(pts, i + 1), angle)) {
+
+ // Remove the middle point
+ pts.splice(i % pts.length, 1);
+ num++;
+ }
+ }
+ return num;
+}
+
+function splitPaths(cmds) {
+
+ var paths = [], current;
+ for (var i = 0; i < cmds.length; i++) {
+ if (cmds[i].type === 'M') {
+ if (current) {
+ paths.push(current);
+ }
+ current = [];
+ }
+ current.push(cmdToArr(cmds[i]));
+ }
+ paths.push(current);
+
+ return paths;
+}
+
+function cmdToArr(cmd) {
+
+ var arr = [ cmd.type ];
+ if (cmd.type === 'M' || cmd.type === 'L') { // moveto or lineto
+ arr.push(cmd.x, cmd.y);
+ } else if (cmd.type === 'C') {
+ arr.push(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+ } else if (cmd.type === 'Q') {
+ arr.push(cmd.x1, cmd.y1, cmd.x, cmd.y);
+ }
+ // else if (cmd.type === 'Z') { /* no-op */ }
+ return arr;
+}
+
+function parseOpts(options, defaults) {
+
+ if (typeof options !== 'object') {
+ options = defaults;
+ }
+ else {
+ for (var key in defaults) {
+ if (typeof options[key] === 'undefined') {
+ options[key] = defaults[key];
+ }
+ }
+ }
+ return options;
+}
+
+//////////////////////// Helpers ////////////////////////////
+
+function at(v, i) {
+ var s = v.length;
+ return v[i < 0 ? i % s + s : i % s];
+}
+
+function collinear(a, b, c, thresholdAngle) {
+
+ if (!thresholdAngle) {
+ return areaTriangle(a, b, c) === 0;
+ }
+
+ if (typeof collinear.tmpPoint1 === 'undefined') {
+ collinear.tmpPoint1 = [];
+ collinear.tmpPoint2 = [];
+ }
+
+ var ab = collinear.tmpPoint1, bc = collinear.tmpPoint2;
+ ab.x = b.x - a.x;
+ ab.y = b.y - a.y;
+ bc.x = c.x - b.x;
+ bc.y = c.y - b.y;
+
+ var dot = ab.x * bc.x + ab.y * bc.y,
+ magA = Math.sqrt(ab.x * ab.x + ab.y * ab.y),
+ magB = Math.sqrt(bc.x * bc.x + bc.y * bc.y),
+ angle = Math.acos(dot / (magA * magB));
+
+ return angle < thresholdAngle;
+}
+
+function areaTriangle(a, b, c) {
+ return (((b[0] - a[0]) * (c[1] - a[1])) - ((c[0] - a[0]) * (b[1] - a[1])));
+}
+
+// Portions of below code copyright 2008 Dmitry Baranovskiy (via MIT license)
+
+function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+
+ var t1 = 1 - t, t13 = Math.pow(t1, 3), t12 = Math.pow(t1, 2), t2 = t * t,
+ t3 = t2 * t, x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x +
+ t3 * p2x, y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y +
+ t3 * p2y, mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
+ my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
+ nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
+ ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
+ ax = t1 * p1x + t * c1x, ay = t1 * p1y + t * c1y,
+ cx = t1 * c2x + t * p2x, cy = t1 * c2y + t * p2y,
+ alpha = (90 - Math.atan2(mx - nx, my - ny) * 180 / Math.PI);
+
+ if (mx > nx || my < ny) { alpha += 180; }
+
+ return { x: x, y: y, m: { x: mx, y: my }, n: { x: nx, y: ny },
+ start: { x: ax, y: ay }, end: { x: cx, y: cy }, alpha: alpha
+ };
+}
+
+function getPointAtSegmentLength(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,length) {
+ return (length == null) ? bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) :
+ findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y,
+ getTatLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length));
+}
+
+function pointAtLength(path, length, istotal) {
+ path = path2curve(path);
+ var x, y, p, l, sp = '', subpaths = {}, point, len = 0;
+ for (var i = 0, ii = path.length; i < ii; i++) {
+ p = path[i];
+ if (p[0] === 'M') {
+ x = +p[1];
+ y = +p[2];
+ } else {
+ l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+ if (len + l > length) {
+ if (!istotal) {
+ point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5],
+ p[6], length - len);
+ return { x: point.x, y: point.y, alpha: point.alpha };
+ }
+ }
+ len += l;
+ x = +p[5];
+ y = +p[6];
+ }
+ sp += p.shift() + p;
+ }
+ subpaths.end = sp;
+
+ point = istotal ? len : findDotsAtSegment
+ (x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
+
+ if (point.alpha) {
+ point = { x: point.x, y: point.y, alpha: point.alpha };
+ }
+
+ return point;
+}
+
+function pathToAbsolute(pathArray) {
+
+ var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0;
+ if (pathArray[0][0] === 'M') {
+ x = +pathArray[0][1];
+ y = +pathArray[0][2];
+ mx = x;
+ my = y;
+ start++;
+ res[0] = ['M', x, y];
+ }
+
+ var dots,crz = pathArray.length===3 && pathArray[0][0]==='M' &&
+ pathArray[1][0].toUpperCase()==='R' && pathArray[2][0].toUpperCase()==='Z';
+
+ for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
+ res.push(r = []);
+ pa = pathArray[i];
+ if (pa[0] !== String.prototype.toUpperCase.call(pa[0])) {
+ r[0] = String.prototype.toUpperCase.call(pa[0]);
+ switch (r[0]) {
+ case 'A':
+ r[1] = pa[1];
+ r[2] = pa[2];
+ r[3] = pa[3];
+ r[4] = pa[4];
+ r[5] = pa[5];
+ r[6] = +(pa[6] + x);
+ r[7] = +(pa[7] + y);
+ break;
+ case 'V':
+ r[1] = +pa[1] + y;
+ break;
+ case 'H':
+ r[1] = +pa[1] + x;
+ break;
+ case 'R':
+ dots = [x, y].concat(pa.slice(1));
+ for (var j = 2, jj = dots.length; j < jj; j++) {
+ dots[j] = +dots[j] + x;
+ dots[++j] = +dots[j] + y;
+ }
+ res.pop();
+ res = res.concat(catmullRom2bezier(dots, crz));
+ break;
+ case 'M':
+ mx = +pa[1] + x;
+ my = +pa[2] + y;
+ break;
+ default:
+ for (j = 1, jj = pa.length; j < jj; j++) {
+ r[j] = +pa[j] + ((j % 2) ? x : y);
+ }
+ }
+ } else if (pa[0] === 'R') {
+ dots = [x, y].concat(pa.slice(1));
+ res.pop();
+ res = res.concat(catmullRom2bezier(dots, crz));
+ r = ['R'].concat(pa.slice(-2));
+ } else {
+ for (var k = 0, kk = pa.length; k < kk; k++) {
+ r[k] = pa[k];
+ }
+ }
+ switch (r[0]) {
+ case 'Z':
+ x = mx;
+ y = my;
+ break;
+ case 'H':
+ x = r[1];
+ break;
+ case 'V':
+ y = r[1];
+ break;
+ case 'M':
+ mx = r[r.length - 2];
+ my = r[r.length - 1];
+ break;
+ default:
+ x = r[r.length - 2];
+ y = r[r.length - 1];
+ }
+ }
+ return res;
+}
+
+function path2curve(path, path2) {
+
+ var p = pathToAbsolute(path), p2 = path2 && pathToAbsolute(path2),
+ attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
+ attrs2 = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
+
+ processPath = function(path, d, pcom) {
+ var nx, ny, tq = { T: 1, Q: 1 };
+ if (!path) { return ['C', d.x, d.y, d.x, d.y, d.x, d.y]; }
+ if (!(path[0] in tq)) { d.qx = d.qy = null; }
+ switch (path[0]) {
+ case 'M':
+ d.X = path[1];
+ d.Y = path[2];
+ break;
+ case 'A':
+ path = ['C'].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))));
+ break;
+ case 'S':
+ if (pcom === 'C' || pcom === 'S') {
+ nx = d.x * 2 - d.bx;
+ ny = d.y * 2 - d.by;
+ } else {
+ nx = d.x;
+ ny = d.y;
+ }
+ path = ['C', nx, ny].concat(path.slice(1));
+ break;
+ case 'T':
+ if (pcom === 'Q' || pcom === 'T') {
+ d.qx = d.x * 2 - d.qx;
+ d.qy = d.y * 2 - d.qy;
+ } else {
+ d.qx = d.x;
+ d.qy = d.y;
+ }
+ path = ['C'].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
+ break;
+ case 'Q':
+ d.qx = path[1];
+ d.qy = path[2];
+ path = ['C'].concat(q2c(d.x,d.y,path[1],path[2],path[3],path[4]));
+ break;
+ case 'L':
+ path = ['C'].concat(l2c(d.x, d.y, path[1], path[2]));
+ break;
+ case 'H':
+ path = ['C'].concat(l2c(d.x, d.y, path[1], d.y));
+ break;
+ case 'V':
+ path = ['C'].concat(l2c(d.x, d.y, d.x, path[1]));
+ break;
+ case 'Z':
+ path = ['C'].concat(l2c(d.x, d.y, d.X, d.Y));
+ break;
+ }
+ return path;
+ },
+
+ fixArc = function(pp, i) {
+ if (pp[i].length > 7) {
+ pp[i].shift();
+ var pi = pp[i];
+ while (pi.length) {
+ pcoms1[i] = 'A';
+ if (p2) { pcoms2[i] = 'A'; }
+ pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)));
+ }
+ pp.splice(i, 1);
+ ii = Math.max(p.length, p2 && p2.length || 0);
+ }
+ },
+
+ fixM = function(path1, path2, a1, a2, i) {
+ if (path1 && path2 && path1[i][0] === 'M' && path2[i][0] !== 'M') {
+ path2.splice(i, 0, ['M', a2.x, a2.y]);
+ a1.bx = 0;
+ a1.by = 0;
+ a1.x = path1[i][1];
+ a1.y = path1[i][2];
+ ii = Math.max(p.length, p2 && p2.length || 0);
+ }
+ },
+
+ pcoms1 = [], // path commands of original path p
+ pcoms2 = [], // path commands of original path p2
+ pfirst = '', // temporary holder for original path command
+ pcom = ''; // holder for previous path command of original path
+
+ for (var i = 0, ii = Math.max(p.length, p2 && p2.length || 0); i < ii; i++) {
+ if (p[i]) { pfirst = p[i][0]; } // save current path command
+
+ if (pfirst !== 'C') {
+ pcoms1[i] = pfirst; // Save current path command
+ if (i) { pcom = pcoms1[i - 1]; } // Get previous path command pcom
+ }
+ p[i] = processPath(p[i], attrs, pcom);
+
+ if (pcoms1[i] !== 'A' && pfirst === 'C') { pcoms1[i] = 'C'; }
+
+ fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1
+
+ if (p2) { // the same procedures is done to p2
+ if (p2[i]) { pfirst = p2[i][0]; }
+ if (pfirst !== 'C') {
+ pcoms2[i] = pfirst;
+ if (i) { pcom = pcoms2[i - 1]; }
+ }
+ p2[i] = processPath(p2[i], attrs2, pcom);
+
+ if (pcoms2[i] !== 'A' && pfirst === 'C') { pcoms2[i] = 'C'; }
+
+ fixArc(p2, i);
+ }
+ fixM(p, p2, attrs, attrs2, i);
+ fixM(p2, p, attrs2, attrs, i);
+ var seg = p[i], seg2 = p2 && p2[i], seglen = seg.length,
+ seg2len = p2 && seg2.length;
+ attrs.x = seg[seglen - 2];
+ attrs.y = seg[seglen - 1];
+ attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
+ attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
+ attrs2.bx = p2 && (parseFloat(seg2[seg2len - 4]) || attrs2.x);
+ attrs2.by = p2 && (parseFloat(seg2[seg2len - 3]) || attrs2.y);
+ attrs2.x = p2 && seg2[seg2len - 2];
+ attrs2.y = p2 && seg2[seg2len - 1];
+ }
+
+ return p2 ? [p, p2] : p;
+}
+
+function a2c(x1, y1, rx, ry, angle, lac, sweep_flag, x2, y2, recursive) {
+ // for more information of where this Math came from visit:
+ // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+ var PI = Math.PI, _120 = PI * 120 / 180, f1, f2, cx, cy,
+ rad = PI / 180 * (+angle || 0), res = [], xy,
+ rotate = function (x, y, rad) {
+ var X = x * Math.cos(rad) - y * Math.sin(rad),
+ Y = x * Math.sin(rad) + y * Math.cos(rad);
+ return { x: X, y: Y };
+ };
+ if (!recursive) {
+ xy = rotate(x1, y1, -rad);
+ x1 = xy.x;
+ y1 = xy.y;
+ xy = rotate(x2, y2, -rad);
+ x2 = xy.x;
+ y2 = xy.y;
+ var x = (x1 - x2) / 2, y = (y1 - y2) / 2,
+ h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+ if (h > 1) {
+ h = Math.sqrt(h);
+ rx = h * rx;
+ ry = h * ry;
+ }
+ var rx2 = rx * rx, ry2 = ry * ry,
+ k = (lac === sweep_flag ? -1 : 1) * Math.sqrt(Math.abs
+ ((rx2 * ry2 - rx2 * y * y - ry2 * x * x)/(rx2 * y * y + ry2 * x * x)));
+
+ cx = k * rx * y / ry + (x1 + x2) / 2;
+ cy = k * -ry * x / rx + (y1 + y2) / 2;
+ f1 = Math.asin(((y1 - cy) / ry).toFixed(9));
+ f2 = Math.asin(((y2 - cy) / ry).toFixed(9));
+
+ f1 = x1 < cx ? PI - f1 : f1;
+ f2 = x2 < cx ? PI - f2 : f2;
+
+ if (f1 < 0) { f1 = PI * 2 + f1; }
+ if (f2 < 0) { f2 = PI * 2 + f2; }
+
+ if (sweep_flag && f1 > f2) {
+ f1 = f1 - PI * 2;
+ }
+ if (!sweep_flag && f2 > f1) {
+ f2 = f2 - PI * 2;
+ }
+ } else {
+ f1 = recursive[0];
+ f2 = recursive[1];
+ cx = recursive[2];
+ cy = recursive[3];
+ }
+ var df = f2 - f1;
+ if (Math.abs(df) > _120) {
+ var f2old = f2, x2old = x2, y2old = y2;
+ f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
+ x2 = cx + rx * Math.cos(f2);
+ y2 = cy + ry * Math.sin(f2);
+ res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old,
+ [f2, f2old, cx, cy]);
+ }
+ df = f2 - f1;
+ var c1 = Math.cos(f1),
+ s1 = Math.sin(f1),
+ c2 = Math.cos(f2),
+ s2 = Math.sin(f2),
+ t = Math.tan(df / 4),
+ hx = 4 / 3 * rx * t,
+ hy = 4 / 3 * ry * t,
+ m1 = [x1, y1],
+ m2 = [x1 + hx * s1, y1 - hy * c1],
+ m3 = [x2 + hx * s2, y2 - hy * c2],
+ m4 = [x2, y2];
+ m2[0] = 2 * m1[0] - m2[0];
+ m2[1] = 2 * m1[1] - m2[1];
+ if (recursive) {
+ return [m2, m3, m4].concat(res);
+ } else {
+ res = [m2, m3, m4].concat(res).join().split(',');
+ var newres = [];
+ for (var i = 0, ii = res.length; i < ii; i++) {
+ newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i],
+ res[i + 1], rad).x;
+ }
+ return newres;
+ }
+}
+
+// http://schepers.cc/getting-to-the-point
+function catmullRom2bezier(crp, z) {
+ var d = [];
+ for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
+ var p = [{
+ x: +crp[i - 2],
+ y: +crp[i - 1]
+ }, {
+ x: +crp[i],
+ y: +crp[i + 1]
+ }, {
+ x: +crp[i + 2],
+ y: +crp[i + 3]
+ }, {
+ x: +crp[i + 4],
+ y: +crp[i + 5]
+ }];
+ if (z) {
+ if (!i) {
+ p[0] = {
+ x: +crp[iLen - 2],
+ y: +crp[iLen - 1]
+ };
+ } else if (iLen - 4 === i) {
+ p[3] = {
+ x: +crp[0],
+ y: +crp[1]
+ };
+ } else if (iLen - 2 === i) {
+ p[2] = {
+ x: +crp[0],
+ y: +crp[1]
+ };
+ p[3] = {
+ x: +crp[2],
+ y: +crp[3]
+ };
+ }
+ } else {
+ if (iLen - 4 === i) {
+ p[3] = p[2];
+ } else if (!i) {
+ p[0] = {
+ x: +crp[i],
+ y: +crp[i + 1]
+ };
+ }
+ }
+ d.push(['C', (-p[0].x + 6 * p[1].x + p[2].x) / 6, (-p[0].y + 6 * p[1].y +
+ p[2].y) / 6, (p[1].x + 6 * p[2].x - p[3].x) / 6, (p[1].y + 6 * p[2].y -
+ p[3].y) / 6, p[2].x, p[2].y ]);
+ }
+
+ return d;
+}
+
+function l2c(x1, y1, x2, y2) { return [x1, y1, x2, y2, x2, y2]; }
+
+function q2c(x1, y1, ax, ay, x2, y2) {
+ var _13 = 1 / 3, _23 = 2 / 3;
+ return [
+ _13 * x1 + _23 * ax, _13 * y1 + _23 * ay,
+ _13 * x2 + _23 * ax, _13 * y2 + _23 * ay, x2, y2
+ ];
+}
+
+function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
+ if (z == null) { z = 1; }
+ z = z > 1 ? 1 : z < 0 ? 0 : z;
+ var z2 = z / 2,
+ n = 12, Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678, -0.5873, 0.5873,
+ -0.7699, 0.7699, -0.9041, 0.9041, -0.9816, 0.9816],
+ sum = 0, Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032,
+ 0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472 ];
+ for (var i = 0; i < n; i++) {
+ var ct = z2 * Tvalues[i] + z2,
+ xbase = base3(ct, x1, x2, x3, x4),
+ ybase = base3(ct, y1, y2, y3, y4),
+ comb = xbase * xbase + ybase * ybase;
+ sum += Cvalues[i] * Math.sqrt(comb);
+ }
+ return z2 * sum;
+}
+
+function getTatLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {
+ if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {
+ return;
+ }
+ var t = 1, step = t / 2, t2 = t - step, l, e = 0.01;
+ l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+ while (Math.abs(l - ll) > e) {
+ step /= 2;
+ t2 += (l < ll ? 1 : -1) * step;
+ l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+ }
+ return t2;
+}
+
+function base3(t, p1, p2, p3, p4) {
+ var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
+ t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
+ return t * t2 - 3 * p1 + 3 * p2;
+}
+
function cacheKey() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; ++i) {
@@ -27280,7 +28499,6 @@ function cacheKey() {
}
i = args.length;
var hash = '';
-
while (i--) {
hash += (args[i] === Object(args[i])) ?
JSON.stringify(args[i]) : args[i];
@@ -27334,11 +28552,11 @@ p5.prototype.append = function(array, value) {
* elements to copy is determined by length. Note that copying values
* overwrites existing values in the destination array. To append values
* instead of overwriting them, use concat().
- *
- * The simplified version with only two arguments — arrayCopy(src, dst) —
+ *
+ * The simplified version with only two arguments, arrayCopy(src, dst),
* copies an entire array to another of the same size. It is equivalent to
* arrayCopy(src, 0, dst, 0, src.length).
- *
+ *
* Using this function is far more efficient for copying array data than
* iterating through a for() loop and copying each element individually.
*
@@ -27347,7 +28565,7 @@ p5.prototype.append = function(array, value) {
* @param {Number} [srcPosition] starting position in the source Array
* @param {Array} dst the destination Array
* @param {Number} [dstPosition] starting position in the destination Array
- * @param {Nimber} [length] number of Array elements to be copied
+ * @param {Number} [length] number of Array elements to be copied
*
* @example
*
@@ -27484,10 +28702,9 @@ p5.prototype.shorten = function(list) {
};
/**
- * Randomizes the order of the elements of an array.
- * Implements Fisher-Yates Shuffle Algorithm
- * http://Bost.Ocks.org/mike/shuffle/
- * http://en.Wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
+ * Randomizes the order of the elements of an array. Implements
+ *
+ * Fisher-Yates Shuffle Algorithm.
*
* @method shuffle
* @param {Array} array Array to shuffle
@@ -27509,7 +28726,8 @@ p5.prototype.shorten = function(list) {
*
*/
p5.prototype.shuffle = function(arr, bool) {
- arr = bool || ArrayBuffer.isView(arr)? arr : arr.slice();
+ var isView = ArrayBuffer && ArrayBuffer.isView && ArrayBuffer.isView(arr);
+ arr = bool || isView ? arr : arr.slice();
var rnd, tmp, idx = arr.length;
while (idx > 1) {
@@ -27944,11 +29162,11 @@ p5.prototype.join = function(list, separator) {
* If no groups are specified in the regular expression, but the sequence
* matches, an array of length 1 (with the matched text as the first element
* of the array) will be returned.
- *
+ *
* To use the function, first check to see if the result is null. If the
* result is null, then the sequence did not match at all. If the sequence
* did match, an array is returned.
- *
+ *
* If there are groups (specified by sets of parentheses) in the regular
* expression, then the contents of each will be returned in the array.
* Element [0] of a regular expression match returns the entire matching
@@ -27980,11 +29198,11 @@ p5.prototype.match = function(str, reg) {
* will be returned. If no groups are specified in the regular expression,
* but the sequence matches, a two dimensional array is still returned, but
* the second dimension is only of length one.
- *
+ *
* To use the function, first check to see if the result is null. If the
* result is null, then the sequence did not match at all. If the sequence
* did match, a 2D array is returned.
- *
+ *
* If there are groups (specified by sets of parentheses) in the regular
* expression, then the contents of each will be returned in the array.
* Assuming a loop with counter variable i, element [i][0] of a regular
@@ -28308,11 +29526,11 @@ function addNfs() {
* @method split
* @param {String} value the String to be split
* @param {String} delim the String used to separate the data
- * @return {Array} Array of Strings
+ * @return {Array} Array of Strings
* @example
*
*
- *var names = "Pat,Xio,Alex"
+ * var names = "Pat,Xio,Alex"
* var splitString = split(names, ",");
* text(splitString[0], 5, 30);
* text(splitString[1], 5, 50);
@@ -28328,7 +29546,7 @@ p5.prototype.split = function(str, delim) {
* The splitTokens() function splits a String at one or many character
* delimiters or "tokens." The delim parameter specifies the character or
* characters to be used as a boundary.
- *
+ *
* If no delim characters are specified, any whitespace character is used to
* split. Whitespace characters include tab (\t), line feed (\n), carriage
* return (\r), form feed (\f), and space.
@@ -28347,8 +29565,8 @@ p5.prototype.split = function(str, delim) {
*
* print(myStrArr); // prints : ["Mango"," Banana"," Lime"]
* }
- *
*
+ *
*/
p5.prototype.splitTokens = function() {
var d,sqo,sqc,str;
@@ -28424,7 +29642,7 @@ var p5 = _dereq_('../core/core');
*
*
* var day = day();
- * text("Current day: \n"+day, 5, 50);
+ * text("Current day: \n" + day, 5, 50);
*
*
*/
@@ -28442,7 +29660,7 @@ p5.prototype.day = function() {
*
*
* var hour = hour();
- * text("Current hour:\n"+hour, 5, 50);
+ * text("Current hour:\n" + hour, 5, 50);
*
*
*/
@@ -28460,7 +29678,7 @@ p5.prototype.hour = function() {
*
*
* var minute = minute();
- * text("Current minute: \n:"+minute, 5, 50);
+ * text("Current minute: \n" + minute, 5, 50);
*
*
*/
@@ -28479,7 +29697,7 @@ p5.prototype.minute = function() {
*
*
* var millisecond = millis();
- * text("Milliseconds \nrunning: "+millisecond, 5, 50);
+ * text("Milliseconds \nrunning: \n" + millisecond, 5, 40);
*
*
*/
@@ -28497,7 +29715,7 @@ p5.prototype.millis = function() {
*
*
* var month = month();
- * text("Current month: \n"+month, 5, 50);
+ * text("Current month: \n" + month, 5, 50);
*
*
*/
@@ -28515,7 +29733,7 @@ p5.prototype.month = function() {
*
*
* var second = second();
- * text("Current second: \n" +second, 5, 50);
+ * text("Current second: \n" + second, 5, 50);
*
*
*/
@@ -28533,7 +29751,7 @@ p5.prototype.second = function() {
*
*
* var year = year();
- * text("Current year: \n" +year, 5, 50);
+ * text("Current year: \n" + year, 5, 50);
*
*
*/
diff --git a/public/mode_assets/p5/empty_project/libraries/p5.sound.js b/public/mode_assets/p5/empty_project/libraries/p5.sound.js
index 2c267c5..65b358a 100644
--- a/public/mode_assets/p5/empty_project/libraries/p5.sound.js
+++ b/public/mode_assets/p5/empty_project/libraries/p5.sound.js
@@ -1,4 +1,4 @@
-/*! p5.sound.js v0.2.16 2015-11-13 */
+/*! p5.sound.js v0.3.0 2016-01-31 */
(function (root, factory) {
if (typeof define === 'function' && define.amd)
define('p5.sound', ['p5'], function (p5) { (factory(p5));});
@@ -593,6 +593,44 @@ helpers = function () {
return o;
};
}(master);
+var errorHandler;
+errorHandler = function () {
+ 'use strict';
+ /**
+ * Helper function to generate an error
+ * with a custom stack trace that points to the sketch
+ * and removes other parts of the stack trace.
+ *
+ * @private
+ *
+ * @param {String} name custom error name
+ * @param {String} errorTrace custom error trace
+ * @param {String} failedPath path to the file that failed to load
+ * @property {String} name custom error name
+ * @property {String} message custom error message
+ * @property {String} stack trace the error back to a line in the user's sketch.
+ * Note: this edits out stack trace within p5.js and p5.sound.
+ * @property {String} originalStack unedited, original stack trace
+ * @property {String} failedPath path to the file that failed to load
+ * @return {Error} returns a custom Error object
+ */
+ var CustomError = function (name, errorTrace, failedPath) {
+ var err = new Error();
+ var tempStack, splitStack;
+ err.name = name;
+ err.originalStack = err.stack + errorTrace;
+ tempStack = err.stack + errorTrace;
+ err.failedPath = failedPath;
+ // only print the part of the stack trace that refers to the user code:
+ var splitStack = tempStack.split('\n');
+ splitStack = splitStack.filter(function (ln) {
+ return !ln.match(/(p5.|native code|globalInit)/g);
+ });
+ err.stack = splitStack.join('\n');
+ return err;
+ };
+ return CustomError;
+}();
var panner;
panner = function () {
'use strict';
@@ -694,20 +732,23 @@ panner = function () {
var soundfile;
soundfile = function () {
'use strict';
+ var CustomError = errorHandler;
var p5sound = master;
var ac = p5sound.audiocontext;
/**
- * p5.SoundFile loads all of the data
- * from a soundfile (i.e. an mp3) into your sketch so that you can play it, manipulate
- * it, and visualize it.
- * Loading happens asynchronously, so the p5.SoundFile will not be available
- * for playback immediately after it is created. Use the loadSound
- * method in preload to ensure that it will be ready by the time
- * setup is called. Or, use a callback function to get notified
- * when the sound is ready.
- * Using the a
- *
- * local server like the p5 Editor is recommended when loading external files.
+ * SoundFile object with a path to a file.
+ *
+ * The p5.SoundFile may not be available immediately because
+ * it loads the file information asynchronously.
+ *
+ * To do something with the sound as soon as it loads
+ * pass the name of a function as the second parameter.
+ *
+ * Only one file path is required. However, audio file formats
+ * (i.e. mp3, ogg, wav and m4a/aac) are not supported by all
+ * web browsers. If you want to ensure compatability, instead of a single
+ * file path, you may include an Array of filepaths, and the browser will
+ * choose a format that works.
*
* @class p5.SoundFile
* @constructor
@@ -715,34 +756,33 @@ soundfile = function () {
* you may include multiple file formats in
* an array. Alternately, accepts an object
* from the HTML5 File API, or a p5.File.
- * @param {Function} [callback] Name of a function to call once file loads
+ * @param {Function} [successCallback] Name of a function to call once file loads
+ * @param {Function} [errorCallback] Name of a function to call if file fails to
+ * load. This function will receive an error or
+ * XMLHttpRequest object with information
+ * about what went wrong.
+ * @param {Function} [whileLoadingCallback] Name of a function to call while file
+ * is loading. That function will
+ * receive percentage loaded
+ * (between 0 and 1) as a
+ * parameter.
+ *
* @return {Object} p5.SoundFile Object
* @example
*
+ *
* function preload() {
* mySound = loadSound('assets/doorbell.mp3');
* }
- *
- * function setup() {
- * createCanvas(100, 100);
- * background(0, 255, 0);
- *
- * textAlign(CENTER);
- * text('click here to play', width/2, height/2);
*
+ * function setup() {
* mySound.setVolume(0.1);
- * }
- *
- * // play sound on mouse press over canvas
- * function mousePressed() {
- * if (mouseX < width && mouseY < height && mouseX > 0 && mouseY > 0) {
- * mySound.play();
- * }
+ * mySound.play();
* }
*
*
*/
- p5.SoundFile = function (paths, onload, whileLoading) {
+ p5.SoundFile = function (paths, onload, onerror, whileLoading) {
if (typeof paths !== 'undefined') {
if (typeof paths == 'string' || typeof paths[0] == 'string') {
var path = p5.prototype._checkFileFormats(paths);
@@ -759,6 +799,9 @@ soundfile = function () {
}
this.file = paths;
}
+ // private _onended callback, set by the method: onended(callback)
+ this._onended = function () {
+ };
this._looping = false;
this._playing = false;
this._paused = false;
@@ -787,21 +830,19 @@ soundfile = function () {
this.mode = 'sustain';
// time that playback was started, in millis
this.startMillis = null;
- this.amplitude = new p5.Amplitude();
- this.output.connect(this.amplitude.input);
// stereo panning
this.panPosition = 0;
this.panner = new p5.Panner(this.output, p5sound.input, 2);
// it is possible to instantiate a soundfile with no path
if (this.url || this.file) {
- this.load(onload);
+ this.load(onload, onerror);
}
// add this p5.SoundFile to the soundArray
p5sound.soundArray.push(this);
if (typeof whileLoading === 'function') {
- this.whileLoading = whileLoading;
+ this._whileLoading = whileLoading;
} else {
- this.whileLoading = function () {
+ this._whileLoading = function () {
};
}
};
@@ -822,10 +863,12 @@ soundfile = function () {
* i.e. ['sound.ogg', 'sound.mp3'].
* Alternately, accepts an object: either
* from the HTML5 File API, or a p5.File.
- * @param {Function} [callback] Name of a function to call once file loads
- * @param {Function} [callback] Name of a function to call while file is loading.
- * This function will receive a percentage from 0.0
- * to 1.0.
+ * @param {Function} [successCallback] Name of a function to call once file loads
+ * @param {Function} [errorCallback] Name of a function to call if there is
+ * an error loading the file.
+ * @param {Function} [whileLoading] Name of a function to call while file is loading.
+ * This function will receive the percentage loaded
+ * so far, from 0.0 to 1.0.
* @return {SoundFile} Returns a p5.SoundFile
* @example
*
@@ -839,12 +882,12 @@ soundfile = function () {
* }
*
*/
- p5.prototype.loadSound = function (path, callback, whileLoading) {
+ p5.prototype.loadSound = function (path, callback, onerror, whileLoading) {
// if loading locally without a server
if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
}
- var s = new p5.SoundFile(path, callback, whileLoading);
+ var s = new p5.SoundFile(path, callback, onerror, whileLoading);
return s;
};
/**
@@ -853,27 +896,62 @@ soundfile = function () {
* as an optional parameter.
*
* @private
- * @param {Function} [callback] Name of a function to call once file loads
+ * @param {Function} [successCallback] Name of a function to call once file loads
+ * @param {Function} [errorCallback] Name of a function to call if there is an error
*/
- p5.SoundFile.prototype.load = function (callback) {
+ p5.SoundFile.prototype.load = function (callback, errorCallback) {
+ var loggedError = false;
+ var self = this;
+ var errorTrace = new Error().stack;
if (this.url != undefined && this.url != '') {
- var sf = this;
var request = new XMLHttpRequest();
request.addEventListener('progress', function (evt) {
- sf._updateProgress(evt);
+ self._updateProgress(evt);
}, false);
request.open('GET', this.url, true);
request.responseType = 'arraybuffer';
- // decode asyncrohonously
- var self = this;
request.onload = function () {
- ac.decodeAudioData(request.response, function (buff) {
- self.buffer = buff;
- self.panner.inputChannels(buff.numberOfChannels);
- if (callback) {
- callback(self);
+ if (request.status == 200) {
+ // on sucess loading file:
+ ac.decodeAudioData(request.response, // success decoding buffer:
+ function (buff) {
+ self.buffer = buff;
+ self.panner.inputChannels(buff.numberOfChannels);
+ if (callback) {
+ callback(self);
+ }
+ }, // error decoding buffer. "e" is undefined in Chrome 11/22/2015
+ function (e) {
+ var err = new CustomError('decodeAudioData', errorTrace, self.url);
+ var msg = 'AudioContext error at decodeAudioData for ' + self.url;
+ if (errorCallback) {
+ err.msg = msg;
+ errorCallback(err);
+ } else {
+ console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+ }
+ });
+ } else {
+ var err = new CustomError('loadSound', errorTrace, self.url);
+ var msg = 'Unable to load ' + self.url + '. The request status was: ' + request.status + ' (' + request.statusText + ')';
+ if (errorCallback) {
+ err.message = msg;
+ errorCallback(err);
+ } else {
+ console.error(msg + '\n The error stack trace includes: \n' + err.stack);
}
- });
+ }
+ };
+ // if there is another error, aside from 404...
+ request.onerror = function (e) {
+ var err = new CustomError('loadSound', errorTrace, self.url);
+ var msg = 'There was no response from the server at ' + self.url + '. Check the url and internet connectivity.';
+ if (errorCallback) {
+ err.message = msg;
+ errorCallback(err);
+ } else {
+ console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+ }
};
request.send();
} else if (this.file != undefined) {
@@ -888,6 +966,10 @@ soundfile = function () {
}
});
};
+ reader.onerror = function (e) {
+ if (onerror)
+ onerror(e);
+ };
reader.readAsArrayBuffer(this.file);
}
};
@@ -895,28 +977,17 @@ soundfile = function () {
p5.SoundFile.prototype._updateProgress = function (evt) {
if (evt.lengthComputable) {
var percentComplete = Math.log(evt.loaded / evt.total * 9.9);
- this.whileLoading(percentComplete);
+ this._whileLoading(percentComplete);
} else {
- console.log('size unknown');
+ // Unable to compute progress information since the total size is unknown
+ this._whileLoading('size unknown');
}
};
/**
- * Returns true when the sound file finishes loading successfully.
+ * Returns true if the sound file finished loading successfully.
*
* @method isLoaded
* @return {Boolean}
- * @example
- *
- * function setup() {
- * mySound = loadSound('assets/doorbell.mp3');
- * }
- *
- * function draw() {
- * background(255);
- * frameRate(1);
- * text('loaded: ' + mySound.isLoaded(), 5, height/2);
- * }
- *
*/
p5.SoundFile.prototype.isLoaded = function () {
if (this.buffer) {
@@ -935,17 +1006,6 @@ soundfile = function () {
* of playback
* @param {Number} [cueStart] (optional) cue start time in seconds
* @param {Number} [duration] (optional) duration of playback in seconds
- * @example
- *
- * function preload() {
- * mySound = loadSound('assets/doorbell.mp3');
- * }
- *
- * function setup() {
- * mySound.setVolume(0.1);
- * mySound.play();
- * }
- *
*/
p5.SoundFile.prototype.play = function (time, rate, amp, _cueStart, duration) {
var self = this;
@@ -968,6 +1028,9 @@ soundfile = function () {
}
// make a new source and counter. They are automatically assigned playbackRate and buffer
this.bufferSourceNode = this._initSourceNode();
+ // garbage collect counterNode and create a new one
+ if (this._counterNode)
+ this._counterNode = undefined;
this._counterNode = this._initCounterNode();
if (_cueStart) {
if (_cueStart >= 0 && _cueStart < this.buffer.duration) {
@@ -1016,18 +1079,21 @@ soundfile = function () {
this.bufferSourceNodes.push(this.bufferSourceNode);
this.bufferSourceNode._arrayIndex = this.bufferSourceNodes.length - 1;
// delete this.bufferSourceNode from the sources array when it is done playing:
- this.bufferSourceNode.onended = function (e) {
- var theNode = this;
- // if (self.bufferSourceNodes.length === 1) {
+ var clearOnEnd = function (e) {
this._playing = false;
- // }
- setTimeout(function () {
- self.bufferSourceNodes.splice(theNode._arrayIndex, 1);
- if (self.bufferSourceNodes.length === 0) {
- self._playing = false;
+ this.removeEventListener('ended', clearOnEnd, false);
+ // call the onended callback
+ self._onended(self);
+ self.bufferSourceNodes.forEach(function (n, i) {
+ if (n._playing === false) {
+ self.bufferSourceNodes.splice(i);
}
- }, 1);
+ });
+ if (self.bufferSourceNodes.length === 0) {
+ self._playing = false;
+ }
};
+ this.bufferSourceNode.onended = clearOnEnd;
} else {
throw 'not ready to play file, buffer has yet to load. Try preload()';
}
@@ -1140,49 +1206,16 @@ soundfile = function () {
}
};
/**
- * Loop the p5.SoundFile - play it over and over again. You can .stop()
- * at any time, or turn off looping with .setLoop(false).
- * The loop method accepts optional parameters to schedule the looping in the future,
- * and to set the playback rate, volume, loopStart, loopEnd.
+ * Loop the p5.SoundFile. Accepts optional parameters to set the
+ * playback rate, playback volume, loopStart, loopEnd.
*
* @method loop
* @param {Number} [startTime] (optional) schedule event to occur
- * seconds from now. Defaults to 0.
- * @param {Number} [rate] (optional) playback rate. Defaults to 1.
- * @param {Number} [amp] (optional) playback volume (max amplitude). Defaults to 1.
+ * seconds from now
+ * @param {Number} [rate] (optional) playback rate
+ * @param {Number} [amp] (optional) playback volume
* @param {Number} [cueLoopStart](optional) startTime in seconds
* @param {Number} [duration] (optional) loop duration in seconds
- * @example
- *
- * function preload() {
- * mySound = loadSound('assets/beat.mp3');
- * }
- *
- * function setup() {
- * var cnv = createCanvas(100, 100);
- *
- * // schedule mouse events on mouse over/out
- * cnv.mouseOver(startLooping);
- * cnv.mouseOut(stopLooping);
- *
- * textAlign(CENTER);
- * text('hover to loop', width/2, height/2);
- * }
- *
- * function startLooping() {
- * background(255, 0, 0);
- * text('looping', width/2, height/2);
- *
- * // start loop now, at playback rate of 3x, volume of 0.1
- * mySound.loop(0, 3, 0.1);
- * }
- *
- * function stopLooping() {
- * background(0, 255, 0);
- * text('stopped', width/2, height/2);
- * mySound.stop();
- * }
- *
*/
p5.SoundFile.prototype.loop = function (startTime, rate, amp, loopStart, duration) {
this._looping = true;
@@ -1277,21 +1310,24 @@ soundfile = function () {
for (var i = 0; i < this.bufferSourceNodes.length; i++) {
if (typeof this.bufferSourceNodes[i] != undefined) {
try {
+ this.bufferSourceNodes[i].onended = function () {
+ };
this.bufferSourceNodes[i].stop(now + time);
} catch (e) {
}
}
}
this._counterNode.stop(now + time);
+ this._onended(this);
}
};
/**
- * Multiply the output volume (amplitude) of a sound file
+ * Multiply the output volume (amplitude) of a sound file
* between 0.0 (silence) and 1.0 (full volume).
* 1.0 is the maximum amplitude of a digital sound, so multiplying
- * by greater than 1.0 may cause digital distortion.
- * To fade, provide a rampTime parameter. For more
- * complex fades, see the Env class.
+ * by greater than 1.0 may cause digital distortion. To
+ * fade, provide a rampTime parameter. For more
+ * complex fades, see the Env class.
*
* Alternately, you can pass in a signal source such as an
* oscillator to modulate the amplitude with an audio signal.
@@ -1302,43 +1338,6 @@ soundfile = function () {
* @param {Number} [rampTime] Fade for t seconds
* @param {Number} [timeFromNow] Schedule this event to happen at
* t seconds in the future
- * @example
- *
- * function preload() {
- * mySound = loadSound('assets/drum.mp3');
- * }
- *
- * function setup() {
- * var cnv = createCanvas(100, 100);
- * background(205, 255, 0);
- * cnv.mouseOver(fadeIn);
- * cnv.mouseOut(fadeOut);
- *
- * // mute to start
- * mySound.setVolume(0);
- * mySound.rate(5)
- * mySound.loop();
- *
- * textAlign(CENTER);
- * text('hover to fade in', width/2, height/2);
- * }
- *
- * function fadeIn() {
- * background(200, 0, 255);
- * text('hover to fade out', width/2, height/2);
- *
- * // fade in to full volume over 0.5 seconds
- * mySound.setVolume(1, 0.5);
- * }
- *
- * function fadeOut() {
- * background(205, 255, 0);
- * text('hover to fade in', width/2, height/2);
- *
- * // fade to 0 over 2 seconds
- * mySound.setVolume(0, 2);
- * }
- *
*/
p5.SoundFile.prototype.setVolume = function (vol, rampTime, tFromNow) {
if (typeof vol === 'number') {
@@ -1660,17 +1659,28 @@ soundfile = function () {
this.setVolume(curVol, 0.01, 0.0101);
this.play();
};
- // private function for onended behavior
- p5.SoundFile.prototype._onEnded = function (s) {
- s.onended = function (s) {
- var now = p5sound.audiocontext.currentTime;
- s.stop(now);
- };
+ /**
+ * Schedule an event to be called when the soundfile
+ * reaches the end of a buffer. If the soundfile is
+ * playing through once, this will be called when it
+ * ends. If it is looping, it will be called when
+ * stop is called.
+ *
+ * @method onended
+ * @param {Function} callback function to call when the
+ * soundfile has ended.
+ */
+ p5.SoundFile.prototype.onended = function (callback) {
+ this._onended = callback;
+ return this;
};
p5.SoundFile.prototype.add = function () {
};
p5.SoundFile.prototype.dispose = function () {
var now = p5sound.audiocontext.currentTime;
+ // remove reference to soundfile
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
this.stop(now);
if (this.buffer && this.bufferSourceNode) {
for (var i = 0; i < this.bufferSourceNodes.length - 1; i++) {
@@ -1731,21 +1741,9 @@ soundfile = function () {
this.panner.disconnect(unit);
};
/**
- * Read the Amplitude (volume level) of a p5.SoundFile. The
- * p5.SoundFile class contains its own instance of the Amplitude
- * class to help make it easy to get a SoundFile's volume level.
- * Accepts an optional smoothing value (0.0 < 1.0).
- *
- * @method getLevel
- * @param {Number} [smoothing] Smoothing is 0.0 by default.
- * Smooths values based on previous values.
- * @return {Number} Volume level (between 0.0 and 1.0)
*/
p5.SoundFile.prototype.getLevel = function (smoothing) {
- if (smoothing) {
- this.amplitude.smoothing = smoothing;
- }
- return this.amplitude.getLevel();
+ console.warn('p5.SoundFile.getLevel has been removed from the library. Use p5.Amplitude instead');
};
/**
* Reset the source for this SoundFile to a
@@ -1797,6 +1795,7 @@ soundfile = function () {
// dispose of scope node if it already exists
if (self._scopeNode) {
self._scopeNode.disconnect();
+ self._scopeNode.onaudioprocess = undefined;
self._scopeNode = null;
}
self._scopeNode = ac.createScriptProcessor(256, 1, 1);
@@ -2109,7 +2108,8 @@ soundfile = function () {
* @param {Number} id ID of the cue, as returned by addCue
*/
p5.SoundFile.prototype.removeCue = function (id) {
- for (var i = 0; i < this._cues.length; i++) {
+ var cueLength = this._cues.length;
+ for (var i = 0; i < cueLength; i++) {
var cue = this._cues[i];
if (cue.id === id) {
this.cues.splice(i, 1);
@@ -2131,12 +2131,14 @@ soundfile = function () {
// have been scheduled using addCue(callback, time).
p5.SoundFile.prototype._onTimeUpdate = function (position) {
var playbackTime = position / this.buffer.sampleRate;
- for (var i = 0; i < this._cues.length; i++) {
- var callbackTime = this._cues[i].time;
- var val = this._cues[i].val;
+ var cueLength = this._cues.length;
+ for (var i = 0; i < cueLength; i++) {
+ var cue = this._cues[i];
+ var callbackTime = cue.time;
+ var val = cue.val;
if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
// pass the scheduled callbackTime as parameter to the callback
- this._cues[i].callback(val);
+ cue.callback(val);
}
}
this._prevTime = playbackTime;
@@ -2149,7 +2151,7 @@ soundfile = function () {
this.id = id;
this.val = val;
};
-}(sndcore, master);
+}(sndcore, errorHandler, master);
var amplitude;
amplitude = function () {
'use strict';
@@ -2230,6 +2232,8 @@ amplitude = function () {
this.output.connect(this.audiocontext.destination);
// connect to p5sound master output by default, unless set by input()
p5sound.meter.connect(this.processor);
+ // add this p5.SoundFile to the soundArray
+ p5sound.soundArray.push(this);
};
/**
* Connects to the p5sound instance (master output) by default.
@@ -2411,6 +2415,15 @@ amplitude = function () {
console.log('Error: smoothing must be between 0 and 1');
}
};
+ p5.Amplitude.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
+ this.input.disconnect();
+ this.output.disconnect();
+ this.input = this.processor = undefined;
+ this.output = undefined;
+ };
}(master);
var fft;
fft = function () {
@@ -2458,17 +2471,17 @@ fft = function () {
* function preload(){
* sound = loadSound('assets/Damscray_DancingTiger.mp3');
* }
- *
+ *
* function setup(){
- * cnv = createCanvas(100,100);
- * sound.amp(0);
- * sound.loop();
+ * var cnv = createCanvas(100,100);
+ * cnv.mouseClicked(togglePlay);
* fft = new p5.FFT();
+ * sound.amp(0.2);
* }
- *
+ *
* function draw(){
* background(0);
- *
+ *
* var spectrum = fft.analyze();
* noStroke();
* fill(0,255,0); // spectrum is green
@@ -2477,7 +2490,7 @@ fft = function () {
* var h = -height + map(spectrum[i], 0, 255, height, 0);
* rect(x, height, width / spectrum.length, h )
* }
- *
+ *
* var waveform = fft.waveform();
* noFill();
* beginShape();
@@ -2489,17 +2502,16 @@ fft = function () {
* vertex(x,y);
* }
* endShape();
- *
- * isMouseOverCanvas();
+ *
+ * text('click to play/pause', 4, 10);
* }
- *
+ *
* // fade sound if mouse is over canvas
- * function isMouseOverCanvas() {
- * var mX = mouseX, mY = mouseY;
- * if (mX > 0 && mX < width && mY < height && mY > 0) {
- * sound.amp(0.5, 0.2);
+ * function togglePlay() {
+ * if (sound.isPlaying()) {
+ * sound.pause();
* } else {
- * sound.amp(0, 0.2);
+ * sound.loop();
* }
* }
*
@@ -2536,6 +2548,8 @@ fft = function () {
5200,
14000
];
+ // add this p5.SoundFile to the soundArray
+ p5sound.soundArray.push(this);
};
/**
* Set the input source for the FFT analysis. If no source is
@@ -2771,6 +2785,87 @@ fft = function () {
var x = this.getEnergy(freq1, freq2);
return x;
};
+ /**
+ * Returns the
+ *
+ * spectral centroid of the input signal.
+ * NOTE: analyze() must be called prior to getCentroid(). Analyze()
+ * tells the FFT to analyze frequency data, and getCentroid() uses
+ * the results determine the spectral centroid.
+ *
+ * @method getCentroid
+ * @return {Number} Spectral Centroid Frequency Frequency of the spectral centroid in Hz.
+ *
+ *
+ * @example
+ *
+ *
+ *
+ *function setup(){
+ * cnv = createCanvas(800,400);
+ * sound = new p5.AudioIn();
+ * sound.start();
+ * fft = new p5.FFT();
+ * sound.connect(fft);
+ *}
+ *
+ *
+ *function draw(){
+ *
+ * var centroidplot = 0.0;
+ * var spectralCentroid = 0;
+ *
+ *
+ * background(0);
+ * stroke(0,255,0);
+ * var spectrum = fft.analyze();
+ * fill(0,255,0); // spectrum is green
+ *
+ * //draw the spectrum
+ *
+ * for (var i = 0; i< spectrum.length; i++){
+ * var x = map(log(i), 0, log(spectrum.length), 0, width);
+ * var h = map(spectrum[i], 0, 255, 0, height);
+ * var rectangle_width = (log(i+1)-log(i))*(width/log(spectrum.length));
+ * rect(x, height, rectangle_width, -h )
+ * }
+
+ * var nyquist = 22050;
+ *
+ * // get the centroid
+ * spectralCentroid = fft.getCentroid();
+ *
+ * // the mean_freq_index calculation is for the display.
+ * var mean_freq_index = spectralCentroid/(nyquist/spectrum.length);
+ *
+ * centroidplot = map(log(mean_freq_index), 0, log(spectrum.length), 0, width);
+ *
+ *
+ * stroke(255,0,0); // the line showing where the centroid is will be red
+ *
+ * rect(centroidplot, 0, width / spectrum.length, height)
+ * noStroke();
+ * fill(255,255,255); // text is white
+ * textSize(40);
+ * text("centroid: "+round(spectralCentroid)+" Hz", 10, 40);
+ *}
+ *
+ */
+ p5.FFT.prototype.getCentroid = function () {
+ var nyquist = p5sound.audiocontext.sampleRate / 2;
+ var cumulative_sum = 0;
+ var centroid_normalization = 0;
+ for (var i = 0; i < this.freqDomain.length; i++) {
+ cumulative_sum += i * this.freqDomain[i];
+ centroid_normalization += this.freqDomain[i];
+ }
+ var mean_freq_index = 0;
+ if (centroid_normalization != 0) {
+ mean_freq_index = cumulative_sum / centroid_normalization;
+ }
+ var spec_centroid_freq = mean_freq_index * (nyquist / this.freqDomain.length);
+ return spec_centroid_freq;
+ };
/**
* Smooth FFT analysis by averaging with the last analysis frame.
*
@@ -2784,6 +2879,13 @@ fft = function () {
}
this.analyser.smoothingTimeConstant = s;
};
+ p5.FFT.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
+ this.analyser.disconnect();
+ this.analyser = undefined;
+ };
// helper methods to convert type from float (dB) to int (0-255)
var freqToFloat = function (fft) {
if (fft.freqDomain instanceof Float32Array === false) {
@@ -2806,13 +2908,16 @@ fft = function () {
}
};
}(master);
-/** Tone.js module by Yotam Mann, MIT License 2014 http://opensource.org/licenses/MIT **/
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
var Tone_core_Tone;
Tone_core_Tone = function () {
'use strict';
function isUndef(val) {
return val === void 0;
}
+ function isFunction(val) {
+ return typeof val === 'function';
+ }
var audioContext;
if (isUndef(window.AudioContext)) {
window.AudioContext = window.webkitAudioContext;
@@ -2825,55 +2930,53 @@ Tone_core_Tone = function () {
} else {
throw new Error('Web Audio is not supported in this browser');
}
- if (typeof AudioContext.prototype.createGain !== 'function') {
+ if (!isFunction(AudioContext.prototype.createGain)) {
AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
}
- if (typeof AudioContext.prototype.createDelay !== 'function') {
+ if (!isFunction(AudioContext.prototype.createDelay)) {
AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
}
- if (typeof AudioContext.prototype.createPeriodicWave !== 'function') {
+ if (!isFunction(AudioContext.prototype.createPeriodicWave)) {
AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
}
- if (typeof AudioBufferSourceNode.prototype.start !== 'function') {
+ if (!isFunction(AudioBufferSourceNode.prototype.start)) {
AudioBufferSourceNode.prototype.start = AudioBufferSourceNode.prototype.noteGrainOn;
}
- if (typeof AudioBufferSourceNode.prototype.stop !== 'function') {
+ if (!isFunction(AudioBufferSourceNode.prototype.stop)) {
AudioBufferSourceNode.prototype.stop = AudioBufferSourceNode.prototype.noteOff;
}
- if (typeof OscillatorNode.prototype.start !== 'function') {
+ if (!isFunction(OscillatorNode.prototype.start)) {
OscillatorNode.prototype.start = OscillatorNode.prototype.noteOn;
}
- if (typeof OscillatorNode.prototype.stop !== 'function') {
+ if (!isFunction(OscillatorNode.prototype.stop)) {
OscillatorNode.prototype.stop = OscillatorNode.prototype.noteOff;
}
- if (typeof OscillatorNode.prototype.setPeriodicWave !== 'function') {
+ if (!isFunction(OscillatorNode.prototype.setPeriodicWave)) {
OscillatorNode.prototype.setPeriodicWave = OscillatorNode.prototype.setWaveTable;
}
- if (!window.Tone) {
- 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);
+ 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 {
- 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);
+ 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);
}
- };
- }
+ }
+ };
var Tone = function (inputs, outputs) {
if (isUndef(inputs) || inputs === 1) {
this.input = this.context.createGain();
@@ -2886,10 +2989,139 @@ Tone_core_Tone = function () {
this.output = new Array(inputs);
}
};
+ 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;
+ };
+ 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;
+ };
+ 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);
+ for (var i = 0; i < superDefs.length; i++) {
+ if (ret.indexOf(superDefs[i]) === -1) {
+ ret.push(superDefs[i]);
+ }
+ }
+ }
+ return ret;
+ };
+ 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';
+ };
Tone.context = audioContext;
Tone.prototype.context = Tone.context;
Tone.prototype.bufferSize = 2048;
- Tone.prototype.bufferTime = Tone.prototype.bufferSize / Tone.context.sampleRate;
+ Tone.prototype.blockTime = 128 / Tone.context.sampleRate;
+ 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;
+ };
+ var _silentNode = null;
+ Tone.prototype.noGC = function () {
+ this.output.connect(_silentNode);
+ return this;
+ };
+ AudioNode.prototype.noGC = function () {
+ this.connect(_silentNode);
+ return this;
+ };
Tone.prototype.connect = function (unit, outputNum, inputNum) {
if (Array.isArray(this.output)) {
outputNum = this.defaultArg(outputNum, 0);
@@ -2897,6 +3129,7 @@ Tone_core_Tone = function () {
} else {
this.output.connect(unit, outputNum, inputNum);
}
+ return this;
};
Tone.prototype.disconnect = function (outputNum) {
if (Array.isArray(this.output)) {
@@ -2905,6 +3138,7 @@ Tone_core_Tone = function () {
} else {
this.output.disconnect();
}
+ return this;
};
Tone.prototype.connectSeries = function () {
if (arguments.length > 1) {
@@ -2915,6 +3149,7 @@ Tone_core_Tone = function () {
currentUnit = toUnit;
}
}
+ return this;
};
Tone.prototype.connectParallel = function () {
var connectFrom = arguments[0];
@@ -2924,6 +3159,7 @@ Tone_core_Tone = function () {
connectFrom.connect(connectTo);
}
}
+ return this;
};
Tone.prototype.chain = function () {
if (arguments.length > 0) {
@@ -2934,24 +3170,26 @@ Tone_core_Tone = function () {
currentUnit = toUnit;
}
}
+ return this;
};
Tone.prototype.fan = function () {
if (arguments.length > 0) {
- for (var i = 1; i < arguments.length; i++) {
+ for (var i = 0; i < arguments.length; i++) {
this.connect(arguments[i]);
}
}
+ return this;
};
AudioNode.prototype.chain = Tone.prototype.chain;
AudioNode.prototype.fan = Tone.prototype.fan;
Tone.prototype.defaultArg = function (given, fallback) {
- if (typeof given === 'object' && typeof fallback === 'object') {
+ if (this.isObject(given) && this.isObject(fallback)) {
var ret = {};
for (var givenProp in given) {
- ret[givenProp] = this.defaultArg(given[givenProp], given[givenProp]);
+ ret[givenProp] = this.defaultArg(fallback[givenProp], given[givenProp]);
}
- for (var prop in fallback) {
- ret[prop] = this.defaultArg(given[prop], fallback[prop]);
+ for (var fallbackProp in fallback) {
+ ret[fallbackProp] = this.defaultArg(given[fallbackProp], fallback[fallbackProp]);
}
return ret;
} else {
@@ -2960,7 +3198,7 @@ Tone_core_Tone = function () {
};
Tone.prototype.optionsObject = function (values, keys, defaults) {
var options = {};
- if (values.length === 1 && typeof values[0] === 'object') {
+ if (values.length === 1 && this.isObject(values[0])) {
options = values[0];
} else {
for (var i = 0; i < keys.length; i++) {
@@ -2974,86 +3212,73 @@ Tone_core_Tone = function () {
}
};
Tone.prototype.isUndef = isUndef;
- Tone.prototype.equalPowerScale = function (percent) {
- var piFactor = 0.5 * Math.PI;
- return Math.sin(percent * piFactor);
+ Tone.prototype.isFunction = isFunction;
+ Tone.prototype.isNumber = function (arg) {
+ return typeof arg === 'number';
};
- Tone.prototype.logScale = function (gain) {
- return Math.max(this.normalize(this.gainToDb(gain), -100, 0), 0);
+ Tone.prototype.isObject = function (arg) {
+ return Object.prototype.toString.call(arg) === '[object Object]' && arg.constructor === Object;
};
- Tone.prototype.expScale = function (gain) {
- return this.dbToGain(this.interpolate(gain, -100, 0));
+ Tone.prototype.isBoolean = function (arg) {
+ return typeof arg === 'boolean';
};
- Tone.prototype.dbToGain = function (db) {
- return Math.pow(2, db / 6);
- };
- Tone.prototype.gainToDb = function (gain) {
- return 20 * (Math.log(gain) / Math.LN10);
+ Tone.prototype.isArray = function (arg) {
+ return Array.isArray(arg);
};
- Tone.prototype.interpolate = function (input, outputMin, outputMax) {
- return input * (outputMax - outputMin) + outputMin;
+ Tone.prototype.isString = function (arg) {
+ return typeof arg === 'string';
};
- Tone.prototype.normalize = function (input, inputMin, inputMax) {
- if (inputMin > inputMax) {
- var tmp = inputMax;
- inputMax = inputMin;
- inputMin = tmp;
- } else if (inputMin == inputMax) {
- return 0;
- }
- return (input - inputMin) / (inputMax - inputMin);
+ Tone.noOp = function () {
};
- Tone.prototype.dispose = function () {
- if (!this.isUndef(this.input)) {
- if (this.input instanceof AudioNode) {
- this.input.disconnect();
+ Tone.prototype._readOnly = function (property) {
+ if (Array.isArray(property)) {
+ for (var i = 0; i < property.length; i++) {
+ this._readOnly(property[i]);
}
- this.input = null;
+ } else {
+ Object.defineProperty(this, property, {
+ writable: false,
+ enumerable: true
+ });
}
- if (!this.isUndef(this.output)) {
- if (this.output instanceof AudioNode) {
- this.output.disconnect();
+ };
+ Tone.prototype._writable = function (property) {
+ if (Array.isArray(property)) {
+ for (var i = 0; i < property.length; i++) {
+ this._writable(property[i]);
}
- this.output = null;
+ } else {
+ Object.defineProperty(this, property, { writable: true });
}
};
- var _silentNode = null;
- Tone.prototype.noGC = function () {
- this.output.connect(_silentNode);
+ Tone.State = {
+ Started: 'started',
+ Stopped: 'stopped',
+ Paused: 'paused'
};
- AudioNode.prototype.noGC = function () {
- this.connect(_silentNode);
+ Tone.prototype.equalPowerScale = function (percent) {
+ var piFactor = 0.5 * Math.PI;
+ return Math.sin(percent * piFactor);
};
- Tone.prototype.now = function () {
- return this.context.currentTime;
+ Tone.prototype.dbToGain = function (db) {
+ return Math.pow(2, db / 6);
};
- Tone.prototype.samplesToSeconds = function (samples) {
- return samples / this.context.sampleRate;
+ Tone.prototype.gainToDb = function (gain) {
+ return 20 * (Math.log(gain) / Math.LN10);
};
- Tone.prototype.toSamples = function (time) {
- var seconds = this.toSeconds(time);
- return Math.round(seconds * this.context.sampleRate);
+ Tone.prototype.now = function () {
+ return this.context.currentTime;
};
- Tone.prototype.toSeconds = function (time, now) {
- now = this.defaultArg(now, this.now());
- if (typeof time === 'number') {
- return time;
- } else if (typeof time === 'string') {
- var plusTime = 0;
- if (time.charAt(0) === '+') {
- time = time.slice(1);
- plusTime = now;
- }
- return parseFloat(time) + plusTime;
- } else {
- return now;
+ Tone.extend = function (child, parent) {
+ if (isUndef(parent)) {
+ parent = Tone;
}
- };
- Tone.prototype.frequencyToSeconds = function (freq) {
- return 1 / parseFloat(freq);
- };
- Tone.prototype.secondsToFrequency = function (seconds) {
- return 1 / seconds;
+ function TempConstructor() {
+ }
+ TempConstructor.prototype = parent.prototype;
+ child.prototype = new TempConstructor();
+ child.prototype.constructor = child;
+ child._super = parent;
};
var newContextCallbacks = [];
Tone._initAudioContext = function (callback) {
@@ -3067,16 +3292,6 @@ Tone_core_Tone = function () {
newContextCallbacks[i](ctx);
}
};
- Tone.extend = function (child, parent) {
- if (isUndef(parent)) {
- parent = Tone;
- }
- function TempConstructor() {
- }
- TempConstructor.prototype = parent.prototype;
- child.prototype = new TempConstructor();
- child.prototype.constructor = child;
- };
Tone.startMobile = function () {
var osc = Tone.context.createOscillator();
var silent = Tone.context.createGain();
@@ -3088,14 +3303,15 @@ Tone_core_Tone = function () {
osc.stop(now + 1);
};
Tone._initAudioContext(function (audioContext) {
- Tone.prototype.bufferTime = Tone.prototype.bufferSize / audioContext.sampleRate;
+ Tone.prototype.blockTime = 128 / audioContext.sampleRate;
_silentNode = audioContext.createGain();
_silentNode.gain.value = 0;
_silentNode.connect(audioContext.destination);
});
+ Tone.version = 'r7-dev';
return Tone;
}();
-/** Tone.js module by Yotam Mann, MIT License 2014 http://opensource.org/licenses/MIT **/
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
var Tone_signal_SignalBase;
Tone_signal_SignalBase = function (Tone) {
'use strict';
@@ -3103,19 +3319,20 @@ Tone_signal_SignalBase = function (Tone) {
};
Tone.extend(Tone.SignalBase);
Tone.SignalBase.prototype.connect = function (node, outputNumber, inputNumber) {
- if (node instanceof Tone.Signal) {
- node.setValue(0);
+ if (Tone.Signal && Tone.Signal === node.constructor || Tone.Param && Tone.Param === node.constructor || Tone.TimelineSignal && Tone.TimelineSignal === node.constructor) {
+ node._param.cancelScheduledValues(0);
+ node._param.value = 0;
+ node.overridden = true;
} else if (node instanceof AudioParam) {
+ node.cancelScheduledValues(0);
node.value = 0;
}
Tone.prototype.connect.call(this, node, outputNumber, inputNumber);
- };
- Tone.SignalBase.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
+ return this;
};
return Tone.SignalBase;
}(Tone_core_Tone);
-/** Tone.js module by Yotam Mann, MIT License 2014 http://opensource.org/licenses/MIT **/
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
var Tone_signal_WaveShaper;
Tone_signal_WaveShaper = function (Tone) {
'use strict';
@@ -3123,10 +3340,10 @@ Tone_signal_WaveShaper = function (Tone) {
this._shaper = this.input = this.output = this.context.createWaveShaper();
this._curve = null;
if (Array.isArray(mapping)) {
- this.setCurve(mapping);
+ this.curve = mapping;
} else if (isFinite(mapping) || this.isUndef(mapping)) {
this._curve = new Float32Array(this.defaultArg(mapping, 1024));
- } else if (typeof mapping === 'function') {
+ } else if (this.isFunction(mapping)) {
this._curve = new Float32Array(this.defaultArg(bufferLen, 1024));
this.setMap(mapping);
}
@@ -3135,213 +3352,756 @@ Tone_signal_WaveShaper = function (Tone) {
Tone.WaveShaper.prototype.setMap = function (mapping) {
for (var i = 0, len = this._curve.length; i < len; i++) {
var normalized = i / len * 2 - 1;
- var normOffOne = i / (len - 1) * 2 - 1;
- this._curve[i] = mapping(normalized, i, normOffOne);
+ this._curve[i] = mapping(normalized, i);
}
this._shaper.curve = this._curve;
+ return this;
};
- Tone.WaveShaper.prototype.setCurve = function (mapping) {
- if (this._isSafari()) {
- var first = mapping[0];
- mapping.unshift(first);
+ 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;
}
- this._curve = new Float32Array(mapping);
- this._shaper.curve = this._curve;
- };
- Tone.WaveShaper.prototype.setOversample = function (oversampling) {
- this._shaper.oversample = oversampling;
- };
- Tone.WaveShaper.prototype._isSafari = function () {
- var ua = navigator.userAgent.toLowerCase();
- return ua.indexOf('safari') !== -1 && ua.indexOf('chrome') === -1;
- };
+ });
+ 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);
+ }
+ }
+ });
Tone.WaveShaper.prototype.dispose = function () {
Tone.prototype.dispose.call(this);
this._shaper.disconnect();
this._shaper = null;
this._curve = null;
+ return this;
};
return Tone.WaveShaper;
}(Tone_core_Tone);
-/** Tone.js module by Yotam Mann, MIT License 2014 http://opensource.org/licenses/MIT **/
-var Tone_signal_Signal;
-Tone_signal_Signal = function (Tone) {
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
+var Tone_core_Type;
+Tone_core_Type = function (Tone) {
'use strict';
- Tone.Signal = function (value) {
- this._scalar = this.context.createGain();
- this.input = this.output = this.context.createGain();
- this._syncRatio = 1;
- this.value = this.defaultArg(value, 0);
- Tone.Signal._constant.chain(this._scalar, this.output);
- };
- Tone.extend(Tone.Signal, Tone.SignalBase);
- Tone.Signal.prototype.getValue = function () {
- return this._scalar.gain.value;
- };
- Tone.Signal.prototype.setValue = function (value) {
- if (this._syncRatio === 0) {
- value = 0;
+ Tone.Type = {
+ Default: 'number',
+ Time: 'time',
+ Frequency: 'frequency',
+ NormalRange: 'normalRange',
+ AudioRange: 'audioRange',
+ Decibels: 'db',
+ Interval: 'interval',
+ BPM: 'bpm',
+ Positive: 'positive',
+ Cents: 'cents',
+ Degrees: 'degrees',
+ MIDI: 'midi',
+ TransportTime: 'transportTime',
+ Ticks: 'tick',
+ Note: 'note',
+ Milliseconds: 'milliseconds',
+ Notation: 'notation'
+ };
+ Tone.prototype.isNowRelative = function () {
+ var nowRelative = new RegExp(/^\s*\+(.)+/i);
+ return function (note) {
+ return nowRelative.test(note);
+ };
+ }();
+ Tone.prototype.isTicks = function () {
+ var tickFormat = new RegExp(/^\d+i$/i);
+ return function (note) {
+ return tickFormat.test(note);
+ };
+ }();
+ Tone.prototype.isNotation = function () {
+ var notationFormat = new RegExp(/^[0-9]+[mnt]$/i);
+ return function (note) {
+ return notationFormat.test(note);
+ };
+ }();
+ Tone.prototype.isTransportTime = function () {
+ var transportTimeFormat = new RegExp(/^(\d+(\.\d+)?\:){1,2}(\d+(\.\d+)?)?$/i);
+ return function (transportTime) {
+ return transportTimeFormat.test(transportTime);
+ };
+ }();
+ Tone.prototype.isNote = function () {
+ var noteFormat = new RegExp(/^[a-g]{1}(b|#|x|bb)?-?[0-9]+$/i);
+ return function (note) {
+ return noteFormat.test(note);
+ };
+ }();
+ Tone.prototype.isFrequency = function () {
+ var freqFormat = new RegExp(/^\d*\.?\d+hz$/i);
+ return function (freq) {
+ return freqFormat.test(freq);
+ };
+ }();
+ function getTransportBpm() {
+ if (Tone.Transport && Tone.Transport.bpm) {
+ return Tone.Transport.bpm.value;
+ } else {
+ return 120;
+ }
+ }
+ function getTransportTimeSignature() {
+ if (Tone.Transport && Tone.Transport.timeSignature) {
+ return Tone.Transport.timeSignature;
} else {
- value *= this._syncRatio;
+ return 4;
+ }
+ }
+ Tone.prototype.notationToSeconds = function (notation, bpm, timeSignature) {
+ bpm = this.defaultArg(bpm, getTransportBpm());
+ timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
+ var beatTime = 60 / bpm;
+ 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;
+ };
+ 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);
+ };
+ Tone.prototype.ticksToSeconds = function (ticks, bpm) {
+ if (this.isUndef(Tone.Transport)) {
+ return 0;
}
- this._scalar.gain.value = value;
+ ticks = parseFloat(ticks);
+ bpm = this.defaultArg(bpm, getTransportBpm());
+ var tickTime = 60 / bpm / Tone.Transport.PPQ;
+ return tickTime * ticks;
};
- Tone.Signal.prototype.setValueAtTime = function (value, time) {
- value *= this._syncRatio;
- this._scalar.gain.setValueAtTime(value, this.toSeconds(time));
+ Tone.prototype.frequencyToSeconds = function (freq) {
+ return 1 / parseFloat(freq);
};
- Tone.Signal.prototype.setCurrentValueNow = function (now) {
- now = this.defaultArg(now, this.now());
- var currentVal = this.getValue();
- this.cancelScheduledValues(now);
- this._scalar.gain.setValueAtTime(currentVal, now);
- return currentVal;
+ Tone.prototype.samplesToSeconds = function (samples) {
+ return samples / this.context.sampleRate;
};
- Tone.Signal.prototype.linearRampToValueAtTime = function (value, endTime) {
- value *= this._syncRatio;
- this._scalar.gain.linearRampToValueAtTime(value, this.toSeconds(endTime));
+ Tone.prototype.secondsToSamples = function (seconds) {
+ return seconds * this.context.sampleRate;
+ };
+ 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(':');
};
- Tone.Signal.prototype.exponentialRampToValueAtTime = function (value, endTime) {
- value *= this._syncRatio;
- try {
- this._scalar.gain.exponentialRampToValueAtTime(value, this.toSeconds(endTime));
- } catch (e) {
- this._scalar.gain.linearRampToValueAtTime(value, this.toSeconds(endTime));
- }
+ Tone.prototype.secondsToFrequency = function (seconds) {
+ return 1 / seconds;
};
- Tone.Signal.prototype.exponentialRampToValueNow = function (value, endTime) {
- var now = this.now();
- this.setCurrentValueNow(now);
- if (endTime.toString().charAt(0) === '+') {
- endTime = endTime.substr(1);
+ Tone.prototype.toTransportTime = function (time, bpm, timeSignature) {
+ var seconds = this.toSeconds(time);
+ return this.secondsToTransportTime(seconds, bpm, timeSignature);
+ };
+ 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;
}
- this.exponentialRampToValueAtTime(value, now + this.toSeconds(endTime));
};
- Tone.Signal.prototype.linearRampToValueNow = function (value, endTime) {
- var now = this.now();
- this.setCurrentValueNow(now);
- value *= this._syncRatio;
- if (endTime.toString().charAt(0) === '+') {
- endTime = endTime.substr(1);
+ Tone.prototype.toTicks = function (time) {
+ if (this.isUndef(Tone.Transport)) {
+ return 0;
+ }
+ var bpm = Tone.Transport.bpm.value;
+ var plusNow = 0;
+ if (this.isNowRelative(time)) {
+ time = time.replace('+', '');
+ plusNow = Tone.Transport.ticks;
+ } else if (this.isUndef(time)) {
+ return Tone.Transport.ticks;
}
- this._scalar.gain.linearRampToValueAtTime(value, now + this.toSeconds(endTime));
+ var seconds = this.toSeconds(time);
+ var quarter = 60 / bpm;
+ var quarters = seconds / quarter;
+ var tickNum = quarters * Tone.Transport.PPQ;
+ return Math.round(tickNum + plusNow);
};
- Tone.Signal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
- value *= this._syncRatio;
- this._scalar.gain.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
+ Tone.prototype.toSamples = function (time) {
+ var seconds = this.toSeconds(time);
+ return Math.round(seconds * this.context.sampleRate);
};
- Tone.Signal.prototype.setValueCurveAtTime = function (values, startTime, duration) {
- for (var i = 0; i < values.length; i++) {
- values[i] *= this._syncRatio;
+ Tone.prototype.toSeconds = function (time, now) {
+ now = this.defaultArg(now, this.now());
+ if (this.isNumber(time)) {
+ return time;
+ } else if (this.isString(time)) {
+ var plusTime = 0;
+ if (this.isNowRelative(time)) {
+ time = time.replace('+', '');
+ plusTime = now;
+ }
+ var betweenParens = time.match(/\(([^)(]+)\)/g);
+ if (betweenParens) {
+ for (var j = 0; j < betweenParens.length; j++) {
+ var symbol = betweenParens[j].replace(/[\(\)]/g, '');
+ var symbolVal = this.toSeconds(symbol);
+ time = time.replace(betweenParens[j], symbolVal);
+ }
+ }
+ if (time.indexOf('@') !== -1) {
+ var quantizationSplit = time.split('@');
+ if (!this.isUndef(Tone.Transport)) {
+ var toQuantize = quantizationSplit[0].trim();
+ if (toQuantize === '') {
+ toQuantize = undefined;
+ }
+ 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 {
+ time = eval(time);
+ } 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;
}
- this._scalar.gain.setValueCurveAtTime(values, this.toSeconds(startTime), this.toSeconds(duration));
};
- Tone.Signal.prototype.cancelScheduledValues = function (startTime) {
- this._scalar.gain.cancelScheduledValues(this.toSeconds(startTime));
+ 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);
+ 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);
+ if (retTripletNotation.split('+').length < retNotation.split('+').length) {
+ return retTripletNotation;
+ } else {
+ return retNotation;
+ }
};
- Tone.Signal.prototype.sync = function (signal, ratio) {
- if (ratio) {
- this._syncRatio = ratio;
+ 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);
+ 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;
+ }
+ 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 {
- if (signal.getValue() !== 0) {
- this._syncRatio = this.getValue() / signal.getValue();
- } else {
- this._syncRatio = 0;
+ return val;
+ }
+ };
+ 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;
+ }
+ };
+ 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
+ };
+ var scaleIndexToNote = [
+ 'C',
+ 'C#',
+ 'D',
+ 'D#',
+ 'E',
+ 'F',
+ 'F#',
+ 'G',
+ 'G#',
+ 'A',
+ 'A#',
+ 'B'
+ ];
+ Tone.A4 = 440;
+ Tone.prototype.noteToFrequency = function (note) {
+ 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;
}
- this._scalar.disconnect();
- this._scalar = this.context.createGain();
- this.connectSeries(signal, this._scalar, this.output);
- this._scalar.gain.value = this._syncRatio;
};
- Tone.Signal.prototype.unsync = function () {
- var currentGain = this.getValue();
- this._scalar.disconnect();
- this._scalar = this.context.createGain();
- this._scalar.gain.value = currentGain / this._syncRatio;
- this._syncRatio = 1;
- Tone.Signal._constant.chain(this._scalar, this.output);
+ 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();
};
- Tone.Signal.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
- this._scalar.disconnect();
- this._scalar = null;
+ Tone.prototype.intervalToFrequencyRatio = function (interval) {
+ return Math.pow(2, interval / 12);
};
- Object.defineProperty(Tone.Signal.prototype, 'value', {
+ Tone.prototype.midiToNote = function (midiNumber) {
+ var octave = Math.floor(midiNumber / 12) - 1;
+ var note = midiNumber % 12;
+ return scaleIndexToNote[note] + octave;
+ };
+ Tone.prototype.noteToMidi = function (note) {
+ 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;
+ }
+ };
+ Tone.prototype.midiToFrequency = function (midi) {
+ return Tone.A4 * Math.pow(2, (midi - 69) / 12);
+ };
+ return Tone;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
+var Tone_core_Param;
+Tone_core_Param = function (Tone) {
+ 'use strict';
+ Tone.Param = function () {
+ var options = this.optionsObject(arguments, [
+ 'param',
+ 'units',
+ 'convert'
+ ], Tone.Param.defaults);
+ this._param = this.input = options.param;
+ this.units = options.units;
+ this.convert = options.convert;
+ this.overridden = false;
+ if (!this.isUndef(options.value)) {
+ this.value = options.value;
+ }
+ };
+ Tone.extend(Tone.Param);
+ Tone.Param.defaults = {
+ 'units': Tone.Type.Default,
+ 'convert': true,
+ 'param': undefined
+ };
+ Object.defineProperty(Tone.Param.prototype, 'value', {
get: function () {
- return this.getValue();
+ return this._toUnits(this._param.value);
},
- set: function (val) {
- this.setValue(val);
+ set: function (value) {
+ var convertedVal = this._fromUnits(value);
+ this._param.value = convertedVal;
}
});
- Tone.Signal._generator = null;
+ 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;
+ }
+ };
+ 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;
+ }
+ };
+ Tone.Param.prototype._minOutput = 0.00001;
+ Tone.Param.prototype.setValueAtTime = function (value, time) {
+ value = this._fromUnits(value);
+ this._param.setValueAtTime(value, this.toSeconds(time));
+ return 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;
+ };
+ Tone.Param.prototype.linearRampToValueAtTime = function (value, endTime) {
+ value = this._fromUnits(value);
+ this._param.linearRampToValueAtTime(value, this.toSeconds(endTime));
+ return 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;
+ };
+ Tone.Param.prototype.exponentialRampToValue = function (value, rampTime) {
+ var now = this.now();
+ var currentVal = this.value;
+ this.setValueAtTime(Math.max(currentVal, this._minOutput), now);
+ this.exponentialRampToValueAtTime(value, now + this.toSeconds(rampTime));
+ return this;
+ };
+ Tone.Param.prototype.linearRampToValue = function (value, rampTime) {
+ var now = this.now();
+ this.setRampPoint(now);
+ this.linearRampToValueAtTime(value, now + this.toSeconds(rampTime));
+ return this;
+ };
+ Tone.Param.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
+ value = this._fromUnits(value);
+ value = Math.max(this._minOutput, value);
+ timeConstant = Math.max(this._minOutput, timeConstant);
+ this._param.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
+ return 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;
+ };
+ Tone.Param.prototype.cancelScheduledValues = function (startTime) {
+ this._param.cancelScheduledValues(this.toSeconds(startTime));
+ return this;
+ };
+ 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;
+ };
+ Tone.Param.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ this._param = null;
+ return this;
+ };
+ return Tone.Param;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
+var Tone_core_Gain;
+Tone_core_Gain = function (Tone) {
+ 'use strict';
+ Tone.Gain = function () {
+ var options = this.optionsObject(arguments, [
+ 'gain',
+ 'units'
+ ], Tone.Gain.defaults);
+ this.input = this.output = this._gainNode = this.context.createGain();
+ 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);
+ Tone.Gain.defaults = {
+ 'gain': 1,
+ 'convert': true
+ };
+ 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;
+}(Tone_core_Tone, Tone_core_Param);
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
+var Tone_signal_Signal;
+Tone_signal_Signal = function (Tone) {
+ 'use strict';
+ Tone.Signal = function () {
+ var options = this.optionsObject(arguments, [
+ 'value',
+ 'units'
+ ], Tone.Signal.defaults);
+ this.output = this._gain = this.context.createGain();
+ options.param = this._gain.gain;
+ Tone.Param.call(this, options);
+ this.input = this._param = this._gain.gain;
+ Tone.Signal._constant.chain(this._gain);
+ };
+ Tone.extend(Tone.Signal, Tone.Param);
+ Tone.Signal.defaults = {
+ 'value': 0,
+ 'units': Tone.Type.Default,
+ 'convert': true
+ };
+ Tone.Signal.prototype.connect = Tone.SignalBase.prototype.connect;
+ Tone.Signal.prototype.dispose = function () {
+ Tone.Param.prototype.dispose.call(this);
+ this._param = null;
+ this._gain.disconnect();
+ this._gain = null;
+ return this;
+ };
Tone.Signal._constant = null;
Tone._initAudioContext(function (audioContext) {
- Tone.Signal._generator = audioContext.createOscillator();
- Tone.Signal._constant = new Tone.WaveShaper([
- 1,
- 1
- ]);
- Tone.Signal._generator.connect(Tone.Signal._constant);
- Tone.Signal._generator.start(0);
- Tone.Signal._generator.noGC();
+ var buffer = audioContext.createBuffer(1, 128, audioContext.sampleRate);
+ var arr = buffer.getChannelData(0);
+ for (var i = 0; i < arr.length; i++) {
+ arr[i] = 1;
+ }
+ Tone.Signal._constant = audioContext.createBufferSource();
+ Tone.Signal._constant.channelCount = 1;
+ Tone.Signal._constant.channelCountMode = 'explicit';
+ Tone.Signal._constant.buffer = buffer;
+ Tone.Signal._constant.loop = true;
+ Tone.Signal._constant.start(0);
+ Tone.Signal._constant.noGC();
});
return Tone.Signal;
-}(Tone_core_Tone);
-/** Tone.js module by Yotam Mann, MIT License 2014 http://opensource.org/licenses/MIT **/
+}(Tone_core_Tone, Tone_signal_WaveShaper, Tone_core_Type, Tone_core_Param);
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
var Tone_signal_Add;
Tone_signal_Add = function (Tone) {
'use strict';
Tone.Add = function (value) {
Tone.call(this, 2, 0);
this._sum = this.input[0] = this.input[1] = this.output = this.context.createGain();
- this._value = null;
- if (isFinite(value)) {
- this._value = new Tone.Signal(value);
- this._value.connect(this._sum);
- }
- };
- Tone.extend(Tone.Add, Tone.SignalBase);
- Tone.Add.prototype.setValue = function (value) {
- if (this._value !== null) {
- this._value.setValue(value);
- } else {
- throw new Error('cannot switch from signal to number');
- }
+ this._param = this.input[1] = new Tone.Signal(value);
+ this._param.connect(this._sum);
};
+ Tone.extend(Tone.Add, Tone.Signal);
Tone.Add.prototype.dispose = function () {
Tone.prototype.dispose.call(this);
+ this._sum.disconnect();
this._sum = null;
- if (this._value) {
- this._value.dispose();
- this._value = null;
- }
+ this._param.dispose();
+ this._param = null;
+ return this;
};
return Tone.Add;
}(Tone_core_Tone);
-/** Tone.js module by Yotam Mann, MIT License 2014 http://opensource.org/licenses/MIT **/
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
var Tone_signal_Multiply;
Tone_signal_Multiply = function (Tone) {
'use strict';
Tone.Multiply = function (value) {
Tone.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(value, 0);
- };
- Tone.extend(Tone.Multiply, Tone.SignalBase);
- Tone.Multiply.prototype.setValue = function (value) {
- this._factor.value = value;
+ this._param = this.input[1] = this.output.gain;
+ this._param.value = this.defaultArg(value, 0);
};
+ Tone.extend(Tone.Multiply, Tone.Signal);
Tone.Multiply.prototype.dispose = function () {
Tone.prototype.dispose.call(this);
+ this._mult.disconnect();
this._mult = null;
- this._factor = null;
+ this._param = null;
+ return this;
};
return Tone.Multiply;
}(Tone_core_Tone);
-/** Tone.js module by Yotam Mann, MIT License 2014 http://opensource.org/licenses/MIT **/
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
var Tone_signal_Scale;
Tone_signal_Scale = function (Tone) {
'use strict';
@@ -3354,17 +4114,27 @@ Tone_signal_Scale = function (Tone) {
this._setRange();
};
Tone.extend(Tone.Scale, Tone.SignalBase);
- Tone.Scale.prototype.setMin = function (min) {
- this._outputMin = min;
- this._setRange();
- };
- Tone.Scale.prototype.setMax = function (max) {
- this._outputMax = max;
- this._setRange();
- };
+ Object.defineProperty(Tone.Scale.prototype, 'min', {
+ get: function () {
+ return this._outputMin;
+ },
+ set: function (min) {
+ this._outputMin = min;
+ this._setRange();
+ }
+ });
+ Object.defineProperty(Tone.Scale.prototype, 'max', {
+ get: function () {
+ return this._outputMax;
+ },
+ set: function (max) {
+ this._outputMax = max;
+ this._setRange();
+ }
+ });
Tone.Scale.prototype._setRange = function () {
- this._add.setValue(this._outputMin);
- this._scale.setValue(this._outputMax - this._outputMin);
+ this._add.value = this._outputMin;
+ this._scale.value = this._outputMax - this._outputMin;
};
Tone.Scale.prototype.dispose = function () {
Tone.prototype.dispose.call(this);
@@ -3372,6 +4142,7 @@ Tone_signal_Scale = function (Tone) {
this._add = null;
this._scale.dispose();
this._scale = null;
+ return this;
};
return Tone.Scale;
}(Tone_core_Tone, Tone_signal_Add, Tone_signal_Multiply);
@@ -3607,6 +4378,7 @@ oscillator = function () {
}
this.started = false;
// components
+ this.phaseAmount = undefined;
this.oscillator = p5sound.audiocontext.createOscillator();
this.f = freq || 440;
// frequency
@@ -3710,7 +4482,6 @@ oscillator = function () {
this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
} else if (vol) {
- console.log(vol);
vol.connect(self.output.gain);
} else {
// return the Gain Node
@@ -3749,13 +4520,21 @@ oscillator = function () {
var now = p5sound.audiocontext.currentTime;
var rampTime = rampTime || 0;
var tFromNow = tFromNow || 0;
- var currentFreq = this.oscillator.frequency.value;
- this.oscillator.frequency.cancelScheduledValues(now);
- this.oscillator.frequency.setValueAtTime(currentFreq, now + tFromNow);
- if (val > 0) {
- this.oscillator.frequency.exponentialRampToValueAtTime(val, tFromNow + rampTime + now);
+ // var currentFreq = this.oscillator.frequency.value;
+ // this.oscillator.frequency.cancelScheduledValues(now);
+ if (rampTime == 0) {
+ this.oscillator.frequency.cancelScheduledValues(now);
+ this.oscillator.frequency.setValueAtTime(val, tFromNow + now);
} else {
- this.oscillator.frequency.linearRampToValueAtTime(val, tFromNow + rampTime + now);
+ if (val > 0) {
+ this.oscillator.frequency.exponentialRampToValueAtTime(val, tFromNow + rampTime + now);
+ } else {
+ this.oscillator.frequency.linearRampToValueAtTime(val, tFromNow + rampTime + now);
+ }
+ }
+ // reset phase if oscillator has a phase
+ if (this.phaseAmount) {
+ this.phase(this.phaseAmount);
}
} else if (val) {
if (val.output) {
@@ -3830,6 +4609,9 @@ oscillator = function () {
};
// get rid of the oscillator
p5.Oscillator.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
if (this.oscillator) {
var now = p5sound.audiocontext.currentTime;
this.stop(now);
@@ -3844,23 +4626,27 @@ oscillator = function () {
}
};
/**
- * Set the phase of an oscillator between 0.0 and 1.0
+ * Set the phase of an oscillator between 0.0 and 1.0.
+ * In this implementation, phase is a delay time
+ * based on the oscillator's current frequency.
*
* @method phase
* @param {Number} phase float between 0.0 and 1.0
*/
p5.Oscillator.prototype.phase = function (p) {
+ var delayAmt = p5.prototype.map(p, 0, 1, 0, 1 / this.f);
+ var now = p5sound.audiocontext.currentTime;
+ this.phaseAmount = p;
if (!this.dNode) {
// create a delay node
this.dNode = p5sound.audiocontext.createDelay();
// put the delay node in between output and panner
- this.output.disconnect();
- this.output.connect(this.dNode);
- this.dNode.connect(this.panner);
+ this.oscillator.disconnect();
+ this.oscillator.connect(this.dNode);
+ this.dNode.connect(this.output);
}
- // set delay time based on PWM width
- var now = p5sound.audiocontext.currentTime;
- this.dNode.delayTime.linearRampToValueAtTime(p5.prototype.map(p, 0, 1, 0, 1 / this.oscillator.frequency.value), now);
+ // set delay time to match phase:
+ this.dNode.delayTime.setValueAtTime(delayAmt, now);
};
// ========================== //
// SIGNAL MATH FOR MODULATION //
@@ -4017,6 +4803,353 @@ oscillator = function () {
};
p5.SqrOsc.prototype = Object.create(p5.Oscillator.prototype);
}(master, Tone_signal_Signal, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale);
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
+var Tone_core_Timeline;
+Tone_core_Timeline = function (Tone) {
+ 'use strict';
+ Tone.Timeline = function () {
+ var options = this.optionsObject(arguments, ['memory'], Tone.Timeline.defaults);
+ this._timeline = [];
+ this._toRemove = [];
+ this._iterating = false;
+ this.memory = options.memory;
+ };
+ Tone.extend(Tone.Timeline);
+ Tone.Timeline.defaults = { 'memory': Infinity };
+ Object.defineProperty(Tone.Timeline.prototype, 'length', {
+ get: function () {
+ return this._timeline.length;
+ }
+ });
+ Tone.Timeline.prototype.addEvent = function (event) {
+ 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 (this.length > this.memory) {
+ var diff = this.length - this.memory;
+ this._timeline.splice(0, diff);
+ }
+ return 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;
+ };
+ 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;
+ }
+ };
+ 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;
+ }
+ };
+ 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;
+ }
+ };
+ 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) {
+ if (this._timeline[0].time >= after) {
+ this._timeline = [];
+ }
+ }
+ return 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;
+ };
+ Tone.Timeline.prototype._search = function (time) {
+ var beginning = 0;
+ var len = this._timeline.length;
+ var end = len;
+ while (beginning <= end && beginning < len) {
+ var midPoint = Math.floor(beginning + (end - beginning) / 2);
+ var event = this._timeline[midPoint];
+ if (event.time === 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) {
+ end = midPoint - 1;
+ } else if (event.time < time) {
+ beginning = midPoint + 1;
+ }
+ }
+ return beginning - 1;
+ };
+ 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 = [];
+ }
+ };
+ Tone.Timeline.prototype.forEach = function (callback) {
+ this._iterate(callback);
+ return this;
+ };
+ Tone.Timeline.prototype.forEachBefore = function (time, callback) {
+ time = this.toSeconds(time);
+ var upperBound = this._search(time);
+ if (upperBound !== -1) {
+ this._iterate(callback, 0, upperBound);
+ }
+ return this;
+ };
+ Tone.Timeline.prototype.forEachAfter = function (time, callback) {
+ time = this.toSeconds(time);
+ var lowerBound = this._search(time);
+ this._iterate(callback, lowerBound + 1);
+ return this;
+ };
+ Tone.Timeline.prototype.forEachFrom = function (time, callback) {
+ time = this.toSeconds(time);
+ var lowerBound = this._search(time);
+ while (lowerBound >= 0 && this._timeline[lowerBound].time >= time) {
+ lowerBound--;
+ }
+ this._iterate(callback, lowerBound + 1);
+ return this;
+ };
+ Tone.Timeline.prototype.forEachAtTime = function (time, callback) {
+ 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;
+ };
+ Tone.Timeline.prototype.dispose = function () {
+ Tone.prototype.dispose.call(this);
+ this._timeline = null;
+ this._toRemove = null;
+ };
+ return Tone.Timeline;
+}(Tone_core_Tone);
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
+var Tone_signal_TimelineSignal;
+Tone_signal_TimelineSignal = function (Tone) {
+ 'use strict';
+ Tone.TimelineSignal = function () {
+ var options = this.optionsObject(arguments, [
+ 'value',
+ 'units'
+ ], Tone.Signal.defaults);
+ Tone.Signal.apply(this, options);
+ options.param = this._param;
+ Tone.Param.call(this, options);
+ this._events = new Tone.Timeline(10);
+ this._initial = this._fromUnits(this._param.value);
+ };
+ Tone.extend(Tone.TimelineSignal, Tone.Param);
+ Tone.TimelineSignal.Type = {
+ Linear: 'linear',
+ Exponential: 'exponential',
+ Target: 'target',
+ Set: 'set'
+ };
+ 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;
+ }
+ });
+ 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
+ });
+ this._param.setValueAtTime(value, startTime);
+ return 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;
+ };
+ 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;
+ };
+ 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;
+ };
+ Tone.TimelineSignal.prototype.cancelScheduledValues = function (after) {
+ this._events.cancel(after);
+ this._param.cancelScheduledValues(this.toSeconds(after));
+ return this;
+ };
+ Tone.TimelineSignal.prototype.setRampPoint = function (time) {
+ time = this.toSeconds(time);
+ var val = this.getValueAtTime(time);
+ var after = this._searchAfter(time);
+ if (after) {
+ 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;
+ };
+ Tone.TimelineSignal.prototype.linearRampToValueBetween = function (value, start, finish) {
+ this.setRampPoint(start);
+ this.linearRampToValueAtTime(value, finish);
+ return this;
+ };
+ Tone.TimelineSignal.prototype.exponentialRampToValueBetween = function (value, start, finish) {
+ this.setRampPoint(start);
+ this.exponentialRampToValueAtTime(value, finish);
+ return this;
+ };
+ Tone.TimelineSignal.prototype._searchBefore = function (time) {
+ return this._events.getEvent(time);
+ };
+ Tone.TimelineSignal.prototype._searchAfter = function (time) {
+ return this._events.getEventAfter(time);
+ };
+ Tone.TimelineSignal.prototype.getValueAtTime = function (time) {
+ var after = this._searchAfter(time);
+ var before = this._searchBefore(time);
+ var value = this._initial;
+ 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;
+ };
+ Tone.TimelineSignal.prototype.connect = Tone.SignalBase.prototype.connect;
+ Tone.TimelineSignal.prototype._exponentialApproach = function (t0, v0, v1, timeConstant, t) {
+ return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
+ };
+ Tone.TimelineSignal.prototype._linearInterpolate = function (t0, v0, t1, v1, t) {
+ return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
+ };
+ 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));
+ };
+ 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;
+}(Tone_core_Tone, Tone_signal_Signal);
var env;
env = function () {
'use strict';
@@ -4024,140 +5157,291 @@ env = function () {
var Add = Tone_signal_Add;
var Mult = Tone_signal_Multiply;
var Scale = Tone_signal_Scale;
+ var TimelineSignal = Tone_signal_TimelineSignal;
var Tone = Tone_core_Tone;
Tone.setContext(p5sound.audiocontext);
- // oscillator or buffer source to clear on env complete
- // to save resources if/when it is retriggered
- var sourceToClear = null;
- // set to true if attack is set, then false on release
- var wasTriggered = false;
- /**
- * Envelopes are pre-defined amplitude distribution over time.
- * The p5.Env accepts up to four time/level pairs, where time
- * determines how long of a ramp before value reaches level.
+ /**
+ *
Envelopes are pre-defined amplitude distribution over time.
* Typically, envelopes are used to control the output volume
* of an object, a series of fades referred to as Attack, Decay,
- * Sustain and Release (ADSR). But p5.Env can control any
- * Web Audio Param, for example it can be passed to an Oscillator
- * frequency like osc.freq(env)
+ * Sustain and Release (
+ * ADSR
+ * ). Envelopes can also control other Web Audio Parameters—for example, a p5.Env can
+ * control an Oscillator's frequency like this: osc.freq(env).
+ * Use setRange to change the attack/release level.
+ * Use setADSR to change attackTime, decayTime, sustainPercent and releaseTime.
+ * Use the play method to play the entire envelope,
+ * the ramp method for a pingable trigger,
+ * or triggerAttack/
+ * triggerRelease to trigger noteOn/noteOff.
*
* @class p5.Env
* @constructor
- * @param {Number} aTime Time (in seconds) before level
- * reaches attackLevel
- * @param {Number} aLevel Typically an amplitude between
- * 0.0 and 1.0
- * @param {Number} dTime Time
- * @param {Number} [dLevel] Amplitude (In a standard ADSR envelope,
- * decayLevel = sustainLevel)
- * @param {Number} [sTime] Time (in seconds)
- * @param {Number} [sLevel] Amplitude 0.0 to 1.0
- * @param {Number} [rTime] Time (in seconds)
- * @param {Number} [rLevel] Amplitude 0.0 to 1.0
* @example
*
- * var aT = 0.1; // attack time in seconds
- * var aL = 0.7; // attack level 0.0 to 1.0
- * var dT = 0.3; // decay time in seconds
- * var dL = 0.1; // decay level 0.0 to 1.0
- * var sT = 0.2; // sustain time in seconds
- * var sL = dL; // sustain level 0.0 to 1.0
- * var rT = 0.5; // release time in seconds
- * // release level defaults to zero
- *
- * var env;
- * var triOsc;
- *
+ * var attackLevel = 1.0;
+ * var releaseLevel = 0;
+ *
+ * var attackTime = 0.001
+ * var decayTime = 0.2;
+ * var susPercent = 0.2;
+ * var releaseTime = 0.5;
+ *
+ * var env, triOsc;
+ *
* function setup() {
- * background(0);
- * noStroke();
- * fill(255);
+ * var cnv = createCanvas(100, 100);
+ *
* textAlign(CENTER);
* text('click to play', width/2, height/2);
*
- * env = new p5.Env(aT, aL, dT, dL, sT, sL, rT);
+ * env = new p5.Env();
+ * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+ * env.setRange(attackLevel, releaseLevel);
+ *
* triOsc = new p5.Oscillator('triangle');
- * triOsc.amp(env); // give the env control of the triOsc's amp
+ * triOsc.amp(env);
* triOsc.start();
+ * triOsc.freq(220);
+ *
+ * cnv.mousePressed(playEnv);
* }
*
- * // mouseClick triggers envelope if over canvas
- * function mouseClicked() {
- * // is mouse over canvas?
- * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
- * env.play(triOsc);
- * }
+ * function playEnv(){
+ * env.play();
* }
*
*/
- p5.Env = function (t1, l1, t2, l2, t3, l3, t4, l4) {
+ p5.Env = function (t1, l1, t2, l2, t3, l3) {
+ var now = p5sound.audiocontext.currentTime;
/**
+ * Time until envelope reaches attackLevel
* @property attackTime
*/
- this.aTime = t1;
+ this.aTime = t1 || 0.1;
/**
+ * Level once attack is complete.
* @property attackLevel
*/
- this.aLevel = l1;
+ this.aLevel = l1 || 1;
/**
+ * Time until envelope reaches decayLevel.
* @property decayTime
*/
- this.dTime = t2 || 0;
+ this.dTime = t2 || 0.5;
/**
+ * Level after decay. The envelope will sustain here until it is released.
* @property decayLevel
*/
this.dLevel = l2 || 0;
/**
- * @property sustainTime
- */
- this.sTime = t3 || 0;
- /**
- * @property sustainLevel
- */
- this.sLevel = l3 || 0;
- /**
+ * Duration of the release portion of the envelope.
* @property releaseTime
*/
- this.rTime = t4 || 0;
+ this.rTime = t3 || 0;
/**
+ * Level at the end of the release.
* @property releaseLevel
*/
- this.rLevel = l4 || 0;
+ this.rLevel = l3 || 0;
+ this._rampHighPercentage = 0.98;
+ this._rampLowPercentage = 0.02;
this.output = p5sound.audiocontext.createGain();
- this.control = new p5.Signal();
+ this.control = new TimelineSignal();
+ this._init();
+ // this makes sure the envelope starts at zero
this.control.connect(this.output);
+ // connect to the output
this.connection = null;
// store connection
//array of math operation signal chaining
this.mathOps = [this.control];
+ //whether envelope should be linear or exponential curve
+ this.isExponential = false;
+ // oscillator or buffer source to clear on env complete
+ // to save resources if/when it is retriggered
+ this.sourceToClear = null;
+ // set to true if attack is set, then false on release
+ this.wasTriggered = false;
// add to the soundArray so we can dispose of the env later
p5sound.soundArray.push(this);
};
+ // this init function just smooths the starting value to zero and gives a start point for the timeline
+ // - it was necessary to remove glitches at the beginning.
+ p5.Env.prototype._init = function () {
+ var now = p5sound.audiocontext.currentTime;
+ var t = now;
+ this.control.setTargetAtTime(0.00001, t, 0.001);
+ //also, compute the correct time constants
+ this._setRampAD(this.aTime, this.dTime);
+ };
/**
* Reset the envelope with a series of time/value pairs.
*
* @method set
- * @param {Number} aTime Time (in seconds) before level
+ * @param {Number} attackTime Time (in seconds) before level
* reaches attackLevel
- * @param {Number} aLevel Typically an amplitude between
+ * @param {Number} attackLevel Typically an amplitude between
* 0.0 and 1.0
- * @param {Number} dTime Time
- * @param {Number} [dLevel] Amplitude (In a standard ADSR envelope,
+ * @param {Number} decayTime Time
+ * @param {Number} decayLevel Amplitude (In a standard ADSR envelope,
* decayLevel = sustainLevel)
- * @param {Number} [sTime] Time (in seconds)
- * @param {Number} [sLevel] Amplitude 0.0 to 1.0
- * @param {Number} [rTime] Time (in seconds)
- * @param {Number} [rLevel] Amplitude 0.0 to 1.0
+ * @param {Number} releaseTime Release Time (in seconds)
+ * @param {Number} releaseLevel Amplitude
*/
- p5.Env.prototype.set = function (t1, l1, t2, l2, t3, l3, t4, l4) {
+ p5.Env.prototype.set = function (t1, l1, t2, l2, t3, l3) {
this.aTime = t1;
this.aLevel = l1;
this.dTime = t2 || 0;
this.dLevel = l2 || 0;
- this.sTime = t3 || 0;
- this.sLevel = l3 || 0;
this.rTime = t4 || 0;
this.rLevel = l4 || 0;
+ // set time constants for ramp
+ this._setRampAD(t1, t2);
+ };
+ /**
+ * Set values like a traditional
+ *
+ * ADSR envelope
+ * .
+ *
+ * @method setADSR
+ * @param {Number} attackTime Time (in seconds before envelope
+ * reaches Attack Level
+ * @param {Number} [decayTime] Time (in seconds) before envelope
+ * reaches Decay/Sustain Level
+ * @param {Number} [susRatio] Ratio between attackLevel and releaseLevel, on a scale from 0 to 1,
+ * where 1.0 = attackLevel, 0.0 = releaseLevel.
+ * The susRatio determines the decayLevel and the level at which the
+ * sustain portion of the envelope will sustain.
+ * For example, if attackLevel is 0.4, releaseLevel is 0,
+ * and susAmt is 0.5, the decayLevel would be 0.2. If attackLevel is
+ * increased to 1.0 (using setRange),
+ * then decayLevel would increase proportionally, to become 0.5.
+ * @param {Number} [releaseTime] Time in seconds from now (defaults to 0)
+ * @example
+ *
+ * var attackLevel = 1.0;
+ * var releaseLevel = 0;
+ *
+ * var attackTime = 0.001
+ * var decayTime = 0.2;
+ * var susPercent = 0.2;
+ * var releaseTime = 0.5;
+ *
+ * var env, triOsc;
+ *
+ * function setup() {
+ * var cnv = createCanvas(100, 100);
+ *
+ * textAlign(CENTER);
+ * text('click to play', width/2, height/2);
+ *
+ * env = new p5.Env();
+ * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+ * env.setRange(attackLevel, releaseLevel);
+ *
+ * triOsc = new p5.Oscillator('triangle');
+ * triOsc.amp(env);
+ * triOsc.start();
+ * triOsc.freq(220);
+ *
+ * cnv.mousePressed(playEnv);
+ * }
+ *
+ * function playEnv(){
+ * env.play();
+ * }
+ *
+ */
+ p5.Env.prototype.setADSR = function (aTime, dTime, sPercent, rTime) {
+ this.aTime = aTime;
+ this.dTime = dTime || 0;
+ // lerp
+ this.sPercent = sPercent || 0;
+ this.dLevel = typeof sPercent !== 'undefined' ? sPercent * (this.aLevel - this.rLevel) + this.rLevel : 0;
+ this.rTime = rTime || 0;
+ // also set time constants for ramp
+ this._setRampAD(aTime, dTime);
+ };
+ /**
+ * Set max (attackLevel) and min (releaseLevel) of envelope.
+ *
+ * @method setRange
+ * @param {Number} aLevel attack level (defaults to 1)
+ * @param {Number} rLevel release level (defaults to 0)
+ * @example
+ *
+ * var attackLevel = 1.0;
+ * var releaseLevel = 0;
+ *
+ * var attackTime = 0.001
+ * var decayTime = 0.2;
+ * var susPercent = 0.2;
+ * var releaseTime = 0.5;
+ *
+ * var env, triOsc;
+ *
+ * function setup() {
+ * var cnv = createCanvas(100, 100);
+ *
+ * textAlign(CENTER);
+ * text('click to play', width/2, height/2);
+ *
+ * env = new p5.Env();
+ * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+ * env.setRange(attackLevel, releaseLevel);
+ *
+ * triOsc = new p5.Oscillator('triangle');
+ * triOsc.amp(env);
+ * triOsc.start();
+ * triOsc.freq(220);
+ *
+ * cnv.mousePressed(playEnv);
+ * }
+ *
+ * function playEnv(){
+ * env.play();
+ * }
+ *
+ */
+ p5.Env.prototype.setRange = function (aLevel, rLevel) {
+ this.aLevel = aLevel || 1;
+ this.rLevel = rLevel || 0;
+ };
+ // private (undocumented) method called when ADSR is set to set time constants for ramp
+ //
+ // Set the
+ // time constants for simple exponential ramps.
+ // The larger the time constant value, the slower the
+ // transition will be.
+ //
+ // method _setRampAD
+ // param {Number} attackTimeConstant attack time constant
+ // param {Number} decayTimeConstant decay time constant
+ //
+ p5.Env.prototype._setRampAD = function (t1, t2) {
+ this._rampAttackTime = this.checkExpInput(t1);
+ this._rampDecayTime = this.checkExpInput(t2);
+ var TCDenominator = 1;
+ /// Aatish Bhatia's calculation for time constant for rise(to adjust 1/1-e calculation to any percentage)
+ TCDenominator = Math.log(1 / this.checkExpInput(1 - this._rampHighPercentage));
+ this._rampAttackTC = t1 / this.checkExpInput(TCDenominator);
+ TCDenominator = Math.log(1 / this._rampLowPercentage);
+ this._rampDecayTC = t2 / this.checkExpInput(TCDenominator);
+ };
+ // private method
+ p5.Env.prototype.setRampPercentages = function (p1, p2) {
+ //set the percentages that the simple exponential ramps go to
+ this._rampHighPercentage = this.checkExpInput(p1);
+ this._rampLowPercentage = this.checkExpInput(p2);
+ var TCDenominator = 1;
+ //now re-compute the time constants based on those percentages
+ /// Aatish Bhatia's calculation for time constant for rise(to adjust 1/1-e calculation to any percentage)
+ TCDenominator = Math.log(1 / this.checkExpInput(1 - this._rampHighPercentage));
+ this._rampAttackTC = this._rampAttackTime / this.checkExpInput(TCDenominator);
+ TCDenominator = Math.log(1 / this._rampLowPercentage);
+ this._rampDecayTC = this._rampDecayTime / this.checkExpInput(TCDenominator);
};
/**
* Assign a parameter to be controlled by this envelope.
@@ -4174,8 +5458,23 @@ env = function () {
this.connect(arguments[i]);
}
};
- p5.Env.prototype.ctrl = function (unit) {
- this.connect(unit);
+ /**
+ * Set whether the envelope ramp is linear (default) or exponential.
+ * Exponential ramps can be useful because we perceive amplitude
+ * and frequency logarithmically.
+ *
+ * @method setExp
+ * @param {Boolean} isExp true is exponential, false is linear
+ */
+ p5.Env.prototype.setExp = function (isExp) {
+ this.isExponential = isExp;
+ };
+ //helper method to protect against zero values being sent to exponential functions
+ p5.Env.prototype.checkExpInput = function (value) {
+ if (value <= 0) {
+ value = 0.0001;
+ }
+ return value;
};
/**
* Play tells the envelope to start acting on a given input.
@@ -4188,32 +5487,59 @@ env = function () {
* @method play
* @param {Object} unit A p5.sound object or
* Web Audio Param.
- * @param {Number} secondsFromNow time from now (in seconds)
+ * @param {Number} [startTime] time from now (in seconds) at which to play
+ * @param {Number} [sustainTime] time to sustain before releasing the envelope
+ * @example
+ *
+ * var attackLevel = 1.0;
+ * var releaseLevel = 0;
+ *
+ * var attackTime = 0.001
+ * var decayTime = 0.2;
+ * var susPercent = 0.2;
+ * var releaseTime = 0.5;
+ *
+ * var env, triOsc;
+ *
+ * function setup() {
+ * var cnv = createCanvas(100, 100);
+ *
+ * textAlign(CENTER);
+ * text('click to play', width/2, height/2);
+ *
+ * env = new p5.Env();
+ * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
+ * env.setRange(attackLevel, releaseLevel);
+ *
+ * triOsc = new p5.Oscillator('triangle');
+ * triOsc.amp(env);
+ * triOsc.start();
+ * triOsc.freq(220);
+ *
+ * cnv.mousePressed(playEnv);
+ * }
+ *
+ * function playEnv(){
+ * // trigger env on triOsc, 0 seconds from now
+ * // After decay, sustain for 0.2 seconds before release
+ * env.play(triOsc, 0, 0.2);
+ * }
+ *
*/
- p5.Env.prototype.play = function (unit, secondsFromNow) {
+ p5.Env.prototype.play = function (unit, secondsFromNow, susTime) {
var now = p5sound.audiocontext.currentTime;
var tFromNow = secondsFromNow || 0;
- var t = now + tFromNow;
+ var susTime = susTime || 0;
if (unit) {
if (this.connection !== unit) {
this.connect(unit);
}
}
- var currentVal = this.control.getValue();
- this.control.cancelScheduledValues(t);
- this.control.linearRampToValueAtTime(currentVal, t);
- // attack
- this.control.linearRampToValueAtTime(this.aLevel, t + this.aTime);
- // decay to decay level
- this.control.linearRampToValueAtTime(this.dLevel, t + this.aTime + this.dTime);
- // hold sustain level
- this.control.linearRampToValueAtTime(this.sLevel, t + this.aTime + this.dTime + this.sTime);
- // release
- this.control.linearRampToValueAtTime(this.rLevel, t + this.aTime + this.dTime + this.sTime + this.rTime);
- var clearTime = t + this.aTime + this.dTime + this.sTime + this.rTime;
+ this.triggerAttack(unit, tFromNow);
+ this.triggerRelease(unit, tFromNow + this.aTime + this.dTime + susTime);
};
/**
- * Trigger the Attack, Decay, and Sustain of the Envelope.
+ * Trigger the Attack, and Decay portion of the Envelope.
* Similar to holding down a key on a piano, but it will
* hold the sustain level until you let go. Input can be
* any p5.sound object, or a setADSR(attackTime, decayTime)
+ * as
+ * time constants for simple exponential ramps.
+ * If the value is higher than current value, it uses attackTime,
+ * while a decrease uses decayTime.
+ *
+ * @method ramp
+ * @param {Object} unit p5.sound Object or Web Audio Param
+ * @param {Number} secondsFromNow When to trigger the ramp
+ * @param {Number} v Target value
+ * @param {Number} [v2] Second target value (optional)
+ * @example
+ *
+ * var env, osc, amp, cnv;
+ *
+ * var attackTime = 0.001;
+ * var decayTime = 0.2;
+ * var attackLevel = 1;
+ * var decayLevel = 0;
+ *
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * fill(0,255,0);
+ * noStroke();
+ *
+ * env = new p5.Env();
+ * env.setADSR(attackTime, decayTime);
+ *
+ * osc = new p5.Oscillator();
+ * osc.amp(env);
+ * osc.start();
+ *
+ * amp = new p5.Amplitude();
+ *
+ * cnv.mousePressed(triggerRamp);
+ * }
+ *
+ * function triggerRamp() {
+ * env.ramp(osc, 0, attackLevel, decayLevel);
+ * }
+ *
+ * function draw() {
+ * background(20,20,20);
+ * text('click me', 10, 20);
+ * var h = map(amp.getLevel(), 0, 0.4, 0, height);;
+ *
+ * rect(0, height, width, -h);
+ * }
+ *
+ */
+ p5.Env.prototype.ramp = function (unit, secondsFromNow, v1, v2) {
+ var now = p5sound.audiocontext.currentTime;
+ var tFromNow = secondsFromNow || 0;
+ var t = now + tFromNow;
+ var destination1 = this.checkExpInput(v1);
+ var destination2 = typeof v2 !== 'undefined' ? this.checkExpInput(v2) : undefined;
+ // connect env to unit if not already connected
+ if (unit) {
+ if (this.connection !== unit) {
+ this.connect(unit);
+ }
+ }
+ //get current value
+ var currentVal = this.checkExpInput(this.control.getValueAtTime(t));
+ this.control.cancelScheduledValues(t);
+ //if it's going up
+ if (destination1 > currentVal) {
+ this.control.setTargetAtTime(destination1, t, this._rampAttackTC);
+ t += this._rampAttackTime;
+ } else if (destination1 < currentVal) {
+ this.control.setTargetAtTime(destination1, t, this._rampDecayTC);
+ t += this._rampDecayTime;
+ }
+ // Now the second part of envelope begins
+ if (destination2 === undefined)
+ return;
+ //if it's going up
+ if (destination2 > destination1) {
+ this.control.setTargetAtTime(destination2, t, this._rampAttackTC);
+ } else if (destination2 < destination1) {
+ this.control.setTargetAtTime(destination2, t, this._rampDecayTC);
+ }
};
p5.Env.prototype.connect = function (unit) {
this.connection = unit;
@@ -4383,6 +5904,9 @@ env = function () {
};
// get rid of the oscillator
p5.Env.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
var now = p5sound.audiocontext.currentTime;
this.disconnect();
try {
@@ -4394,7 +5918,7 @@ env = function () {
mathOps[i].dispose();
}
};
-}(master, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale, Tone_core_Tone);
+}(master, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale, Tone_signal_TimelineSignal, Tone_core_Tone);
var pulse;
pulse = function () {
'use strict';
@@ -4730,6 +6254,9 @@ noise = function () {
*/
p5.Noise.prototype.dispose = function () {
var now = p5sound.audiocontext.currentTime;
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
if (this.noise) {
this.noise.disconnect();
this.stop(now);
@@ -4750,6 +6277,7 @@ var audioin;
audioin = function () {
'use strict';
var p5sound = master;
+ var CustomError = errorHandler;
/**
* Get audio from an input, i.e. your computer's microphone.
*
@@ -4816,24 +6344,35 @@ audioin = function () {
* anything unless you use the connect() method.
*
* @method start
- */
- p5.AudioIn.prototype.start = function () {
+ * @param {Function} successCallback Name of a function to call on
+ * success.
+ * @param {Function} errorCallback Name of a function to call if
+ * there was an error. For example,
+ * some browsers do not support
+ * getUserMedia.
+ */
+ p5.AudioIn.prototype.start = function (successCallback, errorCallback) {
var self = this;
// if _gotSources() i.e. developers determine which source to use
if (p5sound.inputSources[self.currentSource]) {
// set the audio source
var audioSource = p5sound.inputSources[self.currentSource].id;
var constraints = { audio: { optional: [{ sourceId: audioSource }] } };
- navigator.getUserMedia(constraints, this._onStream = function (stream) {
+ window.navigator.getUserMedia(constraints, this._onStream = function (stream) {
self.stream = stream;
self.enabled = true;
// Wrap a MediaStreamSourceNode around the live input
self.mediaStream = p5sound.audiocontext.createMediaStreamSource(stream);
self.mediaStream.connect(self.output);
+ if (successCallback)
+ successCallback();
// only send to the Amplitude reader, so we can see it but not hear it.
self.amplitude.setInput(self.output);
- }, this._onStreamError = function (stream) {
- console.error(stream);
+ }, this._onStreamError = function (e) {
+ if (errorCallback)
+ errorCallback(e);
+ else
+ console.error(e);
});
} else {
// if Firefox where users select their source via browser
@@ -4847,8 +6386,13 @@ audioin = function () {
self.mediaStream.connect(self.output);
// only send to the Amplitude reader, so we can see it but not hear it.
self.amplitude.setInput(self.output);
- }, this._onStreamError = function (stream) {
- console.error(stream);
+ if (successCallback)
+ successCallback();
+ }, this._onStreamError = function (e) {
+ if (errorCallback)
+ errorCallback(e);
+ else
+ console.error(e);
});
}
};
@@ -4987,8 +6531,6 @@ audioin = function () {
* audioGrab.setSource(0);
* });
* }
- * function draw(){
- * }
*
*/
p5.AudioIn.prototype.getSources = function (callback) {
@@ -5030,6 +6572,9 @@ audioin = function () {
};
// private method
p5.AudioIn.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
this.stop();
if (this.output) {
this.output.disconnect();
@@ -5040,7 +6585,7 @@ audioin = function () {
this.amplitude = null;
this.output = null;
};
-}(master);
+}(master, errorHandler);
var filter;
filter = function () {
'use strict';
@@ -5134,6 +6679,8 @@ filter = function () {
if (type) {
this.setType(type);
}
+ // add to the soundArray
+ p5sound.soundArray.push(this);
};
/**
* Filter an audio signal according to a set
@@ -5263,6 +6810,17 @@ filter = function () {
p5.Filter.prototype.disconnect = function () {
this.output.disconnect();
};
+ p5.Filter.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
+ this.input.disconnect();
+ this.input = undefined;
+ this.output.disconnect();
+ this.output = undefined;
+ this.biquad.disconnect();
+ this.biquad = undefined;
+ };
/**
* Constructor: new p5.LowPass() Filter.
* This is the same as creating a p5.Filter and then calling
@@ -5383,15 +6941,6 @@ delay = function () {
this._rightFilter = new p5.Filter();
this._leftFilter.disconnect();
this._rightFilter.disconnect();
- /**
- * Internal filter. Set to lowPass by default, but can be accessed directly.
- * See p5.Filter for methods. Or use the p5.Delay.filter() method to change
- * frequency and q.
- *
- * @property lowPass
- * @type {p5.Filter}
- */
- 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(0.3, this.ac.currentTime);
@@ -5409,6 +6958,8 @@ delay = function () {
// default routing
this.setType(0);
this._maxDelay = this.leftDelay.delayTime.maxValue;
+ // add this p5.SoundFile to the soundArray
+ p5sound.soundArray.push(this);
};
/**
* Add delay to an audio signal according to a set
@@ -5573,11 +7124,37 @@ delay = function () {
p5.Delay.prototype.disconnect = function () {
this.output.disconnect();
};
+ p5.Delay.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
+ this.input.disconnect();
+ this.output.disconnect();
+ this._split.disconnect();
+ this._leftFilter.disconnect();
+ this._rightFilter.disconnect();
+ this._merge.disconnect();
+ this._leftGain.disconnect();
+ this._rightGain.disconnect();
+ this.leftDelay.disconnect();
+ this.rightDelay.disconnect();
+ this.input = undefined;
+ this.output = undefined;
+ this._split = undefined;
+ this._leftFilter = undefined;
+ this._rightFilter = undefined;
+ this._merge = undefined;
+ this._leftGain = undefined;
+ this._rightGain = undefined;
+ this.leftDelay = undefined;
+ this.rightDelay = undefined;
+ };
}(master, filter);
var reverb;
reverb = function () {
'use strict';
var p5sound = master;
+ var CustomError = errorHandler;
/**
* Reverb adds depth to a sound through a large number of decaying
* echoes. It creates the perception that sound is occurring in a
@@ -5741,6 +7318,9 @@ reverb = function () {
this.convolverNode.buffer = impulse;
};
p5.Reverb.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
if (this.convolverNode) {
this.convolverNode.buffer = null;
this.convolverNode = null;
@@ -5776,7 +7356,11 @@ reverb = function () {
* @class p5.Convolver
* @constructor
* @param {String} path path to a sound file
- * @param {[Function]} callback function (optional)
+ * @param {Function} [callback] function to call when loading succeeds
+ * @param {Function} [errorCallback] function to call if loading fails.
+ * This function will receive an error or
+ * XMLHttpRequest object with information
+ * about what went wrong.
* @example
*
* var cVerb, sound;
@@ -5805,7 +7389,7 @@ reverb = function () {
* }
*
*/
- p5.Convolver = function (path, callback) {
+ p5.Convolver = function (path, callback, errorCallback) {
this.ac = p5sound.audiocontext;
/**
* Internally, the p5.Convolver uses the a
@@ -5824,7 +7408,7 @@ reverb = function () {
this.convolverNode.connect(this.output);
if (path) {
this.impulses = [];
- this._loadBuffer(path, callback);
+ this._loadBuffer(path, callback, errorCallback);
} else {
// parameters
this._seconds = 3;
@@ -5843,7 +7427,12 @@ reverb = function () {
*
* @method createConvolver
* @param {String} path path to a sound file
- * @param {[Function]} callback function (optional)
+ * @param {Function} [callback] function to call if loading is successful.
+ * The object will be passed in as the argument
+ * to the callback function.
+ * @param {Function} [errorCallback] function to call if loading is not successful.
+ * A custom error will be passed in as the argument
+ * to the callback function.
* @return {p5.Convolver}
* @example
*
@@ -5873,12 +7462,12 @@ reverb = function () {
* }
*
*/
- p5.prototype.createConvolver = function (path, callback) {
+ p5.prototype.createConvolver = function (path, callback, errorCallback) {
// if loading locally without a server
if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
}
- var cReverb = new p5.Convolver(path, callback);
+ var cReverb = new p5.Convolver(path, callback, errorCallback);
cReverb.impulses = [];
return cReverb;
};
@@ -5888,28 +7477,62 @@ reverb = function () {
*
* @param {String} path
* @param {Function} callback
+ * @param {Function} errorCallback
* @private
*/
- p5.Convolver.prototype._loadBuffer = function (path, callback) {
- path = p5.prototype._checkFileFormats(path);
+ p5.Convolver.prototype._loadBuffer = function (path, callback, errorCallback) {
+ var path = p5.prototype._checkFileFormats(path);
+ var self = this;
+ var errorTrace = new Error().stack;
+ var ac = p5.prototype.getAudioContext();
var request = new XMLHttpRequest();
request.open('GET', path, true);
request.responseType = 'arraybuffer';
- // decode asyncrohonously
- var self = this;
request.onload = function () {
- var ac = p5.prototype.getAudioContext();
- ac.decodeAudioData(request.response, function (buff) {
- var buffer = {};
- var chunks = path.split('/');
- buffer.name = chunks[chunks.length - 1];
- buffer.audioBuffer = buff;
- self.impulses.push(buffer);
- self.convolverNode.buffer = buffer.audioBuffer;
- if (callback) {
- callback(buffer);
+ if (request.status == 200) {
+ // on success loading file:
+ ac.decodeAudioData(request.response, function (buff) {
+ var buffer = {};
+ var chunks = path.split('/');
+ buffer.name = chunks[chunks.length - 1];
+ buffer.audioBuffer = buff;
+ self.impulses.push(buffer);
+ self.convolverNode.buffer = buffer.audioBuffer;
+ if (callback) {
+ callback(buffer);
+ }
+ }, // error decoding buffer. "e" is undefined in Chrome 11/22/2015
+ function (e) {
+ var err = new CustomError('decodeAudioData', errorTrace, self.url);
+ var msg = 'AudioContext error at decodeAudioData for ' + self.url;
+ if (errorCallback) {
+ err.msg = msg;
+ errorCallback(err);
+ } else {
+ console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+ }
+ });
+ } else {
+ var err = new CustomError('loadConvolver', errorTrace, self.url);
+ var msg = 'Unable to load ' + self.url + '. The request status was: ' + request.status + ' (' + request.statusText + ')';
+ if (errorCallback) {
+ err.message = msg;
+ errorCallback(err);
+ } else {
+ console.error(msg + '\n The error stack trace includes: \n' + err.stack);
}
- });
+ }
+ };
+ // if there is another error, aside from 404...
+ request.onerror = function (e) {
+ var err = new CustomError('loadConvolver', errorTrace, self.url);
+ var msg = 'There was no response from the server at ' + self.url + '. Check the url and internet connectivity.';
+ if (errorCallback) {
+ err.message = msg;
+ errorCallback(err);
+ } else {
+ console.error(msg + '\n The error stack trace includes: \n' + err.stack);
+ }
};
request.send();
};
@@ -5963,14 +7586,15 @@ reverb = function () {
*
* @method addImpulse
* @param {String} path path to a sound file
- * @param {[Function]} callback function (optional)
+ * @param {Function} callback function (optional)
+ * @param {Function} errorCallback function (optional)
*/
- p5.Convolver.prototype.addImpulse = function (path, callback) {
+ p5.Convolver.prototype.addImpulse = function (path, callback, errorCallback) {
// if loading locally without a server
if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
}
- this._loadBuffer(path, callback);
+ this._loadBuffer(path, callback, errorCallback);
};
/**
* Similar to .addImpulse, except that the .impulses
@@ -5979,15 +7603,16 @@ reverb = function () {
*
* @method resetImpulse
* @param {String} path path to a sound file
- * @param {[Function]} callback function (optional)
+ * @param {Function} callback function (optional)
+ * @param {Function} errorCallback function (optional)
*/
- p5.Convolver.prototype.resetImpulse = function (path, callback) {
+ p5.Convolver.prototype.resetImpulse = function (path, callback, errorCallback) {
// if loading locally without a server
if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
}
this.impulses = [];
- this._loadBuffer(path, callback);
+ this._loadBuffer(path, callback, errorCallback);
};
/**
* If you have used .addImpulse() to add multiple impulses
@@ -6037,88 +7662,161 @@ reverb = function () {
this.panner = null;
}
};
-}(master, sndcore);
-/** Tone.js module by Yotam Mann, MIT License 2014 http://opensource.org/licenses/MIT **/
+}(master, errorHandler, sndcore);
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
+var Tone_core_TimelineState;
+Tone_core_TimelineState = function (Tone) {
+ 'use strict';
+ Tone.TimelineState = function (initial) {
+ Tone.Timeline.call(this);
+ this._initial = initial;
+ };
+ Tone.extend(Tone.TimelineState, Tone.Timeline);
+ Tone.TimelineState.prototype.getStateAtTime = function (time) {
+ var event = this.getEvent(time);
+ if (event !== null) {
+ return event.state;
+ } else {
+ return this._initial;
+ }
+ };
+ Tone.TimelineState.prototype.setStateAtTime = function (state, time) {
+ this.addEvent({
+ 'state': state,
+ 'time': this.toSeconds(time)
+ });
+ };
+ return Tone.TimelineState;
+}(Tone_core_Tone, Tone_core_Timeline);
+/** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
var Tone_core_Clock;
Tone_core_Clock = function (Tone) {
'use strict';
- Tone.Clock = function (rate, callback) {
- this._oscillator = null;
- this._jsNode = this.context.createScriptProcessor(this.bufferSize, 1, 1);
- this._jsNode.onaudioprocess = this._processBuffer.bind(this);
- this._controlSignal = new Tone.Signal(1);
- this._upTick = false;
- this.tick = this.defaultArg(callback, function () {
- });
- this._jsNode.noGC();
- this.setRate(rate);
+ Tone.Clock = function () {
+ var options = this.optionsObject(arguments, [
+ 'callback',
+ 'frequency'
+ ], Tone.Clock.defaults);
+ this.callback = options.callback;
+ this._lookAhead = 'auto';
+ this._computedLookAhead = 1 / 60;
+ this._threshold = 0.5;
+ this._nextTick = -1;
+ this._lastUpdate = 0;
+ this._loopID = -1;
+ this.frequency = new Tone.TimelineSignal(options.frequency, Tone.Type.Frequency);
+ this.ticks = 0;
+ this._state = new Tone.TimelineState(Tone.State.Stopped);
+ this._boundLoop = this._loop.bind(this);
+ this._readOnly('frequency');
+ this._loop();
};
Tone.extend(Tone.Clock);
- Tone.Clock.prototype.setRate = function (rate, rampTime) {
- var freqVal = this.secondsToFrequency(this.toSeconds(rate));
- if (!rampTime) {
- this._controlSignal.cancelScheduledValues(0);
- this._controlSignal.setValue(freqVal);
- } else {
- this._controlSignal.exponentialRampToValueNow(freqVal, rampTime);
- }
+ Tone.Clock.defaults = {
+ 'callback': Tone.noOp,
+ 'frequency': 1,
+ 'lookAhead': 'auto'
};
- Tone.Clock.prototype.getRate = function () {
- return this._controlSignal.getValue();
+ Object.defineProperty(Tone.Clock.prototype, 'state', {
+ get: function () {
+ return this._state.getStateAtTime(this.now());
+ }
+ });
+ 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);
+ }
+ }
+ });
+ 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;
};
- Tone.Clock.prototype.start = function (time) {
- this._oscillator = this.context.createOscillator();
- this._oscillator.type = 'square';
- this._oscillator.connect(this._jsNode);
- this._controlSignal.connect(this._oscillator.frequency);
- this._upTick = false;
- var startTime = this.toSeconds(time);
- this._oscillator.start(startTime);
- this._oscillator.onended = function () {
- };
+ 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;
};
- Tone.Clock.prototype.stop = function (time, onend) {
- var stopTime = this.toSeconds(time);
- this._oscillator.onended = onend;
- this._oscillator.stop(stopTime);
+ 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;
};
- Tone.Clock.prototype._processBuffer = function (event) {
- var now = this.defaultArg(event.playbackTime, this.now());
- var bufferSize = this._jsNode.bufferSize;
- var incomingBuffer = event.inputBuffer.getChannelData(0);
- var upTick = this._upTick;
- var self = this;
- for (var i = 0; i < bufferSize; i++) {
- var sample = incomingBuffer[i];
- if (sample > 0 && !upTick) {
- upTick = true;
- setTimeout(function () {
- var tickTime = now + self.samplesToSeconds(i + bufferSize * 2);
- return function () {
- self.tick(tickTime);
- };
- }(), 0);
- } else if (sample < 0 && upTick) {
- upTick = false;
+ Tone.Clock.prototype._loop = function (time) {
+ this._loopID = requestAnimationFrame(this._boundLoop);
+ if (this._lookAhead === 'auto') {
+ if (!this.isUndef(time)) {
+ var diff = (time - this._lastUpdate) / 1000;
+ this._lastUpdate = time;
+ if (diff < this._threshold) {
+ this._computedLookAhead = (9 * this._computedLookAhead + diff) / 10;
+ }
+ }
+ } else {
+ this._computedLookAhead = this._lookAhead;
+ }
+ var now = this.now();
+ var lookAhead = this._computedLookAhead * 2;
+ var event = this._state.getEvent(now + lookAhead);
+ var state = Tone.State.Stopped;
+ if (event) {
+ state = event.state;
+ 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) {
+ 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;
}
- this._upTick = upTick;
+ };
+ Tone.Clock.prototype.getStateAtTime = function (time) {
+ return this._state.getStateAtTime(time);
};
Tone.Clock.prototype.dispose = function () {
- this._jsNode.disconnect();
- this._controlSignal.dispose();
- if (this._oscillator) {
- this._oscillator.onended();
- this._oscillator.disconnect();
- }
- this._jsNode.onaudioprocess = function () {
- };
- this._jsNode = null;
- this._controlSignal = null;
- this._oscillator = null;
+ 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;
-}(Tone_core_Tone);
+}(Tone_core_Tone, Tone_signal_TimelineSignal);
var metro;
metro = function () {
'use strict';
@@ -6129,7 +7827,7 @@ metro = function () {
var ac = p5sound.audiocontext;
// var upTick = false;
p5.Metro = function () {
- this.clock = new Clock(ac.sampleRate, this.ontick.bind(this));
+ this.clock = new Clock({ 'callback': this.ontick.bind(this) });
this.syncedParts = [];
this.bpm = 120;
// gets overridden by p5.Part
@@ -6166,9 +7864,11 @@ metro = function () {
};
p5.Metro.prototype.setBPM = function (bpm, rampTime) {
var beatTime = 60 / (bpm * this.tatums);
+ var now = p5sound.audiocontext.currentTime;
tatumTime = beatTime;
- var ramp = rampTime || 0;
- this.clock.setRate(beatTime, rampTime + p5sound.audiocontext.currentTime);
+ var rampTime = rampTime || 0;
+ this.clock.frequency.setValueAtTime(this.clock.frequency.value, now);
+ this.clock.frequency.linearRampToValueAtTime(bpm, now + rampTime);
this.bpm = bpm;
};
p5.Metro.prototype.getBPM = function (tempo) {
@@ -6185,15 +7885,17 @@ metro = function () {
p5.Metro.prototype.pushSync = function (part) {
this.syncedParts.push(part);
};
- p5.Metro.prototype.start = function (time) {
- var t = time || 0;
- this.clock.start(t);
+ p5.Metro.prototype.start = function (timeFromNow) {
+ var t = timeFromNow || 0;
+ var now = p5sound.audiocontext.currentTime;
+ this.clock.start(now + t);
this.setBPM(this.bpm);
};
- p5.Metro.prototype.stop = function (time) {
- var t = time || 0;
+ p5.Metro.prototype.stop = function (timeFromNow) {
+ var t = timeFromNow || 0;
+ var now = p5sound.audiocontext.currentTime;
if (this.clock._oscillator) {
- this.clock.stop(t);
+ this.clock.stop(now + t);
}
};
p5.Metro.prototype.beatLength = function (tatums) {
@@ -6893,6 +8595,9 @@ soundRecorder = function () {
};
p5.SoundRecorder.prototype.dispose = function () {
this._clear();
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
this._callback = function () {
};
if (this.input) {
@@ -7261,6 +8966,8 @@ gain = function () {
// otherwise, Safari distorts
this.input.gain.value = 0.5;
this.input.connect(this.output);
+ // add to the soundArray
+ p5sound.soundArray.push(this);
};
/**
* Connect a source to the gain node.
@@ -7308,11 +9015,20 @@ gain = function () {
this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
};
+ p5.Gain.prototype.dispose = function () {
+ // remove reference from soundArray
+ var index = p5sound.soundArray.indexOf(this);
+ p5sound.soundArray.splice(index, 1);
+ this.output.disconnect();
+ this.input.disconnect();
+ this.output = undefined;
+ this.input = undefined;
+ };
}(master, sndcore);
var src_app;
src_app = function () {
'use strict';
var p5SOUND = sndcore;
return p5SOUND;
-}(sndcore, master, helpers, panner, soundfile, amplitude, fft, signal, oscillator, env, pulse, noise, audioin, filter, delay, reverb, metro, looper, soundRecorder, peakdetect, gain);
+}(sndcore, master, helpers, errorHandler, panner, soundfile, amplitude, fft, signal, oscillator, env, pulse, noise, audioin, filter, delay, reverb, metro, looper, soundRecorder, peakdetect, gain);
}));
\ No newline at end of file
diff --git a/public/mode_assets/p5/example_assets/moonwalk.jpg b/public/mode_assets/p5/example_assets/moonwalk.jpg
index c418e6f..36cc6c0 100644
Binary files a/public/mode_assets/p5/example_assets/moonwalk.jpg and b/public/mode_assets/p5/example_assets/moonwalk.jpg differ
diff --git a/public/mode_assets/p5/examples/Color/02_Brightness.js b/public/mode_assets/p5/examples/Color/02_Brightness.js
index 1f819fc..23089fe 100644
--- a/public/mode_assets/p5/examples/Color/02_Brightness.js
+++ b/public/mode_assets/p5/examples/Color/02_Brightness.js
@@ -13,23 +13,34 @@ function preload() {
function setup() {
createCanvas(720, 200);
+ pixelDensity(1);
img.loadPixels();
loadPixels();
}
function draw() {
- for (var x = 0; x < width; x++) {
- for (var y = 0; y < height; y++ ) {
+ for (var x = 0; x < img.width; x++) {
+ for (var y = 0; y < img.height; y++ ) {
// Calculate the 1D location from a 2D grid
- var loc = 4*(x + y*width);
- var r = img.pixels[loc];
+ var loc = (x + y*img.width)*4;
+ // Get the R,G,B values from image
+ var r,g,b;
+ r = img.pixels[loc];
+ // Calculate an amount to change brightness based on proximity to the mouse
var maxdist = 50;
var d = dist(x, y, mouseX, mouseY);
var adjustbrightness = 255*(maxdist-d)/maxdist;
r += adjustbrightness;
+ // Constrain RGB to make sure they are within 0-255 color range
r = constrain(r, 0, 255);
- pixels[4*(y*width + x)+3] = 255-r;
+ // Make a new color and set pixel in the window
+ //color c = color(r, g, b);
+ var pixloc = (y*width + x)*4;
+ pixels[pixloc] = r;
+ pixels[pixloc+1] = r;
+ pixels[pixloc+2] = r;
+ pixels[pixloc+3] = 255;
}
}
updatePixels();
-}
+}
\ No newline at end of file
diff --git a/public/mode_assets/p5/examples/Color/07_Lerp_Color.js b/public/mode_assets/p5/examples/Color/07_Lerp_Color.js
new file mode 100644
index 0000000..f9f9198
--- /dev/null
+++ b/public/mode_assets/p5/examples/Color/07_Lerp_Color.js
@@ -0,0 +1,41 @@
+/*
+ * @name Lerp Color
+ * @description Loop random shapes,
+ * lerp color from red to blue.
+ */
+function setup() {
+ createCanvas(720, 400);
+ background(255);
+ noStroke();
+}
+
+function draw() {
+ background(255);
+ from = color(255, 0, 0, 0.2 * 255);
+ to = color(0, 0, 255, 0.2 * 255);
+ c1 = lerpColor(from, to, .33);
+ c2 = lerpColor(from, to, .66);
+ for (var i = 0; i < 15; i++) {
+ fill(from);
+ quad(random(-40, 220), random(height),
+ random(-40, 220), random(height),
+ random(-40, 220), random(height),
+ random(-40, 220), random(height));
+ fill(c1);
+ quad(random(140, 380), random(height),
+ random(140, 380), random(height),
+ random(140, 380), random(height),
+ random(140, 380), random(height));
+ fill(c2);
+ quad(random(320, 580), random(height),
+ random(320, 580), random(height),
+ random(320, 580), random(height),
+ random(320, 580), random(height));
+ fill(to);
+ quad(random(500, 760), random(height),
+ random(500, 760), random(height),
+ random(500, 760), random(height),
+ random(500, 760), random(height));
+ }
+ frameRate(5);
+}
\ No newline at end of file
diff --git a/public/mode_assets/p5/examples/Form/07_3D_Primitives.js b/public/mode_assets/p5/examples/Form/07_3D_Primitives.js
new file mode 100644
index 0000000..2585689
--- /dev/null
+++ b/public/mode_assets/p5/examples/Form/07_3D_Primitives.js
@@ -0,0 +1,29 @@
+/*
+ * @name 3D Primitives
+ * @frame 720,400 (optional)
+ * @description Placing mathematically 3D objects in synthetic space.
+ * The box() and sphere() functions take at least one parameter to specify their
+ * size. These shapes are positioned using the translate() function.
+ */
+function setup() {
+ createCanvas(710, 400, WEBGL);
+}
+
+function draw() {
+ background(100);
+ noStroke();
+
+ push();
+ translate(-300, 200);
+ rotateY(1.25);
+ rotateX(-0.9);
+ box(100);
+ pop();
+
+ noFill();
+ stroke(255);
+ push();
+ translate(500, height*0.35, -200);
+ sphere(300);
+ pop();
+}
\ No newline at end of file
diff --git a/public/mode_assets/p5/examples/Lights/02_Directional.js b/public/mode_assets/p5/examples/Lights/02_Directional.js
new file mode 100644
index 0000000..0473b98
--- /dev/null
+++ b/public/mode_assets/p5/examples/Lights/02_Directional.js
@@ -0,0 +1,27 @@
+/*
+ * @name Directional
+ * @frame 710,400
+ * @description Move the mouse the change the direction of the light.
+ * Directional light comes from one direction and is stronger when hitting a
+ * surface squarely and weaker if it hits at a a gentle angle. After hitting a
+ * surface, a directional lights scatters in all directions.
+ */
+var radius = 200;
+
+function setup() {
+ createCanvas(710, 400, WEBGL);
+ noStroke();
+ fill(200);
+}
+
+function draw() {
+ noStroke();
+ background(0);
+ var dirY = (mouseY / height - 0.5) * 4;
+ var dirX = (mouseX / width - 0.5) * 4;
+ directionalLight(204, 204, 204, dirX, dirY, 1);
+ translate(-1.5 * radius, 0, 0);
+ sphere(radius);
+ translate(3 * radius, 0, 0);
+ sphere(radius);
+}
\ No newline at end of file
diff --git a/public/mode_assets/p5/examples/Lights/05_Mixture.js b/public/mode_assets/p5/examples/Lights/05_Mixture.js
new file mode 100644
index 0000000..51ceee0
--- /dev/null
+++ b/public/mode_assets/p5/examples/Lights/05_Mixture.js
@@ -0,0 +1,26 @@
+/*
+ * @name Mixture
+ * @frame 710,400 (optional)
+ * @description Display a box with three different kinds of lights.
+ */
+function setup() {
+ createCanvas(710, 400, WEBGL);
+ noStroke();
+}
+
+function draw() {
+ background(0);
+
+ // Orange point light on the right
+ pointLight(150, 100, 0, 500, 0, 200);
+
+ // Blue directional light from the left
+ directionalLight(0, 102, 255, -1, 0, 0);
+
+ // Yellow spotlight from the front
+ pointLight(255, 255, 109, 0, 0, 300);
+
+ rotateY(map(mouseX, 0, width, 0, PI));
+ rotateX(map(mouseY, 0, height, 0, PI));
+ box(200);
+}
\ No newline at end of file
diff --git a/public/mode_assets/p5/examples/Math/14_noisewave.js b/public/mode_assets/p5/examples/Math/14_noisewave.js
index 4c383a5..846f04f 100644
--- a/public/mode_assets/p5/examples/Math/14_noisewave.js
+++ b/public/mode_assets/p5/examples/Math/14_noisewave.js
@@ -17,7 +17,7 @@ function draw() {
beginShape();
var xoff = 0; // Option #1: 2D Noise
- // float xoff = yoff; // Option #2: 1D Noise
+ // var xoff = yoff; // Option #2: 1D Noise
// Iterate over horizontal pixels
for (var x = 0; x <= width; x += 10) {
@@ -27,7 +27,7 @@ function draw() {
var y = map(noise(xoff, yoff), 0, 1, 200,300);
// Option #2: 1D Noise
- // float y = map(noise(xoff), 0, 1, 200,300);
+ // var y = map(noise(xoff), 0, 1, 200,300);
// Set the vertex
vertex(x, y);
@@ -39,4 +39,4 @@ function draw() {
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
-}
\ No newline at end of file
+}
diff --git a/public/mode_assets/p5/examples/Simulate/10_SoftBody.js b/public/mode_assets/p5/examples/Simulate/10_SoftBody.js
new file mode 100644
index 0000000..33f2e1a
--- /dev/null
+++ b/public/mode_assets/p5/examples/Simulate/10_SoftBody.js
@@ -0,0 +1,110 @@
+/*
+ * @name Soft Body
+ * @description Original example by Ira Greenberg.
+ *
Softbody dynamics simulation using curveVertex() and curveTightness().
+ */
+// center point
+var centerX = 0.0, centerY = 0.0;
+
+var radius = 45, rotAngle = -90;
+var accelX = 0.0, accelY = 0.0;
+var deltaX = 0.0, deltaY = 0.0;
+var springing = 0.0009, damping = 0.98;
+
+//corner nodes
+var nodes = 5;
+
+//zero fill arrays
+var nodeStartX = [];
+var nodeStartY = [];
+var nodeX = [];
+var nodeY = [];
+var angle = [];
+var frequency = [];
+
+// soft-body dynamics
+var organicConstant = 1.0;
+
+function setup() {
+ createCanvas(710, 400);
+
+ //center shape in window
+ centerX = width/2;
+ centerY = height/2;
+
+ //initialize arrays to 0
+ for (var i=0; i