Methods and properties shown in black are part of the p5.js core, items in
- * blue are part of the p5.dom library. You will need to include an extra file
- * in order to access the blue functions. See the
- * using a library
- * section for information on how to include this library. p5.dom comes with
- * p5 complete or you can download the single file
- *
- * here.
- *
- * @module p5.dom
- * @submodule p5.dom
- * @for p5.dom
- * @main
- */
-
-(function (root, factory) {
- if (typeof define === 'function' && define.amd)
- define('p5.dom', ['p5'], function (p5) { (factory(p5));});
- else if (typeof exports === 'object')
- factory(require('../p5'));
- else
- factory(root['p5']);
-}(this, function (p5) {
-
-// =============================================================================
-// p5 additions
-// =============================================================================
-
- /**
- * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.'
- * prefixes to specify an ID or class respectively, and none for a tag) and returns it as
- * a p5.Element. If a class or tag name is given with more than 1 element,
- * only the first element will be returned.
- * The DOM node itself can be accessed with .elt.
- * Returns null if none found. You can also specify a container to search within.
- *
- * @method select
- * @param {String} name id, class, or tag name of element to search for
- * @param {String} [container] id, p5.Element, or HTML element to search within
- * @return {Object/p5.Element|Null} p5.Element containing node found
- * @example
- *
- * // these are all valid calls to select()
- * var a = select('#moo');
- * var b = select('#blah', '#myContainer');
- * var c = select('#foo', b);
- * var d = document.getElementById('beep');
- * var e = select('p', d);
- *
- *
- */
- p5.prototype.select = function (e, p) {
- var res = null;
- var container = getContainer(p);
- if (e[0] === '.'){
- e = e.slice(1);
- res = container.getElementsByClassName(e);
- if (res.length) {
- res = res[0];
- } else {
- res = null;
- }
- }else if (e[0] === '#'){
- e = e.slice(1);
- res = container.getElementById(e);
- }else {
- res = container.getElementsByTagName(e);
- if (res.length) {
- res = res[0];
- } else {
- res = null;
- }
- }
- if (res) {
- return wrapElement(res);
- } else {
- return null;
- }
- };
-
- /**
- * Searches the page for elements with the given class or tag name (using the '.' prefix
- * to specify a class and no prefix for a tag) and returns them as p5.Elements
- * in an array.
- * The DOM node itself can be accessed with .elt.
- * Returns an empty array if none found.
- * You can also specify a container to search within.
- *
- * @method selectAll
- * @param {String} name class or tag name of elements to search for
- * @param {String} [container] id, p5.Element, or HTML element to search within
- * @return {Array} Array of p5.Elements containing nodes found
- * @example
- *
- * function setup() {
- * createButton('btn');
- * createButton('2nd btn');
- * createButton('3rd btn');
- * var buttons = selectAll('button');
- *
- * for (var i = 0; i < buttons.length; i++){
- * buttons[i].size(100,100);
- * }
- * }
- *
- *
- * // these are all valid calls to selectAll()
- * var a = selectAll('.moo');
- * var b = selectAll('div');
- * var c = selectAll('button', '#myContainer');
- * var d = select('#container');
- * var e = selectAll('p', d);
- * var f = document.getElementById('beep');
- * var g = select('.blah', f);
- *
- *
- */
- p5.prototype.selectAll = function (e, p) {
- var arr = [];
- var res;
- var container = getContainer(p);
- if (e[0] === '.'){
- e = e.slice(1);
- res = container.getElementsByClassName(e);
- } else {
- res = container.getElementsByTagName(e);
- }
- if (res) {
- for (var j = 0; j < res.length; j++) {
- var obj = wrapElement(res[j]);
- arr.push(obj);
- }
- }
- return arr;
- };
-
- /**
- * Helper function for select and selectAll
- */
- function getContainer(p) {
- var container = document;
- if (typeof p === 'string' && p[0] === '#'){
- p = p.slice(1);
- container = document.getElementById(p) || document;
- } else if (p instanceof p5.Element){
- container = p.elt;
- } else if (p instanceof HTMLElement){
- container = p;
- }
- return container;
- }
-
- /**
- * Helper function for getElement and getElements.
- */
- function wrapElement(elt) {
- if(elt.tagName === "INPUT" && elt.type === "checkbox") {
- var converted = new p5.Element(elt);
- converted.checked = function(){
- if (arguments.length === 0){
- return this.elt.checked;
- } else if(arguments[0]) {
- this.elt.checked = true;
- } else {
- this.elt.checked = false;
- }
- return this;
- };
- return converted;
- } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") {
- return new p5.MediaElement(elt);
- } else {
- return new p5.Element(elt);
- }
- }
-
- /**
- * Removes all elements created by p5, except any canvas / graphics
- * elements created by createCanvas or createGraphics.
- * Event handlers are removed, and element is removed from the DOM.
- * @method removeElements
- * @example
- *
- * function setup() {
- * createCanvas(100, 100);
- * createDiv('this is some text');
- * createP('this is a paragraph');
- * }
- * function mousePressed() {
- * removeElements(); // this will remove the div and p, not canvas
- * }
- *
- *
- */
- p5.prototype.removeElements = function (e) {
- for (var i=0; i
- * var myDiv;
- * function setup() {
- * myDiv = createDiv('this is some text');
- * }
- *
- */
-
- /**
- * Creates a <p></p> element in the DOM with given inner HTML. Used
- * for paragraph length text.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createP
- * @param {String} html inner HTML for element created
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var myP;
- * function setup() {
- * myP = createP('this is some text');
- * }
- *
- */
-
- /**
- * Creates a <span></span> element in the DOM with given inner HTML.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createSpan
- * @param {String} html inner HTML for element created
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var mySpan;
- * function setup() {
- * mySpan = createSpan('this is some text');
- * }
- *
- */
- var tags = ['div', 'p', 'span'];
- tags.forEach(function(tag) {
- var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1);
- p5.prototype[method] = function(html) {
- var elt = document.createElement(tag);
- elt.innerHTML = typeof html === undefined ? "" : html;
- return addElement(elt, this);
- }
- });
-
- /**
- * Creates an <img> element in the DOM with given src and
- * alternate text.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createImg
- * @param {String} src src path or url for image
- * @param {String} [alt] alternate text to be used if image does not load
- * @param {Function} [successCallback] callback to be called once image data is loaded
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var img;
- * function setup() {
- * img = createImg('http://p5js.org/img/asterisk-01.png');
- * }
- *
- */
- p5.prototype.createImg = function() {
- var elt = document.createElement('img');
- var args = arguments;
- var self;
- var setAttrs = function(){
- self.width = elt.offsetWidth || elt.width;
- self.height = elt.offsetHeight || elt.height;
- if (args.length > 1 && typeof args[1] === 'function'){
- self.fn = args[1];
- self.fn();
- }else if (args.length > 1 && typeof args[2] === 'function'){
- self.fn = args[2];
- self.fn();
- }
- };
- elt.src = args[0];
- if (args.length > 1 && typeof args[1] === 'string'){
- elt.alt = args[1];
- }
- elt.onload = function(){
- setAttrs();
- }
- self = addElement(elt, this);
- return self;
- };
-
- /**
- * Creates an <a></a> element in the DOM for including a hyperlink.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createA
- * @param {String} href url of page to link to
- * @param {String} html inner html of link element to display
- * @param {String} [target] target where new link should open,
- * could be _blank, _self, _parent, _top.
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var myLink;
- * function setup() {
- * myLink = createA('http://p5js.org/', 'this is a link');
- * }
- *
- */
- p5.prototype.createA = function(href, html, target) {
- var elt = document.createElement('a');
- elt.href = href;
- elt.innerHTML = html;
- if (target) elt.target = target;
- return addElement(elt, this);
- };
-
- /** INPUT **/
-
-
- /**
- * Creates a slider <input></input> element in the DOM.
- * Use .size() to set the display length of the slider.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createSlider
- * @param {Number} min minimum value of the slider
- * @param {Number} max maximum value of the slider
- * @param {Number} [value] default value of the slider
- * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value)
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var slider;
- * function setup() {
- * slider = createSlider(0, 255, 100);
- * slider.position(10, 10);
- * slider.style('width', '80px');
- * }
- *
- * function draw() {
- * var val = slider.value();
- * background(val);
- * }
- *
- */
- p5.prototype.createButton = function(label, value) {
- var elt = document.createElement('button');
- elt.innerHTML = label;
- elt.value = value;
- if (value) elt.value = value;
- return addElement(elt, this);
- };
-
- /**
- * Creates a checkbox <input></input> element in the DOM.
- * Calling .checked() on a checkbox returns if it is checked or not
- *
- * @method createCheckbox
- * @param {String} [label] label displayed after checkbox
- * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- */
- p5.prototype.createSelect = function(mult) {
- var elt = document.createElement('select');
- if (mult){
- elt.setAttribute('multiple', 'true');
- }
- var self = addElement(elt, this);
- self.option = function(name, value){
- var opt = document.createElement('option');
- opt.innerHTML = name;
- if (arguments.length > 1)
- opt.value = value;
- else
- opt.value = name;
- elt.appendChild(opt);
- };
- self.selected = function(value){
- var arr = [];
- if (arguments.length > 0){
- for (var i = 0; i < this.elt.length; i++){
- if (value.toString() === this.elt[i].value){
- this.elt.selectedIndex = i;
- }
- }
- return this;
- }else{
- if (mult){
- for (var i = 0; i < this.elt.selectedOptions.length; i++){
- arr.push(this.elt.selectedOptions[i].value);
- }
- return arr;
- }else{
- return this.elt.value;
- }
- }
- };
- return self;
- };
-
- /**
- * Creates a radio button <input></input> element in the DOM.
- * The .option() method can be used to set options for the radio after it is
- * created. The .value() method will return the currently selected option.
- *
- * @method createRadio
- * @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
- * @example
- *
- * var radio;
- *
- * function setup() {
- * radio = createRadio();
- * radio.option('apple', 1);
- * radio.option('bread', 2);
- * radio.option('juice', 3);
- * radio.style('width', '60px');
- * textAlign(CENTER);
- * }
- *
- * function draw() {
- * background(200);
- * var val = radio.value();
- * if (val) {
- * text('item cost is $'+val, width/2, height/2);
- * }
- * }
- *
- */
- p5.prototype.createRadio = function() {
- var radios = document.querySelectorAll("input[type=radio]");
- var count = 0;
- if(radios.length > 1){
- var length = radios.length;
- var prev=radios[0].name;
- var current = radios[1].name;
- count = 1;
- for(var i = 1; i < length; i++) {
- current = radios[i].name;
- if(prev != current){
- count++;
- }
- prev = current;
- }
- }
- else if (radios.length == 1){
- count = 1;
- }
- var elt = document.createElement('div');
- var self = addElement(elt, this);
- var times = -1;
- self.option = function(name, value){
- var opt = document.createElement('input');
- opt.type = 'radio';
- opt.innerHTML = name;
- if (arguments.length > 1)
- opt.value = value;
- else
- opt.value = name;
- opt.setAttribute('name',"defaultradio"+count);
- elt.appendChild(opt);
- if (name){
- times++;
- var ran = Math.random().toString(36).slice(2);
- var label = document.createElement('label');
- opt.setAttribute('id', "defaultradio"+count+"-"+times);
- label.htmlFor = "defaultradio"+count+"-"+times;
- label.appendChild(document.createTextNode(name));
- elt.appendChild(label);
- }
- return opt;
- };
- self.selected = function(){
- var length = this.elt.childNodes.length;
- if(arguments.length == 1) {
- for (var i = 0; i < length; i+=2){
- if(this.elt.childNodes[i].value == arguments[0])
- this.elt.childNodes[i].checked = true;
- }
- return this;
- } else {
- for (var i = 0; i < length; i+=2){
- if(this.elt.childNodes[i].checked == true)
- return this.elt.childNodes[i].value;
- }
- }
- };
- self.value = function(){
- var length = this.elt.childNodes.length;
- if(arguments.length == 1) {
- for (var i = 0; i < length; i+=2){
- if(this.elt.childNodes[i].value == arguments[0])
- this.elt.childNodes[i].checked = true;
- }
- return this;
- } else {
- for (var i = 0; i < length; i+=2){
- if(this.elt.childNodes[i].checked == true)
- return this.elt.childNodes[i].value;
- }
- return "";
- }
- };
- return self
- };
-
- /**
- * Creates an <input></input> element in the DOM for text input.
- * Use .size() to set the display length of the box.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createInput
- * @param {Number} [value] default value of the input box
- * @param {String} [type] type of text, ie text, password etc. Defaults to text
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * function setup(){
- * var inp = createInput('');
- * inp.input(myInputEvent);
- * }
- *
- * function myInputEvent(){
- * console.log('you are typing: ', this.value());
- * }
- *
- *
- */
- p5.prototype.createInput = function(value, type) {
- var elt = document.createElement('input');
- elt.type = type ? type : 'text';
- if (value) elt.value = value;
- return addElement(elt, this);
- };
-
- /**
- * Creates an <input></input> element in the DOM of type 'file'.
- * This allows users to select local files for use in a sketch.
- *
- * @method createFileInput
- * @param {Function} [callback] callback function for when a file loaded
- * @param {String} [multiple] optional to allow multiple files selected
- * @return {Object/p5.Element} pointer to p5.Element holding created DOM element
- * @example
- * var input;
- * var img;
- *
- * function setup() {
- * input = createFileInput(handleFile);
- * input.position(0, 0);
- * }
- *
- * function draw() {
- * if (img) {
- * image(img, 0, 0, width, height);
- * }
- * }
- *
- * function handleFile(file) {
- * print(file);
- * if (file.type === 'image') {
- * img = createImg(file.data);
- * img.hide();
- * }
- * }
- */
- p5.prototype.createFileInput = function(callback, multiple) {
-
- // Is the file stuff supported?
- if (window.File && window.FileReader && window.FileList && window.Blob) {
- // Yup, we're ok and make an input file selector
- var elt = document.createElement('input');
- elt.type = 'file';
-
- // If we get a second argument that evaluates to true
- // then we are looking for multiple files
- if (multiple) {
- // Anything gets the job done
- elt.multiple = 'multiple';
- }
-
- // Function to handle when a file is selected
- // We're simplifying life and assuming that we always
- // want to load every selected file
- function handleFileSelect(evt) {
- // These are the files
- var files = evt.target.files;
- // Load each one and trigger a callback
- for (var i = 0; i < files.length; i++) {
- var f = files[i];
- var reader = new FileReader();
- function makeLoader(theFile) {
- // Making a p5.File object
- var p5file = new p5.File(theFile);
- return function(e) {
- p5file.data = e.target.result;
- callback(p5file);
- };
- };
- reader.onload = makeLoader(f);
-
- // Text or data?
- // This should likely be improved
- if (f.type.indexOf('text') > -1) {
- reader.readAsText(f);
- } else {
- reader.readAsDataURL(f);
- }
- }
- }
-
- // Now let's handle when a file was selected
- elt.addEventListener('change', handleFileSelect, false);
- return addElement(elt, this);
- } else {
- console.log('The File APIs are not fully supported in this browser. Cannot create element.');
- }
- };
-
-
- /** VIDEO STUFF **/
-
- function createMedia(pInst, type, src, callback) {
- var elt = document.createElement(type);
-
- // allow src to be empty
- var src = src || '';
- if (typeof src === 'string') {
- src = [src];
- }
- for (var i=0; ithis
- * page for further information about supported formats.
- *
- * @method createVideo
- * @param {String|Array} src path to a video file, or array of paths for
- * supporting different browsers
- * @param {Object} [callback] callback function to be called upon
- * 'canplaythrough' event fire, that is, when the
- * browser can play the media, and estimates that
- * enough data has been loaded to play the media
- * up to its end without having to stop for
- * further buffering of content
- * @return {Object/p5.Element} pointer to video p5.Element
- */
- p5.prototype.createVideo = function(src, callback) {
- return createMedia(this, 'video', src, callback);
- };
-
- /** AUDIO STUFF **/
-
- /**
- * Creates a hidden HTML5 <audio> element in the DOM for simple audio
- * playback. Appends to the container node if one is specified,
- * otherwise appends to body. The first parameter
- * can be either a single string path to a audio file, or an array of string
- * paths to different formats of the same audio. This is useful for ensuring
- * that your audio can play across different browsers, as each supports
- * different formats. See this
- * page for further information about supported formats.
- *
- * @method createAudio
- * @param {String|Array} src path to an audio file, or array of paths for
- * supporting different browsers
- * @param {Object} [callback] callback function to be called upon
- * 'canplaythrough' event fire, that is, when the
- * browser can play the media, and estimates that
- * enough data has been loaded to play the media
- * up to its end without having to stop for
- * further buffering of content
- * @return {Object/p5.Element} pointer to audio p5.Element
- */
- p5.prototype.createAudio = function(src, callback) {
- return createMedia(this, 'audio', src, callback);
- };
-
-
- /** CAMERA STUFF **/
-
- p5.prototype.VIDEO = 'video';
- p5.prototype.AUDIO = 'audio';
-
- navigator.getUserMedia = navigator.getUserMedia ||
- navigator.webkitGetUserMedia ||
- navigator.mozGetUserMedia ||
- navigator.msGetUserMedia;
-
- /**
- *
Creates a new <video> element that contains the audio/video feed
- * from a webcam. This can be drawn onto the canvas using video().
- *
More specific properties of the feed can be passing in a Constraints object.
- * See the
- * W3C
- * spec for possible properties. Note that not all of these are supported
- * by all browsers.
- *
Security note: A new browser security specification requires that getUserMedia,
- * which is behind createCapture(), only works when you're running the code locally,
- * or on HTTPS. Learn more here
- * and here.
- *
- * @method createCapture
- * @param {String|Constant|Object} type type of capture, either VIDEO or
- * AUDIO if none specified, default both,
- * or a Constraints object
- * @param {Function} callback function to be called once
- * stream has loaded
- * @return {Object/p5.Element} capture video p5.Element
- * @example
- *
- */
- p5.prototype.createCapture = function() {
- var useVideo = true;
- var useAudio = true;
- var constraints;
- var cb;
- for (var i=0; i
- * var h2 = createElement('h2','im an h2 p5.element!');
- *
- */
- p5.prototype.createElement = function(tag, content) {
- var elt = document.createElement(tag);
- if (typeof content !== 'undefined') {
- elt.innerHTML = content;
- }
- return addElement(elt, this);
- };
-
-
-// =============================================================================
-// p5.Element additions
-// =============================================================================
- /**
- *
- * Adds specified class to the element.
- *
- * @for p5.Element
- * @method addClass
- * @param {String} class name of class to add
- * @return {Object/p5.Element}
- * @example
- *
- * var div = createDiv('div');
- * div.addClass('myClass');
- *
- */
- p5.Element.prototype.addClass = function(c) {
- if (this.elt.className) {
- // PEND don't add class more than once
- //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
- //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
- this.elt.className = this.elt.className+' '+c;
- //}
- } else {
- this.elt.className = c;
- }
- return this;
- }
-
- /**
- *
- * Removes specified class from the element.
- *
- * @method removeClass
- * @param {String} class name of class to remove
- * @return {Object/p5.Element}
- */
- p5.Element.prototype.removeClass = function(c) {
- var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
- this.elt.className = this.elt.className.replace(regex, '');
- this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
- return this;
- }
-
- /**
- *
- * Attaches the element as a child to the parent specified.
- * Accepts either a string ID, DOM node, or p5.Element.
- * If no argument is specified, an array of children DOM nodes is returned.
- *
- * @method child
- * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
- * to add to the current element
- * @return {p5.Element}
- * @example
- *
- * var div0 = createDiv('this is the parent');
- * var div1 = createDiv('this is the child');
- * div0.child(div1); // use p5.Element
- *
- *
- * var div0 = createDiv('this is the parent');
- * var div1 = createDiv('this is the child');
- * div1.id('apples');
- * div0.child('apples'); // use id
- *
- *
- * var div0 = createDiv('this is the parent');
- * var elt = document.getElementById('myChildDiv');
- * div0.child(elt); // use element from page
- *
- */
- p5.Element.prototype.child = function(c) {
- if (typeof c === 'undefined'){
- return this.elt.childNodes
- }
- if (typeof c === 'string') {
- if (c[0] === '#') {
- c = c.substring(1);
- }
- c = document.getElementById(c);
- } else if (c instanceof p5.Element) {
- c = c.elt;
- }
- this.elt.appendChild(c);
- return this;
- };
-
- /**
- * Centers a p5 Element either vertically, horizontally,
- * or both, relative to its parent or according to
- * the body if the Element has no parent. If no argument is passed
- * the Element is aligned both vertically and horizontally.
- *
- * @param {String} align passing 'vertical', 'horizontal' aligns element accordingly
- * @return {Object/p5.Element} pointer to p5.Element
- * @example
- *
- * function setup() {
- * var div = createDiv('').size(10,10);
- * div.style('background-color','orange');
- * div.center();
- *
- * }
- *
- */
- p5.Element.prototype.center = function(align) {
- var style = this.elt.style.display;
- var hidden = this.elt.style.display === 'none';
- var parentHidden = this.parent().style.display === 'none';
- var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
-
- if (hidden) this.show();
-
- this.elt.style.display = 'block';
- this.position(0,0);
-
- if (parentHidden) this.parent().style.display = 'block';
-
- var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
- var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
- var y = pos.y;
- var x = pos.x;
-
- if (align === 'both' || align === undefined){
- this.position(wOffset/2, hOffset/2);
- }else if (align === 'horizontal'){
- this.position(wOffset/2, y);
- }else if (align === 'vertical'){
- this.position(x, hOffset/2);
- }
-
- this.style('display', style);
-
- if (hidden) this.hide();
-
- if (parentHidden) this.parent().style.display = 'none';
-
- return this;
- };
-
- /**
- *
- * If an argument is given, sets the inner HTML of the element,
- * replacing any existing html. If true is included as a second
- * argument, html is appended instead of replacing existing html.
- * If no arguments are given, returns
- * the inner HTML of the element.
- *
- * @for p5.Element
- * @method html
- * @param {String} [html] the HTML to be placed inside the element
- * @param {boolean} [append] whether to append HTML to existing
- * @return {Object/p5.Element|String}
- * @example
- *
- * var div = createDiv('').size(100,100);
- * div.html('hi');
- *
- *
- * var div = createDiv('Hello ').size(100,100);
- * div.html('World', true);
- *
- */
- p5.Element.prototype.html = function() {
- if (arguments.length === 0) {
- return this.elt.innerHTML;
- } else if (arguments[1]) {
- this.elt.innerHTML += arguments[0];
- return this;
- } else {
- this.elt.innerHTML = arguments[0];
- return this;
- }
- };
-
- /**
- *
- * Sets the position of the element relative to (0, 0) of the
- * window. Essentially, sets position:absolute and left and top
- * properties of style. If no arguments given returns the x and y position
- * of the element in an object.
- *
- * @method position
- * @param {Number} [x] x-position relative to upper left of window
- * @param {Number} [y] y-position relative to upper left of window
- * @return {Object/p5.Element}
- * @example
- *
- * function setup() {
- * var cnv = createCanvas(100, 100);
- * // positions canvas 50px to the right and 100px
- * // below upper left corner of the window
- * cnv.position(50, 100);
- * }
- *
- */
- p5.Element.prototype.position = function() {
- if (arguments.length === 0){
- return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
- }else{
- this.elt.style.position = 'absolute';
- this.elt.style.left = arguments[0]+'px';
- this.elt.style.top = arguments[1]+'px';
- this.x = arguments[0];
- this.y = arguments[1];
- return this;
- }
- };
-
- /* Helper method called by p5.Element.style() */
- p5.Element.prototype._translate = function(){
- this.elt.style.position = 'absolute';
- // 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)';
- } else if (arguments.length > 2) {
- this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
- if (arguments.length === 3) {
- this.elt.parentElement.style.perspective = '1000px';
- } else {
- this.elt.parentElement.style.perspective = arguments[3]+'px';
- }
- }
- // add any extra transform styling back on end
- this.elt.style.transform += transform;
- return this;
- };
-
- /* 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){
- this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
- }else if (arguments.length === 2){
- this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
- }else if (arguments.length === 3){
- this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
- this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
- this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
- }
- // add remaining transform back on
- this.elt.style.transform += transform;
- return this;
- };
-
- /**
- * Sets the given style (css) property (1st arg) of the element with the
- * given value (2nd arg). If a single argument is given, .style()
- * returns the value of the given property; however, if the single argument
- * is given in css syntax ('text-align:center'), .style() sets the css
- * appropriatly. .style() also handles 2d and 3d css transforms. If
- * the 1st arg is 'rotate', 'translate', or 'position', the following arguments
- * accept Numbers as values. ('translate', 10, 100, 50);
- *
- * @method style
- * @param {String} property property to be set
- * @param {String|Number|p5.Color} [value] value to assign to property
- * @param {String|Number} [value] value to assign to property (rotate/translate)
- * @param {String|Number} [value] value to assign to property (rotate/translate)
- * @param {String|Number} [value] value to assign to property (translate)
- * @return {String|Object/p5.Element} value of property, if no value is specified
- * or p5.Element
- * @example
- *
- * var myDiv = createDiv("I like pandas.");
- * myDiv.style("font-size", "18px");
- * myDiv.style("color", "#ff0000");
- *
- *
- * var col = color(25,23,200,50);
- * 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);
- *
- */
- p5.Element.prototype.style = function(prop, val) {
- var self = this;
-
- if (val instanceof p5.Color) {
- val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')'
- }
-
- if (typeof val === 'undefined') {
- if (prop.indexOf(':') === -1) {
- var styles = window.getComputedStyle(self.elt);
- var style = styles.getPropertyValue(prop);
- return style;
- } else {
- var attrs = prop.split(';');
- for (var i = 0; i < attrs.length; i++) {
- var parts = attrs[i].split(':');
- if (parts[0] && parts[1]) {
- this.elt.style[parts[0].trim()] = parts[1].trim();
- }
- }
- }
- } else {
- if (prop === 'rotate' || prop === 'translate' || prop === 'position'){
- var trans = Array.prototype.shift.apply(arguments);
- var f = this[trans] || this['_'+trans];
- f.apply(this, arguments);
- } else {
- this.elt.style[prop] = val;
- if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
- var numVal = val.replace(/\D+/g, '');
- this[prop] = parseInt(numVal, 10); // pend: is this necessary?
- }
- }
- }
- return this;
- };
-
-
- /**
- *
- * Adds a new attribute or changes the value of an existing attribute
- * on the specified element. If no value is specified, returns the
- * value of the given attribute, or null if attribute is not set.
- *
- * @method attribute
- * @param {String} attr attribute to set
- * @param {String} [value] value to assign to attribute
- * @return {String|Object/p5.Element} value of attribute, if no value is
- * specified or p5.Element
- * @example
- *
- * var myDiv = createDiv("I like pandas.");
- * myDiv.attribute("align", "center");
- *
- */
- p5.Element.prototype.removeAttribute = function(attr) {
- this.elt.removeAttribute(attr);
- return this;
- };
-
-
- /**
- * Either returns the value of the element if no arguments
- * given, or sets the value of the element.
- *
- * @method value
- * @param {String|Number} [value]
- * @return {String|Object/p5.Element} value of element if no value is specified or p5.Element
- * @example
- *
- * // 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('this is a div');
- * div.hide();
- *
- */
- p5.Element.prototype.hide = function() {
- this.elt.style.display = 'none';
- return this;
- };
-
- /**
- *
- * Sets the width and height of the element. AUTO can be used to
- * only adjust one dimension. If no arguments given returns the width and height
- * of the element in an object.
- *
- * @method size
- * @param {Number} [w] width of the element
- * @param {Number} [h] height of the element
- * @return {Object/p5.Element}
- * @example
- *
- * var div = createDiv('this is a div');
- * div.size(100, 100);
- *
- */
- p5.Element.prototype.size = function(w, h) {
- if (arguments.length === 0){
- return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
- }else{
- var aW = w;
- var aH = h;
- var AUTO = p5.prototype.AUTO;
- if (aW !== AUTO || aH !== AUTO) {
- if (aW === AUTO) {
- aW = h * this.width / this.height;
- } else if (aH === AUTO) {
- aH = w * this.height / this.width;
- }
- // set diff for cnv vs normal div
- if (this.elt instanceof HTMLCanvasElement) {
- var j = {};
- var k = this.elt.getContext('2d');
- for (var prop in k) {
- j[prop] = k[prop];
- }
- this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
- this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
- this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
- this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
- for (var prop in j) {
- this.elt.getContext('2d')[prop] = j[prop];
- }
- } else {
- this.elt.style.width = aW+'px';
- this.elt.style.height = aH+'px';
- this.elt.width = aW;
- this.elt.height = aH;
- this.width = aW;
- this.height = aH;
- }
-
- this.width = this.elt.offsetWidth;
- this.height = this.elt.offsetHeight;
-
- if (this._pInst) { // main canvas associated with p5 instance
- if (this._pInst._curElement.elt === this.elt) {
- this._pInst._setProperty('width', this.elt.offsetWidth);
- this._pInst._setProperty('height', this.elt.offsetHeight);
- }
- }
- }
- return this;
- }
- };
-
- /**
- * Removes the element and deregisters all listeners.
- * @method remove
- * @example
- *
- * var myDiv = createDiv('this is some text');
- * myDiv.remove();
- *
- */
- p5.Element.prototype.remove = function() {
- // deregister events
- for (var ev in this._events) {
- this.elt.removeEventListener(ev, this._events[ev]);
- }
- if (this.elt.parentNode) {
- this.elt.parentNode.removeChild(this.elt);
- }
- delete(this);
- };
-
-
-
-// =============================================================================
-// p5.MediaElement additions
-// =============================================================================
-
-
- /**
- * Extends p5.Element to handle audio and video. In addition to the methods
- * of p5.Element, it also contains methods for controlling media. It is not
- * called directly, but p5.MediaElements are created by calling createVideo,
- * createAudio, and createCapture.
- *
- * @class p5.MediaElement
- * @constructor
- * @param {String} elt DOM node that is wrapped
- * @param {Object} [pInst] pointer to p5 instance
- */
- p5.MediaElement = function(elt, pInst) {
- p5.Element.call(this, elt, pInst);
-
- var self = this;
- this.elt.crossOrigin = 'anonymous';
-
- this._prevTime = 0;
- this._cueIDCounter = 0;
- this._cues = [];
- this._pixelDensity = 1;
-
- /**
- * Path to the media element source.
- *
- * @property src
- * @return {String} src
- */
- Object.defineProperty(self, 'src', {
- get: function() {
- var firstChildSrc = self.elt.children[0].src;
- var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
- var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc;
- return ret;
- },
- set: function(newValue) {
- for (var i = 0; i < self.elt.children.length; i++) {
- self.elt.removeChild(self.elt.children[i]);
- }
- var source = document.createElement('source');
- source.src = newValue;
- elt.appendChild(source);
- self.elt.src = newValue;
- },
- });
-
- // private _onended callback, set by the method: onended(callback)
- self._onended = function() {};
- self.elt.onended = function() {
- self._onended(self);
- }
- };
- p5.MediaElement.prototype = Object.create(p5.Element.prototype);
-
-
-
-
- /**
- * Play an HTML5 media element.
- *
- * @method play
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.play = function() {
- if (this.elt.currentTime === this.elt.duration) {
- this.elt.currentTime = 0;
- }
-
- if (this.elt.readyState > 1) {
- this.elt.play();
- } else {
- // in Chrome, playback cannot resume after being stopped and must reload
- this.elt.load();
- this.elt.play();
- }
- return this;
- };
-
- /**
- * Stops an HTML5 media element (sets current time to zero).
- *
- * @method stop
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.stop = function() {
- this.elt.pause();
- this.elt.currentTime = 0;
- return this;
- };
-
- /**
- * Pauses an HTML5 media element.
- *
- * @method pause
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.pause = function() {
- this.elt.pause();
- return this;
- };
-
- /**
- * Set 'loop' to true for an HTML5 media element, and starts playing.
- *
- * @method loop
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.loop = function() {
- this.elt.setAttribute('loop', true);
- this.play();
- return this;
- };
- /**
- * Set 'loop' to false for an HTML5 media element. Element will stop
- * when it reaches the end.
- *
- * @method noLoop
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.noLoop = function() {
- this.elt.setAttribute('loop', false);
- return this;
- };
-
-
- /**
- * Set HTML5 media element to autoplay or not.
- *
- * @method autoplay
- * @param {Boolean} autoplay whether the element should autoplay
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.autoplay = function(val) {
- this.elt.setAttribute('autoplay', val);
- return this;
- };
-
- /**
- * Sets volume for this HTML5 media element. If no argument is given,
- * returns the current volume.
- *
- * @param {Number} [val] volume between 0.0 and 1.0
- * @return {Number|p5.MediaElement} current volume or p5.MediaElement
- * @method volume
- */
- p5.MediaElement.prototype.volume = function(val) {
- if (typeof val === 'undefined') {
- return this.elt.volume;
- } else {
- this.elt.volume = val;
- }
- };
-
- /**
- * If no arguments are given, returns the current playback speed of the
- * element. The speed parameter sets the speed where 2.0 will play the
- * element twice as fast, 0.5 will play at half the speed, and -1 will play
- * the element in normal speed in reverse.(Note that not all browsers support
- * backward playback and even if they do, playback might not be smooth.)
- *
- * @method speed
- * @param {Number} [speed] speed multiplier for element playback
- * @return {Number|Object/p5.MediaElement} current playback speed or p5.MediaElement
- */
- p5.MediaElement.prototype.speed = function(val) {
- if (typeof val === 'undefined') {
- return this.elt.playbackRate;
- } else {
- this.elt.playbackRate = val;
- }
- };
-
- /**
- * If no arguments are given, returns the current time of the element.
- * If an argument is given the current time of the element is set to it.
- *
- * @method time
- * @param {Number} [time] time to jump to (in seconds)
- * @return {Number|Object/p5.MediaElement} current time (in seconds)
- * or p5.MediaElement
- */
- p5.MediaElement.prototype.time = function(val) {
- if (typeof val === 'undefined') {
- return this.elt.currentTime;
- } else {
- this.elt.currentTime = val;
- }
- };
-
- /**
- * Returns the duration of the HTML5 media element.
- *
- * @method duration
- * @return {Number} duration
- */
- p5.MediaElement.prototype.duration = function() {
- return this.elt.duration;
- };
- p5.MediaElement.prototype.pixels = [];
- p5.MediaElement.prototype.loadPixels = function() {
- if (!this.canvas) {
- this.canvas = document.createElement('canvas');
- this.drawingContext = this.canvas.getContext('2d');
- }
- if (this.loadedmetadata) { // wait for metadata for w/h
- if (this.canvas.width !== this.elt.width) {
- this.canvas.width = this.elt.width;
- this.canvas.height = this.elt.height;
- this.width = this.canvas.width;
- this.height = this.canvas.height;
- }
- this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
- p5.Renderer2D.prototype.loadPixels.call(this);
- }
- return this;
- }
- p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
- if (this.loadedmetadata) { // wait for metadata
- p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
- }
- return this;
- }
- p5.MediaElement.prototype.get = function(x, y, w, h){
- if (this.loadedmetadata) { // wait for metadata
- return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
- } else if (typeof x === 'undefined') {
- return new p5.Image(1, 1);
- } else if (w > 1) {
- return new p5.Image(x, y, w, h);
- } else {
- return [0, 0, 0, 255];
- }
- };
- p5.MediaElement.prototype.set = function(x, y, imgOrCol){
- if (this.loadedmetadata) { // wait for metadata
- p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
- }
- };
- p5.MediaElement.prototype.copy = function(){
- p5.Renderer2D.prototype.copy.apply(this, arguments);
- };
- p5.MediaElement.prototype.mask = function(){
- this.loadPixels();
- p5.Image.prototype.mask.apply(this, arguments);
- };
- /**
- * Schedule an event to be called when the audio or video
- * element reaches the end. If the element is looping,
- * this will not be called. The element is passed in
- * as the argument to the onended callback.
- *
- * @method onended
- * @param {Function} callback function to call when the
- * soundfile has ended. The
- * media element will be passed
- * in as the argument to the
- * callback.
- * @return {Object/p5.MediaElement}
- * @example
- *
- */
- p5.MediaElement.prototype.onended = function(callback) {
- this._onended = callback;
- return this;
- };
-
-
- /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
-
- /**
- * Send the audio output of this element to a specified audioNode or
- * p5.sound object. If no element is provided, connects to p5's master
- * output. That connection is established when this method is first called.
- * All connections are removed by the .disconnect() method.
- *
- * This method is meant to be used with the p5.sound.js addon library.
- *
- * @method connect
- * @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API,
- * or an object from the p5.sound library
- */
- p5.MediaElement.prototype.connect = function(obj) {
- var audioContext, masterOutput;
-
- // if p5.sound exists, same audio context
- if (typeof p5.prototype.getAudioContext === 'function') {
- audioContext = p5.prototype.getAudioContext();
- masterOutput = p5.soundOut.input;
- } else {
- try {
- audioContext = obj.context;
- masterOutput = audioContext.destination
- } catch(e) {
- throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
- }
- }
-
- // create a Web Audio MediaElementAudioSourceNode if none already exists
- if (!this.audioSourceNode) {
- this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
-
- // connect to master output when this method is first called
- this.audioSourceNode.connect(masterOutput);
- }
-
- // connect to object if provided
- if (obj) {
- if (obj.input) {
- this.audioSourceNode.connect(obj.input);
- } else {
- this.audioSourceNode.connect(obj);
- }
- }
-
- // otherwise connect to master output of p5.sound / AudioContext
- else {
- this.audioSourceNode.connect(masterOutput);
- }
-
- };
-
- /**
- * Disconnect all Web Audio routing, including to master output.
- * This is useful if you want to re-route the output through
- * audio effects, for example.
- *
- * @method disconnect
- */
- p5.MediaElement.prototype.disconnect = function() {
- if (this.audioSourceNode) {
- this.audioSourceNode.disconnect();
- } else {
- throw 'nothing to disconnect';
- }
- };
-
-
- /*** SHOW / HIDE CONTROLS ***/
-
- /**
- * Show the default MediaElement controls, as determined by the web browser.
- *
- * @method showControls
- */
- p5.MediaElement.prototype.showControls = function() {
- // must set style for the element to show on the page
- this.elt.style['text-align'] = 'inherit';
- this.elt.controls = true;
- };
-
- /**
- * Hide the default mediaElement controls.
- *
- * @method hideControls
- */
- p5.MediaElement.prototype.hideControls = function() {
- this.elt.controls = false;
- };
-
- /*** SCHEDULE EVENTS ***/
-
- /**
- * Schedule events to trigger every time a MediaElement
- * (audio/video) reaches a playback cue point.
- *
- * Accepts a callback function, a time (in seconds) at which to trigger
- * the callback, and an optional parameter for the callback.
- *
- * Time will be passed as the first parameter to the callback function,
- * and param will be the second parameter.
- *
- *
- * @method addCue
- * @param {Number} time Time in seconds, relative to this media
- * element's playback. For example, to trigger
- * an event every time playback reaches two
- * seconds, pass in the number 2. This will be
- * passed as the first parameter to
- * the callback function.
- * @param {Function} callback Name of a function that will be
- * called at the given time. The callback will
- * receive time and (optionally) param as its
- * two parameters.
- * @param {Object} [value] An object to be passed as the
- * second parameter to the
- * callback function.
- * @return {Number} id ID of this cue,
- * useful for removeCue(id)
- * @example
- *
This method is asynchronous, meaning it may not finish before the next
- * line in your sketch is executed. Calling loadTable() 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:
- *
- *
- * @method loadTable
- * @param {String} filename name of the file or URL to load
- * @param {String|Strings} [options] "header" "csv" "tsv"
- * @param {Function} [callback] function to be executed after
- * loadTable() completes. On success, the
- * Table object is passed in as the
- * first argument; otherwise, false
- * is passed in.
- * @return {Object} Table object containing data
- *
- * @example
- *
- *
- * // Given the following CSV file called "mammals.csv"
- * // located in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * //the file can be remote
- * //table = loadTable("http://p5js.org/reference/assets/mammals.csv",
- * // "csv", "header");
- * }
- *
- * function setup() {
- * //count the columns
- * print(table.getRowCount() + " total rows in table");
- * print(table.getColumnCount() + " total columns in table");
- *
- * print(table.getColumn("name"));
- * //["Goat", "Leopard", "Zebra"]
- *
- * //cycle through the table
- * for (var r = 0; r < table.getRowCount(); r++)
- * for (var c = 0; c < table.getColumnCount(); c++) {
- * print(table.getString(r, c));
- * }
- * }
- *
- *
- *
- * @alt
- * randomly generated text from a file, for example "i smell like butter"
- * randomly generated text from a file, for example "i have three feet"
- *
- */
-p5.prototype.loadTable = function (path) {
- var callback = null;
- var options = [];
- var header = false;
- var sep = ',';
- var separatorSet = false;
- var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
-
- for (var i = 1; i < arguments.length; i++) {
- if ((typeof (arguments[i]) === 'function') &&
- (arguments[i] !== decrementPreload)) {
- callback = arguments[i];
- } else if (typeof (arguments[i]) === 'string') {
- options.push(arguments[i]);
- if (arguments[i] === 'header') {
- header = true;
- }
- if (arguments[i] === 'csv') {
- if (separatorSet) {
- throw new Error('Cannot set multiple separator types.');
- } else {
- sep = ',';
- separatorSet = true;
- }
- } else if (arguments[i] === 'tsv') {
- if (separatorSet) {
- throw new Error('Cannot set multiple separator types.');
- } else {
- sep = '\t';
- separatorSet = true;
- }
- }
- }
- }
-
- var t = new p5.Table();
- reqwest({
- url: path,
- crossOrigin: true,
- type: 'csv'
- })
- .then(function (resp) {
- resp = resp.responseText;
-
- var state = {};
-
- // define constants
- var PRE_TOKEN = 0,
- MID_TOKEN = 1,
- POST_TOKEN = 2,
- POST_RECORD = 4;
-
- var QUOTE = '\"',
- CR = '\r',
- LF = '\n';
-
- var records = [];
- var offset = 0;
- var currentRecord = null;
- var currentChar;
-
- var recordBegin = function () {
- state.escaped = false;
- currentRecord = [];
- tokenBegin();
- };
-
- var recordEnd = function () {
- state.currentState = POST_RECORD;
- records.push(currentRecord);
- currentRecord = null;
- };
-
- var tokenBegin = function () {
- state.currentState = PRE_TOKEN;
- state.token = '';
- };
-
- var tokenEnd = function () {
- currentRecord.push(state.token);
- tokenBegin();
- };
-
- while (true) {
- currentChar = resp[offset++];
-
- // EOF
- if (currentChar == null) {
- if (state.escaped) {
- throw new Error('Unclosed quote in file.');
- }
- if (currentRecord) {
- tokenEnd();
- recordEnd();
- break;
- }
- }
- if (currentRecord === null) {
- recordBegin();
- }
-
- // Handle opening quote
- if (state.currentState === PRE_TOKEN) {
- if (currentChar === QUOTE) {
- state.escaped = true;
- state.currentState = MID_TOKEN;
- continue;
- }
- state.currentState = MID_TOKEN;
- }
-
- // mid-token and escaped, look for sequences and end quote
- if (state.currentState === MID_TOKEN && state.escaped) {
- if (currentChar === QUOTE) {
- if (resp[offset] === QUOTE) {
- state.token += QUOTE;
- offset++;
- } else {
- state.escaped = false;
- state.currentState = POST_TOKEN;
- }
- } else {
- state.token += currentChar;
- }
- continue;
- }
-
- // fall-through: mid-token or post-token, not escaped
- if (currentChar === CR) {
- if (resp[offset] === LF) {
- offset++;
- }
- tokenEnd();
- recordEnd();
- } else if (currentChar === LF) {
- tokenEnd();
- recordEnd();
- } else if (currentChar === sep) {
- tokenEnd();
- } else if (state.currentState === MID_TOKEN) {
- state.token += currentChar;
- }
- }
-
- // set up column names
- if (header) {
- t.columns = records.shift();
- } else {
- for (i = 0; i < records[0].length; i++) {
- t.columns[i] = 'null';
- }
- }
- var row;
- for (i = 0; i < records.length; i++) {
- //Handles row of 'undefined' at end of some CSVs
- if (i === records.length - 1 && records[i].length === 1) {
- if (records[i][0] === 'undefined') {
- break;
- }
- }
- row = new p5.TableRow();
- row.arr = records[i];
- row.obj = makeObject(records[i], t.columns);
- t.addRow(row);
- }
- if (callback !== null) {
- callback(t);
- }
- if (decrementPreload && (callback !== decrementPreload)) {
- decrementPreload();
- }
- })
- .fail(function (err, msg) {
- p5._friendlyFileLoadError(2, path);
- // don't get error callback mixed up with decrementPreload
- if ((typeof callback === 'function') &&
- (callback !== decrementPreload)) {
- callback(false);
- }
- });
-
- return t;
-};
-
-// helper function to turn a row into a JSON object
-function makeObject(row, headers) {
- var ret = {};
- headers = headers || [];
- if (typeof (headers) === 'undefined') {
- for (var j = 0; j < row.length; j++) {
- headers[j.toString()] = j;
- }
- }
- for (var i = 0; i < headers.length; i++) {
- var key = headers[i];
- var val = row[i];
- ret[key] = val;
- }
- return ret;
-}
-
-/*global parseXML */
-p5.prototype.parseXML = function (two) {
- var one = new p5.XML();
- var i;
- if (two.children.length) {
- for ( i = 0; i < two.children.length; i++ ) {
- var node = parseXML(two.children[i]);
- one.addChild(node);
- }
- one.setName(two.nodeName);
- one._setCont(two.textContent);
- one._setAttributes(two);
- for (var j = 0; j < one.children.length; j++) {
- one.children[j].parent = one;
- }
- return one;
- }
- else {
- one.setName(two.nodeName);
- one._setCont(two.textContent);
- one._setAttributes(two);
- return one;
- }
-};
-
-/**
- * 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:
- *
- * @method loadXML
- * @param {String} filename name of the file or URL to load
- * @param {Function} [callback] function to be executed after loadXML()
- * completes, XML object is passed in as
- * first argument
- * @param {Function} [errorCallback] function to be executed if
- * there is an error, response is passed
- * in as first argument
- * @return {Object} XML object containing data
- */
-p5.prototype.loadXML = function (path, callback, errorCallback) {
- var ret = {};
- var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
- reqwest({
- url: path,
- type: 'xml',
- crossOrigin: true,
- error: function (resp) {
- // pass to error callback if defined
- if (errorCallback) {
- errorCallback(resp);
- } else { // otherwise log error msg
- console.log(resp.statusText);
- }
- //p5._friendlyFileLoadError(1,path);
- }
- })
- .then(function (resp) {
- var xml = parseXML(resp.documentElement);
- for(var key in xml) {
- ret[key] = xml[key];
- }
- if (typeof callback !== 'undefined') {
- callback(ret);
- }
- if (decrementPreload && (callback !== decrementPreload)) {
- decrementPreload();
- }
- });
- return ret;
-};
-
-// name clash with window.open
-// p5.prototype.open = function() {
-// // TODO
-
-// };
-
-p5.prototype.selectFolder = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-p5.prototype.selectInput = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-/**
- * Method for executing an HTTP GET request. If data type is not specified,
- * p5 will try to guess based on the URL, defaulting to text.
- *
- * @method httpGet
- * @param {String} path name of the file or url to load
- * @param {Object} [data] param data passed sent with request
- * @param {String} [datatype] "json", "jsonp", "xml", or "text"
- * @param {Function} [callback] function to be executed after
- * httpGet() completes, data is passed in
- * as first argument
- * @param {Function} [errorCallback] function to be executed if
- * there is an error, response is passed
- * in as first argument
- */
-p5.prototype.httpGet = function () {
- var args = new Array(arguments.length);
- for (var i = 0; i < args.length; ++i) {
- args[i] = arguments[i];
- }
- args.push('GET');
- p5.prototype.httpDo.apply(this, args);
-};
-
-/**
- * Method for executing an HTTP POST request. If data type is not specified,
- * p5 will try to guess based on the URL, defaulting to text.
- *
- * @method httpPost
- * @param {String} path name of the file or url to load
- * @param {Object} [data] param data passed sent with request
- * @param {String} [datatype] "json", "jsonp", "xml", or "text"
- * @param {Function} [callback] function to be executed after
- * httpGet() completes, data is passed in
- * as first argument
- * @param {Function} [errorCallback] function to be executed if
- * there is an error, response is passed
- * in as first argument
- */
-p5.prototype.httpPost = function () {
- var args = new Array(arguments.length);
- for (var i = 0; i < args.length; ++i) {
- args[i] = arguments[i];
- }
- args.push('POST');
- p5.prototype.httpDo.apply(this, args);
-};
-
-/**
- * Method for executing an HTTP request. If data type is not specified,
- * p5 will try to guess based on the URL, defaulting to text.
- * You may also pass a single object specifying all parameters for the
- * request following the examples inside the reqwest() calls here:
- *
- * https://github.com/ded/reqwest#api
- *
- * @method httpDo
- * @param {String} path name of the file or url to load
- * @param {String} [method] either "GET", "POST", or "PUT",
- * defaults to "GET"
- * @param {Object} [data] param data passed sent with request
- * @param {String} [datatype] "json", "jsonp", "xml", or "text"
- * @param {Function} [callback] function to be executed after
- * httpGet() completes, data is passed in
- * as first argument
- * @param {Function} [errorCallback] function to be executed if
- * there is an error, response is passed
- * in as first argument
- */
-p5.prototype.httpDo = function () {
- if (typeof arguments[0] === 'object') {
- reqwest(arguments[0]);
- } else {
- var method = 'GET';
- var path = arguments[0];
- var data = {};
- var type = '';
- var callback;
- var errorCallback;
-
- for (var i = 1; i < arguments.length; i++) {
- var a = arguments[i];
- if (typeof a === 'string') {
- if (a === 'GET' || a === 'POST' || a === 'PUT' || a === 'DELETE') {
- method = a;
- } else {
- type = a;
- }
- } else if (typeof a === 'object') {
- data = a;
- } else if (typeof a === 'function') {
- if (!callback) {
- callback = a;
- } else {
- errorCallback = a;
- }
- }
- }
-
- // do some sort of smart type checking
- if (type === '') {
- if (path.indexOf('json') !== -1) {
- type = 'json';
- } else if (path.indexOf('xml') !== -1) {
- type = 'xml';
- } else {
- type = 'text';
- }
- }
-
- reqwest({
- url: path,
- method: method,
- data: data,
- type: type,
- crossOrigin: true,
- success: function (resp) {
- if (typeof callback !== 'undefined') {
- if (type === 'text') {
- callback(resp.response);
- } else {
- callback(resp);
- }
- }
- },
- error: function (resp) {
- if (errorCallback) {
- errorCallback(resp);
- } else {
- console.log(resp.statusText);
- }
- }
- });
- }
-};
-
-/**
- * @module IO
- * @submodule Output
- * @for p5
- */
-
-window.URL = window.URL || window.webkitURL;
-
-// private array of p5.PrintWriter objects
-p5.prototype._pWriters = [];
-
-p5.prototype.beginRaw = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-p5.prototype.beginRecord = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-p5.prototype.createOutput = function () {
- // TODO
-
- throw 'not yet implemented';
-};
-
-p5.prototype.createWriter = function (name, extension) {
- var newPW;
- // check that it doesn't already exist
- for (var i in p5.prototype._pWriters) {
- if (p5.prototype._pWriters[i].name === name) {
- // if a p5.PrintWriter w/ this name already exists...
- // return p5.prototype._pWriters[i]; // return it w/ contents intact.
- // or, could return a new, empty one with a unique name:
- newPW = new p5.PrintWriter(name + window.millis(), extension);
- p5.prototype._pWriters.push(newPW);
- return newPW;
- }
- }
- newPW = new p5.PrintWriter(name, extension);
- p5.prototype._pWriters.push(newPW);
- return newPW;
-};
-
-p5.prototype.endRaw = function () {
- // TODO
-
- throw 'not yet implemented';
-};
-
-p5.prototype.endRecord = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-p5.PrintWriter = function (filename, extension) {
- var self = this;
- this.name = filename;
- this.content = '';
- this.print = function (data) {
- this.content += data;
- };
- this.print = function (data) {
- this.content += data + '\n';
- };
- this.flush = function () {
- this.content = '';
- };
- this.close = function () {
- // convert String to Array for the writeFile Blob
- var arr = [];
- arr.push(this.content);
- p5.prototype.writeFile(arr, filename, extension);
- // remove from _pWriters array and delete self
- for (var i in p5.prototype._pWriters) {
- if (p5.prototype._pWriters[i].name === this.name) {
- // remove from _pWriters array
- p5.prototype._pWriters.splice(i, 1);
- }
- }
- self.flush();
- self = {};
- };
-};
-
-p5.prototype.saveBytes = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-// object, filename, options --> saveJSON, saveStrings, saveTable
-// filename, [extension] [canvas] --> saveImage
-
-/**
- *
Save an image, text, json, csv, wav, or html. Prompts download to
- * the client's computer. Note that it is not recommended to call save()
- * within draw if it's looping, as the save() function will open a new save
- * dialog every frame.
- *
The default behavior is to save the canvas as an image. You can
- * optionally specify a filename.
- * For example:
- *
- * save();
- * save('myCanvas.jpg'); // save a specific canvas with a filename
- *
- *
- *
Alternately, the first parameter can be a pointer to a canvas
- * p5.Element, an Array of Strings,
- * an Array of JSON, a JSON object, a p5.Table, a p5.Image, or a
- * p5.SoundFile (requires p5.sound). The second parameter is a filename
- * (including extension). The third parameter is for options specific
- * to this type of object. This method will save a file that fits the
- * given paramaters. For example:
- *
- *
- *
- * save('myCanvas.jpg'); // Saves canvas as an image
- *
- * var cnv = createCanvas(100, 100);
- * save(cnv, 'myCanvas.jpg'); // Saves canvas as an image
- *
- * var gb = createGraphics(100, 100);
- * save(gb, 'myGraphics.jpg'); // Saves p5.Renderer object as an image
- *
- * save(myTable, 'myTable.html'); // Saves table as html file
- * save(myTable, 'myTable.csv',); // Comma Separated Values
- * save(myTable, 'myTable.tsv'); // Tab Separated Values
- *
- * save(myJSON, 'my.json'); // Saves pretty JSON
- * save(myJSON, 'my.json', true); // Optimizes JSON filesize
- *
- * save(img, 'my.png'); // Saves pImage as a png image
- *
- * save(arrayOfStrings, 'my.txt'); // Saves strings to a text file with line
- * // breaks after each item in the array
- *
- *
- * @method save
- * @param {[Object|String]} objectOrFilename If filename is provided, will
- * save canvas as an image with
- * either png or jpg extension
- * depending on the filename.
- * If object is provided, will
- * save depending on the object
- * and filename (see examples
- * above).
- * @param {[String]} filename If an object is provided as the first
- * parameter, then the second parameter
- * indicates the filename,
- * and should include an appropriate
- * file extension (see examples above).
- * @param {[Boolean/String]} options Additional options depend on
- * filetype. For example, when saving JSON,
- * true indicates that the
- * output will be optimized for filesize,
- * rather than readability.
- */
-p5.prototype.save = function (object, _filename, _options) {
- // parse the arguments and figure out which things we are saving
- var args = arguments;
- // =================================================
- // OPTION 1: saveCanvas...
-
- // if no arguments are provided, save canvas
- var cnv = this._curElement.elt;
- if (args.length === 0) {
- p5.prototype.saveCanvas(cnv);
- return;
- }
- // otherwise, parse the arguments
-
- // if first param is a p5Graphics, then saveCanvas
- else if (args[0] instanceof p5.Renderer ||
- args[0] instanceof p5.Graphics) {
- p5.prototype.saveCanvas(args[0].elt, args[1], args[2]);
- return;
- }
-
- // if 1st param is String and only one arg, assume it is canvas filename
- else if (args.length === 1 && typeof (args[0]) === 'string') {
- p5.prototype.saveCanvas(cnv, args[0]);
- }
-
- // =================================================
- // OPTION 2: extension clarifies saveStrings vs. saveJSON
- else {
- var extension = _checkFileExtension(args[1], args[2])[1];
- switch (extension) {
- case 'json':
- p5.prototype.saveJSON(args[0], args[1], args[2]);
- return;
- case 'txt':
- p5.prototype.saveStrings(args[0], args[1], args[2]);
- return;
- // =================================================
- // OPTION 3: decide based on object...
- default:
- if (args[0] instanceof Array) {
- p5.prototype.saveStrings(args[0], args[1], args[2]);
- } else if (args[0] instanceof p5.Table) {
- p5.prototype.saveTable(args[0], args[1], args[2], args[3]);
- } else if (args[0] instanceof p5.Image) {
- p5.prototype.saveCanvas(args[0].canvas, args[1]);
- } else if (args[0] instanceof p5.SoundFile) {
- p5.prototype.saveSound(args[0], args[1], args[2], args[3]);
- }
- }
- }
-};
-
-/**
- * Writes the contents of an Array or a JSON object to a .json file.
- * The file saving process and location of the saved file will
- * vary between web browsers.
- *
- * @method saveJSON
- * @param {Array|Object} json
- * @param {String} filename
- * @param {Boolean} [optimize] If true, removes line breaks
- * and spaces from the output
- * file to optimize filesize
- * (but not readability).
- * @example
- *
- * var json;
- *
- * function setup() {
- *
- * json = {}; // new JSON Object
- *
- * json.id = 0;
- * json.species = 'Panthera leo';
- * json.name = 'Lion';
- *
- * // To save, un-comment the line below, then click 'run'
- * // saveJSON(json, 'lion.json');
- * }
- *
- * // Saves the following to a file called "lion.json":
- * // {
- * // "id": 0,
- * // "species": "Panthera leo",
- * // "name": "Lion"
- * // }
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.prototype.saveJSON = function (json, filename, opt) {
- var stringify;
- if (opt) {
- stringify = JSON.stringify(json);
- } else {
- stringify = JSON.stringify(json, undefined, 2);
- }
- console.log(stringify);
- this.saveStrings(stringify.split('\n'), filename, 'json');
-};
-
-p5.prototype.saveJSONObject = p5.prototype.saveJSON;
-p5.prototype.saveJSONArray = p5.prototype.saveJSON;
-
-p5.prototype.saveStream = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-/**
- * Writes an array of Strings to a text file, one line per String.
- * The file saving process and location of the saved file will
- * vary between web browsers.
- *
- * @method saveStrings
- * @param {Array} list string array to be written
- * @param {String} filename filename for output
- * @example
- *
- * var words = 'apple bear cat dog';
- *
- * // .split() outputs an Array
- * var list = split(words, ' ');
- *
- * // To save the file, un-comment next line and click 'run'
- * // saveStrings(list, 'nouns.txt');
- *
- * // Saves the following to a file called 'nouns.txt':
- * //
- * // apple
- * // bear
- * // cat
- * // dog
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.prototype.saveStrings = function (list, filename, extension) {
- var ext = extension || 'txt';
- var pWriter = this.createWriter(filename, ext);
- for (var i = 0; i < list.length; i++) {
- if (i < list.length - 1) {
- pWriter.print(list[i]);
- } else {
- pWriter.print(list[i]);
- }
- }
- pWriter.close();
- pWriter.flush();
-};
-
-p5.prototype.saveXML = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-p5.prototype.selectOutput = function () {
- // TODO
- throw 'not yet implemented';
-
-};
-
-// =======
-// HELPERS
-// =======
-
-function escapeHelper(content) {
- return content
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''');
-}
-
-/**
- * Writes the contents of a Table object to a file. Defaults to a
- * text file with comma-separated-values ('csv') but can also
- * use tab separation ('tsv'), or generate an HTML table ('html').
- * The file saving process and location of the saved file will
- * vary between web browsers.
- *
- * @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"
- * @example
- *
- * var table;
- *
- * function setup() {
- * table = new p5.Table();
- *
- * table.addColumn('id');
- * table.addColumn('species');
- * table.addColumn('name');
- *
- * var newRow = table.addRow();
- * newRow.setNum('id', table.getRowCount() - 1);
- * newRow.setString('species', 'Panthera leo');
- * newRow.setString('name', 'Lion');
- *
- * // To save, un-comment next line then click 'run'
- * // saveTable(table, 'new.csv');
- * }
- *
- * // Saves the following to a file called 'new.csv':
- * // id,species,name
- * // 0,Panthera leo,Lion
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.prototype.saveTable = function (table, filename, options) {
- var pWriter = this.createWriter(filename, options);
-
- var header = table.columns;
-
- var sep = ','; // default to CSV
- if (options === 'tsv') {
- sep = '\t';
- }
- if (options !== 'html') {
- // make header if it has values
- if (header[0] !== '0') {
- for (var h = 0; h < header.length; h++) {
- if (h < header.length - 1) {
- pWriter.print(header[h] + sep);
- } else {
- pWriter.print(header[h]);
- }
- }
- }
-
- // make rows
- for (var i = 0; i < table.rows.length; i++) {
- var j;
- for (j = 0; j < table.rows[i].arr.length; j++) {
- if (j < table.rows[i].arr.length - 1) {
- pWriter.print(table.rows[i].arr[j] + sep);
- } else if (i < table.rows.length - 1) {
- pWriter.print(table.rows[i].arr[j]);
- } else {
- pWriter.print(table.rows[i].arr[j]); // no line break
- }
- }
- }
- }
-
- // otherwise, make HTML
- else {
- pWriter.print('');
- pWriter.print('');
- var str = ' ';
- pWriter.print(str);
- pWriter.print('');
-
- pWriter.print('');
- pWriter.print('
');
-
- // make header if it has values
- if (header[0] !== '0') {
- pWriter.print('
');
- for (var k = 0; k < header.length; k++) {
- var e = escapeHelper(header[k]);
- pWriter.print('
');
- for (var col = 0; col < table.columns.length; col++) {
- var entry = table.rows[row].getString(col);
- var htmlEntry = escapeHelper(entry);
- pWriter.print('
' + htmlEntry);
- pWriter.print('
');
- }
- pWriter.print('
');
- }
- pWriter.print('
');
- pWriter.print('');
- pWriter.print('');
- }
- // close and flush the pWriter
- pWriter.close();
- pWriter.flush();
-}; // end saveTable()
-
-/**
- * Generate a blob of file data as a url to prepare for download.
- * Accepts an array of data, a filename, and an extension (optional).
- * This is a private function because it does not do any formatting,
- * but it is used by saveStrings, saveJSON, saveTable etc.
- *
- * @param {Array} dataToDownload
- * @param {String} filename
- * @param {[String]} extension
- * @private
- */
-p5.prototype.writeFile = function (dataToDownload, filename, extension) {
- var type = 'application\/octet-stream';
- if (p5.prototype._isSafari()) {
- type = 'text\/plain';
- }
- var blob = new Blob(dataToDownload, {
- 'type': type
- });
- var href = window.URL.createObjectURL(blob);
- p5.prototype.downloadFile(href, filename, extension);
-};
-
-/**
- * Forces download. Accepts a url to filedata/blob, a filename,
- * and an extension (optional).
- * This is a private function because it does not do any formatting,
- * but it is used by saveStrings, saveJSON, saveTable etc.
- *
- * @param {String} href i.e. an href generated by createObjectURL
- * @param {[String]} filename
- * @param {[String]} extension
- */
-p5.prototype.downloadFile = function (href, fName, extension) {
- var fx = _checkFileExtension(fName, extension);
- var filename = fx[0];
- var ext = fx[1];
-
- var a = document.createElement('a');
- a.href = href;
- a.download = filename;
-
- // Firefox requires the link to be added to the DOM before click()
- a.onclick = destroyClickedElement;
- a.style.display = 'none';
- document.body.appendChild(a);
-
- // Safari will open this file in the same page as a confusing Blob.
- if (p5.prototype._isSafari()) {
- var aText = 'Hello, Safari user! To download this file...\n';
- aText += '1. Go to File --> Save As.\n';
- aText += '2. Choose "Page Source" as the Format.\n';
- aText += '3. Name it with this extension: .\"' + ext + '\"';
- alert(aText);
- }
- a.click();
- href = null;
-};
-
-/**
- * Returns a file extension, or another string
- * if the provided parameter has no extension.
- *
- * @param {String} filename
- * @return {Array} [fileName, fileExtension]
- *
- * @private
- */
-function _checkFileExtension(filename, extension) {
- if (!extension || extension === true || extension === 'true') {
- extension = '';
- }
- if (!filename) {
- filename = 'untitled';
- }
- var ext = '';
- // make sure the file will have a name, see if filename needs extension
- if (filename && filename.indexOf('.') > -1) {
- ext = filename.split('.').pop();
- }
- // append extension if it doesn't exist
- if (extension) {
- if (ext !== extension) {
- ext = extension;
- filename = filename + '.' + ext;
- }
- }
- return [filename, ext];
-}
-p5.prototype._checkFileExtension = _checkFileExtension;
-
-/**
- * Returns true if the browser is Safari, false if not.
- * Safari makes trouble for downloading files.
- *
- * @return {Boolean} [description]
- * @private
- */
-p5.prototype._isSafari = function () {
- var x = Object.prototype.toString.call(window.HTMLElement);
- return x.indexOf('Constructor') > 0;
-};
-
-/**
- * Helper function, a callback for download that deletes
- * an invisible anchor element from the DOM once the file
- * has been automatically downloaded.
- *
- * @private
- */
-function destroyClickedElement(event) {
- document.body.removeChild(event.target);
-}
-
-module.exports = p5;
-
-},{"../core/core":37,"../core/error_helpers":40,"opentype.js":8,"reqwest":27}],60:[function(_dereq_,module,exports){
-/**
- * @module IO
- * @submodule Table
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-
-/**
- * Table Options
- *
Generic class for handling tabular data, typically from a
- * CSV, TSV, or other sort of spreadsheet file.
- *
CSV files are
- *
- * comma separated values, often with the data in quotes. TSV
- * files use tabs as separators, and usually don't bother with the
- * quotes.
- *
File names should end with .csv if they're comma separated.
To save tables to your computer, use the save method
- * or the saveTable method.
- *
- * Possible options include:
- *
- *
csv - parse the table as comma-separated values
- *
tsv - parse the table as tab-separated values
- *
header - this table has a header (title) row
- *
- */
-
-/**
- * Table objects store data with multiple rows and columns, much
- * like in a traditional spreadsheet. Tables can be generated from
- * scratch, dynamically, or using data from an existing file.
- *
- * @class p5.Table
- * @constructor
- * @param {Array} [rows] An array of p5.TableRow objects
- * @return {p5.Table} p5.Table generated
- */
-p5.Table = function (rows) {
- /**
- * @property columns
- * @type {Array}
- */
- this.columns = [];
-
- /**
- * @property rows
- * @type {Array}
- */
- this.rows = [];
-};
-
-/**
- * Use addRow() to add a new row of data to a p5.Table object. By default,
- * an empty row is created. Typically, you would store a reference to
- * the new row in a TableRow object (see newRow in the example above),
- * and then set individual values using set().
- *
- * If a p5.TableRow object is included as a parameter, then that row is
- * duplicated and added to the table.
- *
- * @method addRow
- * @param {p5.TableRow} [row] row to be added to the table
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * //add a row
- * var newRow = table.addRow();
- * newRow.setString("id", table.getRowCount() - 1);
- * newRow.setString("species", "Canis Lupus");
- * newRow.setString("name", "Wolf");
- *
- * //print the results
- * for (var r = 0; r < table.getRowCount(); r++)
- * for (var c = 0; c < table.getColumnCount(); c++)
- * print(table.getString(r, c));
- * }
- *
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.Table.prototype.addRow = function(row) {
- // make sure it is a valid TableRow
- var r = row || new p5.TableRow();
-
- if (typeof(r.arr) === 'undefined' || typeof(r.obj) === 'undefined') {
- //r = new p5.prototype.TableRow(r);
- throw 'invalid TableRow: ' + r;
- }
- r.table = this;
- this.rows.push(r);
- return r;
-};
-
-/**
- * Removes a row from the table object.
- *
- * @method removeRow
- * @param {Number} id ID number of the row to remove
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * //remove the first row
- * var r = table.removeRow(0);
- *
- * //print the results
- * for (var r = 0; r < table.getRowCount(); r++)
- * for (var c = 0; c < table.getColumnCount(); c++)
- * print(table.getString(r, c));
- * }
- *
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.Table.prototype.removeRow = function(id) {
- this.rows[id].table = null; // remove reference to table
- var chunk = this.rows.splice(id+1, this.rows.length);
- this.rows.pop();
- this.rows = this.rows.concat(chunk);
-};
-
-
-/**
- * Returns a reference to the specified p5.TableRow. The reference
- * can then be used to get and set values of the selected row.
- *
- * @method getRow
- * @param {Number} rowID ID number of the row to get
- * @return {TableRow} p5.TableRow object
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * var row = table.getRow(1);
- * //print it column by column
- * //note: a row is an object, not an array
- * for (var c = 0; c < table.getColumnCount(); c++)
- * print(row.getString(c));
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.getRow = function(r) {
- return this.rows[r];
-};
-
-/**
- * Gets all rows from the table. Returns an array of p5.TableRows.
- *
- * @method getRows
- * @return {Array} Array of p5.TableRows
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * var rows = table.getRows();
- *
- * //warning: rows is an array of objects
- * for (var r = 0; r < rows.length; r++)
- * rows[r].set("name", "Unicorn");
- *
- * //print the results
- * for (var r = 0; r < table.getRowCount(); r++)
- * for (var c = 0; c < table.getColumnCount(); c++)
- * print(table.getString(r, c));
- * }
- *
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.Table.prototype.getRows = function() {
- return this.rows;
-};
-
-/**
- * Finds the first row in the Table that contains the value
- * provided, and returns a reference to that row. Even if
- * multiple rows are possible matches, only the first matching
- * row is returned. The column to search may be specified by
- * either its ID or title.
- *
- * @method findRow
- * @param {String} value The value to match
- * @param {Number|String} column ID number or title of the
- * column to search
- * @return {TableRow}
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * //find the animal named zebra
- * var row = table.findRow("Zebra", "name");
- * //find the corresponding species
- * print(row.getString("species"));
- * }
- *
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.Table.prototype.findRow = function(value, column) {
- // try the Object
- if (typeof(column) === 'string') {
- for (var i = 0; i < this.rows.length; i++){
- if (this.rows[i].obj[column] === value) {
- return this.rows[i];
- }
- }
- }
- // try the Array
- else {
- for (var j = 0; j < this.rows.length; j++){
- if (this.rows[j].arr[column] === value) {
- return this.rows[j];
- }
- }
- }
- // otherwise...
- return null;
-};
-
-/**
- * Finds the rows in the Table that contain the value
- * provided, and returns references to those rows. Returns an
- * Array, so for must be used to iterate through all the rows,
- * as shown in the example above. The column to search may be
- * specified by either its ID or title.
- *
- * @method findRows
- * @param {String} value The value to match
- * @param {Number|String} column ID number or title of the
- * column to search
- * @return {Array} An Array of TableRow objects
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * //add another goat
- * var newRow = table.addRow();
- * newRow.setString("id", table.getRowCount() - 1);
- * newRow.setString("species", "Scape Goat");
- * newRow.setString("name", "Goat");
- *
- * //find the rows containing animals named Goat
- * var rows = table.findRows("Goat", "name");
- * print(rows.length + " Goats found");
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.findRows = function(value, column) {
- var ret = [];
- if (typeof(column) === 'string') {
- for (var i = 0; i < this.rows.length; i++){
- if (this.rows[i].obj[column] === value) {
- ret.push( this.rows[i] );
- }
- }
- }
- // try the Array
- else {
- for (var j = 0; j < this.rows.length; j++){
- if (this.rows[j].arr[column] === value) {
- ret.push( this.rows[j] );
- }
- }
- }
- return ret;
-};
-
-/**
- * Finds the first row in the Table that matches the regular
- * expression provided, and returns a reference to that row.
- * Even if multiple rows are possible matches, only the first
- * matching row is returned. The column to search may be
- * specified by either its ID or title.
- *
- * @method matchRow
- * @param {String} regexp The regular expression to match
- * @param {String|Number} column The column ID (number) or
- * title (string)
- * @return {TableRow} TableRow object
- */
-p5.Table.prototype.matchRow = function(regexp, column) {
- if (typeof(column) === 'number') {
- for (var j = 0; j < this.rows.length; j++) {
- if ( this.rows[j].arr[column].match(regexp) ) {
- return this.rows[j];
- }
- }
- }
-
- else {
- for (var i = 0; i < this.rows.length; i++) {
- if ( this.rows[i].obj[column].match(regexp) ) {
- return this.rows[i];
- }
- }
- }
- return null;
-};
-
-/**
- * Finds the rows in the Table that match the regular expression provided,
- * and returns references to those rows. Returns an array, so for must be
- * used to iterate through all the rows, as shown in the example. The
- * column to search may be specified by either its ID or title.
- *
- * @method matchRows
- * @param {String} regexp The regular expression to match
- * @param {String|Number} [column] The column ID (number) or
- * title (string)
- * @return {Array} An Array of TableRow objects
- * @example
- * var table;
- *
- * function setup() {
- *
- * table = new p5.Table();
- *
- * table.addColumn('name');
- * table.addColumn('type');
- *
- * var newRow = table.addRow();
- * newRow.setString('name', 'Lion');
- * newRow.setString('type', 'Mammal');
- *
- * newRow = table.addRow();
- * newRow.setString('name', 'Snake');
- * newRow.setString('type', 'Reptile');
- *
- * newRow = table.addRow();
- * newRow.setString('name', 'Mosquito');
- * newRow.setString('type', 'Insect');
- *
- * newRow = table.addRow();
- * newRow.setString('name', 'Lizard');
- * newRow.setString('type', 'Reptile');
- *
- * var rows = table.matchRows('R.*', 'type');
- * for (var i = 0; i < rows.length; i++) {
- * print(rows[i].getString('name') + ': ' + rows[i].getString('type'));
- * }
- * }
- * // Sketch prints:
- * // Snake: Reptile
- * // Lizard: Reptile
- */
-p5.Table.prototype.matchRows = function(regexp, column) {
- var ret = [];
- if (typeof(column) === 'number') {
- for (var j = 0; j < this.rows.length; j++) {
- if ( this.rows[j].arr[column].match(regexp) ) {
- ret.push( this.rows[j] );
- }
- }
- }
-
- else {
- for (var i = 0; i < this.rows.length; i++) {
- if ( this.rows[i].obj[column].match(regexp) ) {
- ret.push( this.rows[i] );
- }
- }
- }
- return ret;
-};
-
-
-/**
- * Retrieves all values in the specified column, and returns them
- * as an array. The column may be specified by either its ID or title.
- *
- * @method getColumn
- * @param {String|Number} column String or Number of the column to return
- * @return {Array} Array of column values
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * //getColumn returns an array that can be printed directly
- * print(table.getColumn("species"));
- * //outputs ["Capra hircus", "Panthera pardus", "Equus zebra"]
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.getColumn = function(value) {
- var ret = [];
- if (typeof(value) === 'string'){
- for (var i = 0; i < this.rows.length; i++){
- ret.push (this.rows[i].obj[value]);
- }
- } else {
- for (var j = 0; j < this.rows.length; j++){
- ret.push (this.rows[j].arr[value]);
- }
- }
- return ret;
-};
-
-/**
- * Removes all rows from a Table. While all rows are removed,
- * columns and column titles are maintained.
- *
- * @method clearRows
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * table.clearRows();
- * print(table.getRowCount() + " total rows in table");
- * print(table.getColumnCount() + " total columns in table");
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.clearRows = function() {
- delete this.rows;
- this.rows = [];
-};
-
-/**
- * Use addColumn() to add a new column to a Table object.
- * Typically, you will want to specify a title, so the column
- * may be easily referenced later by name. (If no title is
- * specified, the new column's title will be null.)
- *
- * @method addColumn
- * @param {String} [title] title of the given column
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * table.addColumn("carnivore");
- * table.set(0, "carnivore", "no");
- * table.set(1, "carnivore", "yes");
- * table.set(2, "carnivore", "no");
- *
- * //print the results
- * for (var r = 0; r < table.getRowCount(); r++)
- * for (var c = 0; c < table.getColumnCount(); c++)
- * print(table.getString(r, c));
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.addColumn = function(title) {
- var t = title || null;
- this.columns.push(t);
-};
-
-/**
- * Returns the total number of columns in a Table.
- *
- * @return {Number} Number of columns in this table
- */
-p5.Table.prototype.getColumnCount = function() {
- return this.columns.length;
-};
-
-/**
- * Returns the total number of rows in a Table.
- *
- * @method getRowCount
- * @return {Number} Number of rows in this table
-
- */
-p5.Table.prototype.getRowCount = function() {
- return this.rows.length;
-};
-
-/**
- *
Removes any of the specified characters (or "tokens").
- *
- *
If no column is specified, then the values in all columns and
- * rows are processed. A specific column may be referenced by
- * either its ID or title.
- *
- * @method removeTokens
- * @param {String} chars String listing characters to be removed
- * @param {String|Number} [column] Column ID (number)
- * or name (string)
- */
-p5.Table.prototype.removeTokens = function(chars, column) {
- var escape= function(s) {
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
- };
- var charArray = [];
- for (var i = 0; i < chars.length; i++) {
- charArray.push( escape( chars.charAt(i) ) );
- }
- var regex = new RegExp(charArray.join('|'), 'g');
-
- if (typeof(column) === 'undefined'){
- for (var c = 0; c < this.columns.length; c++) {
- for (var d = 0; d < this.rows.length; d++) {
- var s = this.rows[d].arr[c];
- s = s.replace(regex, '');
- this.rows[d].arr[c] = s;
- this.rows[d].obj[this.columns[c]] = s;
- }
- }
- }
- else if (typeof(column) === 'string'){
- for (var j = 0; j < this.rows.length; j++) {
- var val = this.rows[j].obj[column];
- val = val.replace(regex, '');
- this.rows[j].obj[column] = val;
- var pos = this.columns.indexOf(column);
- this.rows[j].arr[pos] = val;
- }
- }
- else {
- for (var k = 0; k < this.rows.length; k++) {
- var str = this.rows[k].arr[column];
- str = str.replace(regex, '');
- this.rows[k].arr[column] = str;
- this.rows[k].obj[this.columns[column]] = str;
- }
- }
-};
-
-/**
- * Trims leading and trailing whitespace, such as spaces and tabs,
- * from String table values. If no column is specified, then the
- * values in all columns and rows are trimmed. A specific column
- * may be referenced by either its ID or title.
- *
- * @method trim
- * @param {String|Number} column Column ID (number)
- * or name (string)
- */
-p5.Table.prototype.trim = function(column) {
- var regex = new RegExp( (' '), 'g');
-
- if (typeof(column) === 'undefined'){
- for (var c = 0; c < this.columns.length; c++) {
- for (var d = 0; d < this.rows.length; d++) {
- var s = this.rows[d].arr[c];
- s = s.replace(regex, '');
- this.rows[d].arr[c] = s;
- this.rows[d].obj[this.columns[c]] = s;
- }
- }
- }
- else if (typeof(column) === 'string'){
- for (var j = 0; j < this.rows.length; j++) {
- var val = this.rows[j].obj[column];
- val = val.replace(regex, '');
- this.rows[j].obj[column] = val;
- var pos = this.columns.indexOf(column);
- this.rows[j].arr[pos] = val;
- }
- }
- else {
- for (var k = 0; k < this.rows.length; k++) {
- var str = this.rows[k].arr[column];
- str = str.replace(regex, '');
- this.rows[k].arr[column] = str;
- this.rows[k].obj[this.columns[column]] = str;
- }
- }
-};
-
-/**
- * Use removeColumn() to remove an existing column from a Table
- * object. The column to be removed may be identified by either
- * its title (a String) or its index value (an int).
- * removeColumn(0) would remove the first column, removeColumn(1)
- * would remove the second column, and so on.
- *
- * @method removeColumn
- * @param {String|Number} column columnName (string) or ID (number)
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * table.removeColumn("id");
- * print(table.getColumnCount());
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.removeColumn = function(c) {
- var cString;
- var cNumber;
- if (typeof(c) === 'string') {
- // find the position of c in the columns
- cString = c;
- cNumber = this.columns.indexOf(c);
- console.log('string');
- }
- else{
- cNumber = c;
- cString = this.columns[c];
- }
-
- var chunk = this.columns.splice(cNumber+1, this.columns.length);
- this.columns.pop();
- this.columns = this.columns.concat(chunk);
-
- for (var i = 0; i < this.rows.length; i++){
- var tempR = this.rows[i].arr;
- var chip = tempR.splice(cNumber+1, tempR.length);
- tempR.pop();
- this.rows[i].arr = tempR.concat(chip);
- delete this.rows[i].obj[cString];
- }
-
-};
-
-
-/**
- * Stores a value in the Table's specified row and column.
- * The row is specified by its ID, while the column may be specified
- * by either its ID or title.
- *
- * @method set
- * @param {String|Number} column column ID (Number)
- * or title (String)
- * @param {String|Number} value value to assign
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * table.set(0, "species", "Canis Lupus");
- * table.set(0, "name", "Wolf");
- *
- * //print the results
- * for (var r = 0; r < table.getRowCount(); r++)
- * for (var c = 0; c < table.getColumnCount(); c++)
- * print(table.getString(r, c));
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.set = function(row, column, value) {
- this.rows[row].set(column, value);
-};
-
-/**
- * Stores a Float value in the Table's specified row and column.
- * The row is specified by its ID, while the column may be specified
- * by either its ID or title.
- *
- * @method setNum
- * @param {Number} row row ID
- * @param {String|Number} column column ID (Number)
- * or title (String)
- * @param {Number} value value to assign
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * table.setNum(1, "id", 1);
- *
- * print(table.getColumn(0));
- * //["0", 1, "2"]
- * }
- *
- *
- *
- *@alt
- * no image displayed
- */
-p5.Table.prototype.setNum = function(row, column, value){
- this.rows[row].setNum(column, value);
-};
-
-
-/**
- * Stores a String value in the Table's specified row and column.
- * The row is specified by its ID, while the column may be specified
- * by either its ID or title.
- *
- * @method setString
- * @param {Number} row row ID
- * @param {String|Number} column column ID (Number)
- * or title (String)
- * @param {String} value value to assign
- */
-p5.Table.prototype.setString = function(row, column, value){
- this.rows[row].setString(column, value);
-};
-
-/**
- * Retrieves a value from the Table's specified row and column.
- * The row is specified by its ID, while the column may be specified by
- * either its ID or title.
- *
- * @method get
- * @param {Number} row row ID
- * @param {String|Number} column columnName (string) or
- * ID (number)
- * @return {String|Number}
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * print(table.get(0, 1));
- * //Capra hircus
- * print(table.get(0, "species"));
- * //Capra hircus
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.get = function(row, column) {
- return this.rows[row].get(column);
-};
-
-/**
- * Retrieves a Float value from the Table's specified row and column.
- * The row is specified by its ID, while the column may be specified by
- * either its ID or title.
- *
- * @method getNum
- * @param {Number} row row ID
- * @param {String|Number} column columnName (string) or
- * ID (number)
- * @return {Number}
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * print(table.getNum(1, 0) + 100);
- * //id 1 + 100 = 101
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.getNum = function(row, column) {
- return this.rows[row].getNum(column);
-};
-
-/**
- * Retrieves a String value from the Table's specified row and column.
- * The row is specified by its ID, while the column may be specified by
- * either its ID or title.
- *
- * @method getString
- * @param {Number} row row ID
- * @param {String|Number} column columnName (string) or
- * ID (number)
- * @return {String}
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * var tableArray = table.getArray();
- *
- * //output each row as array
- * for (var i = 0; i < tableArray.length; i++)
- * print(tableArray[i]);
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.getString = function(row, column) {
- return this.rows[row].getString(column);
-};
-
-/**
- * Retrieves all table data and returns as an object. If a column name is
- * passed in, each row object will be stored with that attribute as its
- * title.
- *
- * @method getObject
- * @param {String} headerColumn Name of the column which should be used to
- * title each row object (optional)
- * @return {Object}
- *
- * @example
- *
- *
- * // Given the CSV file "mammals.csv"
- * // in the project's "assets" folder:
- * //
- * // id,species,name
- * // 0,Capra hircus,Goat
- * // 1,Panthera pardus,Leopard
- * // 2,Equus zebra,Zebra
- *
- * var table;
- *
- * function preload() {
- * //my table is comma separated value "csv"
- * //and has a header specifying the columns labels
- * table = loadTable("assets/mammals.csv", "csv", "header");
- * }
- *
- * function setup() {
- * var tableObject = table.getObject();
- *
- * print(tableObject);
- * //outputs an object
- * }
- *
- *
- *
- *@alt
- * no image displayed
- *
- */
-p5.Table.prototype.getObject = function (headerColumn) {
- var tableObject = {};
- var obj, cPos, index;
-
- for(var i = 0; i < this.rows.length; i++) {
- obj = this.rows[i].obj;
-
- if (typeof(headerColumn) === 'string'){
- cPos = this.columns.indexOf(headerColumn); // index of columnID
- if (cPos >= 0) {
- index = obj[headerColumn];
- tableObject[index] = obj;
- } else {
- throw 'This table has no column named "' + headerColumn +'"';
- }
- } else {
- tableObject[i] = this.rows[i].obj;
- }
- }
- return tableObject;
-};
-
-/**
- * Retrieves all table data and returns it as a multidimensional array.
- *
- * @method getArray
- * @return {Array}
- */
-p5.Table.prototype.getArray = function () {
- var tableArray = [];
- for(var i = 0; i < this.rows.length; i++) {
- tableArray.push(this.rows[i].arr);
- }
- return tableArray;
-};
-
-module.exports = p5.Table;
-
-},{"../core/core":37}],61:[function(_dereq_,module,exports){
-/**
- * @module IO
- * @submodule Table
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-/**
- * A TableRow object represents a single row of data values,
- * stored in columns, from a table.
- *
- * A Table Row contains both an ordered array, and an unordered
- * JSON object.
- *
- * @class p5.TableRow
- * @constructor
- * @param {String} [str] optional: populate the row with a
- * string of values, separated by the
- * separator
- * @param {String} [separator] comma separated values (csv) by default
- */
-p5.TableRow = function (str, separator) {
- var arr = [];
- var obj = {};
- if (str){
- separator = separator || ',';
- arr = str.split(separator);
- }
- for (var i = 0; i < arr.length; i++){
- var key = i;
- var val = arr[i];
- obj[key] = val;
- }
- this.arr = arr;
- this.obj = obj;
- this.table = null;
-};
-
-/**
- * Stores a value in the TableRow's specified column.
- * The column may be specified by either its ID or title.
- *
- * @method set
- * @param {String|Number} column Column ID (Number)
- * or Title (String)
- * @param {String|Number} value The value to be stored
- */
-p5.TableRow.prototype.set = function(column, value) {
- // if typeof column is string, use .obj
- if (typeof(column) === 'string'){
- var cPos = this.table.columns.indexOf(column); // index of columnID
- if (cPos >= 0) {
- this.obj[column] = value;
- this.arr[cPos] = value;
- }
- else {
- throw 'This table has no column named "' + column +'"';
- }
- }
-
- // if typeof column is number, use .arr
- else {
- if (column < this.table.columns.length) {
- this.arr[column] = value;
- var cTitle = this.table.columns[column];
- this.obj[cTitle] = value;
- }
- else {
- throw 'Column #' + column + ' is out of the range of this table';
- }
- }
-};
-
-
-/**
- * Stores a Float value in the TableRow's specified column.
- * The column may be specified by either its ID or title.
- *
- * @method setNum
- * @param {String|Number} column Column ID (Number)
- * or Title (String)
- * @param {Number} value The value to be stored
- * as a Float
- */
-p5.TableRow.prototype.setNum = function(column, value){
- var floatVal = parseFloat(value, 10);
- this.set(column, floatVal);
-};
-
-
-/**
- * Stores a String value in the TableRow's specified column.
- * The column may be specified by either its ID or title.
- *
- * @method setString
- * @param {String|Number} column Column ID (Number)
- * or Title (String)
- * @param {String} value The value to be stored
- * as a String
- */
-p5.TableRow.prototype.setString = function(column, value){
- var stringVal = value.toString();
- this.set(column, stringVal);
-};
-
-/**
- * Retrieves a value from the TableRow's specified column.
- * The column may be specified by either its ID or title.
- *
- * @method get
- * @param {String|Number} column columnName (string) or
- * ID (number)
- * @return {String|Number}
- */
-p5.TableRow.prototype.get = function(column) {
- if (typeof(column) === 'string'){
- return this.obj[column];
- } else {
- return this.arr[column];
- }
-};
-
-/**
- * Retrieves a Float value from the TableRow's specified
- * column. The column may be specified by either its ID or
- * title.
- *
- * @method getNum
- * @param {String|Number} column columnName (string) or
- * ID (number)
- * @return {Number} Float Floating point number
- */
-p5.TableRow.prototype.getNum = function(column) {
- var ret;
- if (typeof(column) === 'string'){
- ret = parseFloat(this.obj[column], 10);
- } else {
- ret = parseFloat(this.arr[column], 10);
- }
-
- if (ret.toString() === 'NaN') {
- throw 'Error: ' + this.obj[column]+ ' is NaN (Not a Number)';
- }
- return ret;
-};
-
-/**
- * Retrieves an String value from the TableRow's specified
- * column. The column may be specified by either its ID or
- * title.
- *
- * @method getString
- * @param {String|Number} column columnName (string) or
- * ID (number)
- * @return {String} String
- */
-p5.TableRow.prototype.getString = function(column) {
- if (typeof(column) === 'string'){
- return this.obj[column].toString();
- } else {
- return this.arr[column].toString();
- }
-};
-
-module.exports = p5.TableRow;
-
-},{"../core/core":37}],62:[function(_dereq_,module,exports){
-/**
- * @module IO
- * @submodule XML
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-/**
- * XML is a representation of an XML object, able to parse XML code. Use
- * loadXML() to load external XML files and create XML objects.
- *
- * @class p5.XML
- * @constructor
- * @return {p5.XML} p5.XML object generated
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var children = xml.getChildren("animal");
- *
- * for (var i = 0; i < children.length; i++) {
- * var id = children[i].getNumber("id");
- * var coloring = children[i].getString("species");
- * var name = children[i].getContent();
- * print(id + ", " + coloring + ", " + name);
- * }
- * }
- *
- * // Sketch prints:
- * // 0, Capra hircus, Goat
- * // 1, Panthera pardus, Leopard
- * // 2, Equus zebra, Zebra
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.XML = function () {
- this.name = null; //done
- this.attributes = {}; //done
- this.children = [];
- this.parent = null;
- this.content = null; //done
-};
-
-
-/**
- * Gets a copy of the element's parent. Returns the parent as another
- * p5.XML object.
- *
- * @method getParent
- * @return {Object} element parent
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var children = xml.getChildren("animal");
- * var parent = children[1].getParent();
- * print(parent.getName());
- * }
- *
- * // Sketch prints:
- * // mammals
- *
- */
-p5.XML.prototype.getParent = function() {
- return this.parent;
-};
-
-/**
- * Gets the element's full name, which is returned as a String.
- *
- * @method getName
- * @return {String} the name of the node
- * @example<animal
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * print(xml.getName());
- * }
- *
- * // Sketch prints:
- * // mammals
- *
- */
-p5.XML.prototype.getName = function() {
- return this.name;
-};
-
-/**
- * Sets the element's name, which is specified as a String.
- *
- * @method setName
- * @param {String} the new name of the node
- * @example<animal
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * print(xml.getName());
- * xml.setName("fish");
- * print(xml.getName());
- * }
- *
- * // Sketch prints:
- * // mammals
- * // fish
- *
- */
-p5.XML.prototype.setName = function(name) {
- this.name = name;
-};
-
-/**
- * Checks whether or not the element has any children, and returns the result
- * as a boolean.
- *
- * @method hasChildren
- * @return {boolean}
- * @example<animal
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * print(xml.hasChildren());
- * }
- *
- * // Sketch prints:
- * // true
- *
- */
-p5.XML.prototype.hasChildren = function() {
- return this.children.length > 0;
-};
-
-/**
- * Get the names of all of the element's children, and returns the names as an
- * array of Strings. This is the same as looping through and calling getName()
- * on each child element individually.
- *
- * @method listChildren
- * @return {Array} names of the children of the element
- * @example<animal
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * print(xml.listChildren());
- * }
- *
- * // Sketch prints:
- * // ["animal", "animal", "animal"]
- *
- */
-p5.XML.prototype.listChildren = function() {
- return this.children.map(function(c) { return c.name; });
-};
-
-/**
- * Returns all of the element's children as an array of p5.XML objects. When
- * the name parameter is specified, then it will return all children that match
- * that name.
- *
- * @method getChildren
- * @param {String} [name] element name
- * @return {Array} children of the element
- * @example<animal
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var animals = xml.getChildren("animal");
- *
- * for (var i = 0; i < animals.length; i++) {
- * print(animals[i].getContent());
- * }
- * }
- *
- * // Sketch prints:
- * // "Goat"
- * // "Leopard"
- * // "Zebra"
- *
- */
-p5.XML.prototype.getChildren = function(param) {
- if (param) {
- return this.children.filter(function(c) { return c.name === param; });
- }
- else {
- return this.children;
- }
-};
-
-/**
- * Returns the first of the element's children that matches the name parameter
- * or the child of the given index.It returns undefined if no matching
- * child is found.
- *
- * @method getChild
- * @param {String|Number} name element name or index
- * @return {p5.XML}
- * @example<animal
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.getContent());
- * }
- *
- * // Sketch prints:
- * // "Goat"
- *
- */
-p5.XML.prototype.getChild = function(param) {
- if(typeof param === 'string') {
- return this.children.find(function(c) {
- return c.name === param;
- });
- }
- else {
- return this.children[param];
- }
-};
-
-/**
- * Appends a new child to the element. The child can be specified with
- * either a String, which will be used as the new tag's name, or as a
- * reference to an existing p5.XML object.
- * A reference to the newly created child is returned as an p5.XML object.
- *
- * @method addChild
- * @param {Object} a p5.XML Object which will be the child to be added
- */
-p5.XML.prototype.addChild = function(node) {
- if (node instanceof p5.XML) {
- this.children.push(node);
- } else {
- // PEND
- }
-};
-
-/**
- * Removes the element specified by name or index.
- *
- * @method removeChild
- * @param {String|Number} name element name or index
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * xml.removeChild("animal");
- * var children = xml.getChildren();
- * for (var i=0; i
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * xml.removeChild(1);
- * var children = xml.getChildren();
- * for (var i=0; i
- */
-p5.XML.prototype.removeChild = function(param) {
- var ind = -1;
- if(typeof param === 'string') {
- for (var i=0; i
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.getAttributeCount());
- * }
- *
- * // Sketch prints:
- * // 2
- *
- */
-p5.XML.prototype.getAttributeCount = function() {
- return Object.keys(this.attributes).length;
-};
-
-/**
- * Gets all of the specified element's attributes, and returns them as an
- * array of Strings.
- *
- * @method listAttributes
- * @return {Array} an array of strings containing the names of attributes
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.listAttributes());
- * }
- *
- * // Sketch prints:
- * // ["id", "species"]
- *
- */
-p5.XML.prototype.listAttributes = function() {
- return Object.keys(this.attributes);
-};
-
-/**
- * Checks whether or not an element has the specified attribute.
- *
- * @method hasAttribute
- * @param {String} the attribute to be checked
- * @return {boolean} true if attribute found else false
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.hasAttribute("species"));
- * print(firstChild.hasAttribute("color"));
- * }
- *
- * // Sketch prints:
- * // true
- * // false
- *
- */
-p5.XML.prototype.hasAttribute = function(name) {
- return this.attributes[name] ? true : false;
-};
-
-/**
- * Returns an attribute value of the element as an Number. If the defaultValue
- * parameter is specified and the attribute doesn't exist, then defaultValue
- * is returned. If no defaultValue is specified and the attribute doesn't
- * exist, the value 0 is returned.
- *
- * @method getNumber
- * @param {String} name the non-null full name of the attribute
- * @param {Number} [defaultValue] the default value of the attribute
- * @return {Number}
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.getNumber("id"));
- * }
- *
- * // Sketch prints:
- * // 0
- *
- */
-p5.XML.prototype.getNumber = function(name, defaultValue) {
- return Number(this.attributes[name]) || defaultValue || 0;
-};
-
-/**
- * Returns an attribute value of the element as an String. If the defaultValue
- * parameter is specified and the attribute doesn't exist, then defaultValue
- * is returned. If no defaultValue is specified and the attribute doesn't
- * exist, null is returned.
- *
- * @method getString
- * @param {String} name the non-null full name of the attribute
- * @param {Number} [defaultValue] the default value of the attribute
- * @return {Number}
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.getString("species"));
- * }
- *
- * // Sketch prints:
- * // "Capra hircus"
- *
- */
-p5.XML.prototype.getString = function(name, defaultValue) {
- return String(this.attributes[name]) || defaultValue || null;
-};
-
-/**
- * Sets the content of an element's attribute. The first parameter specifies
- * the attribute name, while the second specifies the new content.
- *
- * @method setAttribute
- * @param {String} name the full name of the attribute
- * @param {Number} value the value of the attribute
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.getString("species"));
- * firstChild.setAttribute("species", "Jamides zebra");
- * print(firstChild.getString("species"));
- * }
- *
- * // Sketch prints:
- * // "Capra hircus"
- * // "Jamides zebra"
- *
- */
-p5.XML.prototype.setAttribute = function(name, value) {
- if (this.attributes[name]) {
- this.attributes[name] = value;
- }
-};
-
-/**
- * Returns the content of an element. If there is no such content,
- * defaultValue is returned if specified, otherwise null is returned.
- *
- * @method getContent
- * @param {String} [defaultValue] value returned if no content is found
- * @return {String}
- * @example
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.getContent());
- * }
- *
- * // Sketch prints:
- * // "Goat"
- *
- * // The following short XML file called "mammals.xml" is parsed
- * // in the code below.
- * //
- * //
- * // <mammals>
- * // <animal id="0" species="Capra hircus">Goat</animal>
- * // <animal id="1" species="Panthera pardus">Leopard</animal>
- * // <animal id="2" species="Equus zebra">Zebra</animal>
- * // </mammals>
- *
- * var xml;
- *
- * function preload() {
- * xml = loadXML("assets/mammals.xml");
- * }
- *
- * function setup() {
- * var firstChild = xml.getChild("animal");
- * print(firstChild.getContent());
- * firstChild.setContent("Mountain Goat");
- * print(firstChild.getContent());
- * }
- *
- * // Sketch prints:
- * // "Goat"
- * // "Mountain Goat"
- *
- */
-p5.XML.prototype.setContent = function( content ) {
- if(!this.children.length) {
- this.content = content;
- }
-};
-
-/* HELPERS */
-/**
- * This method is called while the parsing of XML (when loadXML() is
- * called). The difference between this method and the setContent()
- * method defined later is that this one is used to set the content
- * when the node in question has more nodes under it and so on and
- * not directly text content. While in the other one is used when
- * the node in question directly has text inside it.
- *
- */
-p5.XML.prototype._setCont = function(content) {
- var str;
- str = content;
- str = str.replace(/\s\s+/g, ',');
- //str = str.split(',');
- this.content = str;
-};
-
-/**
- * This method is called while the parsing of XML (when loadXML() is
- * called). The XML node is passed and its attributes are stored in the
- * p5.XML's attribute Object.
- *
- */
-p5.XML.prototype._setAttributes = function(node) {
- var i, att = {};
- for( i = 0; i < node.attributes.length; i++) {
- att[node.attributes[i].nodeName] = node.attributes[i].nodeValue;
- }
- this.attributes = att;
-};
-
-module.exports = p5.XML;
-},{"../core/core":37}],63:[function(_dereq_,module,exports){
-/**
- * @module Math
- * @submodule Calculation
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-/**
- * Calculates the absolute value (magnitude) of a number. Maps to Math.abs().
- * The absolute value of a number is always positive.
- *
- * @method abs
- * @param {Number} n number to compute
- * @return {Number} absolute value of given number
- * @example
- *
- * function setup() {
- * var x = -3;
- * var y = abs(x);
- *
- * print(x); // -3
- * print(y); // 3
- * }
- *
- *
- * @alt
- * no image displayed
- *
- */
-p5.prototype.abs = Math.abs;
-
-/**
- * Calculates the closest int value that is greater than or equal to the
- * value of the parameter. Maps to Math.ceil(). For example, ceil(9.03)
- * returns the value 10.
- *
- * @method ceil
- * @param {Number} n number to round up
- * @return {Number} rounded up number
- * @example
- *
- * function draw() {
- * background(200);
- * // map, mouseX between 0 and 5.
- * var ax = map(mouseX, 0, 100, 0, 5);
- * var ay = 66;
- *
- * //Get the ceiling of the mapped number.
- * var bx = ceil(map(mouseX, 0, 100, 0,5));
- * var by = 33;
- *
- * // Multiply the mapped numbers by 20 to more easily
- * // see the changes.
- * stroke(0);
- * fill(0);
- * line(0, ay, ax * 20, ay);
- * line(0, by, bx * 20, by);
- *
- * // Reformat the float returned by map and draw it.
- * noStroke();
- * text(nfc(ax, 2,2), ax, ay - 5);
- * text(nfc(bx,1,1), bx, by - 5);
- * }
- *
- *
- * @alt
- * 2 horizontal lines & number sets. increase with mouse x. bottom to 2 decimals
- *
- */
-p5.prototype.ceil = Math.ceil;
-
-/**
- * Constrains a value between a minimum and maximum value.
- *
- * @method constrain
- * @param {Number} n number to constrain
- * @param {Number} low minimum limit
- * @param {Number} high maximum limit
- * @return {Number} constrained number
- * @example
- *
- * function draw() {
- * background(200);
- *
- * var leftWall = 25;
- * var rightWall = 75;
- *
- * // xm is just the mouseX, while
- * // xc is the mouseX, but constrained
- * // between the leftWall and rightWall!
- * var xm = mouseX;
- * var xc = constrain(mouseX, leftWall, rightWall);
- *
- * // Draw the walls.
- * stroke(150);
- * line(leftWall, 0, leftWall, height);
- * line(rightWall, 0, rightWall, height);
- *
- * // Draw xm and xc as circles.
- * noStroke();
- * fill(150);
- * ellipse(xm, 33, 9,9); // Not Constrained
- * fill(0);
- * ellipse(xc, 66, 9,9); // Constrained
- * }
- *
- *
- * @alt
- * 2 vertical lines. 2 ellipses move with mouse X 1 does not move passed lines
- *
- */
-p5.prototype.constrain = function(n, low, high) {
- return Math.max(Math.min(n, high), low);
-};
-
-/**
- * Calculates the distance between two points.
- *
- * @method dist
- * @param {Number} x1 x-coordinate of the first point
- * @param {Number} y1 y-coordinate of the first point
- * @param {Number} [z1] z-coordinate of the first point
- * @param {Number} x2 x-coordinate of the second point
- * @param {Number} y2 y-coordinate of the second point
- * @param {Number} [z2] z-coordinate of the second point
- * @return {Number} distance between the two points
- * @example
- *
- * // Move your mouse inside the canvas to see the
- * // change in distance between two points!
- * function draw() {
- * background(200);
- * fill(0);
- *
- * var x1 = 10;
- * var y1 = 90;
- * var x2 = mouseX;
- * var y2 = mouseY;
- *
- * line(x1, y1, x2, y2);
- * ellipse(x1, y1, 7, 7);
- * ellipse(x2, y2, 7, 7);
- *
- * // d is the length of the line
- * // the distance from point 1 to point 2.
- * var d = int(dist(x1, y1, x2, y2));
- *
- * // Let's write d along the line we are drawing!
- * push();
- * translate( (x1+x2)/2, (y1+y2)/2 );
- * rotate( atan2(y2-y1,x2-x1) );
- * text(nfc(d,1,1), 0, -5);
- * pop();
- * // Fancy!
- * }
- *
- *
- * @alt
- * 2 ellipses joined by line. 1 ellipse moves with mouse X&Y. Distance displayed.
- *
- */
-p5.prototype.dist = function(x1, y1, z1, x2, y2, z2) {
- if (arguments.length === 4) {
- // In the case of 2d: z1 means x2 and x2 means y2
- return hypot(z1-x1, x2-y1);
- } else if (arguments.length === 6) {
- return hypot(x2-x1, y2-y1, z2-z1);
- }
-};
-
-/**
- * Returns Euler's number e (2.71828...) raised to the power of the n
- * parameter. Maps to Math.exp().
- *
- * @method exp
- * @param {Number} n exponent to raise
- * @return {Number} e^n
- * @example
- *
- * function draw() {
- * background(200);
- *
- * // Compute the exp() function with a value between 0 and 2
- * var xValue = map(mouseX, 0, width, 0, 2);
- * var yValue = exp(xValue);
- *
- * var y = map(yValue, 0, 8, height, 0);
- *
- * var legend = "exp (" + nfc(xValue, 3) +")\n= " + nf(yValue, 1, 4);
- * stroke(150);
- * line(mouseX, y, mouseX, height);
- * fill(0);
- * text(legend, 5, 15);
- * noStroke();
- * ellipse (mouseX,y, 7, 7);
- *
- * // Draw the exp(x) curve,
- * // over the domain of x from 0 to 2
- * noFill();
- * stroke(0);
- * beginShape();
- * for (var x = 0; x < width; x++) {
- * xValue = map(x, 0, width, 0, 2);
- * yValue = exp(xValue);
- * y = map(yValue, 0, 8, height, 0);
- * vertex(x, y);
- * }
- *
- * endShape();
- * line(0, 0, 0, height);
- * line(0, height-1, width, height-1);
- * }
- *
- *
- * @alt
- * ellipse moves along a curve with mouse x. e^n displayed.
- *
- */
-p5.prototype.exp = Math.exp;
-
-/**
- * Calculates the closest int value that is less than or equal to the
- * value of the parameter. Maps to Math.floor().
- *
- * @method floor
- * @param {Number} n number to round down
- * @return {Number} rounded down number
- * @example
- *
- * function draw() {
- * background(200);
- * //map, mouseX between 0 and 5.
- * var ax = map(mouseX, 0, 100, 0, 5);
- * var ay = 66;
- *
- * //Get the floor of the mapped number.
- * var bx = floor(map(mouseX, 0, 100, 0,5));
- * var by = 33;
- *
- * // Multiply the mapped numbers by 20 to more easily
- * // see the changes.
- * stroke(0);
- * fill(0);
- * line(0, ay, ax * 20, ay);
- * line(0, by, bx * 20, by);
- *
- * // Reformat the float returned by map and draw it.
- * noStroke();
- * text(nfc(ax, 2,2), ax, ay - 5);
- * text(nfc(bx,1,1), bx, by - 5);
- * }
- *
- *
- * @alt
- * 2 horizontal lines & number sets. increase with mouse x. bottom to 2 decimals
- *
- */
-p5.prototype.floor = Math.floor;
-
-/**
- * Calculates a number between two numbers at a specific increment. The amt
- * parameter is the amount to interpolate between the two values where 0.0
- * equal to the first point, 0.1 is very near the first point, 0.5 is
- * half-way in between, etc. The lerp function is convenient for creating
- * motion along a straight path and for drawing dotted lines.
- *
- * @method lerp
- * @param {Number} start first value
- * @param {Number} stop second value
- * @param {Number} amt number between 0.0 and 1.0
- * @return {Number} lerped value
- * @example
- *
- * function setup() {
- * background(200);
- * var a = 20;
- * var b = 80;
- * var c = lerp(a,b, .2);
- * var d = lerp(a,b, .5);
- * var e = lerp(a,b, .8);
- *
- * var y = 50
- *
- * strokeWeight(5);
- * stroke(0); // Draw the original points in black
- * point(a, y);
- * point(b, y);
- *
- * stroke(100); // Draw the lerp points in gray
- * point(c, y);
- * point(d, y);
- * point(e, y);
- * }
- *
- *
- * @alt
- * 5 points horizontally staggered mid-canvas. mid 3 are grey, outer black
- *
- */
-p5.prototype.lerp = function(start, stop, amt) {
- return amt*(stop-start)+start;
-};
-
-/**
- * Calculates the natural logarithm (the base-e logarithm) of a number. This
- * function expects the n parameter to be a value greater than 0.0. Maps to
- * Math.log().
- *
- * @method log
- * @param {Number} n number greater than 0
- * @return {Number} natural logarithm of n
- * @example
- *
- * function draw() {
- * background(200);
- * var maxX = 2.8;
- * var maxY = 1.5;
- *
- * // Compute the natural log of a value between 0 and maxX
- * var xValue = map(mouseX, 0, width, 0, maxX);
- * if (xValue > 0) { // Cannot take the log of a negative number.
- * var yValue = log(xValue);
- * var y = map(yValue, -maxY, maxY, height, 0);
- *
- * // Display the calculation occurring.
- * var legend = "log(" + nf(xValue, 1, 2) + ")\n= " + nf(yValue, 1, 3);
- * stroke(150);
- * line(mouseX, y, mouseX, height);
- * fill(0);
- * text (legend, 5, 15);
- * noStroke();
- * ellipse (mouseX, y, 7, 7);
- * }
- *
- * // Draw the log(x) curve,
- * // over the domain of x from 0 to maxX
- * noFill();
- * stroke(0);
- * beginShape();
- * for(var x=0; x < width; x++) {
- * xValue = map(x, 0, width, 0, maxX);
- * yValue = log(xValue);
- * y = map(yValue, -maxY, maxY, height, 0);
- * vertex(x, y);
- * }
- * endShape();
- * line(0,0,0,height);
- * line(0,height/2,width, height/2);
- * }
- *
- *
- * @alt
- * ellipse moves along a curve with mouse x. natural logarithm of n displayed.
- *
- */
-p5.prototype.log = Math.log;
-
-/**
- * Calculates the magnitude (or length) of a vector. A vector is a direction
- * in space commonly used in computer graphics and linear algebra. Because it
- * has no "start" position, the magnitude of a vector can be thought of as
- * the distance from the coordinate 0,0 to its x,y value. Therefore, mag() is
- * a shortcut for writing dist(0, 0, x, y).
- *
- * @method mag
- * @param {Number} a first value
- * @param {Number} b second value
- * @return {Number} magnitude of vector from (0,0) to (a,b)
- * @example
- *
- * function setup() {
- * var x1 = 20;
- * var x2 = 80;
- * var y1 = 30;
- * var y2 = 70;
- *
- * line(0, 0, x1, y1);
- * print(mag(x1, y1)); // Prints "36.05551275463989"
- * line(0, 0, x2, y1);
- * print(mag(x2, y1)); // Prints "85.44003745317531"
- * line(0, 0, x1, y2);
- * print(mag(x1, y2)); // Prints "72.80109889280519"
- * line(0, 0, x2, y2);
- * print(mag(x2, y2)); // Prints "106.3014581273465"
- * }
- *
- *
- * @alt
- * 4 lines of different length radiate from top left of canvas.
- *
- */
-p5.prototype.mag = function(x, y) {
- return hypot(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).
- *
- * @method map
- * @param {Number} value the incoming value to be converted
- * @param {Number} start1 lower bound of the value's current range
- * @param {Number} stop1 upper bound of the value's current range
- * @param {Number} start2 lower bound of the value's target range
- * @param {Number} stop2 upper bound of the value's target range
- * @return {Number} remapped number
- * @example
- *
- * var value = 25;
- * var m = map(value, 0, 100, 0, width);
- * ellipse(m, 50, 10, 10);
- *
- *
- * @alt
- * 10 by 10 white ellipse with in mid left canvas
- * 2 25 by 25 white ellipses move with mouse x. Bottom has more range from X
- *
- */
-p5.prototype.map = function(n, start1, stop1, start2, stop2) {
- return ((n-start1)/(stop1-start1))*(stop2-start2)+start2;
-};
-
-/**
- * Determines the largest value in a sequence of numbers, and then returns
- * that value. max() accepts any number of Number parameters, or an Array
- * of any length.
- *
- * @method max
- * @param {Number|Array} n0 Numbers to compare
- * @return {Number} maximum Number
- * @example
- *
- * function setup() {
- * // Change the elements in the array and run the sketch
- * // to show how max() works!
- * numArray = new Array(2,1,5,4,8,9);
- * fill(0);
- * noStroke();
- * text("Array Elements", 0, 10);
- * // Draw all numbers in the array
- * var spacing = 15;
- * var elemsY = 25;
- * for(var i = 0; i < numArray.length; i++) {
- * text(numArray[i], i * spacing, elemsY);
- * }
- * maxX = 33;
- * maxY = 80;
- * // Draw the Maximum value in the array.
- * textSize(32);
- * text(max(numArray), maxX, maxY);
- * }
- *
- *
- * @alt
- * Small text at top reads: Array Elements 2 1 5 4 8 9. Large text at center: 9
- *
- */
-p5.prototype.max = function() {
- if (arguments[0] instanceof Array) {
- return Math.max.apply(null,arguments[0]);
- } else {
- return Math.max.apply(null,arguments);
- }
-};
-
-/**
- * Determines the smallest value in a sequence of numbers, and then returns
- * that value. min() accepts any number of Number parameters, or an Array
- * of any length.
- *
- * @method min
- * @param {Number|Array} n0 Numbers to compare
- * @return {Number} minimum Number
- * @example
- *
- * function setup() {
- * // Change the elements in the array and run the sketch
- * // to show how min() works!
- * numArray = new Array(2,1,5,4,8,9);
- * fill(0);
- * noStroke();
- * text("Array Elements", 0, 10);
- * // Draw all numbers in the array
- * var spacing = 15;
- * var elemsY = 25;
- * for(var i = 0; i < numArray.length; i++) {
- * text(numArray[i], i * spacing, elemsY);
- * }
- * maxX = 33;
- * maxY = 80;
- * // Draw the Minimum value in the array.
- * textSize(32);
- * text(min(numArray), maxX, maxY);
- * }
- *
- *
- * @alt
- * Small text at top reads: Array Elements 2 1 5 4 8 9. Large text at center: 1
- *
- */
-p5.prototype.min = function() {
- if (arguments[0] instanceof Array) {
- return Math.min.apply(null,arguments[0]);
- } else {
- return Math.min.apply(null,arguments);
- }
-};
-
-/**
- * Normalizes a number from another range into a value between 0 and 1.
- * Identical to map(value, low, high, 0, 1).
- * Numbers outside of the range are not clamped to 0 and 1, because
- * out-of-range values are often intentional and useful. (See the second
- * example above.)
- *
- * @method norm
- * @param {Number} value incoming value to be normalized
- * @param {Number} start lower bound of the value's current range
- * @param {Number} stop upper bound of the value's current range
- * @return {Number} normalized number
- * @example
- *
- *
- * @alt
- * ellipse moves with mouse. 0 shown left & 100 right and updating values center
- *
- */
-p5.prototype.norm = function(n, start, stop) {
- return this.map(n, start, stop, 0, 1);
-};
-
-/**
- * Facilitates exponential expressions. The pow() function is an efficient
- * way of multiplying numbers by themselves (or their reciprocals) in large
- * quantities. For example, pow(3, 5) is equivalent to the expression
- * 3*3*3*3*3 and pow(3, -5) is equivalent to 1 / 3*3*3*3*3. Maps to
- * Math.pow().
- *
- * @method pow
- * @param {Number} n base of the exponential expression
- * @param {Number} e power by which to raise the base
- * @return {Number} n^e
- * @example
- *
- *
- * @alt
- * small to large ellipses radiating from top left of canvas
- *
- */
-p5.prototype.pow = Math.pow;
-
-/**
- * Calculates the integer closest to the n parameter. For example,
- * round(133.8) returns the value 134. Maps to Math.round().
- *
- * @method round
- * @param {Number} n number to round
- * @return {Number} rounded number
- * @example
- *
- * function draw() {
- * background(200);
- * //map, mouseX between 0 and 5.
- * var ax = map(mouseX, 0, 100, 0, 5);
- * var ay = 66;
- *
- * // Round the mapped number.
- * var bx = round(map(mouseX, 0, 100, 0,5));
- * var by = 33;
- *
- * // Multiply the mapped numbers by 20 to more easily
- * // see the changes.
- * stroke(0);
- * fill(0);
- * line(0, ay, ax * 20, ay);
- * line(0, by, bx * 20, by);
- *
- * // Reformat the float returned by map and draw it.
- * noStroke();
- * text(nfc(ax, 2,2), ax, ay - 5);
- * text(nfc(bx,1,1), bx, by - 5);
- * }
- *
- *
- * @alt
- * horizontal center line squared values displayed on top and regular on bottom.
- *
- */
-p5.prototype.round = Math.round;
-
-/**
- * Squares a number (multiplies a number by itself). The result is always a
- * positive number, as multiplying two negative numbers always yields a
- * positive result. For example, -1 * -1 = 1.
- *
- * @method sq
- * @param {Number} n number to square
- * @return {Number} squared number
- * @example
- *
- *
- * @alt
- * horizontal center line squared values displayed on top and regular on bottom.
- *
- */
-p5.prototype.sq = function(n) { return n*n; };
-
-/**
- * Calculates the square root of a number. The square root of a number is
- * always positive, even though there may be a valid negative root. The
- * square root s of number a is such that s*s = a. It is the opposite of
- * squaring. Maps to Math.sqrt().
- *
- * @method sqrt
- * @param {Number} n non-negative number to square root
- * @return {Number} square root of number
- * @example
- *
- *
- * @alt
- * horizontal center line squareroot values displayed on top and regular on bottom.
- *
- */
-p5.prototype.sqrt = Math.sqrt;
-
-// Calculate the length of the hypotenuse of a right triangle
-// This won't under- or overflow in intermediate steps
-// https://en.wikipedia.org/wiki/Hypot
-function hypot(x, y, z) {
- // Use the native implementation if it's available
- if (typeof Math.hypot === 'function') {
- return Math.hypot.apply(null, arguments);
- }
-
- // Otherwise use the V8 implementation
- // https://github.com/v8/v8/blob/8cd3cf297287e581a49e487067f5cbd991b27123/src/js/math.js#L217
- var length = arguments.length;
- var args = [];
- var max = 0;
- for (var i = 0; i < length; i++) {
- var n = arguments[i];
- n = +n;
- if (n === Infinity || n === -Infinity) {
- return Infinity;
- }
- n = Math.abs(n);
- if (n > max) {
- max = n;
- }
- args[i] = n;
- }
-
- if (max === 0) {
- max = 1;
- }
- var sum = 0;
- var compensation = 0;
- for (var j = 0; j < length; j++) {
- var m = args[j] / max;
- var summand = m * m - compensation;
- var preliminary = sum + summand;
- compensation = (preliminary - sum) - summand;
- sum = preliminary;
- }
- return Math.sqrt(sum) * max;
-}
-
-module.exports = p5;
-
-},{"../core/core":37}],64:[function(_dereq_,module,exports){
-/**
- * @module Math
- * @submodule Math
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-
-/**
- * Creates a new p5.Vector (the datatype for storing vectors). This provides 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.
- *
- * @method createVector
- * @param {Number} [x] x component of the vector
- * @param {Number} [y] y component of the vector
- * @param {Number} [z] z component of the vector
- */
-p5.prototype.createVector = function (x, y, z) {
- if (this instanceof p5) {
- return new p5.Vector(this, arguments);
- } else {
- return new p5.Vector(x, y, z);
- }
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],65:[function(_dereq_,module,exports){
-//////////////////////////////////////////////////////////////
-
-// http://mrl.nyu.edu/~perlin/noise/
-// Adapting from PApplet.java
-// which was adapted from toxi
-// which was adapted from the german demo group farbrausch
-// as used in their demo "art": http://www.farb-rausch.de/fr010src.zip
-
-// someday we might consider using "improved noise"
-// http://mrl.nyu.edu/~perlin/paper445.pdf
-// See: https://github.com/shiffman/The-Nature-of-Code-Examples-p5.js/
-// blob/master/introduction/Noise1D/noise.js
-
-/**
- * @module Math
- * @submodule Noise
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-var PERLIN_YWRAPB = 4;
-var PERLIN_YWRAP = 1<random() function.
- * It was invented by Ken Perlin in the 1980s and been used since in
- * graphical applications to produce procedural textures, natural motion,
- * shapes, terrains etc.
The main difference to the
- * random() function is that Perlin noise is defined in an infinite
- * n-dimensional space where each pair of coordinates corresponds to a
- * fixed semi-random value (fixed only for the lifespan of the program; see
- * the noiseSeed() function). p5.js can compute 1D, 2D and 3D noise,
- * depending on the number of coordinates given. The resulting value will
- * always be between 0.0 and 1.0. The noise value can be animated by moving
- * through the noise space as demonstrated in the example above. The 2nd
- * and 3rd dimension can also be interpreted as time.
The actual
- * noise is structured similar to an audio signal, in respect to the
- * function's use of frequencies. Similar to the concept of harmonics in
- * physics, perlin noise is computed over several octaves which are added
- * together for the final result.
Another way to adjust the
- * character of the resulting sequence is the scale of the input
- * coordinates. As the function works within an infinite space the value of
- * the coordinates doesn't matter as such, only the distance between
- * successive coordinates does (eg. when using noise() within a
- * loop). As a general rule the smaller the difference between coordinates,
- * the smoother the resulting noise sequence will be. Steps of 0.005-0.03
- * work best for most applications, but this will differ depending on use.
- *
- *
- * @method noise
- * @param {Number} x x-coordinate in noise space
- * @param {Number} y y-coordinate in noise space
- * @param {Number} z z-coordinate in noise space
- * @return {Number} Perlin noise value (between 0 and 1) at specified
- * coordinates
- * @example
- *
- * var xoff = 0.0;
- *
- * function draw() {
- * background(204);
- * xoff = xoff + .01;
- * var n = noise(xoff) * width;
- * line(n, 0, n, height);
- * }
- *
- *
- *
- * var noiseScale=0.02;
- *
- * function draw() {
- * background(0);
- * for (var x=0; x < width; x++) {
- * var noiseVal = noise((mouseX+x)*noiseScale, mouseY*noiseScale);
- * stroke(noiseVal*255);
- * line(x, mouseY+noiseVal*80, x, height);
- * }
- * }
- *
- *
- *
- * @alt
- * vertical line moves left to right with updating noise values.
- * horizontal wave pattern effected by mouse x-position & updating noise values.
- *
- */
-
-p5.prototype.noise = function(x,y,z) {
- y = y || 0;
- z = z || 0;
-
- if (perlin == null) {
- perlin = new Array(PERLIN_SIZE + 1);
- for (var i = 0; i < PERLIN_SIZE + 1; i++) {
- perlin[i] = Math.random();
- }
- }
-
- if (x<0) { x=-x; }
- if (y<0) { y=-y; }
- if (z<0) { z=-z; }
-
- var xi=Math.floor(x), yi=Math.floor(y), zi=Math.floor(z);
- var xf = x - xi;
- var yf = y - yi;
- var zf = z - zi;
- var rxf, ryf;
-
- var r=0;
- var ampl=0.5;
-
- var n1,n2,n3;
-
- for (var o=0; o=1.0) { xi++; xf--; }
- if (yf>=1.0) { yi++; yf--; }
- if (zf>=1.0) { zi++; zf--; }
- }
- return r;
-};
-
-
-/**
- *
- * Adjusts the character and level of detail produced by the Perlin noise
- * 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
- * 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()
- * function can be adapted to fit very specific needs and characteristics.
- *
- * @method noiseDetail
- * @param {Number} lod number of octaves to be used by the noise
- * @param {Number} falloff falloff factor for each octave
- * @example
- *
- *
- * @alt
- * 2 vertical grey smokey patterns affected my mouse x-position and noise.
- *
- */
-p5.prototype.noiseDetail = function(lod, falloff) {
- if (lod>0) { perlin_octaves=lod; }
- if (falloff>0) { perlin_amp_falloff=falloff; }
-};
-
-/**
- * Sets the seed value for noise(). By default, noise()
- * produces different results each time the program is run. Set the
- * value parameter to a constant to return the same pseudo-random
- * numbers each time the software is run.
- *
- * @method noiseSeed
- * @param {Number} seed the seed value
- * @example
- *
- * var xoff = 0.0;
- *
- * function setup() {
- * noiseSeed(99);
- * stroke(0, 10);
- * }
- *
- * function draw() {
- * xoff = xoff + .01;
- * var n = noise(xoff) * width;
- * line(n, 0, n, height);
- * }
- *
- *
- *
- * @alt
- * vertical grey lines drawing in pattern affected by noise.
- *
- */
-p5.prototype.noiseSeed = function(seed) {
- // Linear Congruential Generator
- // Variant of a Lehman Generator
- var lcg = (function() {
- // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
- // m is basically chosen to be large (as it is the max period)
- // and for its relationships to a and c
- var m = 4294967296,
- // a - 1 should be divisible by m's prime factors
- a = 1664525,
- // c and m should be co-prime
- c = 1013904223,
- seed, z;
- return {
- setSeed : function(val) {
- // pick a random seed if val is undefined or null
- // the >>> 0 casts the seed to an unsigned 32-bit integer
- z = seed = (val == null ? Math.random() * m : val) >>> 0;
- },
- getSeed : function() {
- return seed;
- },
- rand : function() {
- // define the recurrence relationship
- z = (a * z + c) % m;
- // return a float in [0, 1)
- // if z = m then z / m = 0 therefore (z % m) / m < 1 always
- return z / m;
- }
- };
- }());
-
- lcg.setSeed(seed);
- perlin = new Array(PERLIN_SIZE + 1);
- for (var i = 0; i < PERLIN_SIZE + 1; i++) {
- perlin[i] = lcg.rand();
- }
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],66:[function(_dereq_,module,exports){
-/**
- * @module Math
- * @submodule Math
- * @requires constants
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-var polarGeometry = _dereq_('./polargeometry');
-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
- * 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.
- *
- * @class p5.Vector
- * @constructor
- * @param {Number} [x] x component of the vector
- * @param {Number} [y] y component of the vector
- * @param {Number} [z] z component of the vector
- * @example
- *
- *
- * @alt
- * 2 white ellipses. One center-left the other bottom right and off canvas
- *
- */
-p5.Vector = function() {
- var x,y,z;
- // This is how it comes in with createVector()
- if(arguments[0] instanceof p5) {
- // save reference to p5 if passed in
- this.p5 = arguments[0];
- x = arguments[1][0] || 0;
- y = arguments[1][1] || 0;
- z = arguments[1][2] || 0;
- // This is what we'll get with new p5.Vector()
- } else {
- x = arguments[0] || 0;
- y = arguments[1] || 0;
- z = arguments[2] || 0;
- }
- /**
- * The x component of the vector
- * @property x
- * @type {Number}
- */
- this.x = x;
- /**
- * The y component of the vector
- * @property y
- * @type {Number}
- */
- this.y = y;
- /**
- * The z component of the vector
- * @property z
- * @type {Number}
- */
- this.z = z;
-};
-
-/**
- * Returns a string representation of a vector v by calling String(v)
- * or v.toString(). This method is useful for logging vectors in the
- * console.
- * @method toString
- * @example
- *
- * function setup() {
- * var v = createVector(20,30);
- * print(String(v)); // prints "p5.Vector Object : [20, 30, 0]"
- * }
- *
- *
- */
-p5.Vector.prototype.toString = function p5VectorToString() {
- return 'p5.Vector Object : ['+ this.x +', '+ this.y +', '+ this.z + ']';
-};
-
-/**
- * Sets the x, y, and z component of the vector using two or three separate
- * variables, the data from a p5.Vector, or the values from a float array.
- * @method set
- *
- * @param {Number|p5.Vector|Array} [x] the x component of the vector or a
- * p5.Vector or an Array
- * @param {Number} [y] the y component of the vector
- * @param {Number} [z] the z component of the vector
- * @example
- *
- *
- * function setup() {
- * var v = createVector(1, 2, 3);
- * v.set(4,5,6); // Sets vector to [4, 5, 6]
- *
- * var v1 = createVector(0, 0, 0);
- * var arr = [1, 2, 3];
- * v1.set(arr); // Sets vector to [1, 2, 3]
- * }
- *
- *
- */
-p5.Vector.prototype.set = function (x, y, z) {
- if (x instanceof p5.Vector) {
- this.x = x.x || 0;
- this.y = x.y || 0;
- this.z = x.z || 0;
- return this;
- }
- if (x instanceof Array) {
- this.x = x[0] || 0;
- this.y = x[1] || 0;
- this.z = x[2] || 0;
- return this;
- }
- this.x = x || 0;
- this.y = y || 0;
- this.z = z || 0;
- return this;
-};
-
-/**
- * Gets a copy of the vector, returns a p5.Vector object.
- *
- * @method copy
- * @return {p5.Vector} the copy of the p5.Vector object
- * @example
- *
- */
-p5.Vector.prototype.copy = function () {
- if (this.p5) {
- return new p5.Vector(this.p5,[this.x, this.y, this.z]);
- } else {
- return new p5.Vector(this.x,this.y,this.z);
- }
-};
-
-/**
- * Adds x, y, and z components to a vector, adds one vector to another, or
- * adds two independent vectors together. The version of the method that adds
- * two vectors together is a static method and returns a p5.Vector, the others
- * acts directly on the vector. See the examples for more context.
- *
- * @method add
- * @chainable
- * @param {Number|p5.Vector|Array} x the x component of the vector to be
- * added or a p5.Vector or an Array
- * @param {Number} [y] the y component of the vector to be
- * added
- * @param {Number} [z] the z component of the vector to be
- * added
- * @return {p5.Vector} the p5.Vector object.
- * @example
- *
- *
- * var v = createVector(1, 2, 3);
- * v.add(4,5,6);
- * // v's components are set to [5, 7, 9]
- *
- *
- */
-p5.Vector.prototype.add = function (x, y, z) {
- if (x instanceof p5.Vector) {
- this.x += x.x || 0;
- this.y += x.y || 0;
- this.z += x.z || 0;
- return this;
- }
- if (x instanceof Array) {
- this.x += x[0] || 0;
- this.y += x[1] || 0;
- this.z += x[2] || 0;
- return this;
- }
- this.x += x || 0;
- this.y += y || 0;
- this.z += z || 0;
- return this;
-};
-
-/**
- * Subtracts x, y, and z components from a vector, subtracts one vector from
- * another, or subtracts two independent vectors. The version of the method
- * that subtracts two vectors is a static method and returns a p5.Vector, the
- * other acts directly on the vector. See the examples for more context.
- *
- * @method sub
- * @chainable
- * @param {Number|p5.Vector|Array} x the x component of the vector or a
- * p5.Vector or an Array
- * @param {Number} [y] the y component of the vector
- * @param {Number} [z] the z component of the vector
- * @return {p5.Vector} p5.Vector object.
- * @example
- *
- *
- * var v = createVector(4, 5, 6);
- * v.sub(1, 1, 1);
- * // v's components are set to [3, 4, 5]
- *
- *
- */
-p5.Vector.prototype.sub = function (x, y, z) {
- if (x instanceof p5.Vector) {
- this.x -= x.x || 0;
- this.y -= x.y || 0;
- this.z -= x.z || 0;
- return this;
- }
- if (x instanceof Array) {
- this.x -= x[0] || 0;
- this.y -= x[1] || 0;
- this.z -= x[2] || 0;
- return this;
- }
- this.x -= x || 0;
- this.y -= y || 0;
- this.z -= z || 0;
- return this;
-};
-
-/**
- * Multiply the vector by a scalar. The static version of this method
- * creates a new p5.Vector while the non static version acts on the vector
- * directly. See the examples for more context.
- *
- * @method mult
- * @chainable
- * @param {Number} n the number to multiply with the vector
- * @return {p5.Vector} a reference to the p5.Vector object (allow chaining)
- * @example
- *
- *
- * var v = createVector(1, 2, 3);
- * v.mult(2);
- * // v's components are set to [2, 4, 6]
- *
- *
- */
-p5.Vector.prototype.mult = function (n) {
- this.x *= n || 0;
- this.y *= n || 0;
- this.z *= n || 0;
- return this;
-};
-
-/**
- * Divide the vector by a scalar. The static version of this method creates a
- * new p5.Vector while the non static version acts on the vector directly.
- * See the examples for more context.
- *
- * @method div
- * @chainable
- * @param {number} n the number to divide the vector by
- * @return {p5.Vector} a reference to the p5.Vector object (allow chaining)
- * @example
- *
- *
- * var v = createVector(6, 4, 2);
- * v.div(2); //v's components are set to [3, 2, 1]
- *
- *
- */
-p5.Vector.prototype.div = function (n) {
- this.x /= n;
- this.y /= n;
- this.z /= n;
- return this;
-};
-
-/**
- * Calculates the magnitude (length) of the vector and returns the result as
- * a float (this is simply the equation sqrt(x*x + y*y + z*z).)
- *
- * @method mag
- * @return {Number} magnitude of the vector
- * @example
- *
- *
- * var v = createVector(20.0, 30.0, 40.0);
- * var m = v.mag();
- * print(m); // Prints "53.85164807134504"
- *
- *
- */
-p5.Vector.prototype.mag = function () {
- return Math.sqrt(this.magSq());
-};
-
-/**
- * Calculates the squared magnitude of the vector and returns the result
- * as a float (this is simply the equation (x*x + y*y + z*z).)
- * Faster if the real length is not required in the
- * case of comparing vectors, etc.
- *
- * @method magSq
- * @return {number} squared magnitude of the vector
- * @example
- *
- */
-p5.Vector.prototype.magSq = function () {
- var x = this.x, y = this.y, z = this.z;
- return (x * x + y * y + z * z);
-};
-
-/**
- * Calculates the dot product of two vectors. The version of the method
- * that computes the dot product of two independent vectors is a static
- * method. See the examples for more context.
- *
- *
- * @method dot
- * @param {Number|p5.Vector} x x component of the vector or a p5.Vector
- * @param {Number} [y] y component of the vector
- * @param {Number} [z] z component of the vector
- * @return {Number} the dot product
- *
- * @example
- *
- *
- * var v1 = createVector(1, 2, 3);
- * var v2 = createVector(2, 3, 4);
- *
- * print(v1.dot(v2)); // Prints "20"
- *
- *
- */
-p5.Vector.prototype.dot = function (x, y, z) {
- if (x instanceof p5.Vector) {
- return this.dot(x.x, x.y, x.z);
- }
- return this.x * (x || 0) +
- this.y * (y || 0) +
- this.z * (z || 0);
-};
-
-/**
- * Calculates and returns a vector composed of the cross product between
- * two vectors. Both the static and non static methods return a new p5.Vector.
- * See the examples for more context.
- *
- * @method cross
- * @param {p5.Vector} v p5.Vector to be crossed
- * @return {p5.Vector} p5.Vector composed of cross product
- * @example
- *
- */
-p5.Vector.prototype.cross = function (v) {
- var x = this.y * v.z - this.z * v.y;
- var y = this.z * v.x - this.x * v.z;
- var z = this.x * v.y - this.y * v.x;
- if (this.p5) {
- return new p5.Vector(this.p5,[x,y,z]);
- } else {
- return new p5.Vector(x,y,z);
- }
-};
-
-/**
- * Calculates the Euclidean distance between two points (considering a
- * point as a vector object).
- *
- * @method dist
- * @param {p5.Vector} v the x, y, and z coordinates of a p5.Vector
- * @return {Number} the distance
- * @example
- *
- *
- * var v1 = createVector(1, 0, 0);
- * var v2 = createVector(0, 1, 0);
- *
- * var distance = v1.dist(v2); // distance is 1.4142...
- *
- *
- */
-p5.Vector.prototype.dist = function (v) {
- var d = v.copy().sub(this);
- return d.mag();
-};
-
-/**
- * Normalize the vector to length 1 (make it a unit vector).
- *
- * @method normalize
- * @return {p5.Vector} normalized p5.Vector
- * @example
- *
- *
- * var v = createVector(10, 20, 2);
- * // v has components [10.0, 20.0, 2.0]
- * v.normalize();
- * // v's components are set to
- * // [0.4454354, 0.8908708, 0.089087084]
- *
- *
- *
- */
-p5.Vector.prototype.normalize = function () {
- return this.mag() === 0 ? this : this.div(this.mag());
-};
-
-/**
- * Limit the magnitude of this vector to the value used for the max
- * parameter.
- *
- * @method limit
- * @param {Number} max the maximum magnitude for the vector
- * @return {p5.Vector} the modified p5.Vector
- * @example
- *
- *
- * var v = createVector(10, 20, 2);
- * // v has components [10.0, 20.0, 2.0]
- * v.limit(5);
- * // v's components are set to
- * // [2.2271771, 4.4543543, 0.4454354]
- *
- *
- */
-p5.Vector.prototype.limit = function (max) {
- var mSq = this.magSq();
- if(mSq > max*max) {
- this.div(Math.sqrt(mSq)); //normalize it
- this.mult(max);
- }
- return this;
-};
-
-/**
- * Set the magnitude of this vector to the value used for the len
- * parameter.
- *
- * @method setMag
- * @param {number} len the new length for this vector
- * @return {p5.Vector} the modified p5.Vector
- * @example
- *
- *
- * var v = createVector(10, 20, 2);
- * // v has components [10.0, 20.0, 2.0]
- * v.setMag(10);
- * // v's components are set to [6.0, 8.0, 0.0]
- *
- *
- */
-p5.Vector.prototype.setMag = function (n) {
- return this.normalize().mult(n);
-};
-
-/**
- * Calculate the angle of rotation for this vector (only 2D vectors)
- *
- * @method heading
- * @return {Number} the angle of rotation
- * @example
- *
- */
-p5.Vector.prototype.heading = function () {
- var h = Math.atan2(this.y, this.x);
- if (this.p5) {
- if (this.p5._angleMode === constants.RADIANS) {
- return h;
- } else {
- return polarGeometry.radiansToDegrees(h);
- }
- } else {
- return h;
- }
-};
-
-/**
- * Rotate the vector by an angle (only 2D vectors), magnitude remains the
- * same
- *
- * @method rotate
- * @param {number} angle the angle of rotation
- * @return {p5.Vector} the modified p5.Vector
- * @example
- *
- *
- * var v = createVector(10.0, 20.0);
- * // v has components [10.0, 20.0, 0.0]
- * v.rotate(HALF_PI);
- * // v's components are set to [-20.0, 9.999999, 0.0]
- *
- *
- */
-p5.Vector.prototype.rotate = function (a) {
- var newHeading = this.heading() + a;
- if (this.p5) {
- if (this.p5._angleMode === constants.DEGREES) {
- newHeading = polarGeometry.degreesToRadians(newHeading);
- }
- }
- var mag = this.mag();
- this.x = Math.cos(newHeading) * mag;
- this.y = Math.sin(newHeading) * mag;
- return this;
-};
-
-/**
- * Linear interpolate the vector to another vector
- *
- * @method lerp
- * @param {p5.Vector} x the x component or the p5.Vector to lerp to
- * @param {p5.Vector} [y] y the y component
- * @param {p5.Vector} [z] z the z component
- * @param {Number} amt the amount of interpolation; some value between 0.0
- * (old vector) and 1.0 (new vector). 0.1 is very near
- * the new vector. 0.5 is halfway in between.
- * @return {p5.Vector} the modified p5.Vector
- * @example
- *
- *
- * var v = createVector(1, 1, 0);
- *
- * v.lerp(3, 3, 0, 0.5); // v now has components [2,2,0]
- *
- *
- *
- *
- *
- * var v1 = createVector(0, 0, 0);
- * var v2 = createVector(100, 100, 0);
- *
- * var v3 = p5.Vector.lerp(v1, v2, 0.5);
- * // v3 has components [50,50,0]
- *
- *
- */
-p5.Vector.prototype.lerp = function (x, y, z, amt) {
- if (x instanceof p5.Vector) {
- return this.lerp(x.x, x.y, x.z, y);
- }
- this.x += (x - this.x) * amt || 0;
- this.y += (y - this.y) * amt || 0;
- this.z += (z - this.z) * amt || 0;
- return this;
-};
-
-/**
- * Return a representation of this vector as a float array. This is only
- * for temporary use. If used in any other fashion, the contents should be
- * copied by using the p5.Vector.copy() method to copy into your own
- * array.
- *
- * @method array
- * @return {Array} an Array with the 3 values
- * @example
- *
- * function setup() {
- * var v = createVector(20,30);
- * print(v.array()); // Prints : Array [20, 30, 0]
- * }
- *
- *
- *
- * var v = createVector(10.0, 20.0, 30.0);
- * var f = v.array();
- * print(f[0]); // Prints "10.0"
- * print(f[1]); // Prints "20.0"
- * print(f[2]); // Prints "30.0"
- *
- *
- */
-p5.Vector.prototype.array = function () {
- return [this.x || 0, this.y || 0, this.z || 0];
-};
-
-/**
- * Equality check against a p5.Vector
- *
- * @method equals
- * @param {Number|p5.Vector|Array} [x] the x component of the vector or a
- * p5.Vector or an Array
- * @param {Number} [y] the y component of the vector
- * @param {Number} [z] the z component of the vector
- * @return {Boolean} whether the vectors are equals
- * @example
- *
- */
-p5.Vector.prototype.equals = function (x, y, z) {
- var a, b, c;
- if (x instanceof p5.Vector) {
- a = x.x || 0;
- b = x.y || 0;
- c = x.z || 0;
- } else if (x instanceof Array) {
- a = x[0] || 0;
- b = x[1] || 0;
- c = x[2] || 0;
- } else {
- a = x || 0;
- b = y || 0;
- c = z || 0;
- }
- return this.x === a && this.y === b && this.z === c;
-};
-
-
-// Static Methods
-
-
-/**
- * Make a new 2D unit vector from an angle
- *
- * @method fromAngle
- * @static
- * @param {Number} angle the desired angle
- * @return {p5.Vector} the new p5.Vector object
- * @example
- *
- *
- * function draw() {
- * background (200);
- *
- * // Create a variable, proportional to the mouseX,
- * // varying from 0-360, to represent an angle in degrees.
- * angleMode(DEGREES);
- * var myDegrees = map(mouseX, 0,width, 0,360);
- *
- * // Display that variable in an onscreen text.
- * // (Note the nfc() function to truncate additional decimal places,
- * // and the "\xB0" character for the degree symbol.)
- * var readout = "angle = " + nfc(myDegrees,1,1) + "\xB0"
- * noStroke();
- * fill (0);
- * text (readout, 5, 15);
- *
- * // Create a p5.Vector using the fromAngle function,
- * // and extract its x and y components.
- * var v = p5.Vector.fromAngle(radians(myDegrees));
- * var vx = v.x;
- * var vy = v.y;
- *
- * push();
- * translate (width/2, height/2);
- * noFill();
- * stroke (150);
- * line (0,0, 30,0);
- * stroke (0);
- * line (0,0, 30*vx, 30*vy);
- * pop()
- * }
- *
- *
- */
-p5.Vector.fromAngle = function(angle) {
- if (this.p5) {
- if (this.p5._angleMode === constants.DEGREES) {
- angle = polarGeometry.degreesToRadians(angle);
- }
- }
- if (this.p5) {
- return new p5.Vector(this.p5,[Math.cos(angle),Math.sin(angle),0]);
- } else {
- return new p5.Vector(Math.cos(angle),Math.sin(angle),0);
- }
-};
-
-/**
- * Make a new 2D unit vector from a random angle
- *
- * @method random2D
- * @static
- * @return {p5.Vector} the new p5.Vector object
- * @example
- *
- *
- * var v = p5.Vector.random2D();
- * // May make v's attributes something like:
- * // [0.61554617, -0.51195765, 0.0] or
- * // [-0.4695841, -0.14366731, 0.0] or
- * // [0.6091097, -0.22805278, 0.0]
- *
- *
- */
-p5.Vector.random2D = function () {
- var angle;
- // A lot of nonsense to determine if we know about a
- // p5 sketch and whether we should make a random angle in degrees or radians
- if (this.p5) {
- if (this.p5._angleMode === constants.DEGREES) {
- angle = this.p5.random(360);
- } else {
- angle = this.p5.random(constants.TWO_PI);
- }
- } else {
- angle = Math.random()*Math.PI*2;
- }
- return this.fromAngle(angle);
-};
-
-/**
- * Make a new random 3D unit vector.
- *
- * @method random3D
- * @static
- * @return {p5.Vector} the new p5.Vector object
- * @example
- *
- *
- * var v = p5.Vector.random3D();
- * // May make v's attributes something like:
- * // [0.61554617, -0.51195765, 0.599168] or
- * // [-0.4695841, -0.14366731, -0.8711202] or
- * // [0.6091097, -0.22805278, -0.7595902]
- *
- *
- */
-p5.Vector.random3D = function () {
- var angle,vz;
- // If we know about p5
- if (this.p5) {
- angle = this.p5.random(0,constants.TWO_PI);
- vz = this.p5.random(-1,1);
- } else {
- angle = Math.random()*Math.PI*2;
- vz = Math.random()*2-1;
- }
- var vx = Math.sqrt(1-vz*vz)*Math.cos(angle);
- var vy = Math.sqrt(1-vz*vz)*Math.sin(angle);
- if (this.p5) {
- return new p5.Vector(this.p5,[vx,vy,vz]);
- } else {
- return new p5.Vector(vx,vy,vz);
- }
-};
-
-
-/**
- * Adds two vectors together and returns a new one.
- *
- * @static
- * @param {p5.Vector} v1 a p5.Vector to add
- * @param {p5.Vector} v2 a p5.Vector to add
- * @param {p5.Vector} target if undefined a new vector will be created
- * @return {p5.Vector} the resulting p5.Vector
- *
- */
-
-p5.Vector.add = function (v1, v2, target) {
- if (!target) {
- target = v1.copy();
- } else {
- target.set(v1);
- }
- target.add(v2);
- return target;
-};
-
-/**
- * Subtracts one p5.Vector from another and returns a new one. The second
- * vector (v2) is subtracted from the first (v1), resulting in v1-v2.
- *
- * @static
- * @param {p5.Vector} v1 a p5.Vector to subtract from
- * @param {p5.Vector} v2 a p5.Vector to subtract
- * @param {p5.Vector} target if undefined a new vector will be created
- * @return {p5.Vector} the resulting p5.Vector
- */
-
-p5.Vector.sub = function (v1, v2, target) {
- if (!target) {
- target = v1.copy();
- } else {
- target.set(v1);
- }
- target.sub(v2);
- return target;
-};
-
-
-/**
- * Multiplies a vector by a scalar and returns a new vector.
- *
- * @static
- * @param {p5.Vector} v the p5.Vector to multiply
- * @param {Number} n the scalar
- * @param {p5.Vector} target if undefined a new vector will be created
- * @return {p5.Vector} the resulting new p5.Vector
- */
-p5.Vector.mult = function (v, n, target) {
- if (!target) {
- target = v.copy();
- } else {
- target.set(v);
- }
- target.mult(n);
- return target;
-};
-
-/**
- * Divides a vector by a scalar and returns a new vector.
- *
- * @static
- * @param {p5.Vector} v the p5.Vector to divide
- * @param {Number} n the scalar
- * @param {p5.Vector} target if undefined a new vector will be created
- * @return {p5.Vector} the resulting new p5.Vector
- */
-p5.Vector.div = function (v, n, target) {
- if (!target) {
- target = v.copy();
- } else {
- target.set(v);
- }
- target.div(n);
- return target;
-};
-
-
-/**
- * Calculates the dot product of two vectors.
- *
- * @static
- * @param {p5.Vector} v1 the first p5.Vector
- * @param {p5.Vector} v2 the second p5.Vector
- * @return {Number} the dot product
- */
-p5.Vector.dot = function (v1, v2) {
- return v1.dot(v2);
-};
-
-/**
- * Calculates the cross product of two vectors.
- *
- * @static
- * @param {p5.Vector} v1 the first p5.Vector
- * @param {p5.Vector} v2 the second p5.Vector
- * @return {Number} the cross product
- */
-p5.Vector.cross = function (v1, v2) {
- return v1.cross(v2);
-};
-
-/**
- * Calculates the Euclidean distance between two points (considering a
- * point as a vector object).
- *
- * @static
- * @param {p5.Vector} v1 the first p5.Vector
- * @param {p5.Vector} v2 the second p5.Vector
- * @return {Number} the distance
- */
-p5.Vector.dist = function (v1,v2) {
- return v1.dist(v2);
-};
-
-/**
- * Linear interpolate a vector to another vector and return the result as a
- * new vector.
- *
- * @static
- * @param {p5.Vector} v1 a starting p5.Vector
- * @param {p5.Vector} v2 the p5.Vector to lerp to
- * @param {Number} the amount of interpolation; some value between 0.0
- * (old vector) and 1.0 (new vector). 0.1 is very near
- * the new vector. 0.5 is halfway in between.
- */
-p5.Vector.lerp = function (v1, v2, amt, target) {
- if (!target) {
- target = v1.copy();
- } else {
- target.set(v1);
- }
- target.lerp(v2, amt);
- return target;
-};
-
-/**
- * Calculates and returns the angle (in radians) between two vectors.
- * @method angleBetween
- * @static
- * @param {p5.Vector} v1 the x, y, and z components of a p5.Vector
- * @param {p5.Vector} v2 the x, y, and z components of a p5.Vector
- * @return {Number} the angle between (in radians)
- * @example
- *
- *
- * var v1 = createVector(1, 0, 0);
- * var v2 = createVector(0, 1, 0);
- *
- * var angle = p5.Vector.angleBetween(v1, v2);
- * // angle is PI/2
- *
- *
- */
-p5.Vector.angleBetween = function (v1, v2) {
- var angle = Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
- if (this.p5) {
- if (this.p5._angleMode === constants.DEGREES) {
- angle = polarGeometry.radiansToDegrees(angle);
- }
- }
- return angle;
-};
-
-/**
- * @static
- */
-p5.Vector.mag = function (vecT){
- var x = vecT.x,
- y = vecT.y,
- z = vecT.z;
- var magSq = x * x + y * y + z * z;
- return Math.sqrt(magSq);
-};
-
-module.exports = p5.Vector;
-
-},{"../core/constants":36,"../core/core":37,"./polargeometry":67}],67:[function(_dereq_,module,exports){
-
-module.exports = {
-
- degreesToRadians: function(x) {
- return 2 * Math.PI * x / 360;
- },
-
- radiansToDegrees: function(x) {
- return 360 * x / (2 * Math.PI);
- }
-
-};
-
-},{}],68:[function(_dereq_,module,exports){
-/**
- * @module Math
- * @submodule Random
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-var seeded = false;
-var previous = false;
-var y2 = 0;
-
-// Linear Congruential Generator
-// Variant of a Lehman Generator
-var lcg = (function() {
- // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
- // m is basically chosen to be large (as it is the max period)
- // and for its relationships to a and c
- var m = 4294967296,
- // a - 1 should be divisible by m's prime factors
- a = 1664525,
- // c and m should be co-prime
- c = 1013904223,
- seed, z;
- return {
- setSeed : function(val) {
- // pick a random seed if val is undefined or null
- // the >>> 0 casts the seed to an unsigned 32-bit integer
- z = seed = (val == null ? Math.random() * m : val) >>> 0;
- },
- getSeed : function() {
- return seed;
- },
- rand : function() {
- // define the recurrence relationship
- z = (a * z + c) % m;
- // return a float in [0, 1)
- // if z = m then z / m = 0 therefore (z % m) / m < 1 always
- return z / m;
- }
- };
-}());
-
-/**
- * Sets the seed value for random().
- *
- * By default, random() produces different results each time the program
- * is run. Set the seed parameter to a constant to return the same
- * pseudo-random numbers each time the software is run.
- *
- * @method randomSeed
- * @param {Number} seed the seed value
- * @example
- *
- *
- * randomSeed(99);
- * for (var i=0; i < 100; i++) {
- * var r = random(0, 255);
- * stroke(r);
- * line(i, 0, i, 100);
- * }
- *
- *
- *
- * @alt
- * many vertical lines drawn in white, black or grey.
- *
- */
-p5.prototype.randomSeed = function(seed) {
- lcg.setSeed(seed);
- seeded = true;
- previous = false;
-};
-
-/**
- * Return a random floating-point number.
- *
- * Takes either 0, 1 or 2 arguments.
- *
- * If no argument is given, returns a random number from 0
- * up to (but not including) 1.
- *
- * If one argument is given and it is a number, returns a random number from 0
- * up to (but not including) the number.
- *
- * If one argument is given and it is an array, returns a random element from
- * that array.
- *
- * If two arguments are given, returns a random number from the
- * first argument up to (but not including) the second argument.
- *
- * @method random
- * @param {Number} [min] the lower bound (inclusive)
- * @param {Number} [max] the upper bound (exclusive)
- * @return {Number|mixed} the random number or a random element in choices
- * @example
- *
- *
- * for (var i = 0; i < 100; i++) {
- * var r = random(50);
- * stroke(r*5);
- * line(50, i, 50+r, i);
- * }
- *
- *
- *
- *
- * for (var i = 0; i < 100; i++) {
- * var r = random(-50, 50);
- * line(50,i,50+r,i);
- * }
- *
- *
- *
- *
- * // Get a random element from an array using the random(Array) syntax
- * var words = [ "apple", "bear", "cat", "dog" ];
- * var word = random(words); // select random word
- * text(word,10,50); // draw the word
- *
- *
- *
- * @alt
- * 100 horizontal lines from center canvas to right. size+fill change each time
- * 100 horizontal lines from center of canvas. height & side change each render
- * word displayed at random. Either apple, bear, cat, or dog
- *
- */
-/**
- * @method random
- * @param {Array} choices the array to choose from
- * @return {mixed} the random element from the array
- * @example
- */
-p5.prototype.random = function (min, max) {
-
- var rand;
-
- if (seeded) {
- rand = lcg.rand();
- } else {
- rand = Math.random();
- }
- if (typeof min === 'undefined') {
- return rand;
- } else
- if (typeof max === 'undefined') {
- if (min instanceof Array) {
- return min[Math.floor(rand * min.length)];
- } else {
- return rand * min;
- }
- } else {
- if (min > max) {
- var tmp = min;
- min = max;
- max = tmp;
- }
-
- return rand * (max-min) + min;
- }
-};
-
-
-/**
- *
- * 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
- * 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.
- *
- * @method randomGaussian
- * @param {Number} mean the mean
- * @param {Number} sd the standard deviation
- * @return {Number} the random number
- * @example
- *
- * for (var y = 0; y < 100; y++) {
- * var x = randomGaussian(50,15);
- * line(50, y, x, y);
- *}
- *
- *
- * @alt
- * 100 horizontal lines from center of canvas. height & side change each render
- * black lines radiate from center of canvas. size determined each render
- */
-p5.prototype.randomGaussian = function(mean, sd) {
- var y1,x1,x2,w;
- if (previous) {
- y1 = y2;
- previous = false;
- } else {
- do {
- x1 = this.random(2) - 1;
- x2 = this.random(2) - 1;
- w = x1 * x1 + x2 * x2;
- } while (w >= 1);
- w = Math.sqrt((-2 * Math.log(w))/w);
- y1 = x1 * w;
- y2 = x2 * w;
- previous = true;
- }
-
- var m = mean || 0;
- var s = sd || 1;
- return y1*s + m;
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],69:[function(_dereq_,module,exports){
-/**
- * @module Math
- * @submodule Trigonometry
- * @for p5
- * @requires core
- * @requires polargeometry
- * @requires constants
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-var polarGeometry = _dereq_('./polargeometry');
-var constants = _dereq_('../core/constants');
-
-p5.prototype._angleMode = constants.RADIANS;
-
-/**
- * The inverse of cos(), returns the arc cosine of a value. This function
- * expects the values in the range of -1 to 1 and values are returned in
- * the range 0 to PI (3.1415927).
- *
- * @method acos
- * @param {Number} value the value whose arc cosine is to be returned
- * @return {Number} the arc cosine of the given value
- *
- * @example
- *
- *
- * var a = PI;
- * var c = cos(a);
- * var ac = acos(c);
- * // Prints: "3.1415927 : -1.0 : 3.1415927"
- * print(a + " : " + c + " : " + ac);
- *
- *
- *
- *
- *
- * var a = PI + PI/4.0;
- * var c = cos(a);
- * var ac = acos(c);
- * // Prints: "3.926991 : -0.70710665 : 2.3561943"
- * print(a + " : " + c + " : " + ac);
- *
- *
- */
-p5.prototype.acos = function(ratio) {
- if (this._angleMode === constants.RADIANS) {
- return Math.acos(ratio);
- } else {
- return polarGeometry.radiansToDegrees(Math.acos(ratio));
- }
-};
-
-/**
- * The inverse of sin(), returns the arc sine of a value. This function
- * expects the values in the range of -1 to 1 and values are returned
- * in the range -PI/2 to PI/2.
- *
- * @method asin
- * @param {Number} value the value whose arc sine is to be returned
- * @return {Number} the arc sine of the given value
- *
- * @example
- *
- *
- * var a = PI + PI/3;
- * var s = sin(a);
- * var as = asin(s);
- * // Prints: "1.0471976 : 0.86602545 : 1.0471976"
- * print(a + " : " + s + " : " + as);
- *
- *
- *
- *
- *
- * var a = PI + PI/3.0;
- * var s = sin(a);
- * var as = asin(s);
- * // Prints: "4.1887903 : -0.86602545 : -1.0471976"
- * print(a + " : " + s + " : " + as);
- *
- *
- *
- */
-p5.prototype.asin = function(ratio) {
- if (this._angleMode === constants.RADIANS) {
- return Math.asin(ratio);
- } else {
- return polarGeometry.radiansToDegrees(Math.asin(ratio));
- }
-};
-
-/**
- * The inverse of tan(), returns the arc tangent of a value. This function
- * expects the values in the range of -Infinity to Infinity (exclusive) and
- * values are returned in the range -PI/2 to PI/2.
- *
- * @method atan
- * @param {Number} value the value whose arc tangent is to be returned
- * @return {Number} the arc tangent of the given value
- *
- * @example
- *
- *
- * var a = PI + PI/3;
- * var t = tan(a);
- * var at = atan(t);
- * // Prints: "1.0471976 : 1.7320509 : 1.0471976"
- * print(a + " : " + t + " : " + at);
- *
- *
- *
- *
- *
- * var a = PI + PI/3.0;
- * var t = tan(a);
- * var at = atan(t);
- * // Prints: "4.1887903 : 1.7320513 : 1.0471977"
- * print(a + " : " + t + " : " + at);
- *
- *
- *
- */
-p5.prototype.atan = function(ratio) {
- if (this._angleMode === constants.RADIANS) {
- return Math.atan(ratio);
- } else {
- return polarGeometry.radiansToDegrees(Math.atan(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.
- *
- * @method atan2
- * @param {Number} y y-coordinate of the point
- * @param {Number} x x-coordinate of the point
- * @return {Number} the arc tangent of the given point
- *
- * @example
- *
- *
- * @alt
- * 60 by 10 rect at center of canvas rotates with mouse movements
- *
- */
-p5.prototype.atan2 = function (y, x) {
- if (this._angleMode === constants.RADIANS) {
- return Math.atan2(y, x);
- } else {
- return polarGeometry.radiansToDegrees(Math.atan2(y, x));
- }
-};
-
-/**
- * Calculates the cosine of an angle. This function takes into account the
- * current angleMode. Values are returned in the range -1 to 1.
- *
- * @method cos
- * @param {Number} angle the angle
- * @return {Number} the cosine of the angle
- *
- * @example
- *
- *
- * var a = 0.0;
- * var inc = TWO_PI/25.0;
- * for (var i = 0; i < 25; i++) {
- * line(i*4, 50, i*4, 50+cos(a)*40.0);
- * a = a + inc;
- * }
- *
- *
- *
- * @alt
- * vertical black lines form wave patterns, extend-down on left and right side
- *
- */
-p5.prototype.cos = function(angle) {
- if (this._angleMode === constants.RADIANS) {
- return Math.cos(angle);
- } else {
- return Math.cos(this.radians(angle));
- }
-};
-
-/**
- * Calculates the sine of an angle. This function takes into account the
- * current angleMode. Values are returned in the range -1 to 1.
- *
- * @method sin
- * @param {Number} angle the angle
- * @return {Number} the sine of the angle
- *
- * @example
- *
- *
- * var a = 0.0;
- * var inc = TWO_PI/25.0;
- * for (var i = 0; i < 25; i++) {
- * line(i*4, 50, i*4, 50+sin(a)*40.0);
- * a = a + inc;
- * }
- *
- *
- *
- * @alt
- * vertical black lines extend down and up from center to form wave pattern
- *
- */
-p5.prototype.sin = function(angle) {
- if (this._angleMode === constants.RADIANS) {
- return Math.sin(angle);
- } else {
- return Math.sin(this.radians(angle));
- }
-};
-
-/**
- * Calculates the tangent of an angle. This function takes into account
- * the current angleMode. Values are returned in the range -1 to 1.
- *
- * @method tan
- * @param {Number} angle the angle
- * @return {Number} the tangent of the angle
- *
- * @example
- *
- *
- * var a = 0.0;
- * var inc = TWO_PI/50.0;
- * for (var i = 0; i < 100; i = i+2) {
- * line(i, 50, i, 50+tan(a)*2.0);
- * a = a + inc;
- * }
- *
- *
- *
- * @alt
- * vertical black lines end down and up from center to form spike pattern
- *
- */
-p5.prototype.tan = function(angle) {
- if (this._angleMode === constants.RADIANS) {
- return Math.tan(angle);
- } else {
- return Math.tan(this.radians(angle));
- }
-};
-
-/**
- * Converts a radian measurement to its corresponding value in degrees.
- * Radians and degrees are two ways of measuring the same thing. There are
- * 360 degrees in a circle and 2*PI radians in a circle. For example,
- * 90° = PI/2 = 1.5707964.
- *
- * @method degrees
- * @param {Number} radians the radians value to convert to degrees
- * @return {Number} the converted angle
- *
- *
- * @example
- *
- *
- * var rad = PI/4;
- * var deg = degrees(rad);
- * print(rad + " radians is " + deg + " degrees");
- * // Prints: 0.7853981633974483 radians is 45 degrees
- *
- *
- *
- */
-p5.prototype.degrees = function(angle) {
- return polarGeometry.radiansToDegrees(angle);
-};
-
-/**
- * Converts a degree measurement to its corresponding value in radians.
- * Radians and degrees are two ways of measuring the same thing. There are
- * 360 degrees in a circle and 2*PI radians in a circle. For example,
- * 90° = PI/2 = 1.5707964.
- *
- * @method radians
- * @param {Number} degrees the degree value to convert to radians
- * @return {Number} the converted angle
- *
- * @example
- *
- *
- * var deg = 45.0;
- * var rad = radians(deg);
- * print(deg + " degrees is " + rad + " radians");
- * // Prints: 45 degrees is 0.7853981633974483 radians
- *
- *
- */
-p5.prototype.radians = function(angle) {
- return polarGeometry.degreesToRadians(angle);
-};
-
-/**
- * Sets the current mode of p5 to given mode. Default mode is RADIANS.
- *
- * @method angleMode
- * @param {Constant} mode either RADIANS or DEGREES
- *
- * @example
- *
- *
- * function draw(){
- * background(204);
- * angleMode(DEGREES); // Change the mode to DEGREES
- * var a = atan2(mouseY-height/2, mouseX-width/2);
- * translate(width/2, height/2);
- * push();
- * rotate(a);
- * rect(-20, -5, 40, 10); // Larger rectangle is rotating in degrees
- * pop();
- * angleMode(RADIANS); // Change the mode to RADIANS
- * rotate(a); // var a stays the same
- * rect(-40, -5, 20, 10); // Smaller rectangle is rotating in radians
- * }
- *
- *
- *
- * @alt
- * 40 by 10 rect in center rotates with mouse moves. 20 by 10 rect moves faster.
- *
- *
- */
-p5.prototype.angleMode = function(mode) {
- if (mode === constants.DEGREES || mode === constants.RADIANS) {
- this._angleMode = mode;
- }
-};
-
-module.exports = p5;
-
-},{"../core/constants":36,"../core/core":37,"./polargeometry":67}],70:[function(_dereq_,module,exports){
-/**
- * @module Typography
- * @submodule Attributes
- * @for p5
- * @requires core
- * @requires constants
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-/**
- * Sets the current alignment for drawing text. Accepts two
- * arguments: horizAlign (LEFT, CENTER, or RIGHT) and
- * vertAlign (TOP, BOTTOM, CENTER, or BASELINE).
- *
- * The horizAlign parameter is in reference to the x value
- * of the text() function, while the vertAlign parameter is
- * in reference to the y value.
- *
- * So if you write textAlign(LEFT), you are aligning the left
- * edge of your text to the x value you give in text(). If you
- * write textAlign(RIGHT, TOP), you are aligning the right edge
- * of your text to the x value and the top of edge of the text
- * to the y value.
- *
- * @method textAlign
- * @param {Constant} horizAlign horizontal alignment, either LEFT,
- * CENTER, or RIGHT
- * @param {Constant} vertAlign vertical alignment, either TOP,
- * BOTTOM, CENTER, or BASELINE
- * @return {Number}
- * @example
- *
- *
- * @alt
- *Letters ABCD displayed at top right, EFGH at center and IJKL at bottom left.
- *
- */
-p5.prototype.textAlign = function(horizAlign, vertAlign) {
- return this._renderer.textAlign.apply(this._renderer, arguments);
-};
-
-/**
- * Sets/gets the spacing, in pixels, between lines of text. This
- * setting will be used in all subsequent calls to the text() function.
- *
- * @method textLeading
- * @param {Number} leading the size in pixels for spacing between lines
- * @return {Object|Number}
- * @example
- *
- *
- * // Text to display. The "\n" is a "new line" character
- * lines = "L1\nL2\nL3";
- * textSize(12);
- *
- * textLeading(10); // Set leading to 10
- * text(lines, 10, 25);
- *
- * textLeading(20); // Set leading to 20
- * text(lines, 40, 25);
- *
- * textLeading(30); // Set leading to 30
- * text(lines, 70, 25);
- *
- *
- *
- * @alt
- *set L1 L2 & L3 displayed vertically 3 times. spacing increases for each set
- *
- */
-p5.prototype.textLeading = function(theLeading) {
- return this._renderer.textLeading.apply(this._renderer, arguments);
-};
-
-/**
- * Sets/gets the current font size. This size will be used in all subsequent
- * calls to the text() function. Font size is measured in pixels.
- *
- * @method textSize
- * @param {Number} theSize the size of the letters in units of pixels
- * @return {Object|Number}
- * @example
- *
- *
- * @alt
- *Font Size 12 displayed small, Font Size 14 medium & Font Size 16 large
- *
- */
-p5.prototype.textSize = function(theSize) {
- return this._renderer.textSize.apply(this._renderer, arguments);
-};
-
-/**
- * Sets/gets the style of the text for system fonts to NORMAL, ITALIC, or BOLD.
- * Note: this may be is overridden by CSS styling. For non-system fonts
- * (opentype, truetype, etc.) please load styled fonts instead.
- *
- * @method textStyle
- * @param {Number/Constant} theStyle styling for text, either NORMAL,
- * ITALIC, or BOLD
- * @return {Object|String}
- * @example
- *
- *
- * @alt
- *words Font Style Normal displayed normally, Italic in italic and bold in bold
- *
- */
-p5.prototype.textStyle = function(theStyle) {
- return this._renderer.textStyle.apply(this._renderer, arguments);
-};
-
-/**
- * Calculates and returns the width of any character or text string.
- *
- * @method textWidth
- * @param {String} theText the String of characters to measure
- * @return {Number}
- * @example
- *
- *
- * @alt
- *Letter P and p5.js are displayed with vertical lines at end. P is wide
- *
- */
-p5.prototype.textWidth = function(theText) {
- if (theText.length === 0) {
- return 0;
- }
- return this._renderer.textWidth.apply(this._renderer, arguments);
-};
-
-/**
- * Returns the ascent of the current font at its current size. The ascent
- * represents the distance, in pixels, of the tallest character above
- * the baseline.
- *
- * @return {Number}
- * @example
- *
- *
- * var base = height * 0.75;
- * var scalar = 0.8; // Different for each font
- *
- * textSize(32); // Set initial text size
- * var asc = textAscent() * scalar; // Calc ascent
- * line(0, base - asc, width, base - asc);
- * text("dp", 0, base); // Draw text on baseline
- *
- * textSize(64); // Increase text size
- * asc = textAscent() * scalar; // Recalc ascent
- * line(40, base - asc, width, base - asc);
- * text("dp", 40, base); // Draw text on baseline
- *
- *
- */
-p5.prototype.textAscent = function() {
- return this._renderer.textAscent();
-};
-
-/**
- * Returns the descent of the current font at its current size. The descent
- * represents the distance, in pixels, of the character with the longest
- * descender below the baseline.
- *
- * @return {Number}
- * @example
- *
- *
- * var base = height * 0.75;
- * var scalar = 0.8; // Different for each font
- *
- * textSize(32); // Set initial text size
- * var desc = textDescent() * scalar; // Calc ascent
- * line(0, base+desc, width, base+desc);
- * text("dp", 0, base); // Draw text on baseline
- *
- * textSize(64); // Increase text size
- * desc = textDescent() * scalar; // Recalc ascent
- * line(40, base + desc, width, base + desc);
- * text("dp", 40, base); // Draw text on baseline
- *
- *
- */
-p5.prototype.textDescent = function() {
- return this._renderer.textDescent();
-};
-
-/**
- * Helper function to measure ascent and descent.
- */
-p5.prototype._updateTextMetrics = function() {
- return this._renderer._updateTextMetrics();
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],71:[function(_dereq_,module,exports){
-/**
- * @module Typography
- * @submodule Loading & Displaying
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-var constants = _dereq_('../core/constants');
-
-_dereq_('../core/error_helpers');
-
-
-/**
- * Draws text to the screen. Displays the information specified in the first
- * parameter on the screen in the position specified by the additional
- * parameters. A default font will be used unless a font is set with the
- * textFont() function and a default size will be used unless a font is set
- * 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
- * does not fit completely within the rectangle specified will not be drawn
- * to the screen.
- *
- * @method text
- * @param {String} str the alphanumeric symbols to be displayed
- * @param {Number} x x-coordinate of text
- * @param {Number} y y-coordinate of text
- * @param {Number} x2 by default, the width of the text box,
- * see rectMode() for more info
- * @param {Number} y2 by default, the height of the text box,
- * see rectMode() for more info
- * @return {Object} this
- * @example
- *
- *
- * s = "The quick brown fox jumped over the lazy dog.";
- * fill(50);
- * text(s, 10, 10, 70, 80); // Text wraps within text box
- *
- *
- *
- * @alt
- *'word' displayed 3 times going from black, blue to translucent blue
- * The quick brown fox jumped over the lazy dog.
- *
- */
-p5.prototype.text = function(str, x, y, maxWidth, maxHeight) {
- return (!(this._renderer._doFill || this._renderer._doStroke)) ? this :
- this._renderer.text.apply(this._renderer, arguments);
-};
-
-/**
- * Sets the current font that will be drawn with the text() function.
- *
- * @method textFont
- * @param {Object|String} f a font loaded via loadFont(), or a String
- * representing a web safe font (a font
- * that is generally available across all systems).
- * @return {Object} this
- * @example
- *
- */
-p5.prototype.append = function(array, value) {
- array.push(value);
- return array;
-};
-
-/**
- * Copies an array (or part of an array) to another array. The src array is
- * copied to the dst array, beginning at the position specified by
- * srcPosition and into the position specified by dstPosition. The number of
- * 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),
- * 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.
- *
- * @method arrayCopy
- * @param {Array} src the source Array
- * @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 {Number} [length] number of Array elements to be copied
- *
- * @example
- *
- */
-p5.prototype.arrayCopy = function(
- src,
- srcPosition,
- dst,
- dstPosition,
- length) {
-
- // the index to begin splicing from dst array
- var start,
- end;
-
- if (typeof length !== 'undefined') {
-
- end = Math.min(length, src.length);
- start = dstPosition;
- src = src.slice(srcPosition, end + srcPosition);
-
- } else {
-
- if (typeof dst !== 'undefined') { // src, dst, length
- // rename so we don't get confused
- end = dst;
- end = Math.min(end, src.length);
- } else { // src, dst
- end = src.length;
- }
-
- start = 0;
- // rename so we don't get confused
- dst = srcPosition;
- src = src.slice(0, end);
- }
-
- // Since we are not returning the array and JavaScript is pass by reference
- // we must modify the actual values of the array
- // instead of reassigning arrays
- Array.prototype.splice.apply(dst, [start, end].concat(src));
-
-};
-
-/**
- * Concatenates two arrays, maps to Array.concat(). Does not modify the
- * input arrays.
- *
- * @method concat
- * @param {Array} a first Array to concatenate
- * @param {Array} b second Array to concatenate
- * @return {Array} concatenated array
- *
- * @example
- *
- */
-p5.prototype.shorten = function(list) {
- list.pop();
- return list;
-};
-
-/**
- * Randomizes the order of the elements of an array. Implements
- *
- * Fisher-Yates Shuffle Algorithm.
- *
- * @method shuffle
- * @param {Array} array Array to shuffle
- * @param {Boolean} [bool] modify passed array
- * @return {Array} shuffled Array
- * @example
- *
- * function setup() {
- * var regularArr = ['ABC', 'def', createVector(), TAU, Math.E];
- * print(regularArr);
- * shuffle(regularArr, true); // force modifications to passed array
- * print(regularArr);
- *
- * // By default shuffle() returns a shuffled cloned array:
- * var newArr = shuffle(regularArr);
- * print(regularArr);
- * print(newArr);
- * }
- *
- */
-p5.prototype.shuffle = function(arr, bool) {
- var isView = ArrayBuffer && ArrayBuffer.isView && ArrayBuffer.isView(arr);
- arr = bool || isView ? arr : arr.slice();
-
- var rnd, tmp, idx = arr.length;
- while (idx > 1) {
- rnd = Math.random()*idx | 0;
-
- tmp = arr[--idx];
- arr[idx] = arr[rnd];
- arr[rnd] = tmp;
- }
-
- return arr;
-};
-
-/**
- * Sorts an array of numbers from smallest to largest, or puts an array of
- * words in alphabetical order. The original array is not modified; a
- * re-ordered array is returned. The count parameter states the number of
- * elements to sort. For example, if there are 12 elements in an array and
- * count is set to 5, only the first 5 elements in the array will be sorted.
- *
- * @method sort
- * @param {Array} list Array to sort
- * @param {Number} [count] number of elements to sort, starting from 0
- *
- * @example
- *
- * function setup() {
- * var words = new Array("banana", "apple", "pear","lime");
- * print(words); // ["banana", "apple", "pear", "lime"]
- * var count = 4; // length of array
- *
- * words = sort(words, count);
- * print(words); // ["apple", "banana", "lime", "pear"]
- * }
- *
- *
- * function setup() {
- * var numbers = new Array(2,6,1,5,14,9,8,12);
- * print(numbers); // [2,6,1,5,14,9,8,12]
- * var count = 5; // Less than the length of the array
- *
- * numbers = sort(numbers, count);
- * print(numbers); // [1,2,5,6,14,9,8,12]
- * }
- *
- */
-p5.prototype.sort = function(list, count) {
- var arr = count ? list.slice(0, Math.min(count, list.length)) : list;
- var rest = count ? list.slice(Math.min(count, list.length)) : [];
- if (typeof arr[0] === 'string') {
- arr = arr.sort();
- } else {
- arr = arr.sort(function(a,b){return a-b;});
- }
- return arr.concat(rest);
-};
-
-/**
- * Inserts a value or an array of values into an existing array. The first
- * parameter specifies the initial array to be modified, and the second
- * parameter defines the data to be inserted. The third parameter is an index
- * value which specifies the array position from which to insert data.
- * (Remember that array index numbering starts at zero, so the first position
- * is 0, the second position is 1, and so on.)
- *
- * @method splice
- * @param {Array} list Array to splice into
- * @param {any} value value to be spliced in
- * @param {Number} position in the array from which to insert data
- *
- * @example
- *
- * function setup() {
- * var myArray = new Array(0,1,2,3,4);
- * var insArray = new Array("A","B","C");
- * print(myArray); // [0,1,2,3,4]
- * print(insArray); // ["A","B","C"]
- *
- * splice(myArray, insArray, 3);
- * print(myArray); // [0,1,2,"A","B","C",3,4]
- * }
- *
- */
-p5.prototype.splice = function(list, value, index) {
-
- // note that splice returns spliced elements and not an array
- Array.prototype.splice.apply(list, [index, 0].concat(value));
-
- return list;
-};
-
-/**
- * Extracts an array of elements from an existing array. The list parameter
- * defines the array from which the elements will be copied, and the start
- * and count parameters specify which elements to extract. If no count is
- * given, elements will be extracted from the start to the end of the array.
- * When specifying the start, remember that the first array element is 0.
- * This function does not change the source array.
- *
- * @method subset
- * @param {Array} list Array to extract from
- * @param {Number} start position to begin
- * @param {Number} [count] number of values to extract
- * @return {Array} Array of extracted elements
- *
- * @example
- *
- * function setup() {
- * var myArray = new Array(1,2,3,4,5);
- * print(myArray); // [1,2,3,4,5]
- *
- * var sub1 = subset(myArray, 0, 3);
- * var sub2 = subset(myArray, 2, 2);
- * print(sub1); // [1,2,3]
- * print(sub2); // [3,4]
- * }
- *
- */
-p5.prototype.subset = function(list, start, count) {
- if (typeof count !== 'undefined') {
- return list.slice(start, start + count);
- } else {
- return list.slice(start, list.length);
- }
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],74:[function(_dereq_,module,exports){
-/**
- * @module Data
- * @submodule Conversion
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-/**
- * Converts a string to its floating point representation. The contents of a
- * string must resemble a number, or NaN (not a number) will be returned.
- * For example, float("1234.56") evaluates to 1234.56, but float("giraffe")
- * will return NaN.
- *
- * When an array of values is passed in, then an array of floats of the same
- * length is returned.
- *
- * @method float
- * @param {String} str float string to parse
- * @return {Number} floating point representation of string
- * @example
- *
- * var str = '20';
- * var diameter = float(str);
- * ellipse(width/2, height/2, diameter, diameter);
- *
- *
- * @alt
- * 20 by 20 white ellipse in the center of the canvas
- *
- */
-p5.prototype.float = function(str) {
- if (str instanceof Array) {
- return str.map(parseFloat);
- }
- return parseFloat(str);
-};
-
-/**
- * Converts a boolean, string, or float to its integer representation.
- * When an array of values is passed in, then an int array of the same length
- * is returned.
- *
- * @method int
- * @param {String|Boolean|Number|Array} n value to parse
- * @return {Number} integer representation of value
- * @example
- *
- */
-p5.prototype.int = function(n, radix) {
- radix = radix || 10;
- if (typeof n === 'string') {
- return parseInt(n, radix);
- } else if (typeof n === 'number') {
- return n | 0;
- } else if (typeof n === 'boolean') {
- return n ? 1 : 0;
- } else if (n instanceof Array) {
- return n.map(function(n) { return p5.prototype.int(n, radix); });
- }
-};
-
-/**
- * Converts a boolean, string or number to its string representation.
- * When an array of values is passed in, then an array of strings of the same
- * length is returned.
- *
- * @method str
- * @param {String|Boolean|Number|Array} n value to parse
- * @return {String} string representation of value
- * @example
- *
- */
-p5.prototype.str = function(n) {
- if (n instanceof Array) {
- return n.map(p5.prototype.str);
- } else {
- return String(n);
- }
-};
-
-/**
- * Converts a number or string to its boolean representation.
- * For a number, any non-zero value (positive or negative) evaluates to true,
- * while zero evaluates to false. For a string, the value "true" evaluates to
- * true, while any other value evaluates to false. When an array of number or
- * string values is passed in, then a array of booleans of the same length is
- * returned.
- *
- * @method boolean
- * @param {String|Boolean|Number|Array} n value to parse
- * @return {Boolean} boolean representation of value
- * @example
- *
- */
-p5.prototype.boolean = function(n) {
- if (typeof n === 'number') {
- return n !== 0;
- } else if (typeof n === 'string') {
- return n.toLowerCase() === 'true';
- } else if (typeof n === 'boolean') {
- return n;
- } else if (n instanceof Array) {
- return n.map(p5.prototype.boolean);
- }
-};
-
-/**
- * Converts a number, string or boolean to its byte representation.
- * A byte can be only a whole number between -128 and 127, so when a value
- * outside of this range is converted, it wraps around to the corresponding
- * byte representation. When an array of number, string or boolean values is
- * passed in, then an array of bytes the same length is returned.
- *
- * @method byte
- * @param {String|Boolean|Number|Array} n value to parse
- * @return {Number} byte representation of value
- * @example
- *
- */
-p5.prototype.byte = function(n) {
- var nn = p5.prototype.int(n, 10);
- if (typeof nn === 'number') {
- return ((nn + 128) % 256) - 128;
- } else if (nn instanceof Array) {
- return nn.map(p5.prototype.byte);
- }
-};
-
-/**
- * Converts a number or string to its corresponding single-character
- * string representation. If a string parameter is provided, it is first
- * parsed as an integer and then translated into a single-character string.
- * When an array of number or string values is passed in, then an array of
- * single-character strings of the same length is returned.
- *
- * @method char
- * @param {String|Number|Array} n value to parse
- * @return {String} string representation of value
- * @example
- *
- */
-p5.prototype.char = function(n) {
- if (typeof n === 'number' && !isNaN(n)) {
- return String.fromCharCode(n);
- } else if (n instanceof Array) {
- return n.map(p5.prototype.char);
- } else if (typeof n === 'string') {
- return p5.prototype.char(parseInt(n, 10));
- }
-};
-
-/**
- * Converts a single-character string to its corresponding integer
- * representation. When an array of single-character string values is passed
- * in, then an array of integers of the same length is returned.
- *
- * @method unchar
- * @param {String|Array} n value to parse
- * @return {Number} integer representation of value
- * @example
- *
- */
-p5.prototype.unchar = function(n) {
- if (typeof n === 'string' && n.length === 1) {
- return n.charCodeAt(0);
- } else if (n instanceof Array) {
- return n.map(p5.prototype.unchar);
- }
-};
-
-/**
- * Converts a number to a string in its equivalent hexadecimal notation. If a
- * second parameter is passed, it is used to set the number of characters to
- * generate in the hexadecimal notation. When an array is passed in, an
- * array of strings in hexadecimal notation of the same length is returned.
- *
- * @method hex
- * @param {Number|Array} n value to parse
- * @return {String} hexadecimal string representation of value
- * @example
- *
- */
-p5.prototype.unhex = function(n) {
- if (n instanceof Array) {
- return n.map(p5.prototype.unhex);
- } else {
- return parseInt('0x' + n, 16);
- }
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],75:[function(_dereq_,module,exports){
-/**
- * @module Data
- * @submodule String Functions
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-//return p5; //LM is this a mistake?
-
-/**
- * Combines an array of Strings into one String, each separated by the
- * character(s) used for the separator parameter. To join arrays of ints or
- * floats, it's necessary to first convert them to Strings using nf() or
- * nfs().
- *
- * @method join
- * @param {Array} list array of Strings to be joined
- * @param {String} separator String to be placed between each item
- * @return {String} joined String
- * @example
- *
- *
- * @alt
- * "hello world!" displayed middle left of canvas.
- *
- */
-p5.prototype.join = function(list, separator) {
- return list.join(separator);
-};
-
-/**
- * This function is used to apply a regular expression to a piece of text,
- * and return matching groups (elements found inside parentheses) as a
- * String array. If there are no matches, a null value will be returned.
- * 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
- * string, and the match groups start at element [1] (the first group is [1],
- * the second [2], and so on).
- *
- * @method match
- * @param {String} str the String to be searched
- * @param {String} regexp the regexp to be used for matching
- * @return {Array} Array of Strings found
- * @example
- *
- *
- * var string = "Hello p5js*!"
- * var regexp = "p5js\\*"
- * var match = match(string, regexp);
- * text(match, 5, 50);
- *
- *
- *
- * @alt
- * "p5js*" displayed middle left of canvas.
- *
- */
-p5.prototype.match = function(str, reg) {
- return str.match(reg);
-};
-
-/**
- * This function is used to apply a regular expression to a piece of text,
- * and return a list of matching groups (elements found inside parentheses)
- * as a two-dimensional String array. If there are no matches, a null value
- * 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
- * expression match returns the entire matching string, and the match groups
- * start at element [i][1] (the first group is [i][1], the second [i][2],
- * and so on).
- *
- * @method matchAll
- * @param {String} str the String to be searched
- * @param {String} regexp the regexp to be used for matching
- * @return {Array} 2d Array of Strings found
- * @example
- *
-
- */
-p5.prototype.matchAll = function(str, reg) {
- var re = new RegExp(reg, 'g');
- var match = re.exec(str);
- var matches = [];
- while (match !== null) {
- matches.push(match);
- // matched text: match[0]
- // match start: match.index
- // capturing group n: match[n]
- match = re.exec(str);
- }
- return matches;
-};
-
-/**
- * Utility function for formatting numbers into strings. There are two
- * versions: one for formatting floats, and one for formatting ints.
- * The values for the digits, left, and right parameters should always
- * be positive integers.
- *
- * @method nf
- * @param {Number|Array} num the Number to format
- * @param {Number} [left] number of digits to the left of the
- * decimal point
- * @param {Number} [right] number of digits to the right of the
- * decimal point
- * @return {String|Array} formatted String
- * @example
- *
- *
- * @alt
- * "11,253,106.115" top middle and "1.00,1.00,2.00" displayed bottom mid
- *
- */
-p5.prototype.nfc = function () {
- if (arguments[0] instanceof Array) {
- var a = arguments[1];
- return arguments[0].map(function (x) {
- return doNfc(x, a);
- });
- } else {
- return doNfc.apply(this, arguments);
- }
-};
-function doNfc() {
- var num = arguments[0].toString();
- var dec = num.indexOf('.');
- var rem = dec !== -1 ? num.substring(dec) : '';
- var n = dec !== -1 ? num.substring(0, dec) : num;
- n = n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
- if (arguments[1] === 0) {
- rem = '';
- }
- else if(arguments[1] !== undefined){
- if(arguments[1] > rem.length){
- rem+= dec === -1 ? '.' : '';
- var len = arguments[1] - rem.length + 1;
- for(var i =0; i< len; i++){
- rem += '0';
- }
- }
- else{
- rem = rem.substring(0, arguments[1] + 1);
- }
- }
- return n + rem;
-}
-
-/**
- * Utility function for formatting numbers into strings. Similar to nf() but
- * puts a "+" in front of positive numbers and a "-" in front of negative
- * numbers. There are two versions: one for formatting floats, and one for
- * formatting ints. The values for left, and right parameters
- * should always be positive integers.
- *
- * @method nfp
- * @param {Number|Array} num the Number to format
- * @param {Number} [left] number of digits to the left of the decimal
- * point
- * @param {Number} [right] number of digits to the right of the
- * decimal point
- * @return {String|Array} formatted String
- * @example
- *
- *
- * @alt
- * "+11253106.11" top middle and "-11253106.11" displayed bottom middle
- *
- */
-p5.prototype.nfp = function() {
- var nfRes = this.nf.apply(this, arguments);
- if (nfRes instanceof Array) {
- return nfRes.map(addNfp);
- } else {
- return addNfp(nfRes);
- }
-};
-
-function addNfp() {
- return (
- parseFloat(arguments[0]) > 0) ?
- '+'+arguments[0].toString() :
- arguments[0].toString();
-}
-
-/**
- * Utility function for formatting numbers into strings. Similar to nf() but
- * puts a " " (space) in front of positive numbers and a "-" in front of
- * negative numbers. There are two versions: one for formatting floats, and
- * one for formatting ints. The values for the digits, left, and right
- * parameters should always be positive integers.
- *
- * @method nfs
- * @param {Number|Array} num the Number to format
- * @param {Number} [left] number of digits to the left of the decimal
- * point
- * @param {Number} [right] number of digits to the right of the
- * decimal point
- * @return {String|Array} formatted String
- * @example
- *
- *
- * @alt
- * "11253106.11" top middle and "-11253106.11" displayed bottom middle
- *
- */
-p5.prototype.nfs = function() {
- var nfRes = this.nf.apply(this, arguments);
- if (nfRes instanceof Array) {
- return nfRes.map(addNfs);
- } else {
- return addNfs(nfRes);
- }
-};
-
-function addNfs() {
- return (
- parseFloat(arguments[0]) > 0) ?
- ' '+arguments[0].toString() :
- arguments[0].toString();
-}
-
-/**
- * The split() function maps to String.split(), it breaks a String into
- * pieces using a character or string as the delimiter. The delim parameter
- * specifies the character or characters that mark the boundaries between
- * each piece. A String[] array is returned that contains each of the pieces.
- *
- * The splitTokens() function works in a similar fashion, except that it
- * splits using a range of characters instead of a specific character or
- * sequence.
- *
- * @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
- * @example
- *
- *
- * @alt
- * "pat" top left, "Xio" mid left and "Alex" displayed bottom left
- *
- */
-p5.prototype.split = function(str, delim) {
- return str.split(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.
- *
- * @method splitTokens
- * @param {String} value the String to be split
- * @param {String} [delim] list of individual Strings that will be used as
- * separators
- * @return {Array} Array of Strings
- * @example
- *
- *
- * function setup() {
- * var myStr = "Mango, Banana, Lime";
- * var myStrArr = splitTokens(myStr, ",");
- *
- * print(myStrArr); // prints : ["Mango"," Banana"," Lime"]
- * }
- *
- *
- */
-p5.prototype.splitTokens = function() {
- var d,sqo,sqc,str;
- str = arguments[1];
- if (arguments.length > 1) {
- sqc = /\]/g.exec(str);
- sqo = /\[/g.exec(str);
- if ( sqo && sqc ) {
- str = str.slice(0, sqc.index) + str.slice(sqc.index+1);
- sqo = /\[/g.exec(str);
- str = str.slice(0, sqo.index) + str.slice(sqo.index+1);
- d = new RegExp('[\\['+str+'\\]]','g');
- } else if ( sqc ) {
- str = str.slice(0, sqc.index) + str.slice(sqc.index+1);
- d = new RegExp('[' + str + '\\]]', 'g');
- } else if(sqo) {
- str = str.slice(0, sqo.index) + str.slice(sqo.index+1);
- d = new RegExp('[' + str + '\\[]', 'g');
- } else {
- d = new RegExp('[' + str + ']', 'g');
- }
- } else {
- d = /\s/g;
- }
- return arguments[0].split(d).filter(function(n){return n;});
-};
-
-/**
- * Removes whitespace characters from the beginning and end of a String. In
- * addition to standard whitespace characters such as space, carriage return,
- * and tab, this function also removes the Unicode "nbsp" character.
- *
- * @method trim
- * @param {String|Array} str a String or Array of Strings to be trimmed
- * @return {String|Array} a trimmed String or Array of Strings
- * @example
- *
- *
- * var string = trim(" No new lines\n ");
- * text(string +" here", 2, 50);
- *
- *
- *
- * @alt
- * "No new lines here" displayed center canvas
- *
- */
-p5.prototype.trim = function(str) {
- if (str instanceof Array) {
- return str.map(this.trim);
- } else {
- return str.trim();
- }
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],76:[function(_dereq_,module,exports){
-/**
- * @module IO
- * @submodule Time & Date
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-/**
- * p5.js communicates with the clock on your computer. The day() function
- * returns the current day as a value from 1 - 31.
- *
- * @method day
- * @return {Number} the current day
- * @example
- *
- *
- * @alt
- * Current day is displayed
- *
- */
-p5.prototype.day = function() {
- return new Date().getDate();
-};
-
-/**
- * p5.js communicates with the clock on your computer. The hour() function
- * returns the current hour as a value from 0 - 23.
- *
- * @method hour
- * @return {Number} the current hour
- * @example
- *
- *
- * var h = hour();
- * text("Current hour:\n" + h, 5, 50);
- *
- *
- *
- * @alt
- * Current hour is displayed
- *
- */
-p5.prototype.hour = function() {
- return new Date().getHours();
-};
-
-/**
- * p5.js communicates with the clock on your computer. The minute() function
- * returns the current minute as a value from 0 - 59.
- *
- * @method minute
- * @return {Number} the current minute
- * @example
- *
- *
- * var m = minute();
- * text("Current minute: \n" + m, 5, 50);
- *
- *
- *
- * @alt
- * Current minute is displayed
- *
- */
-p5.prototype.minute = function() {
- return new Date().getMinutes();
-};
-
-/**
- * Returns the number of milliseconds (thousandths of a second) since
- * starting the program. This information is often used for timing events and
- * animation sequences.
- *
- * @method millis
- * @return {Number} the number of milliseconds since starting the program
- * @example
- *
- *
- * @alt
- * number of milliseconds since program has started displayed
- *
- */
-p5.prototype.millis = function() {
- return window.performance.now();
-};
-
-/**
- * p5.js communicates with the clock on your computer. The month() function
- * returns the current month as a value from 1 - 12.
- *
- * @method month
- * @return {Number} the current month
- * @example
- *
- *
- * var m = month();
- * text("Current month: \n" + m, 5, 50);
- *
- *
- *
- * @alt
- * Current month is displayed
- *
- */
-p5.prototype.month = function() {
- return new Date().getMonth() + 1; //January is 0!
-};
-
-/**
- * p5.js communicates with the clock on your computer. The second() function
- * returns the current second as a value from 0 - 59.
- *
- * @method second
- * @return {Number} the current second
- * @example
- *
- *
- * @alt
- * Current second is displayed
- *
- */
-p5.prototype.second = function() {
- return new Date().getSeconds();
-};
-
-/**
- * p5.js communicates with the clock on your computer. The year() function
- * returns the current year as an integer (2014, 2015, 2016, etc).
- *
- * @method year
- * @return {Number} the current year
- * @example
- *
- *
- * var y = year();
- * text("Current year: \n" + y, 5, 50);
- *
- *
- *
- * @alt
- * Current year is displayed
- *
- */
-p5.prototype.year = function() {
- return new Date().getFullYear();
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],77:[function(_dereq_,module,exports){
-/**
- * @module Lights, Camera
- * @submodule Camera
- * @for p5
- * @requires core
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-
-/**
- * Sets camera position
- * @method camera
- * @param {Number} x camera position value on x axis
- * @param {Number} y camera position value on y axis
- * @param {Number} z camera position value on z axis
- * @return {p5} the p5 object
- * @example
- *
- *
- * function setup(){
- * createCanvas(100, 100, WEBGL);
- * }
- * function draw(){
- * //move the camera away from the plane by a sin wave
- * camera(0, 0, sin(frameCount * 0.01) * 100);
- * plane(120, 120);
- * }
- *
- *
- *
- * @alt
- * blue square shrinks in size grows to fill canvas. disappears then loops.
- *
- */
-p5.prototype.camera = function(x, y, z){
- //what it manipulates is the model view matrix
- this._renderer.translate(-x, -y, -z);
-};
-
-/**
- * Sets perspective camera
- * @method perspective
- * @param {Number} fovy camera frustum vertical field of view,
- * from bottom to top of view, in degrees
- * @param {Number} aspect camera frustum aspect ratio
- * @param {Number} near frustum near plane length
- * @param {Number} far frustum far plane length
- * @return {p5} the p5 object
- * @example
- *
- *
- * @alt
- * spot light on canvas changes position with mouse
- *
- */
-p5.prototype.pointLight = function(v1, v2, v3, a, x, y, z) {
- var gl = this._renderer.GL;
- var shaderProgram = this._renderer._getShader(
- 'lightVert', 'lightTextureFrag');
-
- gl.useProgram(shaderProgram);
- shaderProgram.uPointLightColor = gl.getUniformLocation(
- shaderProgram,
- 'uPointLightColor[' + this._renderer.pointLightCount + ']');
-
- //@TODO: check parameters number
- var color = this._renderer._pInst.color.apply(
- this._renderer._pInst, [v1, v2, v3]);
- var colors = color._array;
-
- gl.uniform3f( shaderProgram.uPointLightColor,
- colors[0], colors[1], colors[2]);
-
- var _x, _y, _z;
-
- var args = new Array(arguments.length);
- for (var i = 0; i < args.length; ++i) {
- args[i] = arguments[i];
- }
- if(typeof args[args.length-1] === 'number'){
- _x = args[args.length-3];
- _y = args[args.length-2];
- _z = args[args.length-1];
-
- }else{
- try{
- _x = args[args.length-1].x;
- _y = args[args.length-1].y;
- _z = args[args.length-1].z;
- }
- catch(error){
- throw error;
- }
- }
-
- shaderProgram.uPointLightLocation = gl.getUniformLocation(
- shaderProgram,
- 'uPointLightLocation[' + this._renderer.pointLightCount + ']');
- gl.uniform3f( shaderProgram.uPointLightLocation, _x, _y, _z);
-
- //in case there's no material color for the geometry
- shaderProgram.uMaterialColor = gl.getUniformLocation(
- shaderProgram, 'uMaterialColor' );
- gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1);
-
- this._renderer.pointLightCount ++;
- shaderProgram.uPointLightCount =
- gl.getUniformLocation(shaderProgram, 'uPointLightCount');
- gl.uniform1i(shaderProgram.uPointLightCount,
- this._renderer.pointLightCount);
-
- return this;
-};
-
-module.exports = p5;
-
-},{"../core/core":37}],80:[function(_dereq_,module,exports){
-/**
- * @module Shape
- * @submodule 3D Models
- * @for p5
- * @requires core
- * @requires p5.Geometry3D
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-_dereq_('./p5.Geometry');
-
-/**
- * Load a 3d model from an OBJ file.
- *
- * One of the limitations of the OBJ format is that it doesn't have a built-in
- * sense of scale. This means that models exported from different programs might
- * be very different sizes. If your model isn't displaying, try calling
- * loadModel() with the normalized parameter set to true. This will resize the
- * model to a scale appropriate for p5. You can also make additional changes to
- * the final size of your model with the scale() function.
- *
- * @method loadModel
- * @param {String} path Path of the model to be loaded
- * @param {Boolean} [normalize] If true, scale the model to a
- * standardized size when loading
- * @param {Function(p5.Geometry3D)} [successCallback] Function to be called
- * once the model is loaded. Will be passed
- * the 3D model object.
- * @param {Function(Event)} [failureCallback] called with event error if
- * the image fails to load.
- * @return {p5.Geometry} the p5.Geometry3D object
- * @example
- *
- *
- * @alt
- * Vertically rotating 3-d teapot with red, green and blue gradient.
- *
- */
-p5.prototype.loadModel = function () {
- var path = arguments[0];
- var normalize;
- var successCallback;
- var failureCallback;
- if(typeof arguments[1] === 'boolean') {
- normalize = arguments[1];
- successCallback = arguments[2];
- failureCallback = arguments[3];
- } else {
- normalize = false;
- successCallback = arguments[1];
- failureCallback = arguments[2];
- }
-
- var model = new p5.Geometry();
- model.gid = path + '|' + normalize;
- this.loadStrings(path, function(strings) {
- parseObj(model, strings);
-
- if (normalize) {
- model.normalize();
- }
-
- if (typeof successCallback === 'function') {
- successCallback(model);
- }
- }.bind(this), failureCallback);
-
- return model;
-};
-
-/**
- * Parse OBJ lines into model. For reference, this is what a simple model of a
- * square might look like:
- *
- * v -0.5 -0.5 0.5
- * v -0.5 -0.5 -0.5
- * v -0.5 0.5 -0.5
- * v -0.5 0.5 0.5
- *
- * f 4 3 2 1
- */
-function parseObj( model, lines ) {
- // OBJ allows a face to specify an index for a vertex (in the above example),
- // but it also allows you to specify a custom combination of vertex, UV
- // coordinate, and vertex normal. So, "3/4/3" would mean, "use vertex 3 with
- // UV coordinate 4 and vertex normal 3". In WebGL, every vertex with different
- // parameters must be a different vertex, so loadedVerts is used to
- // temporarily store the parsed vertices, normals, etc., and indexedVerts is
- // used to map a specific combination (keyed on, for example, the string
- // "3/4/3"), to the actual index of the newly created vertex in the final
- // object.
- var loadedVerts = {'v' : [],
- 'vt' : [],
- 'vn' : []};
- var indexedVerts = {};
-
- for (var line = 0; line < lines.length; ++line) {
- // Each line is a separate object (vertex, face, vertex normal, etc)
- // For each line, split it into tokens on whitespace. The first token
- // describes the type.
- var tokens = lines[line].trim().split(/\b\s+/);
-
- if (tokens.length > 0) {
- if (tokens[0] === 'v' || tokens[0] === 'vn') {
- // Check if this line describes a vertex or vertex normal.
- // It will have three numeric parameters.
- var vertex = new p5.Vector(parseFloat(tokens[1]),
- parseFloat(tokens[2]),
- parseFloat(tokens[3]));
- loadedVerts[tokens[0]].push(vertex);
- } else if (tokens[0] === 'vt') {
- // Check if this line describes a texture coordinate.
- // It will have two numeric parameters.
- var texVertex = [parseFloat(tokens[1]), parseFloat(tokens[2])];
- loadedVerts[tokens[0]].push(texVertex);
- } else if (tokens[0] === 'f') {
- // Check if this line describes a face.
- // OBJ faces can have more than three points. Triangulate points.
- for (var tri = 3; tri < tokens.length; ++tri) {
- var face = [];
-
- var vertexTokens = [1, tri - 1, tri];
-
- for (var tokenInd = 0; tokenInd < vertexTokens.length; ++tokenInd) {
- // Now, convert the given token into an index
- var vertString = tokens[vertexTokens[tokenInd]];
- var vertIndex = 0;
-
- // TODO: Faces can technically use negative numbers to refer to the
- // previous nth vertex. I haven't seen this used in practice, but
- // it might be good to implement this in the future.
-
- if (indexedVerts[vertString] !== undefined) {
- vertIndex = indexedVerts[vertString];
- } else {
- var vertParts = vertString.split('/');
- for (var i = 0; i < vertParts.length; i++) {
- vertParts[i] = parseInt(vertParts[i]) - 1;
- }
-
- vertIndex = indexedVerts[vertString] = model.vertices.length;
- model.vertices.push(loadedVerts.v[vertParts[0]].copy());
- if (loadedVerts.vt[vertParts[1]]) {
- model.uvs.push(loadedVerts.vt[vertParts[1]].slice());
- } else {
- model.uvs.push([0, 0]);
- }
-
- if (loadedVerts.vn[vertParts[2]]) {
- model.vertexNormals.push(loadedVerts.vn[vertParts[2]].copy());
- }
- }
-
- face.push(vertIndex);
- }
-
- model.faces.push(face);
- }
- }
- }
- }
-
- // If the model doesn't have normals, compute the normals
- if(model.vertexNormals.length === 0) {
- model.computeNormals();
- }
-
- return model;
-}
-
-/**
- * Render a 3d model to the screen.
- *
- * @method model
- * @param {p5.Geometry} model Loaded 3d model to be rendered
- * @example
- *
- *
- * @alt
- * radiating light source from top right of canvas
- *
- */
-p5.prototype.ambientMaterial = function(v1, v2, v3, a) {
- var gl = this._renderer.GL;
- var shaderProgram =
- this._renderer._getShader('lightVert', 'lightTextureFrag');
-
- gl.useProgram(shaderProgram);
- shaderProgram.uMaterialColor = gl.getUniformLocation(
- shaderProgram, 'uMaterialColor' );
- var colors = this._renderer._applyColorBlend.apply(this._renderer, arguments);
-
- gl.uniform4f(shaderProgram.uMaterialColor,
- colors[0], colors[1], colors[2], colors[3]);
-
- shaderProgram.uSpecular = gl.getUniformLocation(
- shaderProgram, 'uSpecular' );
- gl.uniform1i(shaderProgram.uSpecular, false);
-
- gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false);
-
- return this;
-};
-
-/**
- * Specular material for geometry with a given color. You can view all
- * possible materials in this
- * example.
- * @method specularMaterial
- * @param {Number|Array|String|p5.Color} v1 gray value,
- * red or hue value (depending on the current color mode),
- * or color Array, or CSS color string
- * @param {Number} [v2] optional: green or saturation value
- * @param {Number} [v3] optional: blue or brightness value
- * @param {Number} [a] optional: opacity
- * @return {p5} the p5 object
- * @example
- *
- *
- * @alt
- * red canvas
- *
- */
-p5.RendererGL.prototype.fill = function(v1, v2, v3, a) {
- var gl = this.GL;
- var shaderProgram;
- //see material.js for more info on color blending in webgl
- var colors = this._applyColorBlend.apply(this, arguments);
- this.curFillColor = colors;
- this.drawMode = 'fill';
- if(this.isImmediateDrawing){
- shaderProgram =
- this._getShader('immediateVert','vertexColorFrag');
- gl.useProgram(shaderProgram);
- } else {
- shaderProgram =
- this._getShader('normalVert', 'basicFrag');
- gl.useProgram(shaderProgram);
- //RetainedMode uses a webgl uniform to pass color vals
- //in ImmediateMode, we want access to each vertex so therefore
- //we cannot use a uniform.
- shaderProgram.uMaterialColor = gl.getUniformLocation(
- shaderProgram, 'uMaterialColor' );
- gl.uniform4f( shaderProgram.uMaterialColor,
- colors[0],
- colors[1],
- colors[2],
- colors[3]);
- }
- return this;
-};
-p5.RendererGL.prototype.stroke = function(r, g, b, a) {
- var color = this._pInst.color.apply(this._pInst, arguments);
- var colorNormalized = color._array;
- this.curStrokeColor = colorNormalized;
- this.drawMode = 'stroke';
- return this;
-};
-
-//@TODO
-p5.RendererGL.prototype._strokeCheck = function(){
- if(this.drawMode === 'stroke'){
- throw new Error(
- 'stroke for shapes in 3D not yet implemented, use fill for now :('
- );
- }
-};
-
-/**
- * [strokeWeight description]
- * @param {Number} pointSize stroke point size
- * @return {[type]} [description]
- * @todo strokeWeight currently works on points only.
- * implement on all wireframes and strokes.
- */
-p5.RendererGL.prototype.strokeWeight = function(pointSize) {
- this.pointSize = pointSize;
- return this;
-};
-//////////////////////////////////////////////
-// HASH | for material and geometry
-//////////////////////////////////////////////
-
-p5.RendererGL.prototype.geometryInHash = function(gId){
- return this.gHash[gId] !== undefined;
-};
-
-p5.RendererGL.prototype.materialInHash = function(mId){
- return this.mHash[mId] !== undefined;
-};
-
-/**
- * [resize description]
- * @param {[type]} w [description]
- * @param {[tyoe]} h [description]
- * @return {[type]} [description]
- */
-p5.RendererGL.prototype.resize = function(w,h) {
- var gl = this.GL;
- p5.Renderer.prototype.resize.call(this, w, h);
- gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
- // If we're using the default camera, update the aspect ratio
- if(this._curCamera === 'default') {
- this._curCamera = null;
- this._setDefaultCamera();
- }
-};
-
-/**
- * clears color and depth buffers
- * with r,g,b,a
- * @param {Number} r normalized red val.
- * @param {Number} g normalized green val.
- * @param {Number} b normalized blue val.
- * @param {Number} a normalized alpha val.
- */
-p5.RendererGL.prototype.clear = function() {
- var gl = this.GL;
- gl.clearColor(arguments[0],
- arguments[1],
- arguments[2],
- arguments[3]);
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-};
-
-/**
- * [translate description]
- * @param {[type]} x [description]
- * @param {[type]} y [description]
- * @param {[type]} z [description]
- * @return {[type]} [description]
- * @todo implement handle for components or vector as args
- */
-p5.RendererGL.prototype.translate = function(x, y, z) {
- this.uMVMatrix.translate([x,-y,z]);
- return this;
-};
-
-/**
- * Scales the Model View Matrix by a vector
- * @param {Number | p5.Vector | Array} x [description]
- * @param {Number} [y] y-axis scalar
- * @param {Number} [z] z-axis scalar
- * @return {this} [description]
- */
-p5.RendererGL.prototype.scale = function(x,y,z) {
- this.uMVMatrix.scale([x,y,z]);
- return this;
-};
-
-p5.RendererGL.prototype.rotate = function(rad, axis){
- this.uMVMatrix.rotate(rad, axis);
- return this;
-};
-
-p5.RendererGL.prototype.rotateX = function(rad) {
- this.rotate(rad, [1,0,0]);
- return this;
-};
-
-p5.RendererGL.prototype.rotateY = function(rad) {
- this.rotate(rad, [0,1,0]);
- return this;
-};
-
-p5.RendererGL.prototype.rotateZ = function(rad) {
- this.rotate(rad, [0,0,1]);
- return this;
-};
-
-/**
- * pushes a copy of the model view matrix onto the
- * MV Matrix stack.
- */
-p5.RendererGL.prototype.push = function() {
- uMVMatrixStack.push(this.uMVMatrix.copy());
-};
-
-/**
- * [pop description]
- * @return {[type]} [description]
- */
-p5.RendererGL.prototype.pop = function() {
- if (uMVMatrixStack.length === 0) {
- throw new Error('Invalid popMatrix!');
- }
- this.uMVMatrix = uMVMatrixStack.pop();
-};
-
-p5.RendererGL.prototype.resetMatrix = function() {
- this.uMVMatrix = p5.Matrix.identity();
- this.translate(0, 0, -800);
- return this;
-};
-
-// Text/Typography
-// @TODO:
-p5.RendererGL.prototype._applyTextProperties = function() {
- //@TODO finish implementation
- console.error('text commands not yet implemented in webgl');
-};
-module.exports = p5.RendererGL;
-
-},{"../core/core":37,"../core/p5.Renderer":43,"./p5.Matrix":83,"./shader":88}],87:[function(_dereq_,module,exports){
-/**
- * @module Shape
- * @submodule 3D Primitives
- * @for p5
- * @requires core
- * @requires p5.Geometry
- */
-
-'use strict';
-
-var p5 = _dereq_('../core/core');
-_dereq_('./p5.Geometry');
-/**
- * Draw a plane with given a width and height
- * @method plane
- * @param {Number} width width of the plane
- * @param {Number} height height of the plane
- * @param {Number} [detailX] Optional number of triangle
- * subdivisions in x-dimension
- * @param {Number} [detailY] Optional number of triangle
- * subdivisions in y-dimension
- * @return {p5} the p5 object
- * @example
- *
- *
- * //draw a plane with width 200 and height 200
- * function setup(){
- * createCanvas(100, 100, WEBGL);
- * }
- *
- * function draw(){
- * background(200);
- * plane(50, 50);
- * }
- *
- *
- *
- * @alt
- * Nothing displayed on canvas
- * Rotating interior view of a box with sides that change color.
- * 3d red and green gradient.
- * Rotating interior view of a cylinder with sides that change color.
- * Rotating view of a cylinder with sides that change color.
- * 3d red and green gradient.
- * rotating view of a multi-colored cylinder with concave sides.
- */
-p5.prototype.plane = function(){
- var args = new Array(arguments.length);
- for (var i = 0; i < args.length; ++i) {
- args[i] = arguments[i];
- }
- var width = args[0] || 50;
- var height = args[1] || width;
- var detailX = typeof args[2] === 'number' ? args[2] : 1;
- var detailY = typeof args[3] === 'number' ? args[3] : 1;
-
- var gId = 'plane|'+width+'|'+height+'|'+detailX+'|'+detailY;
-
- if(!this._renderer.geometryInHash(gId)){
- var _plane = function(){
- var u,v,p;
- for (var i = 0; i <= this.detailY; i++){
- v = i / this.detailY;
- for (var j = 0; j <= this.detailX; j++){
- u = j / this.detailX;
- p = new p5.Vector(width * u - width/2,
- height * v - height/2,
- 0);
- this.vertices.push(p);
- this.uvs.push([u,v]);
- }
- }
- };
- var planeGeom =
- new p5.Geometry(detailX, detailY, _plane);
- planeGeom
- .computeFaces()
- .computeNormals();
- this._renderer.createBuffers(gId, planeGeom);
- }
-
- this._renderer.drawBuffers(gId);
-
-};
-
-/**
- * Draw a box with given width, height and depth
- * @method box
- * @param {Number} width width of the box
- * @param {Number} Height height of the box
- * @param {Number} depth depth of the box
- * @param {Number} [detailX] Optional number of triangle
- * subdivisions in x-dimension
- * @param {Number} [detailY] Optional number of triangle
- * subdivisions in y-dimension
- * @return {p5} the p5 object
- * @example
- *
- * p5.SoundFile: Load and play sound files.
- * p5.Amplitude: Get the current volume of a sound.
- * p5.AudioIn: Get sound from an input source, typically
- * a computer microphone.
- * p5.FFT: Analyze the frequency of sound. Returns
- * results from the frequency spectrum or time domain (waveform).
- * p5.Oscillator: Generate Sine,
- * Triangle, Square and Sawtooth waveforms. Base class of
- * p5.Noise and p5.Pulse.
- *
- * p5.Env: An Envelope is a series
- * of fades over time. Often used to control an object's
- * output gain level as an "ADSR Envelope" (Attack, Decay,
- * Sustain, Release). Can also modulate other parameters.
- * p5.Delay: A delay effect with
- * parameters for feedback, delayTime, and lowpass filter.
- * p5.Filter: Filter the frequency range of a
- * sound.
- *
- * p5.Reverb: Add reverb to a sound by specifying
- * duration and decay.
- * p5.Convolver: Extends
- * p5.Reverb to simulate the sound of real
- * physical spaces through convolution.
- * p5.SoundRecorder: Record sound for playback
- * / save the .wav file.
- * p5.Phrase, p5.Part and
- * p5.Score: Compose musical sequences.
- *
- * p5.sound is on GitHub.
- * Download the latest version
- * here.
- *
- * @module p5.sound
- * @submodule p5.sound
- * @for p5.sound
- * @main
- */
-/**
- * p5.sound developed by Jason Sigal for the Processing Foundation, Google Summer of Code 2014. The MIT License (MIT).
- *
- * http://github.com/therewasaguy/p5.sound
- *
- * Some of the many audio libraries & resources that inspire p5.sound:
- * - TONE.js (c) Yotam Mann, 2014. Licensed under The MIT License (MIT). https://github.com/TONEnoTONE/Tone.js
- * - buzz.js (c) Jay Salvat, 2013. Licensed under The MIT License (MIT). http://buzz.jaysalvat.com/
- * - Boris Smus Web Audio API book, 2013. Licensed under the Apache License http://www.apache.org/licenses/LICENSE-2.0
- * - wavesurfer.js https://github.com/katspaugh/wavesurfer.js
- * - Web Audio Components by Jordan Santell https://github.com/web-audio-components
- * - Wilm Thoben's Sound library for Processing https://github.com/processing/processing/tree/master/java/libraries/sound
- *
- * Web Audio API: http://w3.org/TR/webaudio/
- */
-var sndcore;
-sndcore = function () {
- 'use strict';
- /* AudioContext Monkeypatch
- Copyright 2013 Chris Wilson
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- (function (global, exports, perf) {
- exports = exports || {};
- 'use strict';
- function fixSetTarget(param) {
- if (!param)
- // if NYI, just return
- return;
- if (!param.setTargetAtTime)
- param.setTargetAtTime = param.setTargetValueAtTime;
- }
- if (window.hasOwnProperty('webkitAudioContext') && !window.hasOwnProperty('AudioContext')) {
- window.AudioContext = webkitAudioContext;
- if (typeof AudioContext.prototype.createGain !== 'function')
- AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
- if (typeof AudioContext.prototype.createDelay !== 'function')
- AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
- if (typeof AudioContext.prototype.createScriptProcessor !== 'function')
- AudioContext.prototype.createScriptProcessor = AudioContext.prototype.createJavaScriptNode;
- if (typeof AudioContext.prototype.createPeriodicWave !== 'function')
- AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
- AudioContext.prototype.internal_createGain = AudioContext.prototype.createGain;
- AudioContext.prototype.createGain = function () {
- var node = this.internal_createGain();
- fixSetTarget(node.gain);
- return node;
- };
- AudioContext.prototype.internal_createDelay = AudioContext.prototype.createDelay;
- AudioContext.prototype.createDelay = function (maxDelayTime) {
- var node = maxDelayTime ? this.internal_createDelay(maxDelayTime) : this.internal_createDelay();
- fixSetTarget(node.delayTime);
- return node;
- };
- AudioContext.prototype.internal_createBufferSource = AudioContext.prototype.createBufferSource;
- AudioContext.prototype.createBufferSource = function () {
- var node = this.internal_createBufferSource();
- if (!node.start) {
- node.start = function (when, offset, duration) {
- if (offset || duration)
- this.noteGrainOn(when || 0, offset, duration);
- else
- this.noteOn(when || 0);
- };
- } else {
- node.internal_start = node.start;
- node.start = function (when, offset, duration) {
- if (typeof duration !== 'undefined')
- node.internal_start(when || 0, offset, duration);
- else
- node.internal_start(when || 0, offset || 0);
- };
- }
- if (!node.stop) {
- node.stop = function (when) {
- this.noteOff(when || 0);
- };
- } else {
- node.internal_stop = node.stop;
- node.stop = function (when) {
- node.internal_stop(when || 0);
- };
- }
- fixSetTarget(node.playbackRate);
- return node;
- };
- AudioContext.prototype.internal_createDynamicsCompressor = AudioContext.prototype.createDynamicsCompressor;
- AudioContext.prototype.createDynamicsCompressor = function () {
- var node = this.internal_createDynamicsCompressor();
- fixSetTarget(node.threshold);
- fixSetTarget(node.knee);
- fixSetTarget(node.ratio);
- fixSetTarget(node.reduction);
- fixSetTarget(node.attack);
- fixSetTarget(node.release);
- return node;
- };
- AudioContext.prototype.internal_createBiquadFilter = AudioContext.prototype.createBiquadFilter;
- AudioContext.prototype.createBiquadFilter = function () {
- var node = this.internal_createBiquadFilter();
- fixSetTarget(node.frequency);
- fixSetTarget(node.detune);
- fixSetTarget(node.Q);
- fixSetTarget(node.gain);
- return node;
- };
- if (typeof AudioContext.prototype.createOscillator !== 'function') {
- AudioContext.prototype.internal_createOscillator = AudioContext.prototype.createOscillator;
- AudioContext.prototype.createOscillator = function () {
- var node = this.internal_createOscillator();
- if (!node.start) {
- node.start = function (when) {
- this.noteOn(when || 0);
- };
- } else {
- node.internal_start = node.start;
- node.start = function (when) {
- node.internal_start(when || 0);
- };
- }
- if (!node.stop) {
- node.stop = function (when) {
- this.noteOff(when || 0);
- };
- } else {
- node.internal_stop = node.stop;
- node.stop = function (when) {
- node.internal_stop(when || 0);
- };
- }
- if (!node.setPeriodicWave)
- node.setPeriodicWave = node.setWaveTable;
- fixSetTarget(node.frequency);
- fixSetTarget(node.detune);
- return node;
- };
- }
- }
- if (window.hasOwnProperty('webkitOfflineAudioContext') && !window.hasOwnProperty('OfflineAudioContext')) {
- window.OfflineAudioContext = webkitOfflineAudioContext;
- }
- return exports;
- }(window));
- // <-- end MonkeyPatch.
- // Create the Audio Context
- var audiocontext = new window.AudioContext();
- /**
- *
Returns the Audio Context for this sketch. Useful for users
- * who would like to dig deeper into the Web Audio API
- * .
- *
- * @method getAudioContext
- * @return {Object} AudioContext for this sketch
- */
- p5.prototype.getAudioContext = function () {
- return audiocontext;
- };
- // Polyfill for AudioIn, also handled by p5.dom createCapture
- navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
- /**
- * Determine which filetypes are supported (inspired by buzz.js)
- * The audio element (el) will only be used to test browser support for various audio formats
- */
- var el = document.createElement('audio');
- p5.prototype.isSupported = function () {
- return !!el.canPlayType;
- };
- var isOGGSupported = function () {
- return !!el.canPlayType && el.canPlayType('audio/ogg; codecs="vorbis"');
- };
- var isMP3Supported = function () {
- return !!el.canPlayType && el.canPlayType('audio/mpeg;');
- };
- var isWAVSupported = function () {
- return !!el.canPlayType && el.canPlayType('audio/wav; codecs="1"');
- };
- var isAACSupported = function () {
- return !!el.canPlayType && (el.canPlayType('audio/x-m4a;') || el.canPlayType('audio/aac;'));
- };
- var isAIFSupported = function () {
- return !!el.canPlayType && el.canPlayType('audio/x-aiff;');
- };
- p5.prototype.isFileSupported = function (extension) {
- switch (extension.toLowerCase()) {
- case 'mp3':
- return isMP3Supported();
- case 'wav':
- return isWAVSupported();
- case 'ogg':
- return isOGGSupported();
- case 'aac', 'm4a', 'mp4':
- return isAACSupported();
- case 'aif', 'aiff':
- return isAIFSupported();
- default:
- return false;
- }
- };
- // if it is iOS, we have to have a user interaction to start Web Audio
- // http://paulbakaus.com/tutorials/html5/web-audio-on-ios/
- var iOS = navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false;
- if (iOS) {
- var iosStarted = false;
- var startIOS = function () {
- if (iosStarted)
- return;
- // create empty buffer
- var buffer = audiocontext.createBuffer(1, 1, 22050);
- var source = audiocontext.createBufferSource();
- source.buffer = buffer;
- // connect to output (your speakers)
- source.connect(audiocontext.destination);
- // play the file
- source.start(0);
- console.log('start ios!');
- if (audiocontext.state === 'running') {
- iosStarted = true;
- }
- };
- document.addEventListener('touchend', startIOS, false);
- document.addEventListener('touchstart', startIOS, false);
- }
-}();
-var master;
-master = function () {
- 'use strict';
- /**
- * Master contains AudioContext and the master sound output.
- */
- var Master = function () {
- var audiocontext = p5.prototype.getAudioContext();
- this.input = audiocontext.createGain();
- this.output = audiocontext.createGain();
- //put a hard limiter on the output
- this.limiter = audiocontext.createDynamicsCompressor();
- this.limiter.threshold.value = 0;
- this.limiter.ratio.value = 20;
- this.audiocontext = audiocontext;
- this.output.disconnect();
- // an array of input sources
- this.inputSources = [];
- // connect input to limiter
- this.input.connect(this.limiter);
- // connect limiter to output
- this.limiter.connect(this.output);
- // meter is just for global Amplitude / FFT analysis
- this.meter = audiocontext.createGain();
- this.fftMeter = audiocontext.createGain();
- this.output.connect(this.meter);
- this.output.connect(this.fftMeter);
- // connect output to destination
- this.output.connect(this.audiocontext.destination);
- // an array of all sounds in the sketch
- this.soundArray = [];
- // an array of all musical parts in the sketch
- this.parts = [];
- // file extensions to search for
- this.extensions = [];
- };
- // create a single instance of the p5Sound / master output for use within this sketch
- var p5sound = new Master();
- /**
- * Returns a number representing the master amplitude (volume) for sound
- * in this sketch.
- *
- * @method getMasterVolume
- * @return {Number} Master amplitude (volume) for sound in this sketch.
- * Should be between 0.0 (silence) and 1.0.
- */
- p5.prototype.getMasterVolume = function () {
- return p5sound.output.gain.value;
- };
- /**
- *
Scale the output of all sound in this sketch
- * Scaled 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.
- *
- * Alternately, you can pass in a signal source such as an
- * oscillator to modulate the amplitude with an audio signal.
- *
- *
How This Works: When you load the p5.sound module, it
- * creates a single instance of p5sound. All sound objects in this
- * module output to p5sound before reaching your computer's output.
- * So if you change the amplitude of p5sound, it impacts all of the
- * sound in this module.
- *
- *
If no value is provided, returns a Web Audio API Gain Node
- *
- * @method masterVolume
- * @param {Number|Object} volume Volume (amplitude) between 0.0
- * and 1.0 or modulating signal/oscillator
- * @param {Number} [rampTime] Fade for t seconds
- * @param {Number} [timeFromNow] Schedule this event to happen at
- * t seconds in the future
- */
- p5.prototype.masterVolume = function (vol, rampTime, tFromNow) {
- if (typeof vol === 'number') {
- var rampTime = rampTime || 0;
- var tFromNow = tFromNow || 0;
- var now = p5sound.audiocontext.currentTime;
- var currentVol = p5sound.output.gain.value;
- p5sound.output.gain.cancelScheduledValues(now + tFromNow);
- p5sound.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
- p5sound.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
- } else if (vol) {
- vol.connect(p5sound.output.gain);
- } else {
- // return the Gain Node
- return p5sound.output.gain;
- }
- };
- /**
- * `p5.soundOut` is the p5.sound master output. It sends output to
- * the destination of this window's web audio context. It contains
- * Web Audio API nodes including a dyanmicsCompressor (.limiter),
- * and Gain Nodes for .input and .output.
- *
- * @property soundOut
- * @type {Object}
- */
- p5.prototype.soundOut = p5.soundOut = p5sound;
- /**
- * a silent connection to the DesinationNode
- * which will ensure that anything connected to it
- * will not be garbage collected
- *
- * @private
- */
- p5.soundOut._silentNode = p5sound.audiocontext.createGain();
- p5.soundOut._silentNode.gain.value = 0;
- p5.soundOut._silentNode.connect(p5sound.audiocontext.destination);
- return p5sound;
-}(sndcore);
-var helpers;
-helpers = function () {
- 'use strict';
- var p5sound = master;
- /**
- * Returns a number representing the sample rate, in samples per second,
- * of all sound objects in this audio context. It is determined by the
- * sampling rate of your operating system's sound card, and it is not
- * currently possile to change.
- * It is often 44100, or twice the range of human hearing.
- *
- * @method sampleRate
- * @return {Number} samplerate samples per second
- */
- p5.prototype.sampleRate = function () {
- return p5sound.audiocontext.sampleRate;
- };
- /**
- * Returns the closest MIDI note value for
- * a given frequency.
- *
- * @param {Number} frequency A freqeuncy, for example, the "A"
- * above Middle C is 440Hz
- * @return {Number} MIDI note value
- */
- p5.prototype.freqToMidi = function (f) {
- var mathlog2 = Math.log(f / 440) / Math.log(2);
- var m = Math.round(12 * mathlog2) + 57;
- return m;
- };
- /**
- * Returns the frequency value of a MIDI note value.
- * General MIDI treats notes as integers where middle C
- * is 60, C# is 61, D is 62 etc. Useful for generating
- * musical frequencies with oscillators.
- *
- * @method midiToFreq
- * @param {Number} midiNote The number of a MIDI note
- * @return {Number} Frequency value of the given MIDI note
- * @example
- *
- * var notes = [60, 64, 67, 72];
- * var i = 0;
- *
- * function setup() {
- * osc = new p5.Oscillator('Triangle');
- * osc.start();
- * frameRate(1);
- * }
- *
- * function draw() {
- * var freq = midiToFreq(notes[i]);
- * osc.freq(freq);
- * i++;
- * if (i >= notes.length){
- * i = 0;
- * }
- * }
- *
- */
- p5.prototype.midiToFreq = function (m) {
- return 440 * Math.pow(2, (m - 69) / 12);
- };
- /**
- * List the SoundFile formats that you will include. LoadSound
- * will search your directory for these extensions, and will pick
- * a format that is compatable with the client's web browser.
- * Here is a free online file
- * converter.
- *
- * @method soundFormats
- * @param {String|Strings} formats i.e. 'mp3', 'wav', 'ogg'
- * @example
- *
- * function preload() {
- * // set the global sound formats
- * soundFormats('mp3', 'ogg');
- *
- * // load either beatbox.mp3, or .ogg, depending on browser
- * mySound = loadSound('../sounds/beatbox.mp3');
- * }
- *
- * function setup() {
- * mySound.play();
- * }
- *
- */
- p5.prototype.soundFormats = function () {
- // reset extensions array
- p5sound.extensions = [];
- // add extensions
- for (var i = 0; i < arguments.length; i++) {
- arguments[i] = arguments[i].toLowerCase();
- if ([
- 'mp3',
- 'wav',
- 'ogg',
- 'm4a',
- 'aac'
- ].indexOf(arguments[i]) > -1) {
- p5sound.extensions.push(arguments[i]);
- } else {
- throw arguments[i] + ' is not a valid sound format!';
- }
- }
- };
- p5.prototype.disposeSound = function () {
- for (var i = 0; i < p5sound.soundArray.length; i++) {
- p5sound.soundArray[i].dispose();
- }
- };
- // register removeSound to dispose of p5sound SoundFiles, Convolvers,
- // Oscillators etc when sketch ends
- p5.prototype.registerMethod('remove', p5.prototype.disposeSound);
- p5.prototype._checkFileFormats = function (paths) {
- var path;
- // if path is a single string, check to see if extension is provided
- if (typeof paths === 'string') {
- path = paths;
- // see if extension is provided
- var extTest = path.split('.').pop();
- // if an extension is provided...
- if ([
- 'mp3',
- 'wav',
- 'ogg',
- 'm4a',
- 'aac'
- ].indexOf(extTest) > -1) {
- var supported = p5.prototype.isFileSupported(extTest);
- if (supported) {
- path = path;
- } else {
- var pathSplit = path.split('.');
- var pathCore = pathSplit[pathSplit.length - 1];
- for (var i = 0; i < p5sound.extensions.length; i++) {
- var extension = p5sound.extensions[i];
- var supported = p5.prototype.isFileSupported(extension);
- if (supported) {
- pathCore = '';
- if (pathSplit.length === 2) {
- pathCore += pathSplit[0];
- }
- for (var i = 1; i <= pathSplit.length - 2; i++) {
- var p = pathSplit[i];
- pathCore += '.' + p;
- }
- path = pathCore += '.';
- path = path += extension;
- break;
- }
- }
- }
- } else {
- for (var i = 0; i < p5sound.extensions.length; i++) {
- var extension = p5sound.extensions[i];
- var supported = p5.prototype.isFileSupported(extension);
- if (supported) {
- path = path + '.' + extension;
- break;
- }
- }
- }
- } else if (typeof paths === 'object') {
- for (var i = 0; i < paths.length; i++) {
- var extension = paths[i].split('.').pop();
- var supported = p5.prototype.isFileSupported(extension);
- if (supported) {
- // console.log('.'+extension + ' is ' + supported +
- // ' supported by your browser.');
- path = paths[i];
- break;
- }
- }
- }
- return path;
- };
- /**
- * Used by Osc and Env to chain signal math
- */
- p5.prototype._mathChain = function (o, math, thisChain, nextChain, type) {
- // if this type of math already exists in the chain, replace it
- for (var i in o.mathOps) {
- if (o.mathOps[i] instanceof type) {
- o.mathOps[i].dispose();
- thisChain = i;
- if (thisChain < o.mathOps.length - 1) {
- nextChain = o.mathOps[i + 1];
- }
- }
- }
- o.mathOps[thisChain - 1].disconnect();
- o.mathOps[thisChain - 1].connect(math);
- math.connect(nextChain);
- o.mathOps[thisChain] = math;
- 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';
- var p5sound = master;
- var ac = p5sound.audiocontext;
- // Stereo panner
- // if there is a stereo panner node use it
- if (typeof ac.createStereoPanner !== 'undefined') {
- p5.Panner = function (input, output, numInputChannels) {
- this.stereoPanner = this.input = ac.createStereoPanner();
- input.connect(this.stereoPanner);
- this.stereoPanner.connect(output);
- };
- p5.Panner.prototype.pan = function (val, tFromNow) {
- var time = tFromNow || 0;
- var t = ac.currentTime + time;
- this.stereoPanner.pan.linearRampToValueAtTime(val, t);
- };
- p5.Panner.prototype.inputChannels = function (numChannels) {
- };
- p5.Panner.prototype.connect = function (obj) {
- this.stereoPanner.connect(obj);
- };
- p5.Panner.prototype.disconnect = function (obj) {
- this.stereoPanner.disconnect();
- };
- } else {
- // if there is no createStereoPanner object
- // such as in safari 7.1.7 at the time of writing this
- // use this method to create the effect
- p5.Panner = function (input, output, numInputChannels) {
- this.input = ac.createGain();
- input.connect(this.input);
- this.left = ac.createGain();
- this.right = ac.createGain();
- this.left.channelInterpretation = 'discrete';
- this.right.channelInterpretation = 'discrete';
- // if input is stereo
- if (numInputChannels > 1) {
- this.splitter = ac.createChannelSplitter(2);
- this.input.connect(this.splitter);
- this.splitter.connect(this.left, 1);
- this.splitter.connect(this.right, 0);
- } else {
- this.input.connect(this.left);
- this.input.connect(this.right);
- }
- this.output = ac.createChannelMerger(2);
- this.left.connect(this.output, 0, 1);
- this.right.connect(this.output, 0, 0);
- this.output.connect(output);
- };
- // -1 is left, +1 is right
- p5.Panner.prototype.pan = function (val, tFromNow) {
- var time = tFromNow || 0;
- var t = ac.currentTime + time;
- var v = (val + 1) / 2;
- var rightVal = Math.cos(v * Math.PI / 2);
- var leftVal = Math.sin(v * Math.PI / 2);
- this.left.gain.linearRampToValueAtTime(leftVal, t);
- this.right.gain.linearRampToValueAtTime(rightVal, t);
- };
- p5.Panner.prototype.inputChannels = function (numChannels) {
- if (numChannels === 1) {
- this.input.disconnect();
- this.input.connect(this.left);
- this.input.connect(this.right);
- } else if (numChannels === 2) {
- if (typeof (this.splitter === 'undefined')) {
- this.splitter = ac.createChannelSplitter(2);
- }
- this.input.disconnect();
- this.input.connect(this.splitter);
- this.splitter.connect(this.left, 1);
- this.splitter.connect(this.right, 0);
- }
- };
- p5.Panner.prototype.connect = function (obj) {
- this.output.connect(obj);
- };
- p5.Panner.prototype.disconnect = function (obj) {
- this.output.disconnect();
- };
- }
- // 3D panner
- p5.Panner3D = function (input, output) {
- var panner3D = ac.createPanner();
- panner3D.panningModel = 'HRTF';
- panner3D.distanceModel = 'linear';
- panner3D.setPosition(0, 0, 0);
- input.connect(panner3D);
- panner3D.connect(output);
- panner3D.pan = function (xVal, yVal, zVal) {
- panner3D.setPosition(xVal, yVal, zVal);
- };
- return panner3D;
- };
-}(master);
-var soundfile;
-soundfile = function () {
- 'use strict';
- var CustomError = errorHandler;
- var p5sound = master;
- var ac = p5sound.audiocontext;
- /**
- *
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
- * @param {String/Array} path path to a sound file (String). Optionally,
- * you may include multiple file formats in
- * an array. Alternately, accepts an object
- * from the HTML5 File API, or a p5.File.
- * @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 progress of the request to
- * load the sound file
- * (between 0 and 1) as its first
- * parameter. This progress
- * does not account for the additional
- * time needed to decode the audio data.
- *
- * @return {Object} p5.SoundFile Object
- * @example
- *
- */
- 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);
- this.url = path;
- } else if (typeof paths == 'object') {
- if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
- // The File API isn't supported in this browser
- throw 'Unable to load file because the File API is not supported';
- }
- }
- // if type is a p5.File...get the actual file
- if (paths.file) {
- paths = paths.file;
- }
- this.file = paths;
- }
- // private _onended callback, set by the method: onended(callback)
- this._onended = function () {
- };
- this._looping = false;
- this._playing = false;
- this._paused = false;
- this._pauseTime = 0;
- // cues for scheduling events with addCue() removeCue()
- this._cues = [];
- // position of the most recently played sample
- this._lastPos = 0;
- this._counterNode;
- this._scopeNode;
- // array of sources so that they can all be stopped!
- this.bufferSourceNodes = [];
- // current source
- this.bufferSourceNode = null;
- this.buffer = null;
- this.playbackRate = 1;
- this.gain = 1;
- this.input = p5sound.audiocontext.createGain();
- this.output = p5sound.audiocontext.createGain();
- this.reversed = false;
- // start and end of playback / loop
- this.startTime = 0;
- this.endTime = null;
- this.pauseTime = 0;
- // "restart" would stop playback before retriggering
- this.mode = 'sustain';
- // time that playback was started, in millis
- this.startMillis = null;
- // 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, onerror);
- }
- // add this p5.SoundFile to the soundArray
- p5sound.soundArray.push(this);
- if (typeof whileLoading === 'function') {
- this._whileLoading = whileLoading;
- } else {
- this._whileLoading = function () {
- };
- }
- };
- // register preload handling of loadSound
- p5.prototype.registerPreloadMethod('loadSound', p5.prototype);
- /**
- * loadSound() returns a new p5.SoundFile from a specified
- * path. If called during preload(), the p5.SoundFile will be ready
- * to play in time for setup() and draw(). If called outside of
- * preload, the p5.SoundFile will not be ready immediately, so
- * loadSound accepts a callback as the second parameter. Using a
- *
- * local server is recommended when loading external files.
- *
- * @method loadSound
- * @param {String/Array} path Path to the sound file, or an array with
- * paths to soundfiles in multiple formats
- * i.e. ['sound.ogg', 'sound.mp3'].
- * Alternately, accepts an object: either
- * from the HTML5 File API, or a p5.File.
- * @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
- *
- */
- p5.SoundFile.prototype.playMode = function (str) {
- var s = str.toLowerCase();
- // if restart, stop all other sounds from playing
- if (s === 'restart' && this.buffer && this.bufferSourceNode) {
- for (var i = 0; i < this.bufferSourceNodes.length - 1; i++) {
- var now = p5sound.audiocontext.currentTime;
- this.bufferSourceNodes[i].stop(now);
- }
- }
- // set play mode to effect future playback
- if (s === 'restart' || s === 'sustain') {
- this.mode = s;
- } else {
- throw 'Invalid play mode. Must be either "restart" or "sustain"';
- }
- };
- /**
- * Pauses a file that is currently playing. If the file is not
- * playing, then nothing will happen.
- *
- * After pausing, .play() will resume from the paused
- * position.
- * If p5.SoundFile had been set to loop before it was paused,
- * it will continue to loop after it is unpaused with .play().
- *
- * @method pause
- * @param {Number} [startTime] (optional) schedule event to occur
- * seconds from now
- * @example
- *
- */
- p5.SoundFile.prototype.pause = function (time) {
- var now = p5sound.audiocontext.currentTime;
- var time = time || 0;
- var pTime = time + now;
- if (this.isPlaying() && this.buffer && this.bufferSourceNode) {
- this.pauseTime = this.currentTime();
- this.bufferSourceNode.stop(pTime);
- this._counterNode.stop(pTime);
- this._paused = true;
- this._playing = false;
- this._pauseTime = this.currentTime();
- } else {
- this._pauseTime = 0;
- }
- };
- /**
- * 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
- * @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
- */
- p5.SoundFile.prototype.loop = function (startTime, rate, amp, loopStart, duration) {
- this._looping = true;
- this.play(startTime, rate, amp, loopStart, duration);
- };
- /**
- * Set a p5.SoundFile's looping flag to true or false. If the sound
- * is currently playing, this change will take effect when it
- * reaches the end of the current playback.
- *
- * @param {Boolean} Boolean set looping to true or false
- */
- p5.SoundFile.prototype.setLoop = function (bool) {
- if (bool === true) {
- this._looping = true;
- } else if (bool === false) {
- this._looping = false;
- } else {
- throw 'Error: setLoop accepts either true or false';
- }
- if (this.bufferSourceNode) {
- this.bufferSourceNode.loop = this._looping;
- this._counterNode.loop = this._looping;
- }
- };
- /**
- * Returns 'true' if a p5.SoundFile is currently looping and playing, 'false' if not.
- *
- * @return {Boolean}
- */
- p5.SoundFile.prototype.isLooping = function () {
- if (!this.bufferSourceNode) {
- return false;
- }
- if (this._looping === true && this.isPlaying() === true) {
- return true;
- }
- return false;
- };
- /**
- * Returns true if a p5.SoundFile is playing, false if not (i.e.
- * paused or stopped).
- *
- * @method isPlaying
- * @return {Boolean}
- */
- p5.SoundFile.prototype.isPlaying = function () {
- return this._playing;
- };
- /**
- * Returns true if a p5.SoundFile is paused, false if not (i.e.
- * playing or stopped).
- *
- * @method isPaused
- * @return {Boolean}
- */
- p5.SoundFile.prototype.isPaused = function () {
- return this._paused;
- };
- /**
- * Stop soundfile playback.
- *
- * @method stop
- * @param {Number} [startTime] (optional) schedule event to occur
- * in seconds from now
- */
- p5.SoundFile.prototype.stop = function (timeFromNow) {
- var time = timeFromNow || 0;
- if (this.mode == 'sustain') {
- this.stopAll(time);
- this._playing = false;
- this.pauseTime = 0;
- this._paused = false;
- } else if (this.buffer && this.bufferSourceNode) {
- var now = p5sound.audiocontext.currentTime;
- var t = time || 0;
- this.pauseTime = 0;
- this.bufferSourceNode.stop(now + t);
- this._counterNode.stop(now + t);
- this._playing = false;
- this._paused = false;
- }
- };
- /**
- * Stop playback on all of this soundfile's sources.
- * @private
- */
- p5.SoundFile.prototype.stopAll = function (_time) {
- var now = p5sound.audiocontext.currentTime;
- var time = _time || 0;
- if (this.buffer && this.bufferSourceNode) {
- 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
- * 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.
- *
- * Alternately, you can pass in a signal source such as an
- * oscillator to modulate the amplitude with an audio signal.
- *
- * @method setVolume
- * @param {Number|Object} volume Volume (amplitude) between 0.0
- * and 1.0 or modulating signal/oscillator
- * @param {Number} [rampTime] Fade for t seconds
- * @param {Number} [timeFromNow] Schedule this event to happen at
- * t seconds in the future
- */
- p5.SoundFile.prototype.setVolume = function (vol, rampTime, tFromNow) {
- if (typeof vol === 'number') {
- var rampTime = rampTime || 0;
- var tFromNow = tFromNow || 0;
- var now = p5sound.audiocontext.currentTime;
- var currentVol = this.output.gain.value;
- this.output.gain.cancelScheduledValues(now + tFromNow);
- this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
- this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
- } else if (vol) {
- vol.connect(this.output.gain);
- } else {
- // return the Gain Node
- return this.output.gain;
- }
- };
- // same as setVolume, to match Processing Sound
- p5.SoundFile.prototype.amp = p5.SoundFile.prototype.setVolume;
- // these are the same thing
- p5.SoundFile.prototype.fade = p5.SoundFile.prototype.setVolume;
- p5.SoundFile.prototype.getVolume = function () {
- return this.output.gain.value;
- };
- /**
- * Set the stereo panning of a p5.sound object to
- * a floating point number between -1.0 (left) and 1.0 (right).
- * Default is 0.0 (center).
- *
- * @method pan
- * @param {Number} [panValue] Set the stereo panner
- * @param {Number} timeFromNow schedule this event to happen
- * seconds from now
- * @example
- *
- *
- * var ball = {};
- * var soundFile;
- *
- * function setup() {
- * soundFormats('ogg', 'mp3');
- * soundFile = loadSound('assets/beatbox.mp3');
- * }
- *
- * function draw() {
- * background(0);
- * ball.x = constrain(mouseX, 0, width);
- * ellipse(ball.x, height/2, 20, 20)
- * }
- *
- * function mousePressed(){
- * // map the ball's x location to a panning degree
- * // between -1.0 (left) and 1.0 (right)
- * var panning = map(ball.x, 0., width,-1.0, 1.0);
- * soundFile.pan(panning);
- * soundFile.play();
- * }
- *
- */
- p5.SoundFile.prototype.pan = function (pval, tFromNow) {
- this.panPosition = pval;
- this.panner.pan(pval, tFromNow);
- };
- /**
- * Returns the current stereo pan position (-1.0 to 1.0)
- *
- * @return {Number} Returns the stereo pan setting of the Oscillator
- * as a number between -1.0 (left) and 1.0 (right).
- * 0.0 is center and default.
- */
- p5.SoundFile.prototype.getPan = function () {
- return this.panPosition;
- };
- /**
- * Set the playback rate of a sound file. Will change the speed and the pitch.
- * Values less than zero will reverse the audio buffer.
- *
- * @method rate
- * @param {Number} [playbackRate] Set the playback rate. 1.0 is normal,
- * .5 is half-speed, 2.0 is twice as fast.
- * Values less than zero play backwards.
- * @example
- *
- * var song;
- *
- * function preload() {
- * song = loadSound('assets/Damscray_DancingTiger.mp3');
- * }
- *
- * function setup() {
- * song.loop();
- * }
- *
- * function draw() {
- * background(200);
- *
- * // Set the rate to a range between 0.1 and 4
- * // Changing the rate also alters the pitch
- * var speed = map(mouseY, 0.1, height, 0, 2);
- * speed = constrain(speed, 0.01, 4);
- * song.rate(speed);
- *
- * // Draw a circle to show what is going on
- * stroke(0);
- * fill(51, 100);
- * ellipse(mouseX, 100, 48, 48);
- * }
- *
- *
- *
- *
- */
- p5.SoundFile.prototype.rate = function (playbackRate) {
- if (this.playbackRate === playbackRate && this.bufferSourceNode) {
- if (this.bufferSourceNode.playbackRate.value === playbackRate) {
- return;
- }
- }
- this.playbackRate = playbackRate;
- var rate = playbackRate;
- if (this.playbackRate === 0 && this._playing) {
- this.pause();
- }
- if (this.playbackRate < 0 && !this.reversed) {
- var cPos = this.currentTime();
- var cRate = this.bufferSourceNode.playbackRate.value;
- // this.pause();
- this.reverseBuffer();
- rate = Math.abs(playbackRate);
- var newPos = (cPos - this.duration()) / rate;
- this.pauseTime = newPos;
- } else if (this.playbackRate > 0 && this.reversed) {
- this.reverseBuffer();
- }
- if (this.bufferSourceNode) {
- var now = p5sound.audiocontext.currentTime;
- this.bufferSourceNode.playbackRate.cancelScheduledValues(now);
- this.bufferSourceNode.playbackRate.linearRampToValueAtTime(Math.abs(rate), now);
- this._counterNode.playbackRate.cancelScheduledValues(now);
- this._counterNode.playbackRate.linearRampToValueAtTime(Math.abs(rate), now);
- }
- };
- // TO DO: document this
- p5.SoundFile.prototype.setPitch = function (num) {
- var newPlaybackRate = midiToFreq(num) / midiToFreq(60);
- this.rate(newPlaybackRate);
- };
- p5.SoundFile.prototype.getPlaybackRate = function () {
- return this.playbackRate;
- };
- /**
- * Returns the duration of a sound file in seconds.
- *
- * @method duration
- * @return {Number} The duration of the soundFile in seconds.
- */
- p5.SoundFile.prototype.duration = function () {
- // Return Duration
- if (this.buffer) {
- return this.buffer.duration;
- } else {
- return 0;
- }
- };
- /**
- * Return the current position of the p5.SoundFile playhead, in seconds.
- * Note that if you change the playbackRate while the p5.SoundFile is
- * playing, the results may not be accurate.
- *
- * @method currentTime
- * @return {Number} currentTime of the soundFile in seconds.
- */
- p5.SoundFile.prototype.currentTime = function () {
- // TO DO --> make reverse() flip these values appropriately
- if (this._pauseTime > 0) {
- return this._pauseTime;
- } else {
- return this._lastPos / ac.sampleRate;
- }
- };
- /**
- * Move the playhead of the song to a position, in seconds. Start
- * and Stop time. If none are given, will reset the file to play
- * entire duration from start to finish.
- *
- * @method jump
- * @param {Number} cueTime cueTime of the soundFile in seconds.
- * @param {Number} duration duration in seconds.
- */
- p5.SoundFile.prototype.jump = function (cueTime, duration) {
- if (cueTime < 0 || cueTime > this.buffer.duration) {
- throw 'jump time out of range';
- }
- if (duration > this.buffer.duration - cueTime) {
- throw 'end time out of range';
- }
- var cTime = cueTime || 0;
- var eTime = duration || this.buffer.duration - cueTime;
- if (this.isPlaying()) {
- this.stop();
- }
- this.play(0, this.playbackRate, this.output.gain.value, cTime, eTime);
- };
- /**
- * Return the number of channels in a sound file.
- * For example, Mono = 1, Stereo = 2.
- *
- * @method channels
- * @return {Number} [channels]
- */
- p5.SoundFile.prototype.channels = function () {
- return this.buffer.numberOfChannels;
- };
- /**
- * Return the sample rate of the sound file.
- *
- * @method sampleRate
- * @return {Number} [sampleRate]
- */
- p5.SoundFile.prototype.sampleRate = function () {
- return this.buffer.sampleRate;
- };
- /**
- * Return the number of samples in a sound file.
- * Equal to sampleRate * duration.
- *
- * @method frames
- * @return {Number} [sampleCount]
- */
- p5.SoundFile.prototype.frames = function () {
- return this.buffer.length;
- };
- /**
- * Returns an array of amplitude peaks in a p5.SoundFile that can be
- * used to draw a static waveform. Scans through the p5.SoundFile's
- * audio buffer to find the greatest amplitudes. Accepts one
- * parameter, 'length', which determines size of the array.
- * Larger arrays result in more precise waveform visualizations.
- *
- * Inspired by Wavesurfer.js.
- *
- * @method getPeaks
- * @params {Number} [length] length is the size of the returned array.
- * Larger length results in more precision.
- * Defaults to 5*width of the browser window.
- * @returns {Float32Array} Array of peaks.
- */
- p5.SoundFile.prototype.getPeaks = function (length) {
- if (this.buffer) {
- // set length to window's width if no length is provided
- if (!length) {
- length = window.width * 5;
- }
- if (this.buffer) {
- var buffer = this.buffer;
- var sampleSize = buffer.length / length;
- var sampleStep = ~~(sampleSize / 10) || 1;
- var channels = buffer.numberOfChannels;
- var peaks = new Float32Array(Math.round(length));
- for (var c = 0; c < channels; c++) {
- var chan = buffer.getChannelData(c);
- for (var i = 0; i < length; i++) {
- var start = ~~(i * sampleSize);
- var end = ~~(start + sampleSize);
- var max = 0;
- for (var j = start; j < end; j += sampleStep) {
- var value = chan[j];
- if (value > max) {
- max = value;
- } else if (-value > max) {
- max = value;
- }
- }
- if (c === 0 || Math.abs(max) > peaks[i]) {
- peaks[i] = max;
- }
- }
- }
- return peaks;
- }
- } else {
- throw 'Cannot load peaks yet, buffer is not loaded';
- }
- };
- /**
- * Reverses the p5.SoundFile's buffer source.
- * Playback must be handled separately (see example).
- *
- * @method reverseBuffer
- * @example
- *
- */
- p5.SoundFile.prototype.reverseBuffer = function () {
- var curVol = this.getVolume();
- this.setVolume(0, 0.01, 0);
- this.pause();
- if (this.buffer) {
- for (var i = 0; i < this.buffer.numberOfChannels; i++) {
- Array.prototype.reverse.call(this.buffer.getChannelData(i));
- }
- // set reversed flag
- this.reversed = !this.reversed;
- } else {
- throw 'SoundFile is not done loading';
- }
- this.setVolume(curVol, 0.01, 0.0101);
- this.play();
- };
- /**
- * 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++) {
- if (this.bufferSourceNodes[i] !== null) {
- this.bufferSourceNodes[i].disconnect();
- try {
- this.bufferSourceNodes[i].stop(now);
- } catch (e) {
- }
- this.bufferSourceNodes[i] = null;
- }
- }
- if (this.isPlaying()) {
- try {
- this._counterNode.stop(now);
- } catch (e) {
- console.log(e);
- }
- this._counterNode = null;
- }
- }
- if (this.output) {
- this.output.disconnect();
- this.output = null;
- }
- if (this.panner) {
- this.panner.disconnect();
- this.panner = null;
- }
- };
- /**
- * Connects the output of a p5sound object to input of another
- * p5.sound object. For example, you may connect a p5.SoundFile to an
- * FFT or an Effect. If no parameter is given, it will connect to
- * the master output. Most p5sound objects connect to the master
- * output when they are created.
- *
- * @method connect
- * @param {Object} [object] Audio object that accepts an input
- */
- p5.SoundFile.prototype.connect = function (unit) {
- if (!unit) {
- this.panner.connect(p5sound.input);
- } else {
- if (unit.hasOwnProperty('input')) {
- this.panner.connect(unit.input);
- } else {
- this.panner.connect(unit);
- }
- }
- };
- /**
- * Disconnects the output of this p5sound object.
- *
- * @method disconnect
- */
- p5.SoundFile.prototype.disconnect = function () {
- this.panner.disconnect();
- };
- /**
- */
- p5.SoundFile.prototype.getLevel = function (smoothing) {
- console.warn('p5.SoundFile.getLevel has been removed from the library. Use p5.Amplitude instead');
- };
- /**
- * Reset the source for this SoundFile to a
- * new path (URL).
- *
- * @method setPath
- * @param {String} path path to audio file
- * @param {Function} callback Callback
- */
- p5.SoundFile.prototype.setPath = function (p, callback) {
- var path = p5.prototype._checkFileFormats(p);
- this.url = path;
- this.load(callback);
- };
- /**
- * Replace the current Audio Buffer with a new Buffer.
- *
- * @param {Array} buf Array of Float32 Array(s). 2 Float32 Arrays
- * will create a stereo source. 1 will create
- * a mono source.
- */
- p5.SoundFile.prototype.setBuffer = function (buf) {
- var numChannels = buf.length;
- var size = buf[0].length;
- var newBuffer = ac.createBuffer(numChannels, size, ac.sampleRate);
- if (!buf[0] instanceof Float32Array) {
- buf[0] = new Float32Array(buf[0]);
- }
- for (var channelNum = 0; channelNum < numChannels; channelNum++) {
- var channel = newBuffer.getChannelData(channelNum);
- channel.set(buf[channelNum]);
- }
- this.buffer = newBuffer;
- // set numbers of channels on input to the panner
- this.panner.inputChannels(numChannels);
- };
- //////////////////////////////////////////////////
- // script processor node with an empty buffer to help
- // keep a sample-accurate position in playback buffer.
- // Inspired by Chinmay Pendharkar's technique for Sonoport --> http://bit.ly/1HwdCsV
- // Copyright [2015] [Sonoport (Asia) Pte. Ltd.],
- // Licensed under the Apache License http://apache.org/licenses/LICENSE-2.0
- ////////////////////////////////////////////////////////////////////////////////////
- // initialize counterNode, set its initial buffer and playbackRate
- p5.SoundFile.prototype._initCounterNode = function () {
- var self = this;
- var now = ac.currentTime;
- var cNode = ac.createBufferSource();
- // 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);
- // create counter buffer of the same length as self.buffer
- cNode.buffer = _createCounterBuffer(self.buffer);
- cNode.playbackRate.setValueAtTime(self.playbackRate, now);
- cNode.connect(self._scopeNode);
- self._scopeNode.connect(p5.soundOut._silentNode);
- self._scopeNode.onaudioprocess = function (processEvent) {
- var inputBuffer = processEvent.inputBuffer.getChannelData(0);
- // update the lastPos
- self._lastPos = inputBuffer[inputBuffer.length - 1] || 0;
- // do any callbacks that have been scheduled
- self._onTimeUpdate(self._lastPos);
- };
- return cNode;
- };
- // initialize sourceNode, set its initial buffer and playbackRate
- p5.SoundFile.prototype._initSourceNode = function () {
- var self = this;
- var now = ac.currentTime;
- var bufferSourceNode = ac.createBufferSource();
- bufferSourceNode.buffer = self.buffer;
- bufferSourceNode.playbackRate.value = self.playbackRate;
- return bufferSourceNode;
- };
- var _createCounterBuffer = function (buffer) {
- var array = new Float32Array(buffer.length);
- var audioBuf = ac.createBuffer(1, buffer.length, 44100);
- for (var index = 0; index < buffer.length; index++) {
- array[index] = index;
- }
- audioBuf.getChannelData(0).set(array);
- return audioBuf;
- };
- /**
- * processPeaks returns an array of timestamps where it thinks there is a beat.
- *
- * This is an asynchronous function that processes the soundfile in an offline audio context,
- * and sends the results to your callback function.
- *
- * The process involves running the soundfile through a lowpass filter, and finding all of the
- * peaks above the initial threshold. If the total number of peaks are below the minimum number of peaks,
- * it decreases the threshold and re-runs the analysis until either minPeaks or minThreshold are reached.
- *
- * @method processPeaks
- * @param {Function} callback a function to call once this data is returned
- * @param {Number} [initThreshold] initial threshold defaults to 0.9
- * @param {Number} [minThreshold] minimum threshold defaults to 0.22
- * @param {Number} [minPeaks] minimum number of peaks defaults to 200
- * @return {Array} Array of timestamped peaks
- */
- p5.SoundFile.prototype.processPeaks = function (callback, _initThreshold, _minThreshold, _minPeaks) {
- var bufLen = this.buffer.length;
- var sampleRate = this.buffer.sampleRate;
- var buffer = this.buffer;
- var initialThreshold = _initThreshold || 0.9, threshold = initialThreshold, minThreshold = _minThreshold || 0.22, minPeaks = _minPeaks || 200;
- // Create offline context
- var offlineContext = new OfflineAudioContext(1, bufLen, sampleRate);
- // create buffer source
- var source = offlineContext.createBufferSource();
- source.buffer = buffer;
- // Create filter. TO DO: allow custom setting of filter
- var filter = offlineContext.createBiquadFilter();
- filter.type = 'lowpass';
- source.connect(filter);
- filter.connect(offlineContext.destination);
- // start playing at time:0
- source.start(0);
- offlineContext.startRendering();
- // Render the song
- // act on the result
- offlineContext.oncomplete = function (e) {
- var data = {};
- var filteredBuffer = e.renderedBuffer;
- var bufferData = filteredBuffer.getChannelData(0);
- // step 1:
- // create Peak instances, add them to array, with strength and sampleIndex
- do {
- allPeaks = getPeaksAtThreshold(bufferData, threshold);
- threshold -= 0.005;
- } while (Object.keys(allPeaks).length < minPeaks && threshold >= minThreshold);
- // step 2:
- // find intervals for each peak in the sampleIndex, add tempos array
- var intervalCounts = countIntervalsBetweenNearbyPeaks(allPeaks);
- // step 3: find top tempos
- var groups = groupNeighborsByTempo(intervalCounts, filteredBuffer.sampleRate);
- // sort top intervals
- var topTempos = groups.sort(function (intA, intB) {
- return intB.count - intA.count;
- }).splice(0, 5);
- // set this SoundFile's tempo to the top tempo ??
- this.tempo = topTempos[0].tempo;
- // step 4:
- // new array of peaks at top tempo within a bpmVariance
- var bpmVariance = 5;
- var tempoPeaks = getPeaksAtTopTempo(allPeaks, topTempos[0].tempo, filteredBuffer.sampleRate, bpmVariance);
- callback(tempoPeaks);
- };
- };
- // process peaks
- var Peak = function (amp, i) {
- this.sampleIndex = i;
- this.amplitude = amp;
- this.tempos = [];
- this.intervals = [];
- };
- var allPeaks = [];
- // 1. for processPeaks() Function to identify peaks above a threshold
- // returns an array of peak indexes as frames (samples) of the original soundfile
- function getPeaksAtThreshold(data, threshold) {
- var peaksObj = {};
- var length = data.length;
- for (var i = 0; i < length; i++) {
- if (data[i] > threshold) {
- var amp = data[i];
- var peak = new Peak(amp, i);
- peaksObj[i] = peak;
- // Skip forward ~ 1/8s to get past this peak.
- i += 6000;
- }
- i++;
- }
- return peaksObj;
- }
- // 2. for processPeaks()
- function countIntervalsBetweenNearbyPeaks(peaksObj) {
- var intervalCounts = [];
- var peaksArray = Object.keys(peaksObj).sort();
- for (var index = 0; index < peaksArray.length; index++) {
- // find intervals in comparison to nearby peaks
- for (var i = 0; i < 10; i++) {
- var startPeak = peaksObj[peaksArray[index]];
- var endPeak = peaksObj[peaksArray[index + i]];
- if (startPeak && endPeak) {
- var startPos = startPeak.sampleIndex;
- var endPos = endPeak.sampleIndex;
- var interval = endPos - startPos;
- // add a sample interval to the startPeek in the allPeaks array
- if (interval > 0) {
- startPeak.intervals.push(interval);
- }
- // tally the intervals and return interval counts
- var foundInterval = intervalCounts.some(function (intervalCount, p) {
- if (intervalCount.interval === interval) {
- intervalCount.count++;
- return intervalCount;
- }
- });
- // store with JSON like formatting
- if (!foundInterval) {
- intervalCounts.push({
- interval: interval,
- count: 1
- });
- }
- }
- }
- }
- return intervalCounts;
- }
- // 3. for processPeaks --> find tempo
- function groupNeighborsByTempo(intervalCounts, sampleRate) {
- var tempoCounts = [];
- intervalCounts.forEach(function (intervalCount, i) {
- try {
- // Convert an interval to tempo
- var theoreticalTempo = Math.abs(60 / (intervalCount.interval / sampleRate));
- theoreticalTempo = mapTempo(theoreticalTempo);
- var foundTempo = tempoCounts.some(function (tempoCount) {
- if (tempoCount.tempo === theoreticalTempo)
- return tempoCount.count += intervalCount.count;
- });
- if (!foundTempo) {
- if (isNaN(theoreticalTempo)) {
- return;
- }
- tempoCounts.push({
- tempo: Math.round(theoreticalTempo),
- count: intervalCount.count
- });
- }
- } catch (e) {
- throw e;
- }
- });
- return tempoCounts;
- }
- // 4. for processPeaks - get peaks at top tempo
- function getPeaksAtTopTempo(peaksObj, tempo, sampleRate, bpmVariance) {
- var peaksAtTopTempo = [];
- var peaksArray = Object.keys(peaksObj).sort();
- // TO DO: filter out peaks that have the tempo and return
- for (var i = 0; i < peaksArray.length; i++) {
- var key = peaksArray[i];
- var peak = peaksObj[key];
- for (var j = 0; j < peak.intervals.length; j++) {
- var intervalBPM = Math.round(Math.abs(60 / (peak.intervals[j] / sampleRate)));
- intervalBPM = mapTempo(intervalBPM);
- var dif = intervalBPM - tempo;
- if (Math.abs(intervalBPM - tempo) < bpmVariance) {
- // convert sampleIndex to seconds
- peaksAtTopTempo.push(peak.sampleIndex / 44100);
- }
- }
- }
- // filter out peaks that are very close to each other
- peaksAtTopTempo = peaksAtTopTempo.filter(function (peakTime, index, arr) {
- var dif = arr[index + 1] - peakTime;
- if (dif > 0.01) {
- return true;
- }
- });
- return peaksAtTopTempo;
- }
- // helper function for processPeaks
- function mapTempo(theoreticalTempo) {
- // these scenarios create infinite while loop
- if (!isFinite(theoreticalTempo) || theoreticalTempo == 0) {
- return;
- }
- // Adjust the tempo to fit within the 90-180 BPM range
- while (theoreticalTempo < 90)
- theoreticalTempo *= 2;
- while (theoreticalTempo > 180 && theoreticalTempo > 90)
- theoreticalTempo /= 2;
- return theoreticalTempo;
- }
- /*** SCHEDULE EVENTS ***/
- /**
- * Schedule events to trigger every time a MediaElement
- * (audio/video) reaches a playback cue point.
- *
- * Accepts a callback function, a time (in seconds) at which to trigger
- * the callback, and an optional parameter for the callback.
- *
- * Time will be passed as the first parameter to the callback function,
- * and param will be the second parameter.
- *
- *
- * @method addCue
- * @param {Number} time Time in seconds, relative to this media
- * element's playback. For example, to trigger
- * an event every time playback reaches two
- * seconds, pass in the number 2. This will be
- * passed as the first parameter to
- * the callback function.
- * @param {Function} callback Name of a function that will be
- * called at the given time. The callback will
- * receive time and (optionally) param as its
- * two parameters.
- * @param {Object} [value] An object to be passed as the
- * second parameter to the
- * callback function.
- * @return {Number} id ID of this cue,
- * useful for removeCue(id)
- * @example
- *
- */
- p5.SoundFile.prototype.addCue = function (time, callback, val) {
- var id = this._cueIDCounter++;
- var cue = new Cue(callback, time, id, val);
- this._cues.push(cue);
- // if (!this.elt.ontimeupdate) {
- // this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
- // }
- return id;
- };
- /**
- * Remove a callback based on its ID. The ID is returned by the
- * addCue method.
- *
- * @method removeCue
- * @param {Number} id ID of the cue, as returned by addCue
- */
- p5.SoundFile.prototype.removeCue = function (id) {
- 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);
- }
- }
- if (this._cues.length === 0) {
- }
- };
- /**
- * Remove all of the callbacks that had originally been scheduled
- * via the addCue method.
- *
- * @method clearCues
- */
- p5.SoundFile.prototype.clearCues = function () {
- this._cues = [];
- };
- // private method that checks for cues to be fired if events
- // have been scheduled using addCue(callback, time).
- p5.SoundFile.prototype._onTimeUpdate = function (position) {
- var playbackTime = position / this.buffer.sampleRate;
- 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
- cue.callback(val);
- }
- }
- this._prevTime = playbackTime;
- };
- // Cue inspired by JavaScript setTimeout, and the
- // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
- var Cue = function (callback, time, id, val) {
- this.callback = callback;
- this.time = time;
- this.id = id;
- this.val = val;
- };
-}(sndcore, errorHandler, master);
-var amplitude;
-amplitude = function () {
- 'use strict';
- var p5sound = master;
- /**
- * Amplitude measures volume between 0.0 and 1.0.
- * Listens to all p5sound by default, or use setInput()
- * to listen to a specific sound source. Accepts an optional
- * smoothing value, which defaults to 0.
- *
- * @class p5.Amplitude
- * @constructor
- * @param {Number} [smoothing] between 0.0 and .999 to smooth
- * amplitude readings (defaults to 0)
- * @return {Object} Amplitude Object
- * @example
- *
- */
- p5.Amplitude.prototype.getLevel = function (channel) {
- if (typeof channel !== 'undefined') {
- if (this.normalize) {
- return this.stereoVolNorm[channel];
- } else {
- return this.stereoVol[channel];
- }
- } else if (this.normalize) {
- return this.volNorm;
- } else {
- return this.volume;
- }
- };
- /**
- * Determines whether the results of Amplitude.process() will be
- * Normalized. To normalize, Amplitude finds the difference the
- * loudest reading it has processed and the maximum amplitude of
- * 1.0. Amplitude adds this difference to all values to produce
- * results that will reliably map between 0.0 and 1.0. However,
- * if a louder moment occurs, the amount that Normalize adds to
- * all the values will change. Accepts an optional boolean parameter
- * (true or false). Normalizing is off by default.
- *
- * @method toggleNormalize
- * @param {boolean} [boolean] set normalize to true (1) or false (0)
- */
- p5.Amplitude.prototype.toggleNormalize = function (bool) {
- if (typeof bool === 'boolean') {
- this.normalize = bool;
- } else {
- this.normalize = !this.normalize;
- }
- };
- /**
- * Smooth Amplitude analysis by averaging with the last analysis
- * frame. Off by default.
- *
- * @method smooth
- * @param {Number} set smoothing from 0.0 <= 1
- */
- p5.Amplitude.prototype.smooth = function (s) {
- if (s >= 0 && s < 1) {
- this.smoothing = s;
- } else {
- 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 () {
- 'use strict';
- var p5sound = master;
- /**
- *
FFT (Fast Fourier Transform) is an analysis algorithm that
- * isolates individual
- *
- * audio frequencies within a waveform.
- *
- *
Once instantiated, a p5.FFT object can return an array based on
- * two types of analyses: • FFT.waveform() computes
- * amplitude values along the time domain. The array indices correspond
- * to samples across a brief moment in time. Each value represents
- * amplitude of the waveform at that sample of time.
- * • FFT.analyze() computes amplitude values along the
- * frequency domain. The array indices correspond to frequencies (i.e.
- * pitches), from the lowest to the highest that humans can hear. Each
- * value represents amplitude at that slice of the frequency spectrum.
- * Use with getEnergy() to measure amplitude at specific
- * frequencies, or within a range of frequencies.
- *
- *
FFT analyzes a very short snapshot of sound called a sample
- * buffer. It returns an array of amplitude measurements, referred
- * to as bins. The array is 1024 bins long by default.
- * You can change the bin array length, but it must be a power of 2
- * between 16 and 1024 in order for the FFT algorithm to function
- * correctly. The actual size of the FFT buffer is twice the
- * number of bins, so given a standard sample rate, the buffer is
- * 2048/44100 seconds long.
- *
- *
- * @class p5.FFT
- * @constructor
- * @param {Number} [smoothing] Smooth results of Freq Spectrum.
- * 0.0 < smoothing < 1.0.
- * Defaults to 0.8.
- * @param {Number} [bins] Length of resulting array.
- * Must be a power of two between
- * 16 and 1024. Defaults to 1024.
- * @return {Object} FFT Object
- * @example
- *
- * function preload(){
- * sound = loadSound('assets/Damscray_DancingTiger.mp3');
- * }
- *
- * function setup(){
- * 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
- * for (var i = 0; i< spectrum.length; i++){
- * var x = map(i, 0, spectrum.length, 0, width);
- * var h = -height + map(spectrum[i], 0, 255, height, 0);
- * rect(x, height, width / spectrum.length, h )
- * }
- *
- * var waveform = fft.waveform();
- * noFill();
- * beginShape();
- * stroke(255,0,0); // waveform is red
- * strokeWeight(1);
- * for (var i = 0; i< waveform.length; i++){
- * var x = map(i, 0, waveform.length, 0, width);
- * var y = map( waveform[i], -1, 1, 0, height);
- * vertex(x,y);
- * }
- * endShape();
- *
- * text('click to play/pause', 4, 10);
- * }
- *
- * // fade sound if mouse is over canvas
- * function togglePlay() {
- * if (sound.isPlaying()) {
- * sound.pause();
- * } else {
- * sound.loop();
- * }
- * }
- *
- */
- p5.FFT = function (smoothing, bins) {
- this.smoothing = smoothing || 0.8;
- this.bins = bins || 1024;
- var FFT_SIZE = bins * 2 || 2048;
- this.input = this.analyser = p5sound.audiocontext.createAnalyser();
- // default connections to p5sound fftMeter
- p5sound.fftMeter.connect(this.analyser);
- this.analyser.smoothingTimeConstant = this.smoothing;
- this.analyser.fftSize = FFT_SIZE;
- this.freqDomain = new Uint8Array(this.analyser.frequencyBinCount);
- this.timeDomain = new Uint8Array(this.analyser.frequencyBinCount);
- // predefined frequency ranages, these will be tweakable
- this.bass = [
- 20,
- 140
- ];
- this.lowMid = [
- 140,
- 400
- ];
- this.mid = [
- 400,
- 2600
- ];
- this.highMid = [
- 2600,
- 5200
- ];
- this.treble = [
- 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
- * provided, FFT will analyze all sound in the sketch.
- *
- * @method setInput
- * @param {Object} [source] p5.sound object (or web audio API source node)
- */
- p5.FFT.prototype.setInput = function (source) {
- if (!source) {
- p5sound.fftMeter.connect(this.analyser);
- } else {
- if (source.output) {
- source.output.connect(this.analyser);
- } else if (source.connect) {
- source.connect(this.analyser);
- }
- p5sound.fftMeter.disconnect();
- }
- };
- /**
- * Returns an array of amplitude values (between -1.0 and +1.0) that represent
- * a snapshot of amplitude readings in a single buffer. Length will be
- * equal to bins (defaults to 1024). Can be used to draw the waveform
- * of a sound.
- *
- * @method waveform
- * @param {Number} [bins] Must be a power of two between
- * 16 and 1024. Defaults to 1024.
- * @param {String} [precision] If any value is provided, will return results
- * in a Float32 Array which is more precise
- * than a regular array.
- * @return {Array} Array Array of amplitude values (-1 to 1)
- * over time. Array length = bins.
- *
- */
- p5.FFT.prototype.waveform = function () {
- var bins, mode, normalArray;
- for (var i = 0; i < arguments.length; i++) {
- if (typeof arguments[i] === 'number') {
- bins = arguments[i];
- this.analyser.fftSize = bins * 2;
- }
- if (typeof arguments[i] === 'string') {
- mode = arguments[i];
- }
- }
- // getFloatFrequencyData doesnt work in Safari as of 5/2015
- if (mode && !p5.prototype._isSafari()) {
- timeToFloat(this, this.timeDomain);
- this.analyser.getFloatTimeDomainData(this.timeDomain);
- return this.timeDomain;
- } else {
- timeToInt(this, this.timeDomain);
- this.analyser.getByteTimeDomainData(this.timeDomain);
- var normalArray = new Array();
- for (var i = 0; i < this.timeDomain.length; i++) {
- var scaled = p5.prototype.map(this.timeDomain[i], 0, 255, -1, 1);
- normalArray.push(scaled);
- }
- return normalArray;
- }
- };
- /**
- * Returns an array of amplitude values (between 0 and 255)
- * across the frequency spectrum. Length is equal to FFT bins
- * (1024 by default). The array indices correspond to frequencies
- * (i.e. pitches), from the lowest to the highest that humans can
- * hear. Each value represents amplitude at that slice of the
- * frequency spectrum. Must be called prior to using
- * getEnergy().
- *
- * @method analyze
- * @param {Number} [bins] Must be a power of two between
- * 16 and 1024. Defaults to 1024.
- * @param {Number} [scale] If "dB," returns decibel
- * float measurements between
- * -140 and 0 (max).
- * Otherwise returns integers from 0-255.
- * @return {Array} spectrum Array of energy (amplitude/volume)
- * values across the frequency spectrum.
- * Lowest energy (silence) = 0, highest
- * possible is 255.
- * @example
- *
- * var osc;
- * var fft;
- *
- * function setup(){
- * createCanvas(100,100);
- * osc = new p5.Oscillator();
- * osc.amp(0);
- * osc.start();
- * fft = new p5.FFT();
- * }
- *
- * function draw(){
- * background(0);
- *
- * var freq = map(mouseX, 0, 800, 20, 15000);
- * freq = constrain(freq, 1, 20000);
- * osc.freq(freq);
- *
- * var spectrum = fft.analyze();
- * noStroke();
- * fill(0,255,0); // spectrum is green
- * for (var i = 0; i< spectrum.length; i++){
- * var x = map(i, 0, spectrum.length, 0, width);
- * var h = -height + map(spectrum[i], 0, 255, height, 0);
- * rect(x, height, width / spectrum.length, h );
- * }
- *
- * stroke(255);
- * text('Freq: ' + round(freq)+'Hz', 10, 10);
- *
- * isMouseOverCanvas();
- * }
- *
- * // only play sound when mouse is over canvas
- * function isMouseOverCanvas() {
- * var mX = mouseX, mY = mouseY;
- * if (mX > 0 && mX < width && mY < height && mY > 0) {
- * osc.amp(0.5, 0.2);
- * } else {
- * osc.amp(0, 0.2);
- * }
- * }
- *
- *
- *
- */
- p5.FFT.prototype.analyze = function () {
- var bins, mode;
- for (var i = 0; i < arguments.length; i++) {
- if (typeof arguments[i] === 'number') {
- bins = this.bins = arguments[i];
- this.analyser.fftSize = this.bins * 2;
- }
- if (typeof arguments[i] === 'string') {
- mode = arguments[i];
- }
- }
- if (mode && mode.toLowerCase() === 'db') {
- freqToFloat(this);
- this.analyser.getFloatFrequencyData(this.freqDomain);
- return this.freqDomain;
- } else {
- freqToInt(this, this.freqDomain);
- this.analyser.getByteFrequencyData(this.freqDomain);
- var normalArray = Array.apply([], this.freqDomain);
- normalArray.length === this.analyser.fftSize;
- normalArray.constructor === Array;
- return normalArray;
- }
- };
- /**
- * Returns the amount of energy (volume) at a specific
- *
- * frequency, or the average amount of energy between two
- * frequencies. Accepts Number(s) corresponding
- * to frequency (in Hz), or a String corresponding to predefined
- * frequency ranges ("bass", "lowMid", "mid", "highMid", "treble").
- * Returns a range between 0 (no energy/volume at that frequency) and
- * 255 (maximum energy).
- * NOTE: analyze() must be called prior to getEnergy(). Analyze()
- * tells the FFT to analyze frequency data, and getEnergy() uses
- * the results determine the value at a specific frequency or
- * range of frequencies.
- *
- * @method getEnergy
- * @param {Number|String} frequency1 Will return a value representing
- * energy at this frequency. Alternately,
- * the strings "bass", "lowMid" "mid",
- * "highMid", and "treble" will return
- * predefined frequency ranges.
- * @param {Number} [frequency2] If a second frequency is given,
- * will return average amount of
- * energy that exists between the
- * two frequencies.
- * @return {Number} Energy Energy (volume/amplitude) from
- * 0 and 255.
- *
- */
- p5.FFT.prototype.getEnergy = function (frequency1, frequency2) {
- var nyquist = p5sound.audiocontext.sampleRate / 2;
- if (frequency1 === 'bass') {
- frequency1 = this.bass[0];
- frequency2 = this.bass[1];
- } else if (frequency1 === 'lowMid') {
- frequency1 = this.lowMid[0];
- frequency2 = this.lowMid[1];
- } else if (frequency1 === 'mid') {
- frequency1 = this.mid[0];
- frequency2 = this.mid[1];
- } else if (frequency1 === 'highMid') {
- frequency1 = this.highMid[0];
- frequency2 = this.highMid[1];
- } else if (frequency1 === 'treble') {
- frequency1 = this.treble[0];
- frequency2 = this.treble[1];
- }
- if (typeof frequency1 !== 'number') {
- throw 'invalid input for getEnergy()';
- } else if (!frequency2) {
- var index = Math.round(frequency1 / nyquist * this.freqDomain.length);
- return this.freqDomain[index];
- } else if (frequency1 && frequency2) {
- // if second is higher than first
- if (frequency1 > frequency2) {
- var swap = frequency2;
- frequency2 = frequency1;
- frequency1 = swap;
- }
- var lowIndex = Math.round(frequency1 / nyquist * this.freqDomain.length);
- var highIndex = Math.round(frequency2 / nyquist * this.freqDomain.length);
- var total = 0;
- var numFrequencies = 0;
- // add up all of the values for the frequencies
- for (var i = lowIndex; i <= highIndex; i++) {
- total += this.freqDomain[i];
- numFrequencies += 1;
- }
- // divide by total number of frequencies
- var toReturn = total / numFrequencies;
- return toReturn;
- } else {
- throw 'invalid input for getEnergy()';
- }
- };
- // compatability with v.012, changed to getEnergy in v.0121. Will be deprecated...
- p5.FFT.prototype.getFreq = function (freq1, freq2) {
- console.log('getFreq() is deprecated. Please use getEnergy() instead.');
- 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.
- *
- * @method smooth
- * @param {Number} smoothing 0.0 < smoothing < 1.0.
- * Defaults to 0.8.
- */
- p5.FFT.prototype.smooth = function (s) {
- if (s) {
- this.smoothing = s;
- }
- 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;
- };
- /**
- * Returns an array of average amplitude values for a given number
- * of frequency bands split equally. N defaults to 16.
- * NOTE: analyze() must be called prior to linAverages(). Analyze()
- * tells the FFT to analyze frequency data, and linAverages() uses
- * the results to group them into a smaller set of averages.
- *
- * @method linAverages
- * @param {Number} N Number of returned frequency groups
- * @return {Array} linearAverages Array of average amplitude values for each group
- */
- p5.FFT.prototype.linAverages = function (N) {
- var N = N || 16;
- // This prevents undefined, null or 0 values of N
- var spectrum = this.freqDomain;
- var spectrumLength = spectrum.length;
- var spectrumStep = Math.floor(spectrumLength / N);
- var linearAverages = new Array(N);
- // Keep a second index for the current average group and place the values accordingly
- // with only one loop in the spectrum data
- var groupIndex = 0;
- for (var specIndex = 0; specIndex < spectrumLength; specIndex++) {
- linearAverages[groupIndex] = linearAverages[groupIndex] !== undefined ? (linearAverages[groupIndex] + spectrum[specIndex]) / 2 : spectrum[specIndex];
- // Increase the group index when the last element of the group is processed
- if (specIndex % spectrumStep == spectrumStep - 1) {
- groupIndex++;
- }
- }
- return linearAverages;
- };
- /**
- * Returns an array of average amplitude values of the spectrum, for a given
- * set of
- * Octave Bands
- * NOTE: analyze() must be called prior to logAverages(). Analyze()
- * tells the FFT to analyze frequency data, and logAverages() uses
- * the results to group them into a smaller set of averages.
- *
- * @method logAverages
- * @param {Array} octaveBands Array of Octave Bands objects for grouping
- * @return {Array} logAverages Array of average amplitude values for each group
- */
- p5.FFT.prototype.logAverages = function (octaveBands) {
- var nyquist = p5sound.audiocontext.sampleRate / 2;
- var spectrum = this.freqDomain;
- var spectrumLength = spectrum.length;
- var logAverages = new Array(octaveBands.length);
- // Keep a second index for the current average group and place the values accordingly
- // With only one loop in the spectrum data
- var octaveIndex = 0;
- for (var specIndex = 0; specIndex < spectrumLength; specIndex++) {
- var specIndexFrequency = Math.round(specIndex * nyquist / this.freqDomain.length);
- // Increase the group index if the current frequency exceeds the limits of the band
- if (specIndexFrequency > octaveBands[octaveIndex].hi) {
- octaveIndex++;
- }
- logAverages[octaveIndex] = logAverages[octaveIndex] !== undefined ? (logAverages[octaveIndex] + spectrum[specIndex]) / 2 : spectrum[specIndex];
- }
- return logAverages;
- };
- /**
- * Calculates and Returns the 1/N
- * Octave Bands
- * N defaults to 3 and minimum central frequency to 15.625Hz.
- * (1/3 Octave Bands ~= 31 Frequency Bands)
- * Setting fCtr0 to a central value of a higher octave will ignore the lower bands
- * and produce less frequency groups.
- *
- * @method getOctaveBands
- * @param {Number} N Specifies the 1/N type of generated octave bands
- * @param {Number} fCtr0 Minimum central frequency for the lowest band
- * @return {Array} octaveBands Array of octave band objects with their bounds
- */
- p5.FFT.prototype.getOctaveBands = function (N, fCtr0) {
- var N = N || 3;
- // Default to 1/3 Octave Bands
- var fCtr0 = fCtr0 || 15.625;
- // Minimum central frequency, defaults to 15.625Hz
- var octaveBands = [];
- var lastFrequencyBand = {
- lo: fCtr0 / Math.pow(2, 1 / (2 * N)),
- ctr: fCtr0,
- hi: fCtr0 * Math.pow(2, 1 / (2 * N))
- };
- octaveBands.push(lastFrequencyBand);
- var nyquist = p5sound.audiocontext.sampleRate / 2;
- while (lastFrequencyBand.hi < nyquist) {
- var newFrequencyBand = {};
- newFrequencyBand.lo = lastFrequencyBand.hi, newFrequencyBand.ctr = lastFrequencyBand.ctr * Math.pow(2, 1 / N), newFrequencyBand.hi = newFrequencyBand.ctr * Math.pow(2, 1 / (2 * N)), octaveBands.push(newFrequencyBand);
- lastFrequencyBand = newFrequencyBand;
- }
- return octaveBands;
- };
- // helper methods to convert type from float (dB) to int (0-255)
- var freqToFloat = function (fft) {
- if (fft.freqDomain instanceof Float32Array === false) {
- fft.freqDomain = new Float32Array(fft.analyser.frequencyBinCount);
- }
- };
- var freqToInt = function (fft) {
- if (fft.freqDomain instanceof Uint8Array === false) {
- fft.freqDomain = new Uint8Array(fft.analyser.frequencyBinCount);
- }
- };
- var timeToFloat = function (fft) {
- if (fft.timeDomain instanceof Float32Array === false) {
- fft.timeDomain = new Float32Array(fft.analyser.frequencyBinCount);
- }
- };
- var timeToInt = function (fft) {
- if (fft.timeDomain instanceof Uint8Array === false) {
- fft.timeDomain = new Uint8Array(fft.analyser.frequencyBinCount);
- }
- };
-}(master);
-/** 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;
- }
- if (isUndef(window.OfflineAudioContext)) {
- window.OfflineAudioContext = window.webkitOfflineAudioContext;
- }
- if (!isUndef(AudioContext)) {
- audioContext = new AudioContext();
- } else {
- throw new Error('Web Audio is not supported in this browser');
- }
- if (!isFunction(AudioContext.prototype.createGain)) {
- AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
- }
- if (!isFunction(AudioContext.prototype.createDelay)) {
- AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
- }
- if (!isFunction(AudioContext.prototype.createPeriodicWave)) {
- AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
- }
- if (!isFunction(AudioBufferSourceNode.prototype.start)) {
- AudioBufferSourceNode.prototype.start = AudioBufferSourceNode.prototype.noteGrainOn;
- }
- if (!isFunction(AudioBufferSourceNode.prototype.stop)) {
- AudioBufferSourceNode.prototype.stop = AudioBufferSourceNode.prototype.noteOff;
- }
- if (!isFunction(OscillatorNode.prototype.start)) {
- OscillatorNode.prototype.start = OscillatorNode.prototype.noteOn;
- }
- if (!isFunction(OscillatorNode.prototype.stop)) {
- OscillatorNode.prototype.stop = OscillatorNode.prototype.noteOff;
- }
- if (!isFunction(OscillatorNode.prototype.setPeriodicWave)) {
- OscillatorNode.prototype.setPeriodicWave = OscillatorNode.prototype.setWaveTable;
- }
- AudioNode.prototype._nativeConnect = AudioNode.prototype.connect;
- AudioNode.prototype.connect = function (B, outNum, inNum) {
- if (B.input) {
- if (Array.isArray(B.input)) {
- if (isUndef(inNum)) {
- inNum = 0;
- }
- this.connect(B.input[inNum]);
- } else {
- this.connect(B.input, outNum, inNum);
- }
- } else {
- try {
- if (B instanceof AudioNode) {
- this._nativeConnect(B, outNum, inNum);
- } else {
- this._nativeConnect(B, outNum);
- }
- } catch (e) {
- throw new Error('error connecting to node: ' + B);
- }
- }
- };
- var Tone = function (inputs, outputs) {
- if (isUndef(inputs) || inputs === 1) {
- this.input = this.context.createGain();
- } else if (inputs > 1) {
- this.input = new Array(inputs);
- }
- if (isUndef(outputs) || outputs === 1) {
- this.output = this.context.createGain();
- } else if (outputs > 1) {
- 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.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);
- this.output[outputNum].connect(unit, 0, inputNum);
- } else {
- this.output.connect(unit, outputNum, inputNum);
- }
- return this;
- };
- Tone.prototype.disconnect = function (outputNum) {
- if (Array.isArray(this.output)) {
- outputNum = this.defaultArg(outputNum, 0);
- this.output[outputNum].disconnect();
- } else {
- this.output.disconnect();
- }
- return this;
- };
- Tone.prototype.connectSeries = function () {
- if (arguments.length > 1) {
- var currentUnit = arguments[0];
- for (var i = 1; i < arguments.length; i++) {
- var toUnit = arguments[i];
- currentUnit.connect(toUnit);
- currentUnit = toUnit;
- }
- }
- return this;
- };
- Tone.prototype.connectParallel = function () {
- var connectFrom = arguments[0];
- if (arguments.length > 1) {
- for (var i = 1; i < arguments.length; i++) {
- var connectTo = arguments[i];
- connectFrom.connect(connectTo);
- }
- }
- return this;
- };
- Tone.prototype.chain = function () {
- if (arguments.length > 0) {
- var currentUnit = this;
- for (var i = 0; i < arguments.length; i++) {
- var toUnit = arguments[i];
- currentUnit.connect(toUnit);
- currentUnit = toUnit;
- }
- }
- return this;
- };
- Tone.prototype.fan = function () {
- if (arguments.length > 0) {
- 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 (this.isObject(given) && this.isObject(fallback)) {
- var ret = {};
- for (var givenProp in given) {
- ret[givenProp] = this.defaultArg(fallback[givenProp], given[givenProp]);
- }
- for (var fallbackProp in fallback) {
- ret[fallbackProp] = this.defaultArg(given[fallbackProp], fallback[fallbackProp]);
- }
- return ret;
- } else {
- return isUndef(given) ? fallback : given;
- }
- };
- Tone.prototype.optionsObject = function (values, keys, defaults) {
- var options = {};
- if (values.length === 1 && this.isObject(values[0])) {
- options = values[0];
- } else {
- for (var i = 0; i < keys.length; i++) {
- options[keys[i]] = values[i];
- }
- }
- if (!this.isUndef(defaults)) {
- return this.defaultArg(options, defaults);
- } else {
- return options;
- }
- };
- Tone.prototype.isUndef = isUndef;
- Tone.prototype.isFunction = isFunction;
- Tone.prototype.isNumber = function (arg) {
- return typeof arg === 'number';
- };
- Tone.prototype.isObject = function (arg) {
- return Object.prototype.toString.call(arg) === '[object Object]' && arg.constructor === Object;
- };
- Tone.prototype.isBoolean = function (arg) {
- return typeof arg === 'boolean';
- };
- Tone.prototype.isArray = function (arg) {
- return Array.isArray(arg);
- };
- Tone.prototype.isString = function (arg) {
- return typeof arg === 'string';
- };
- Tone.noOp = function () {
- };
- Tone.prototype._readOnly = function (property) {
- if (Array.isArray(property)) {
- for (var i = 0; i < property.length; i++) {
- this._readOnly(property[i]);
- }
- } else {
- Object.defineProperty(this, property, {
- writable: false,
- enumerable: true
- });
- }
- };
- Tone.prototype._writable = function (property) {
- if (Array.isArray(property)) {
- for (var i = 0; i < property.length; i++) {
- this._writable(property[i]);
- }
- } else {
- Object.defineProperty(this, property, { writable: true });
- }
- };
- Tone.State = {
- Started: 'started',
- Stopped: 'stopped',
- Paused: 'paused'
- };
- Tone.prototype.equalPowerScale = function (percent) {
- var piFactor = 0.5 * Math.PI;
- return Math.sin(percent * piFactor);
- };
- 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.now = function () {
- return this.context.currentTime;
- };
- Tone.extend = function (child, parent) {
- if (isUndef(parent)) {
- parent = Tone;
- }
- function TempConstructor() {
- }
- TempConstructor.prototype = parent.prototype;
- child.prototype = new TempConstructor();
- child.prototype.constructor = child;
- child._super = parent;
- };
- var newContextCallbacks = [];
- Tone._initAudioContext = function (callback) {
- callback(Tone.context);
- newContextCallbacks.push(callback);
- };
- Tone.setContext = function (ctx) {
- Tone.prototype.context = ctx;
- Tone.context = ctx;
- for (var i = 0; i < newContextCallbacks.length; i++) {
- newContextCallbacks[i](ctx);
- }
- };
- Tone.startMobile = function () {
- var osc = Tone.context.createOscillator();
- var silent = Tone.context.createGain();
- silent.gain.value = 0;
- osc.connect(silent);
- silent.connect(Tone.context.destination);
- var now = Tone.context.currentTime;
- osc.start(now);
- osc.stop(now + 1);
- };
- Tone._initAudioContext(function (audioContext) {
- 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 2016 http://opensource.org/licenses/MIT **/
-var Tone_signal_SignalBase;
-Tone_signal_SignalBase = function (Tone) {
- 'use strict';
- Tone.SignalBase = function () {
- };
- Tone.extend(Tone.SignalBase);
- Tone.SignalBase.prototype.connect = function (node, outputNumber, inputNumber) {
- 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);
- return this;
- };
- return Tone.SignalBase;
-}(Tone_core_Tone);
-/** 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';
- Tone.WaveShaper = function (mapping, bufferLen) {
- this._shaper = this.input = this.output = this.context.createWaveShaper();
- this._curve = null;
- if (Array.isArray(mapping)) {
- this.curve = mapping;
- } else if (isFinite(mapping) || this.isUndef(mapping)) {
- this._curve = new Float32Array(this.defaultArg(mapping, 1024));
- } else if (this.isFunction(mapping)) {
- this._curve = new Float32Array(this.defaultArg(bufferLen, 1024));
- this.setMap(mapping);
- }
- };
- Tone.extend(Tone.WaveShaper, Tone.SignalBase);
- Tone.WaveShaper.prototype.setMap = function (mapping) {
- for (var i = 0, len = this._curve.length; i < len; i++) {
- var normalized = i / len * 2 - 1;
- this._curve[i] = mapping(normalized, i);
- }
- this._shaper.curve = this._curve;
- return this;
- };
- 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;
- }
- });
- 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 2016 http://opensource.org/licenses/MIT **/
-var Tone_core_Type;
-Tone_core_Type = function (Tone) {
- 'use strict';
- 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 {
- 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;
- }
- ticks = parseFloat(ticks);
- bpm = this.defaultArg(bpm, getTransportBpm());
- var tickTime = 60 / bpm / Tone.Transport.PPQ;
- return tickTime * ticks;
- };
- Tone.prototype.frequencyToSeconds = function (freq) {
- return 1 / parseFloat(freq);
- };
- Tone.prototype.samplesToSeconds = function (samples) {
- return samples / this.context.sampleRate;
- };
- 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.prototype.secondsToFrequency = function (seconds) {
- return 1 / seconds;
- };
- 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;
- }
- };
- 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;
- }
- 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.prototype.toSamples = function (time) {
- var seconds = this.toSeconds(time);
- return Math.round(seconds * this.context.sampleRate);
- };
- 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;
- }
- };
- 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;
- }
- };
- 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 {
- 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;
- }
- };
- 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.prototype.intervalToFrequencyRatio = function (interval) {
- return Math.pow(2, interval / 12);
- };
- 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._toUnits(this._param.value);
- },
- set: function (value) {
- var convertedVal = this._fromUnits(value);
- this._param.value = convertedVal;
- }
- });
- 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) {
- 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_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._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;
- this._param.dispose();
- this._param = null;
- return this;
- };
- return Tone.Add;
-}(Tone_core_Tone);
-/** 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._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._param = null;
- return this;
- };
- return Tone.Multiply;
-}(Tone_core_Tone);
-/** 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';
- Tone.Scale = function (outputMin, outputMax) {
- this._outputMin = this.defaultArg(outputMin, 0);
- this._outputMax = this.defaultArg(outputMax, 1);
- this._scale = this.input = new Tone.Multiply(1);
- this._add = this.output = new Tone.Add(0);
- this._scale.connect(this._add);
- this._setRange();
- };
- Tone.extend(Tone.Scale, Tone.SignalBase);
- 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.value = this._outputMin;
- this._scale.value = this._outputMax - this._outputMin;
- };
- Tone.Scale.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
- this._add.dispose();
- this._add = null;
- this._scale.dispose();
- this._scale = null;
- return this;
- };
- return Tone.Scale;
-}(Tone_core_Tone, Tone_signal_Add, Tone_signal_Multiply);
-var signal;
-signal = function () {
- 'use strict';
- // Signal is built with the Tone.js signal by Yotam Mann
- // https://github.com/TONEnoTONE/Tone.js/
- var Signal = Tone_signal_Signal;
- var Add = Tone_signal_Add;
- var Mult = Tone_signal_Multiply;
- var Scale = Tone_signal_Scale;
- var Tone = Tone_core_Tone;
- var p5sound = master;
- Tone.setContext(p5sound.audiocontext);
- /**
- *
p5.Signal is a constant audio-rate signal used by p5.Oscillator
- * and p5.Envelope for modulation math.
- *
- *
This is necessary because Web Audio is processed on a seprate clock.
- * For example, the p5 draw loop runs about 60 times per second. But
- * the audio clock must process samples 44100 times per second. If we
- * want to add a value to each of those samples, we can't do it in the
- * draw loop, but we can do it by adding a constant-rate audio signal.This class mostly functions behind the scenes in p5.sound, and returns
- * a Tone.Signal from the Tone.js library by Yotam Mann.
- * If you want to work directly with audio signals for modular
- * synthesis, check out
- * tone.js.
- *
- * @class p5.Signal
- * @constructor
- * @return {Tone.Signal} A Signal object from the Tone.js library
- * @example
- *
- * function setup() {
- * carrier = new p5.Oscillator('sine');
- * carrier.amp(1); // set amplitude
- * carrier.freq(220); // set frequency
- * carrier.start(); // start oscillating
- *
- * modulator = new p5.Oscillator('sawtooth');
- * modulator.disconnect();
- * modulator.amp(1);
- * modulator.freq(4);
- * modulator.start();
- *
- * // Modulator's default amplitude range is -1 to 1.
- * // Multiply it by -200, so the range is -200 to 200
- * // then add 220 so the range is 20 to 420
- * carrier.freq( modulator.mult(-200).add(220) );
- * }
- *
- */
- p5.Signal = function (value) {
- var s = new Signal(value);
- // p5sound.soundArray.push(s);
- return s;
- };
- /**
- * Fade to value, for smooth transitions
- *
- * @method fade
- * @param {Number} value Value to set this signal
- * @param {[Number]} secondsFromNow Length of fade, in seconds from now
- */
- Signal.prototype.fade = Signal.prototype.linearRampToValueAtTime;
- Mult.prototype.fade = Signal.prototype.fade;
- Add.prototype.fade = Signal.prototype.fade;
- Scale.prototype.fade = Signal.prototype.fade;
- /**
- * Connect a p5.sound object or Web Audio node to this
- * p5.Signal so that its amplitude values can be scaled.
- *
- * @param {Object} input
- */
- Signal.prototype.setInput = function (_input) {
- _input.connect(this);
- };
- Mult.prototype.setInput = Signal.prototype.setInput;
- Add.prototype.setInput = Signal.prototype.setInput;
- Scale.prototype.setInput = Signal.prototype.setInput;
- // signals can add / mult / scale themselves
- /**
- * Add a constant value to this audio signal,
- * and return the resulting audio signal. Does
- * not change the value of the original signal,
- * instead it returns a new p5.SignalAdd.
- *
- * @method add
- * @param {Number} number
- * @return {p5.SignalAdd} object
- */
- Signal.prototype.add = function (num) {
- var add = new Add(num);
- // add.setInput(this);
- this.connect(add);
- return add;
- };
- Mult.prototype.add = Signal.prototype.add;
- Add.prototype.add = Signal.prototype.add;
- Scale.prototype.add = Signal.prototype.add;
- /**
- * Multiply this signal by a constant value,
- * and return the resulting audio signal. Does
- * not change the value of the original signal,
- * instead it returns a new p5.SignalMult.
- *
- * @method mult
- * @param {Number} number to multiply
- * @return {Tone.Multiply} object
- */
- Signal.prototype.mult = function (num) {
- var mult = new Mult(num);
- // mult.setInput(this);
- this.connect(mult);
- return mult;
- };
- Mult.prototype.mult = Signal.prototype.mult;
- Add.prototype.mult = Signal.prototype.mult;
- Scale.prototype.mult = Signal.prototype.mult;
- /**
- * Scale this signal value to a given range,
- * and return the result as an audio signal. Does
- * not change the value of the original signal,
- * instead it returns a new p5.SignalScale.
- *
- * @method scale
- * @param {Number} number to multiply
- * @param {Number} inMin input range minumum
- * @param {Number} inMax input range maximum
- * @param {Number} outMin input range minumum
- * @param {Number} outMax input range maximum
- * @return {p5.SignalScale} object
- */
- Signal.prototype.scale = function (inMin, inMax, outMin, outMax) {
- var mapOutMin, mapOutMax;
- if (arguments.length === 4) {
- mapOutMin = p5.prototype.map(outMin, inMin, inMax, 0, 1) - 0.5;
- mapOutMax = p5.prototype.map(outMax, inMin, inMax, 0, 1) - 0.5;
- } else {
- mapOutMin = arguments[0];
- mapOutMax = arguments[1];
- }
- var scale = new Scale(mapOutMin, mapOutMax);
- this.connect(scale);
- return scale;
- };
- Mult.prototype.scale = Signal.prototype.scale;
- Add.prototype.scale = Signal.prototype.scale;
- Scale.prototype.scale = Signal.prototype.scale;
-}(Tone_signal_Signal, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale, Tone_core_Tone, master);
-var oscillator;
-oscillator = function () {
- 'use strict';
- var p5sound = master;
- var Signal = Tone_signal_Signal;
- var Add = Tone_signal_Add;
- var Mult = Tone_signal_Multiply;
- var Scale = Tone_signal_Scale;
- /**
- *
Creates a signal that oscillates between -1.0 and 1.0.
- * By default, the oscillation takes the form of a sinusoidal
- * shape ('sine'). Additional types include 'triangle',
- * 'sawtooth' and 'square'. The frequency defaults to
- * 440 oscillations per second (440Hz, equal to the pitch of an
- * 'A' note).
- *
- *
Set the type of oscillation with setType(), or by creating a
- * specific oscillator.
For example:
- * new p5.SinOsc(freq)
- * new p5.TriOsc(freq)
- * new p5.SqrOsc(freq)
- * new p5.SawOsc(freq).
- *
- *
- * @class p5.Oscillator
- * @constructor
- * @param {Number} [freq] frequency defaults to 440Hz
- * @param {String} [type] type of oscillator. Options:
- * 'sine' (default), 'triangle',
- * 'sawtooth', 'square'
- * @return {Object} Oscillator object
- * @example
- *
- */
- p5.Oscillator = function (freq, type) {
- if (typeof freq === 'string') {
- var f = type;
- type = freq;
- freq = f;
- }
- if (typeof type === 'number') {
- var f = type;
- type = freq;
- freq = f;
- }
- this.started = false;
- // components
- this.phaseAmount = undefined;
- this.oscillator = p5sound.audiocontext.createOscillator();
- this.f = freq || 440;
- // frequency
- this.oscillator.type = type || 'sine';
- this.oscillator.frequency.setValueAtTime(this.f, p5sound.audiocontext.currentTime);
- var o = this.oscillator;
- // connections
- this.output = p5sound.audiocontext.createGain();
- this._freqMods = [];
- // modulators connected to this oscillator's frequency
- // set default output gain to 0.5
- this.output.gain.value = 0.5;
- this.output.gain.setValueAtTime(0.5, p5sound.audiocontext.currentTime);
- this.oscillator.connect(this.output);
- // stereo panning
- this.panPosition = 0;
- this.connection = p5sound.input;
- // connect to p5sound by default
- this.panner = new p5.Panner(this.output, this.connection, 1);
- //array of math operation signal chaining
- this.mathOps = [this.output];
- // add to the soundArray so we can dispose of the osc later
- p5sound.soundArray.push(this);
- };
- /**
- * Start an oscillator. Accepts an optional parameter to
- * determine how long (in seconds from now) until the
- * oscillator starts.
- *
- * @method start
- * @param {Number} [time] startTime in seconds from now.
- * @param {Number} [frequency] frequency in Hz.
- */
- p5.Oscillator.prototype.start = function (time, f) {
- if (this.started) {
- var now = p5sound.audiocontext.currentTime;
- this.stop(now);
- }
- if (!this.started) {
- var freq = f || this.f;
- var type = this.oscillator.type;
- // set old osc free to be garbage collected (memory)
- if (this.oscillator) {
- this.oscillator.disconnect();
- this.oscillator = undefined;
- }
- // var detune = this.oscillator.frequency.value;
- this.oscillator = p5sound.audiocontext.createOscillator();
- this.oscillator.frequency.exponentialRampToValueAtTime(Math.abs(freq), p5sound.audiocontext.currentTime);
- this.oscillator.type = type;
- // this.oscillator.detune.value = detune;
- this.oscillator.connect(this.output);
- time = time || 0;
- this.oscillator.start(time + p5sound.audiocontext.currentTime);
- this.freqNode = this.oscillator.frequency;
- // if other oscillators are already connected to this osc's freq
- for (var i in this._freqMods) {
- if (typeof this._freqMods[i].connect !== 'undefined') {
- this._freqMods[i].connect(this.oscillator.frequency);
- }
- }
- this.started = true;
- }
- };
- /**
- * Stop an oscillator. Accepts an optional parameter
- * to determine how long (in seconds from now) until the
- * oscillator stops.
- *
- * @method stop
- * @param {Number} secondsFromNow Time, in seconds from now.
- */
- p5.Oscillator.prototype.stop = function (time) {
- if (this.started) {
- var t = time || 0;
- var now = p5sound.audiocontext.currentTime;
- this.oscillator.stop(t + now);
- this.started = false;
- }
- };
- /**
- * Set the amplitude between 0 and 1.0. Or, pass in an object
- * such as an oscillator to modulate amplitude with an audio signal.
- *
- * @method amp
- * @param {Number|Object} vol between 0 and 1.0
- * or a modulating signal/oscillator
- * @param {Number} [rampTime] create a fade that lasts rampTime
- * @param {Number} [timeFromNow] schedule this event to happen
- * seconds from now
- * @return {AudioParam} gain If no value is provided,
- * returns the Web Audio API
- * AudioParam that controls
- * this oscillator's
- * gain/amplitude/volume)
- */
- p5.Oscillator.prototype.amp = function (vol, rampTime, tFromNow) {
- var self = this;
- if (typeof vol === 'number') {
- var rampTime = rampTime || 0;
- var tFromNow = tFromNow || 0;
- var now = p5sound.audiocontext.currentTime;
- var currentVol = this.output.gain.value;
- this.output.gain.cancelScheduledValues(now);
- this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
- this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
- } else if (vol) {
- vol.connect(self.output.gain);
- } else {
- // return the Gain Node
- return this.output.gain;
- }
- };
- // these are now the same thing
- p5.Oscillator.prototype.fade = p5.Oscillator.prototype.amp;
- p5.Oscillator.prototype.getAmp = function () {
- return this.output.gain.value;
- };
- /**
- * Set frequency of an oscillator to a value. Or, pass in an object
- * such as an oscillator to modulate the frequency with an audio signal.
- *
- * @method freq
- * @param {Number|Object} Frequency Frequency in Hz
- * or modulating signal/oscillator
- * @param {Number} [rampTime] Ramp time (in seconds)
- * @param {Number} [timeFromNow] Schedule this event to happen
- * at x seconds from now
- * @return {AudioParam} Frequency If no value is provided,
- * returns the Web Audio API
- * AudioParam that controls
- * this oscillator's frequency
- * @example
- *
- * var osc = new p5.Oscillator(300);
- * osc.start();
- * osc.freq(40, 10);
- *
- */
- p5.Oscillator.prototype.freq = function (val, rampTime, tFromNow) {
- if (typeof val === 'number' && !isNaN(val)) {
- this.f = val;
- var now = p5sound.audiocontext.currentTime;
- var rampTime = rampTime || 0;
- var tFromNow = tFromNow || 0;
- // 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 {
- 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) {
- val = val.output;
- }
- val.connect(this.oscillator.frequency);
- // keep track of what is modulating this param
- // so it can be re-connected if
- this._freqMods.push(val);
- } else {
- // return the Frequency Node
- return this.oscillator.frequency;
- }
- };
- p5.Oscillator.prototype.getFreq = function () {
- return this.oscillator.frequency.value;
- };
- /**
- * Set type to 'sine', 'triangle', 'sawtooth' or 'square'.
- *
- * @method setType
- * @param {String} type 'sine', 'triangle', 'sawtooth' or 'square'.
- */
- p5.Oscillator.prototype.setType = function (type) {
- this.oscillator.type = type;
- };
- p5.Oscillator.prototype.getType = function () {
- return this.oscillator.type;
- };
- /**
- * Connect to a p5.sound / Web Audio object.
- *
- * @method connect
- * @param {Object} unit A p5.sound or Web Audio object
- */
- p5.Oscillator.prototype.connect = function (unit) {
- if (!unit) {
- this.panner.connect(p5sound.input);
- } else if (unit.hasOwnProperty('input')) {
- this.panner.connect(unit.input);
- this.connection = unit.input;
- } else {
- this.panner.connect(unit);
- this.connection = unit;
- }
- };
- /**
- * Disconnect all outputs
- *
- * @method disconnect
- */
- p5.Oscillator.prototype.disconnect = function (unit) {
- this.output.disconnect();
- this.panner.disconnect();
- this.output.connect(this.panner);
- this.oscMods = [];
- };
- /**
- * Pan between Left (-1) and Right (1)
- *
- * @method pan
- * @param {Number} panning Number between -1 and 1
- * @param {Number} timeFromNow schedule this event to happen
- * seconds from now
- */
- p5.Oscillator.prototype.pan = function (pval, tFromNow) {
- this.panPosition = pval;
- this.panner.pan(pval, tFromNow);
- };
- p5.Oscillator.prototype.getPan = function () {
- return this.panPosition;
- };
- // 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);
- this.disconnect();
- this.panner = null;
- this.oscillator = null;
- }
- // if it is a Pulse
- if (this.osc2) {
- this.osc2.dispose();
- }
- };
- /**
- * 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.oscillator.disconnect();
- this.oscillator.connect(this.dNode);
- this.dNode.connect(this.output);
- }
- // set delay time to match phase:
- this.dNode.delayTime.setValueAtTime(delayAmt, now);
- };
- // ========================== //
- // SIGNAL MATH FOR MODULATION //
- // ========================== //
- // return sigChain(this, scale, thisChain, nextChain, Scale);
- var sigChain = function (o, mathObj, thisChain, nextChain, type) {
- var chainSource = o.oscillator;
- // if this type of math already exists in the chain, replace it
- for (var i in o.mathOps) {
- if (o.mathOps[i] instanceof type) {
- chainSource.disconnect();
- o.mathOps[i].dispose();
- thisChain = i;
- // assume nextChain is output gain node unless...
- if (thisChain < o.mathOps.length - 2) {
- nextChain = o.mathOps[i + 1];
- }
- }
- }
- if (thisChain == o.mathOps.length - 1) {
- o.mathOps.push(nextChain);
- }
- // assume source is the oscillator unless i > 0
- if (i > 0) {
- chainSource = o.mathOps[i - 1];
- }
- chainSource.disconnect();
- chainSource.connect(mathObj);
- mathObj.connect(nextChain);
- o.mathOps[thisChain] = mathObj;
- return o;
- };
- /**
- * Add a value to the p5.Oscillator's output amplitude,
- * and return the oscillator. Calling this method again
- * will override the initial add() with a new value.
- *
- * @method add
- * @param {Number} number Constant number to add
- * @return {p5.Oscillator} Oscillator Returns this oscillator
- * with scaled output
- *
- */
- p5.Oscillator.prototype.add = function (num) {
- var add = new Add(num);
- var thisChain = this.mathOps.length - 1;
- var nextChain = this.output;
- return sigChain(this, add, thisChain, nextChain, Add);
- };
- /**
- * Multiply the p5.Oscillator's output amplitude
- * by a fixed value (i.e. turn it up!). Calling this method
- * again will override the initial mult() with a new value.
- *
- * @method mult
- * @param {Number} number Constant number to multiply
- * @return {p5.Oscillator} Oscillator Returns this oscillator
- * with multiplied output
- */
- p5.Oscillator.prototype.mult = function (num) {
- var mult = new Mult(num);
- var thisChain = this.mathOps.length - 1;
- var nextChain = this.output;
- return sigChain(this, mult, thisChain, nextChain, Mult);
- };
- /**
- * Scale this oscillator's amplitude values to a given
- * range, and return the oscillator. Calling this method
- * again will override the initial scale() with new values.
- *
- * @method scale
- * @param {Number} inMin input range minumum
- * @param {Number} inMax input range maximum
- * @param {Number} outMin input range minumum
- * @param {Number} outMax input range maximum
- * @return {p5.Oscillator} Oscillator Returns this oscillator
- * with scaled output
- */
- p5.Oscillator.prototype.scale = function (inMin, inMax, outMin, outMax) {
- var mapOutMin, mapOutMax;
- if (arguments.length === 4) {
- mapOutMin = p5.prototype.map(outMin, inMin, inMax, 0, 1) - 0.5;
- mapOutMax = p5.prototype.map(outMax, inMin, inMax, 0, 1) - 0.5;
- } else {
- mapOutMin = arguments[0];
- mapOutMax = arguments[1];
- }
- var scale = new Scale(mapOutMin, mapOutMax);
- var thisChain = this.mathOps.length - 1;
- var nextChain = this.output;
- return sigChain(this, scale, thisChain, nextChain, Scale);
- };
- // ============================== //
- // SinOsc, TriOsc, SqrOsc, SawOsc //
- // ============================== //
- /**
- * Constructor: new p5.SinOsc().
- * This creates a Sine Wave Oscillator and is
- * equivalent to new p5.Oscillator('sine')
- * or creating a p5.Oscillator and then calling
- * its method setType('sine').
- * See p5.Oscillator for methods.
- *
- * @method p5.SinOsc
- * @param {[Number]} freq Set the frequency
- */
- p5.SinOsc = function (freq) {
- p5.Oscillator.call(this, freq, 'sine');
- };
- p5.SinOsc.prototype = Object.create(p5.Oscillator.prototype);
- /**
- * Constructor: new p5.TriOsc().
- * This creates a Triangle Wave Oscillator and is
- * equivalent to new p5.Oscillator('triangle')
- * or creating a p5.Oscillator and then calling
- * its method setType('triangle').
- * See p5.Oscillator for methods.
- *
- * @method p5.TriOsc
- * @param {[Number]} freq Set the frequency
- */
- p5.TriOsc = function (freq) {
- p5.Oscillator.call(this, freq, 'triangle');
- };
- p5.TriOsc.prototype = Object.create(p5.Oscillator.prototype);
- /**
- * Constructor: new p5.SawOsc().
- * This creates a SawTooth Wave Oscillator and is
- * equivalent to new p5.Oscillator('sawtooth')
- * or creating a p5.Oscillator and then calling
- * its method setType('sawtooth').
- * See p5.Oscillator for methods.
- *
- * @method p5.SawOsc
- * @param {[Number]} freq Set the frequency
- */
- p5.SawOsc = function (freq) {
- p5.Oscillator.call(this, freq, 'sawtooth');
- };
- p5.SawOsc.prototype = Object.create(p5.Oscillator.prototype);
- /**
- * Constructor: new p5.SqrOsc().
- * This creates a Square Wave Oscillator and is
- * equivalent to new p5.Oscillator('square')
- * or creating a p5.Oscillator and then calling
- * its method setType('square').
- * See p5.Oscillator for methods.
- *
- * @method p5.SqrOsc
- * @param {[Number]} freq Set the frequency
- */
- p5.SqrOsc = function (freq) {
- p5.Oscillator.call(this, freq, 'square');
- };
- 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';
- var p5sound = master;
- 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);
- /**
- *
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
- * ). 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.
- * 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 = function (t1, l1, t2, l2, t3, l3) {
- var now = p5sound.audiocontext.currentTime;
- /**
- * Time until envelope reaches attackLevel
- * @property attackTime
- */
- this.aTime = t1 || 0.1;
- /**
- * Level once attack is complete.
- * @property attackLevel
- */
- this.aLevel = l1 || 1;
- /**
- * Time until envelope reaches decayLevel.
- * @property decayTime
- */
- this.dTime = t2 || 0.5;
- /**
- * Level after decay. The envelope will sustain here until it is released.
- * @property decayLevel
- */
- this.dLevel = l2 || 0;
- /**
- * Duration of the release portion of the envelope.
- * @property releaseTime
- */
- this.rTime = t3 || 0;
- /**
- * Level at the end of the release.
- * @property releaseLevel
- */
- this.rLevel = l3 || 0;
- this._rampHighPercentage = 0.98;
- this._rampLowPercentage = 0.02;
- this.output = p5sound.audiocontext.createGain();
- 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} attackTime Time (in seconds) before level
- * reaches attackLevel
- * @param {Number} attackLevel Typically an amplitude between
- * 0.0 and 1.0
- * @param {Number} decayTime Time
- * @param {Number} decayLevel Amplitude (In a standard ADSR envelope,
- * decayLevel = sustainLevel)
- * @param {Number} releaseTime Release Time (in seconds)
- * @param {Number} releaseLevel Amplitude
- * @example
- *
- * var t1 = 0.1; // attack time in seconds
- * var l1 = 0.7; // attack level 0.0 to 1.0
- * var t2 = 0.3; // decay time in seconds
- * var l2 = 0.1; // decay level 0.0 to 1.0
- * var t3 = 0.2; // sustain time in seconds
- * var l3 = dL; // sustain level 0.0 to 1.0
- * // release level defaults to zero
- *
- * var env;
- * var triOsc;
- *
- * function setup() {
- * background(0);
- * noStroke();
- * fill(255);
- * textAlign(CENTER);
- * text('click to play', width/2, height/2);
- *
- * env = new p5.Env(t1, l1, t2, l2, t3, l3);
- * triOsc = new p5.Oscillator('triangle');
- * triOsc.amp(env); // give the env control of the triOsc's amp
- * triOsc.start();
- * }
- *
- * // mouseClick triggers envelope if over canvas
- * function mouseClicked() {
- * // is mouse over canvas?
- * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
- * env.play(triOsc);
- * }
- * }
- *
- *
- */
- 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.rTime = t3 || 0;
- this.rLevel = l3 || 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.
- * If a p5.Sound object is given, then the p5.Env will control its
- * output gain. If multiple inputs are provided, the env will
- * control all of them.
- *
- * @method setInput
- * @param {Object} unit A p5.sound object or
- * Web Audio Param.
- */
- p5.Env.prototype.setInput = function (unit) {
- for (var i = 0; i < arguments.length; i++) {
- this.connect(arguments[i]);
- }
- };
- /**
- * 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 = 1e-8;
- }
- return value;
- };
- /**
- * Play tells the envelope to start acting on a given input.
- * If the input is a p5.sound object (i.e. AudioIn, Oscillator,
- * SoundFile), then Env will control its output volume.
- * Envelopes can also be used to control any
- * Web Audio Audio Param.
- *
- * @method play
- * @param {Object} unit A p5.sound object or
- * Web Audio Param.
- * @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, susTime) {
- var now = p5sound.audiocontext.currentTime;
- var tFromNow = secondsFromNow || 0;
- var susTime = susTime || 0;
- if (unit) {
- if (this.connection !== unit) {
- this.connect(unit);
- }
- }
- this.triggerAttack(unit, tFromNow);
- this.triggerRelease(unit, tFromNow + this.aTime + this.dTime + susTime);
- };
- /**
- * 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
- * Web Audio Param.
- *
- * @method triggerAttack
- * @param {Object} unit p5.sound Object or Web Audio Param
- * @param {Number} secondsFromNow time from now (in seconds)
- * @example
- *
- */
- p5.Env.prototype.triggerAttack = function (unit, secondsFromNow) {
- var now = p5sound.audiocontext.currentTime;
- var tFromNow = secondsFromNow || 0;
- var t = now + tFromNow;
- this.lastAttack = t;
- this.wasTriggered = true;
- if (unit) {
- if (this.connection !== unit) {
- this.connect(unit);
- }
- }
- // get and set value (with linear ramp) to anchor automation
- var valToSet = this.control.getValueAtTime(t);
- this.control.cancelScheduledValues(t);
- // not sure if this is necessary
- if (this.isExponential == true) {
- this.control.exponentialRampToValueAtTime(this.checkExpInput(valToSet), t);
- } else {
- this.control.linearRampToValueAtTime(valToSet, t);
- }
- // after each ramp completes, cancel scheduled values
- // (so they can be overridden in case env has been re-triggered)
- // then, set current value (with linearRamp to avoid click)
- // then, schedule the next automation...
- // attack
- t += this.aTime;
- if (this.isExponential == true) {
- this.control.exponentialRampToValueAtTime(this.checkExpInput(this.aLevel), t);
- valToSet = this.checkExpInput(this.control.getValueAtTime(t));
- this.control.cancelScheduledValues(t);
- this.control.exponentialRampToValueAtTime(valToSet, t);
- } else {
- this.control.linearRampToValueAtTime(this.aLevel, t);
- valToSet = this.control.getValueAtTime(t);
- this.control.cancelScheduledValues(t);
- this.control.linearRampToValueAtTime(valToSet, t);
- }
- // decay to decay level (if using ADSR, then decay level == sustain level)
- t += this.dTime;
- if (this.isExponential == true) {
- this.control.exponentialRampToValueAtTime(this.checkExpInput(this.dLevel), t);
- valToSet = this.checkExpInput(this.control.getValueAtTime(t));
- this.control.cancelScheduledValues(t);
- this.control.exponentialRampToValueAtTime(valToSet, t);
- } else {
- this.control.linearRampToValueAtTime(this.dLevel, t);
- valToSet = this.control.getValueAtTime(t);
- this.control.cancelScheduledValues(t);
- this.control.linearRampToValueAtTime(valToSet, t);
- }
- };
- /**
- * Trigger the Release of the Envelope. This is similar to releasing
- * the key on a piano and letting the sound fade according to the
- * release level and release time.
- *
- * @method triggerRelease
- * @param {Object} unit p5.sound Object or Web Audio Param
- * @param {Number} secondsFromNow time to trigger the release
- * @example
- *
- */
- p5.Env.prototype.triggerRelease = function (unit, secondsFromNow) {
- // only trigger a release if an attack was triggered
- if (!this.wasTriggered) {
- // this currently causes a bit of trouble:
- // if a later release has been scheduled (via the play function)
- // a new earlier release won't interrupt it, because
- // this.wasTriggered has already been set to false.
- // If we want new earlier releases to override, then we need to
- // keep track of the last release time, and if the new release time is
- // earlier, then use it.
- return;
- }
- var now = p5sound.audiocontext.currentTime;
- var tFromNow = secondsFromNow || 0;
- var t = now + tFromNow;
- if (unit) {
- if (this.connection !== unit) {
- this.connect(unit);
- }
- }
- // get and set value (with linear or exponential ramp) to anchor automation
- var valToSet = this.control.getValueAtTime(t);
- this.control.cancelScheduledValues(t);
- // not sure if this is necessary
- if (this.isExponential == true) {
- this.control.exponentialRampToValueAtTime(this.checkExpInput(valToSet), t);
- } else {
- this.control.linearRampToValueAtTime(valToSet, t);
- }
- // release
- t += this.rTime;
- if (this.isExponential == true) {
- this.control.exponentialRampToValueAtTime(this.checkExpInput(this.rLevel), t);
- valToSet = this.checkExpInput(this.control.getValueAtTime(t));
- this.control.cancelScheduledValues(t);
- this.control.exponentialRampToValueAtTime(valToSet, t);
- } else {
- this.control.linearRampToValueAtTime(this.rLevel, t);
- valToSet = this.control.getValueAtTime(t);
- this.control.cancelScheduledValues(t);
- this.control.linearRampToValueAtTime(valToSet, t);
- }
- this.wasTriggered = false;
- };
- /**
- * Exponentially ramp to a value using the first two
- * values from 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
- *
- */
- 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;
- // assume we're talking about output gain
- // unless given a different audio param
- if (unit instanceof p5.Oscillator || unit instanceof p5.SoundFile || unit instanceof p5.AudioIn || unit instanceof p5.Reverb || unit instanceof p5.Noise || unit instanceof p5.Filter || unit instanceof p5.Delay) {
- unit = unit.output.gain;
- }
- if (unit instanceof AudioParam) {
- //set the initial value
- unit.setValueAtTime(0, p5sound.audiocontext.currentTime);
- }
- if (unit instanceof p5.Signal) {
- unit.setValue(0);
- }
- this.output.connect(unit);
- };
- p5.Env.prototype.disconnect = function (unit) {
- this.output.disconnect();
- };
- // Signal Math
- /**
- * Add a value to the p5.Oscillator's output amplitude,
- * and return the oscillator. Calling this method
- * again will override the initial add() with new values.
- *
- * @method add
- * @param {Number} number Constant number to add
- * @return {p5.Env} Envelope Returns this envelope
- * with scaled output
- */
- p5.Env.prototype.add = function (num) {
- var add = new Add(num);
- var thisChain = this.mathOps.length;
- var nextChain = this.output;
- return p5.prototype._mathChain(this, add, thisChain, nextChain, Add);
- };
- /**
- * Multiply the p5.Env's output amplitude
- * by a fixed value. Calling this method
- * again will override the initial mult() with new values.
- *
- * @method mult
- * @param {Number} number Constant number to multiply
- * @return {p5.Env} Envelope Returns this envelope
- * with scaled output
- */
- p5.Env.prototype.mult = function (num) {
- var mult = new Mult(num);
- var thisChain = this.mathOps.length;
- var nextChain = this.output;
- return p5.prototype._mathChain(this, mult, thisChain, nextChain, Mult);
- };
- /**
- * Scale this envelope's amplitude values to a given
- * range, and return the envelope. Calling this method
- * again will override the initial scale() with new values.
- *
- * @method scale
- * @param {Number} inMin input range minumum
- * @param {Number} inMax input range maximum
- * @param {Number} outMin input range minumum
- * @param {Number} outMax input range maximum
- * @return {p5.Env} Envelope Returns this envelope
- * with scaled output
- */
- p5.Env.prototype.scale = function (inMin, inMax, outMin, outMax) {
- var scale = new Scale(inMin, inMax, outMin, outMax);
- var thisChain = this.mathOps.length;
- var nextChain = this.output;
- return p5.prototype._mathChain(this, scale, thisChain, nextChain, Scale);
- };
- // 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 {
- this.control.dispose();
- this.control = null;
- } catch (e) {
- }
- for (var i = 1; i < this.mathOps.length; i++) {
- mathOps[i].dispose();
- }
- };
-}(master, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale, Tone_signal_TimelineSignal, Tone_core_Tone);
-var pulse;
-pulse = function () {
- 'use strict';
- var p5sound = master;
- /**
- * Creates a Pulse object, an oscillator that implements
- * Pulse Width Modulation.
- * The pulse is created with two oscillators.
- * Accepts a parameter for frequency, and to set the
- * width between the pulses. See
- * p5.Oscillator for a full list of methods.
- *
- * @class p5.Pulse
- * @constructor
- * @param {Number} [freq] Frequency in oscillations per second (Hz)
- * @param {Number} [w] Width between the pulses (0 to 1.0,
- * defaults to 0)
- * @example
- *
Note: This uses the getUserMedia/
- * Stream API, which is not supported by certain browsers. Access in Chrome browser
- * is limited to localhost and https, but access over http may be limited.
- *
- * @class p5.AudioIn
- * @constructor
- * @param {Function} [errorCallback] A function to call if there is an error
- * accessing the AudioIn. For example,
- * Safari and iOS devices do not
- * currently allow microphone access.
- * @return {Object} AudioIn
- * @example
- *
- * var mic;
- * function setup(){
- * mic = new p5.AudioIn()
- * mic.start();
- * }
- * function draw(){
- * background(0);
- * micLevel = mic.getLevel();
- * ellipse(width/2, constrain(height-micLevel*height*5, 0, height), 10, 10);
- * }
- *
- */
- p5.AudioIn = function (errorCallback) {
- // set up audio input
- this.input = p5sound.audiocontext.createGain();
- this.output = p5sound.audiocontext.createGain();
- this.stream = null;
- this.mediaStream = null;
- this.currentSource = 0;
- /**
- * Client must allow browser to access their microphone / audioin source.
- * Default: false. Will become true when the client enables acces.
- *
- * @property {Boolean} enabled
- */
- this.enabled = false;
- // create an amplitude, connect to it by default but not to master out
- this.amplitude = new p5.Amplitude();
- this.output.connect(this.amplitude.input);
- // Some browsers let developer determine their input sources
- if (typeof window.MediaStreamTrack === 'undefined') {
- if (errorCallback) {
- errorCallback();
- } else {
- window.alert('This browser does not support AudioIn');
- }
- } else if (typeof window.MediaStreamTrack.getSources === 'function') {
- // Chrome supports getSources to list inputs. Dev picks default
- window.MediaStreamTrack.getSources(this._gotSources);
- } else {
- }
- // add to soundArray so we can dispose on close
- p5sound.soundArray.push(this);
- };
- /**
- * Start processing audio input. This enables the use of other
- * AudioIn methods like getLevel(). Note that by default, AudioIn
- * is not connected to p5.sound's output. So you won't hear
- * anything unless you use the connect() method.
- *
- * Certain browsers limit access to the user's microphone. For example,
- * Chrome only allows access from localhost and over https. For this reason,
- * you may want to include an errorCallback—a function that is called in case
- * the browser won't provide mic access.
- *
- * @method start
- * @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 stream was already started...
- // 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 }] } };
- 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 (e) {
- if (errorCallback)
- errorCallback(e);
- else
- console.error(e);
- });
- } else {
- // if Firefox where users select their source via browser
- // if (typeof MediaStreamTrack.getSources === 'undefined') {
- // Only get the audio stream.
- window.navigator.getUserMedia({ 'audio': true }, 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);
- // only send to the Amplitude reader, so we can see it but not hear it.
- self.amplitude.setInput(self.output);
- if (successCallback)
- successCallback();
- }, this._onStreamError = function (e) {
- if (errorCallback)
- errorCallback(e);
- else
- console.error(e);
- });
- }
- };
- /**
- * Turn the AudioIn off. If the AudioIn is stopped, it cannot getLevel().
- * If re-starting, the user may be prompted for permission access.
- *
- * @method stop
- */
- p5.AudioIn.prototype.stop = function () {
- if (this.stream) {
- // assume only one track
- this.stream.getTracks()[0].stop();
- }
- };
- /**
- * Connect to an audio unit. If no parameter is provided, will
- * connect to the master output (i.e. your speakers).
- *
- * @method connect
- * @param {Object} [unit] An object that accepts audio input,
- * such as an FFT
- */
- p5.AudioIn.prototype.connect = function (unit) {
- if (unit) {
- if (unit.hasOwnProperty('input')) {
- this.output.connect(unit.input);
- } else if (unit.hasOwnProperty('analyser')) {
- this.output.connect(unit.analyser);
- } else {
- this.output.connect(unit);
- }
- } else {
- this.output.connect(p5sound.input);
- }
- };
- /**
- * Disconnect the AudioIn from all audio units. For example, if
- * connect() had been called, disconnect() will stop sending
- * signal to your speakers.
- *
- * @method disconnect
- */
- p5.AudioIn.prototype.disconnect = function () {
- this.output.disconnect();
- // stay connected to amplitude even if not outputting to p5
- this.output.connect(this.amplitude.input);
- };
- /**
- * Read the Amplitude (volume level) of an AudioIn. The AudioIn
- * class contains its own instance of the Amplitude class to help
- * make it easy to get a microphone's volume level. Accepts an
- * optional smoothing value (0.0 < 1.0). NOTE: AudioIn must
- * .start() before using .getLevel().
- *
- * @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.AudioIn.prototype.getLevel = function (smoothing) {
- if (smoothing) {
- this.amplitude.smoothing = smoothing;
- }
- return this.amplitude.getLevel();
- };
- /**
- * Add input sources to the list of available sources.
- *
- * @private
- */
- p5.AudioIn.prototype._gotSources = function (sourceInfos) {
- for (var i = 0; i < sourceInfos.length; i++) {
- var sourceInfo = sourceInfos[i];
- if (sourceInfo.kind === 'audio') {
- // add the inputs to inputSources
- //p5sound.inputSources.push(sourceInfo);
- return sourceInfo;
- }
- }
- };
- /**
- * Set amplitude (volume) of a mic input between 0 and 1.0.
- *
- * @method amp
- * @param {Number} vol between 0 and 1.0
- * @param {Number} [time] ramp time (optional)
- */
- p5.AudioIn.prototype.amp = function (vol, t) {
- if (t) {
- var rampTime = t || 0;
- var currentVol = this.output.gain.value;
- this.output.gain.cancelScheduledValues(p5sound.audiocontext.currentTime);
- this.output.gain.setValueAtTime(currentVol, p5sound.audiocontext.currentTime);
- this.output.gain.linearRampToValueAtTime(vol, rampTime + p5sound.audiocontext.currentTime);
- } else {
- this.output.gain.cancelScheduledValues(p5sound.audiocontext.currentTime);
- this.output.gain.setValueAtTime(vol, p5sound.audiocontext.currentTime);
- }
- };
- p5.AudioIn.prototype.listSources = function () {
- console.log('listSources is deprecated - please use AudioIn.getSources');
- console.log('input sources: ');
- if (p5sound.inputSources.length > 0) {
- return p5sound.inputSources;
- } else {
- return 'This browser does not support MediaStreamTrack.getSources()';
- }
- };
- /**
- * Chrome only. Returns a list of available input sources
- * and allows the user to set the media source. Firefox allows
- * the user to choose from input sources in the permissions dialogue
- * instead of enumerating available sources and selecting one.
- * Note: in order to have descriptive media names your page must be
- * served over a secure (HTTPS) connection and the page should
- * request user media before enumerating devices. Otherwise device
- * ID will be a long device ID number and does not specify device
- * type. For example see
- * https://simpl.info/getusermedia/sources/index.html vs.
- * http://simpl.info/getusermedia/sources/index.html
- *
- * @method getSources
- * @param {Function} callback a callback to handle the sources
- * when they have been enumerated
- * @example
- *
- * var audiograb;
- *
- * function setup(){
- * //new audioIn
- * audioGrab = new p5.AudioIn();
- *
- * audioGrab.getSources(function(sourceList) {
- * //print out the array of available sources
- * console.log(sourceList);
- * //set the source to the first item in the inputSources array
- * audioGrab.setSource(0);
- * });
- * }
- *
- */
- p5.AudioIn.prototype.getSources = function (callback) {
- if (typeof window.MediaStreamTrack.getSources === 'function') {
- window.MediaStreamTrack.getSources(function (data) {
- for (var i = 0, max = data.length; i < max; i++) {
- var sourceInfo = data[i];
- if (sourceInfo.kind === 'audio') {
- // add the inputs to inputSources
- p5sound.inputSources.push(sourceInfo);
- }
- }
- callback(p5sound.inputSources);
- });
- } else {
- console.log('This browser does not support MediaStreamTrack.getSources()');
- }
- };
- /**
- * Set the input source. Accepts a number representing a
- * position in the array returned by listSources().
- * This is only available in browsers that support
- * MediaStreamTrack.getSources(). Instead, some browsers
- * give users the option to set their own media source.
- *
- * @method setSource
- * @param {number} num position of input source in the array
- */
- p5.AudioIn.prototype.setSource = function (num) {
- // TO DO - set input by string or # (array position)
- var self = this;
- if (p5sound.inputSources.length > 0 && num < p5sound.inputSources.length) {
- // set the current source
- self.currentSource = num;
- console.log('set source to ' + p5sound.inputSources[self.currentSource].id);
- } else {
- console.log('unable to set input source');
- }
- };
- // 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();
- }
- if (this.amplitude) {
- this.amplitude.disconnect();
- }
- this.amplitude = null;
- this.output = null;
- };
-}(master, errorHandler);
-var filter;
-filter = function () {
- 'use strict';
- var p5sound = master;
- /**
- * A p5.Filter uses a Web Audio Biquad Filter to filter
- * the frequency response of an input source. Inheriting
- * classes include:
- * * p5.LowPass - allows frequencies below
- * the cutoff frequency to pass through, and attenuates
- * frequencies above the cutoff.
- * * p5.HighPass - the opposite of a lowpass
- * filter.
- * * p5.BandPass - allows a range of
- * frequencies to pass through and attenuates the frequencies
- * below and above this frequency range.
- *
- * The .res() method controls either width of the
- * bandpass, or resonance of the low/highpass cutoff frequency.
- *
- * @class p5.Filter
- * @constructor
- * @param {[String]} type 'lowpass' (default), 'highpass', 'bandpass'
- * @return {Object} p5.Filter
- * @example
- *
- * var fft, noise, filter;
- *
- * function setup() {
- * fill(255, 40, 255);
- *
- * filter = new p5.BandPass();
- *
- * noise = new p5.Noise();
- * // disconnect unfiltered noise,
- * // and connect to filter
- * noise.disconnect();
- * noise.connect(filter);
- * noise.start();
- *
- * fft = new p5.FFT();
- * }
- *
- * function draw() {
- * background(30);
- *
- * // set the BandPass frequency based on mouseX
- * var freq = map(mouseX, 0, width, 20, 10000);
- * filter.freq(freq);
- * // give the filter a narrow band (lower res = wider bandpass)
- * filter.res(50);
- *
- * // draw filtered spectrum
- * var spectrum = fft.analyze();
- * noStroke();
- * for (var i = 0; i < spectrum.length; i++) {
- * var x = map(i, 0, spectrum.length, 0, width);
- * var h = -height + map(spectrum[i], 0, 255, height, 0);
- * rect(x, height, width/spectrum.length, h);
- * }
- *
- * isMouseOverCanvas();
- * }
- *
- * function isMouseOverCanvas() {
- * var mX = mouseX, mY = mouseY;
- * if (mX > 0 && mX < width && mY < height && mY > 0) {
- * noise.amp(0.5, 0.2);
- * } else {
- * noise.amp(0, 0.2);
- * }
- * }
- *
- */
- p5.Filter = function (type) {
- this.ac = p5sound.audiocontext;
- this.input = this.ac.createGain();
- this.output = this.ac.createGain();
- /**
- * The p5.Filter is built with a
- *
- * Web Audio BiquadFilter Node.
- *
- * @property biquadFilter
- * @type {Object} Web Audio Delay Node
- */
- this.biquad = this.ac.createBiquadFilter();
- this.input.connect(this.biquad);
- this.biquad.connect(this.output);
- this.connect();
- if (type) {
- this.setType(type);
- }
- // add to the soundArray
- p5sound.soundArray.push(this);
- };
- /**
- * Filter an audio signal according to a set
- * of filter parameters.
- *
- * @method process
- * @param {Object} Signal An object that outputs audio
- * @param {[Number]} freq Frequency in Hz, from 10 to 22050
- * @param {[Number]} res Resonance/Width of the filter frequency
- * from 0.001 to 1000
- */
- p5.Filter.prototype.process = function (src, freq, res) {
- src.connect(this.input);
- this.set(freq, res);
- };
- /**
- * Set the frequency and the resonance of the filter.
- *
- * @method set
- * @param {Number} freq Frequency in Hz, from 10 to 22050
- * @param {Number} res Resonance (Q) from 0.001 to 1000
- * @param {Number} [timeFromNow] schedule this event to happen
- * seconds from now
- */
- p5.Filter.prototype.set = function (freq, res, time) {
- if (freq) {
- this.freq(freq, time);
- }
- if (res) {
- this.res(res, time);
- }
- };
- /**
- * Set the filter frequency, in Hz, from 10 to 22050 (the range of
- * human hearing, although in reality most people hear in a narrower
- * range).
- *
- * @method freq
- * @param {Number} freq Filter Frequency
- * @param {Number} [timeFromNow] schedule this event to happen
- * seconds from now
- * @return {Number} value Returns the current frequency value
- */
- p5.Filter.prototype.freq = function (freq, time) {
- var self = this;
- var t = time || 0;
- if (freq <= 0) {
- freq = 1;
- }
- if (typeof freq === 'number') {
- self.biquad.frequency.value = freq;
- self.biquad.frequency.cancelScheduledValues(this.ac.currentTime + 0.01 + t);
- self.biquad.frequency.exponentialRampToValueAtTime(freq, this.ac.currentTime + 0.02 + t);
- } else if (freq) {
- freq.connect(this.biquad.frequency);
- }
- return self.biquad.frequency.value;
- };
- /**
- * Controls either width of a bandpass frequency,
- * or the resonance of a low/highpass cutoff frequency.
- *
- * @method res
- * @param {Number} res Resonance/Width of filter freq
- * from 0.001 to 1000
- * @param {Number} [timeFromNow] schedule this event to happen
- * seconds from now
- * @return {Number} value Returns the current res value
- */
- p5.Filter.prototype.res = function (res, time) {
- var self = this;
- var t = time || 0;
- if (typeof res == 'number') {
- self.biquad.Q.value = res;
- self.biquad.Q.cancelScheduledValues(self.ac.currentTime + 0.01 + t);
- self.biquad.Q.linearRampToValueAtTime(res, self.ac.currentTime + 0.02 + t);
- } else if (res) {
- freq.connect(this.biquad.Q);
- }
- return self.biquad.Q.value;
- };
- /**
- * Set the type of a p5.Filter. Possible types include:
- * "lowpass" (default), "highpass", "bandpass",
- * "lowshelf", "highshelf", "peaking", "notch",
- * "allpass".
- *
- * @method setType
- * @param {String}
- */
- p5.Filter.prototype.setType = function (t) {
- this.biquad.type = t;
- };
- /**
- * Set the output level of the filter.
- *
- * @method amp
- * @param {Number} volume amplitude between 0 and 1.0
- * @param {Number} [rampTime] create a fade that lasts rampTime
- * @param {Number} [timeFromNow] schedule this event to happen
- * seconds from now
- */
- p5.Filter.prototype.amp = function (vol, rampTime, tFromNow) {
- var rampTime = rampTime || 0;
- var tFromNow = tFromNow || 0;
- var now = p5sound.audiocontext.currentTime;
- var currentVol = this.output.gain.value;
- this.output.gain.cancelScheduledValues(now);
- this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
- this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
- };
- /**
- * Send output to a p5.sound or web audio object
- *
- * @method connect
- * @param {Object} unit
- */
- p5.Filter.prototype.connect = function (unit) {
- var u = unit || p5.soundOut.input;
- this.output.connect(u);
- };
- /**
- * Disconnect all output.
- *
- * @method disconnect
- */
- 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
- * its method setType('lowpass').
- * See p5.Filter for methods.
- *
- * @method p5.LowPass
- */
- p5.LowPass = function () {
- p5.Filter.call(this, 'lowpass');
- };
- p5.LowPass.prototype = Object.create(p5.Filter.prototype);
- /**
- * Constructor: new p5.HighPass() Filter.
- * This is the same as creating a p5.Filter and then calling
- * its method setType('highpass').
- * See p5.Filter for methods.
- *
- * @method p5.HighPass
- */
- p5.HighPass = function () {
- p5.Filter.call(this, 'highpass');
- };
- p5.HighPass.prototype = Object.create(p5.Filter.prototype);
- /**
- * Constructor: new p5.BandPass() Filter.
- * This is the same as creating a p5.Filter and then calling
- * its method setType('bandpass').
- * See p5.Filter for methods.
- *
- * @method p5.BandPass
- */
- p5.BandPass = function () {
- p5.Filter.call(this, 'bandpass');
- };
- p5.BandPass.prototype = Object.create(p5.Filter.prototype);
-}(master);
-var delay;
-delay = function () {
- 'use strict';
- var p5sound = master;
- var Filter = filter;
- /**
- * Delay is an echo effect. It processes an existing sound source,
- * and outputs a delayed version of that sound. The p5.Delay can
- * produce different effects depending on the delayTime, feedback,
- * filter, and type. In the example below, a feedback of 0.5 will
- * produce a looping delay that decreases in volume by
- * 50% each repeat. A filter will cut out the high frequencies so
- * that the delay does not sound as piercing as the original source.
- *
- * @class p5.Delay
- * @constructor
- * @return {Object} Returns a p5.Delay object
- * @example
- *
- * var noise, env, delay;
- *
- * function setup() {
- * background(0);
- * noStroke();
- * fill(255);
- * textAlign(CENTER);
- * text('click to play', width/2, height/2);
- *
- * noise = new p5.Noise('brown');
- * noise.amp(0);
- * noise.start();
- *
- * delay = new p5.Delay();
- *
- * // delay.process() accepts 4 parameters:
- * // source, delayTime, feedback, filter frequency
- * // play with these numbers!!
- * delay.process(noise, .12, .7, 2300);
- *
- * // play the noise with an envelope,
- * // a series of fades ( time / value pairs )
- * env = new p5.Env(.01, 0.2, .2, .1);
- * }
- *
- * // mouseClick triggers envelope
- * function mouseClicked() {
- * // is mouse over canvas?
- * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
- * env.play(noise);
- * }
- * }
- *
- */
- p5.Delay = function () {
- this.ac = p5sound.audiocontext;
- this.input = this.ac.createGain();
- this.output = this.ac.createGain();
- this._split = this.ac.createChannelSplitter(2);
- this._merge = this.ac.createChannelMerger(2);
- this._leftGain = this.ac.createGain();
- this._rightGain = this.ac.createGain();
- /**
- * The p5.Delay is built with two
- *
- * Web Audio Delay Nodes, one for each stereo channel.
- *
- * @property leftDelay
- * @type {Object} Web Audio Delay Node
- */
- this.leftDelay = this.ac.createDelay();
- /**
- * The p5.Delay is built with two
- *
- * Web Audio Delay Nodes, one for each stereo channel.
- *
- * @property rightDelay
- * @type {Object} Web Audio Delay Node
- */
- this.rightDelay = this.ac.createDelay();
- this._leftFilter = new p5.Filter();
- this._rightFilter = new p5.Filter();
- this._leftFilter.disconnect();
- this._rightFilter.disconnect();
- 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);
- this._rightFilter.biquad.Q.setValueAtTime(0.3, this.ac.currentTime);
- // graph routing
- this.input.connect(this._split);
- this.leftDelay.connect(this._leftGain);
- this.rightDelay.connect(this._rightGain);
- this._leftGain.connect(this._leftFilter.input);
- this._rightGain.connect(this._rightFilter.input);
- this._merge.connect(this.output);
- this.output.connect(p5.soundOut.input);
- this._leftFilter.biquad.gain.setValueAtTime(1, this.ac.currentTime);
- this._rightFilter.biquad.gain.setValueAtTime(1, this.ac.currentTime);
- // 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
- * of delay parameters.
- *
- * @method process
- * @param {Object} Signal An object that outputs audio
- * @param {Number} [delayTime] Time (in seconds) of the delay/echo.
- * Some browsers limit delayTime to
- * 1 second.
- * @param {Number} [feedback] sends the delay back through itself
- * in a loop that decreases in volume
- * each time.
- * @param {Number} [lowPass] Cutoff frequency. Only frequencies
- * below the lowPass will be part of the
- * delay.
- */
- p5.Delay.prototype.process = function (src, _delayTime, _feedback, _filter) {
- var feedback = _feedback || 0;
- var delayTime = _delayTime || 0;
- if (feedback >= 1) {
- throw new Error('Feedback value will force a positive feedback loop.');
- }
- if (delayTime >= this._maxDelay) {
- throw new Error('Delay Time exceeds maximum delay time of ' + this._maxDelay + ' second.');
- }
- src.connect(this.input);
- this.leftDelay.delayTime.setValueAtTime(delayTime, this.ac.currentTime);
- this.rightDelay.delayTime.setValueAtTime(delayTime, this.ac.currentTime);
- this._leftGain.gain.setValueAtTime(feedback, this.ac.currentTime);
- this._rightGain.gain.setValueAtTime(feedback, this.ac.currentTime);
- if (_filter) {
- this._leftFilter.freq(_filter);
- this._rightFilter.freq(_filter);
- }
- };
- /**
- * Set the delay (echo) time, in seconds. Usually this value will be
- * a floating point number between 0.0 and 1.0.
- *
- * @method delayTime
- * @param {Number} delayTime Time (in seconds) of the delay
- */
- p5.Delay.prototype.delayTime = function (t) {
- // if t is an audio node...
- if (typeof t !== 'number') {
- t.connect(this.leftDelay.delayTime);
- t.connect(this.rightDelay.delayTime);
- } else {
- this.leftDelay.delayTime.cancelScheduledValues(this.ac.currentTime);
- this.rightDelay.delayTime.cancelScheduledValues(this.ac.currentTime);
- this.leftDelay.delayTime.linearRampToValueAtTime(t, this.ac.currentTime);
- this.rightDelay.delayTime.linearRampToValueAtTime(t, this.ac.currentTime);
- }
- };
- /**
- * Feedback occurs when Delay sends its signal back through its input
- * in a loop. The feedback amount determines how much signal to send each
- * time through the loop. A feedback greater than 1.0 is not desirable because
- * it will increase the overall output each time through the loop,
- * creating an infinite feedback loop.
- *
- * @method feedback
- * @param {Number|Object} feedback 0.0 to 1.0, or an object such as an
- * Oscillator that can be used to
- * modulate this param
- */
- p5.Delay.prototype.feedback = function (f) {
- // if f is an audio node...
- if (typeof f !== 'number') {
- f.connect(this._leftGain.gain);
- f.connect(this._rightGain.gain);
- } else if (f >= 1) {
- throw new Error('Feedback value will force a positive feedback loop.');
- } else {
- this._leftGain.gain.exponentialRampToValueAtTime(f, this.ac.currentTime);
- this._rightGain.gain.exponentialRampToValueAtTime(f, this.ac.currentTime);
- }
- };
- /**
- * Set a lowpass filter frequency for the delay. A lowpass filter
- * will cut off any frequencies higher than the filter frequency.
- *
- * @method filter
- * @param {Number|Object} cutoffFreq A lowpass filter will cut off any
- * frequencies higher than the filter frequency.
- * @param {Number|Object} res Resonance of the filter frequency
- * cutoff, or an object (i.e. a p5.Oscillator)
- * that can be used to modulate this parameter.
- * High numbers (i.e. 15) will produce a resonance,
- * low numbers (i.e. .2) will produce a slope.
- */
- p5.Delay.prototype.filter = function (freq, q) {
- this._leftFilter.set(freq, q);
- this._rightFilter.set(freq, q);
- };
- /**
- * Choose a preset type of delay. 'pingPong' bounces the signal
- * from the left to the right channel to produce a stereo effect.
- * Any other parameter will revert to the default delay setting.
- *
- * @method setType
- * @param {String|Number} type 'pingPong' (1) or 'default' (0)
- */
- p5.Delay.prototype.setType = function (t) {
- if (t === 1) {
- t = 'pingPong';
- }
- this._split.disconnect();
- this._leftFilter.disconnect();
- this._rightFilter.disconnect();
- this._split.connect(this.leftDelay, 0);
- this._split.connect(this.rightDelay, 1);
- switch (t) {
- case 'pingPong':
- this._rightFilter.setType(this._leftFilter.biquad.type);
- this._leftFilter.output.connect(this._merge, 0, 0);
- this._rightFilter.output.connect(this._merge, 0, 1);
- this._leftFilter.output.connect(this.rightDelay);
- this._rightFilter.output.connect(this.leftDelay);
- break;
- default:
- this._leftFilter.output.connect(this._merge, 0, 0);
- this._leftFilter.output.connect(this._merge, 0, 1);
- this._leftFilter.output.connect(this.leftDelay);
- this._leftFilter.output.connect(this.rightDelay);
- }
- };
- /**
- * Set the output level of the delay effect.
- *
- * @method amp
- * @param {Number} volume amplitude between 0 and 1.0
- * @param {Number} [rampTime] create a fade that lasts rampTime
- * @param {Number} [timeFromNow] schedule this event to happen
- * seconds from now
- */
- p5.Delay.prototype.amp = function (vol, rampTime, tFromNow) {
- var rampTime = rampTime || 0;
- var tFromNow = tFromNow || 0;
- var now = p5sound.audiocontext.currentTime;
- var currentVol = this.output.gain.value;
- this.output.gain.cancelScheduledValues(now);
- this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
- this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
- };
- /**
- * Send output to a p5.sound or web audio object
- *
- * @method connect
- * @param {Object} unit
- */
- p5.Delay.prototype.connect = function (unit) {
- var u = unit || p5.soundOut.input;
- this.output.connect(u);
- };
- /**
- * Disconnect all output.
- *
- * @method disconnect
- */
- 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
- * physical space. The p5.Reverb has paramters for Time (how long does the
- * reverb last) and decayRate (how much the sound decays with each echo)
- * that can be set with the .set() or .process() methods. The p5.Convolver
- * extends p5.Reverb allowing you to recreate the sound of actual physical
- * spaces through convolution.
- *
- * @class p5.Reverb
- * @constructor
- * @example
- *
- * var soundFile, reverb;
- * function preload() {
- * soundFile = loadSound('assets/Damscray_DancingTiger.mp3');
- * }
- *
- * function setup() {
- * reverb = new p5.Reverb();
- * soundFile.disconnect(); // so we'll only hear reverb...
- *
- * // connect soundFile to reverb, process w/
- * // 3 second reverbTime, decayRate of 2%
- * reverb.process(soundFile, 3, 2);
- * soundFile.play();
- * }
- *
- */
- p5.Reverb = function () {
- this.ac = p5sound.audiocontext;
- this.convolverNode = this.ac.createConvolver();
- this.input = this.ac.createGain();
- this.output = this.ac.createGain();
- // otherwise, Safari distorts
- this.input.gain.value = 0.5;
- this.input.connect(this.convolverNode);
- this.convolverNode.connect(this.output);
- // default params
- this._seconds = 3;
- this._decay = 2;
- this._reverse = false;
- this._buildImpulse();
- this.connect();
- p5sound.soundArray.push(this);
- };
- /**
- * Connect a source to the reverb, and assign reverb parameters.
- *
- * @method process
- * @param {Object} src p5.sound / Web Audio object with a sound
- * output.
- * @param {Number} [seconds] Duration of the reverb, in seconds.
- * Min: 0, Max: 10. Defaults to 3.
- * @param {Number} [decayRate] Percentage of decay with each echo.
- * Min: 0, Max: 100. Defaults to 2.
- * @param {Boolean} [reverse] Play the reverb backwards or forwards.
- */
- p5.Reverb.prototype.process = function (src, seconds, decayRate, reverse) {
- src.connect(this.input);
- var rebuild = false;
- if (seconds) {
- this._seconds = seconds;
- rebuild = true;
- }
- if (decayRate) {
- this._decay = decayRate;
- }
- if (reverse) {
- this._reverse = reverse;
- }
- if (rebuild) {
- this._buildImpulse();
- }
- };
- /**
- * Set the reverb settings. Similar to .process(), but without
- * assigning a new input.
- *
- * @method set
- * @param {Number} [seconds] Duration of the reverb, in seconds.
- * Min: 0, Max: 10. Defaults to 3.
- * @param {Number} [decayRate] Percentage of decay with each echo.
- * Min: 0, Max: 100. Defaults to 2.
- * @param {Boolean} [reverse] Play the reverb backwards or forwards.
- */
- p5.Reverb.prototype.set = function (seconds, decayRate, reverse) {
- var rebuild = false;
- if (seconds) {
- this._seconds = seconds;
- rebuild = true;
- }
- if (decayRate) {
- this._decay = decayRate;
- }
- if (reverse) {
- this._reverse = reverse;
- }
- if (rebuild) {
- this._buildImpulse();
- }
- };
- /**
- * Set the output level of the delay effect.
- *
- * @method amp
- * @param {Number} volume amplitude between 0 and 1.0
- * @param {Number} [rampTime] create a fade that lasts rampTime
- * @param {Number} [timeFromNow] schedule this event to happen
- * seconds from now
- */
- p5.Reverb.prototype.amp = function (vol, rampTime, tFromNow) {
- var rampTime = rampTime || 0;
- var tFromNow = tFromNow || 0;
- var now = p5sound.audiocontext.currentTime;
- var currentVol = this.output.gain.value;
- this.output.gain.cancelScheduledValues(now);
- this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
- this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
- };
- /**
- * Send output to a p5.sound or web audio object
- *
- * @method connect
- * @param {Object} unit
- */
- p5.Reverb.prototype.connect = function (unit) {
- var u = unit || p5.soundOut.input;
- this.output.connect(u.input ? u.input : u);
- };
- /**
- * Disconnect all output.
- *
- * @method disconnect
- */
- p5.Reverb.prototype.disconnect = function () {
- this.output.disconnect();
- };
- /**
- * Inspired by Simple Reverb by Jordan Santell
- * https://github.com/web-audio-components/simple-reverb/blob/master/index.js
- *
- * Utility function for building an impulse response
- * based on the module parameters.
- *
- * @private
- */
- p5.Reverb.prototype._buildImpulse = function () {
- var rate = this.ac.sampleRate;
- var length = rate * this._seconds;
- var decay = this._decay;
- var impulse = this.ac.createBuffer(2, length, rate);
- var impulseL = impulse.getChannelData(0);
- var impulseR = impulse.getChannelData(1);
- var n, i;
- for (i = 0; i < length; i++) {
- n = this.reverse ? length - i : i;
- impulseL[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
- impulseR[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
- }
- 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;
- }
- if (typeof this.output !== 'undefined') {
- this.output.disconnect();
- this.output = null;
- }
- if (typeof this.panner !== 'undefined') {
- this.panner.disconnect();
- this.panner = null;
- }
- };
- // =======================================================================
- // *** p5.Convolver ***
- // =======================================================================
- /**
- *
p5.Convolver extends p5.Reverb. It can emulate the sound of real
- * physical spaces through a process called
- * convolution.
- *
- *
Convolution multiplies any audio input by an "impulse response"
- * to simulate the dispersion of sound over time. The impulse response is
- * generated from an audio file that you provide. One way to
- * generate an impulse response is to pop a balloon in a reverberant space
- * and record the echo. Convolution can also be used to experiment with
- * sound.
- *
- *
Use the method createConvolution(path) to instantiate a
- * p5.Convolver with a path to your impulse response audio file.
- *
- * @class p5.Convolver
- * @constructor
- * @param {String} path path to a sound file
- * @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;
- * function preload() {
- * // We have both MP3 and OGG versions of all sound assets
- * soundFormats('ogg', 'mp3');
- *
- * // Try replacing 'bx-spring' with other soundfiles like
- * // 'concrete-tunnel' 'small-plate' 'drum' 'beatbox'
- * cVerb = createConvolver('assets/bx-spring.mp3');
- *
- * // Try replacing 'Damscray_DancingTiger' with
- * // 'beat', 'doorbell', lucky_dragons_-_power_melody'
- * sound = loadSound('assets/Damscray_DancingTiger.mp3');
- * }
- *
- * function setup() {
- * // disconnect from master output...
- * sound.disconnect();
- *
- * // ...and process with cVerb
- * // so that we only hear the convolution
- * cVerb.process(sound);
- *
- * sound.play();
- * }
- *
- */
- p5.Convolver = function (path, callback, errorCallback) {
- this.ac = p5sound.audiocontext;
- /**
- * Internally, the p5.Convolver uses the a
- *
- * Web Audio Convolver Node.
- *
- * @property convolverNode
- * @type {Object} Web Audio Convolver Node
- */
- this.convolverNode = this.ac.createConvolver();
- this.input = this.ac.createGain();
- this.output = this.ac.createGain();
- // otherwise, Safari distorts
- this.input.gain.value = 0.5;
- this.input.connect(this.convolverNode);
- this.convolverNode.connect(this.output);
- if (path) {
- this.impulses = [];
- this._loadBuffer(path, callback, errorCallback);
- } else {
- // parameters
- this._seconds = 3;
- this._decay = 2;
- this._reverse = false;
- this._buildImpulse();
- }
- this.connect();
- p5sound.soundArray.push(this);
- };
- p5.Convolver.prototype = Object.create(p5.Reverb.prototype);
- p5.prototype.registerPreloadMethod('createConvolver', p5.prototype);
- /**
- * Create a p5.Convolver. Accepts a path to a soundfile
- * that will be used to generate an impulse response.
- *
- * @method createConvolver
- * @param {String} path path to a sound file
- * @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
- *
- * var cVerb, sound;
- * function preload() {
- * // We have both MP3 and OGG versions of all sound assets
- * soundFormats('ogg', 'mp3');
- *
- * // Try replacing 'bx-spring' with other soundfiles like
- * // 'concrete-tunnel' 'small-plate' 'drum' 'beatbox'
- * cVerb = createConvolver('assets/bx-spring.mp3');
- *
- * // Try replacing 'Damscray_DancingTiger' with
- * // 'beat', 'doorbell', lucky_dragons_-_power_melody'
- * sound = loadSound('assets/Damscray_DancingTiger.mp3');
- * }
- *
- * function setup() {
- * // disconnect from master output...
- * sound.disconnect();
- *
- * // ...and process with cVerb
- * // so that we only hear the convolution
- * cVerb.process(sound);
- *
- * sound.play();
- * }
- *
- */
- 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, errorCallback);
- cReverb.impulses = [];
- return cReverb;
- };
- /**
- * Private method to load a buffer as an Impulse Response,
- * assign it to the convolverNode, and add to the Array of .impulses.
- *
- * @param {String} path
- * @param {Function} callback
- * @param {Function} errorCallback
- * @private
- */
- 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';
- request.onload = function () {
- 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();
- };
- p5.Convolver.prototype.set = null;
- /**
- * Connect a source to the reverb, and assign reverb parameters.
- *
- * @method process
- * @param {Object} src p5.sound / Web Audio object with a sound
- * output.
- * @example
- *
- * var cVerb, sound;
- * function preload() {
- * soundFormats('ogg', 'mp3');
- *
- * cVerb = createConvolver('assets/concrete-tunnel.mp3');
- *
- * sound = loadSound('assets/beat.mp3');
- * }
- *
- * function setup() {
- * // disconnect from master output...
- * sound.disconnect();
- *
- * // ...and process with (i.e. connect to) cVerb
- * // so that we only hear the convolution
- * cVerb.process(sound);
- *
- * sound.play();
- * }
- *
- */
- p5.Convolver.prototype.process = function (src) {
- src.connect(this.input);
- };
- /**
- * If you load multiple impulse files using the .addImpulse method,
- * they will be stored as Objects in this Array. Toggle between them
- * with the toggleImpulse(id) method.
- *
- * @property impulses
- * @type {Array} Array of Web Audio Buffers
- */
- p5.Convolver.prototype.impulses = [];
- /**
- * Load and assign a new Impulse Response to the p5.Convolver.
- * The impulse is added to the .impulses array. Previous
- * impulses can be accessed with the .toggleImpulse(id)
- * method.
- *
- * @method addImpulse
- * @param {String} path path to a sound file
- * @param {Function} callback function (optional)
- * @param {Function} errorCallback function (optional)
- */
- 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, errorCallback);
- };
- /**
- * Similar to .addImpulse, except that the .impulses
- * Array is reset to save memory. A new .impulses
- * array is created with this impulse as the only item.
- *
- * @method resetImpulse
- * @param {String} path path to a sound file
- * @param {Function} callback function (optional)
- * @param {Function} errorCallback function (optional)
- */
- 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, errorCallback);
- };
- /**
- * If you have used .addImpulse() to add multiple impulses
- * to a p5.Convolver, then you can use this method to toggle between
- * the items in the .impulses Array. Accepts a parameter
- * to identify which impulse you wish to use, identified either by its
- * original filename (String) or by its position in the .impulses
- * Array (Number).
- * You can access the objects in the .impulses Array directly. Each
- * Object has two attributes: an .audioBuffer (type:
- * Web Audio
- * AudioBuffer) and a .name, a String that corresponds
- * with the original filename.
- *
- * @method toggleImpulse
- * @param {String|Number} id Identify the impulse by its original filename
- * (String), or by its position in the
- * .impulses Array (Number).
- */
- p5.Convolver.prototype.toggleImpulse = function (id) {
- if (typeof id === 'number' && id < this.impulses.length) {
- this.convolverNode.buffer = this.impulses[id].audioBuffer;
- }
- if (typeof id === 'string') {
- for (var i = 0; i < this.impulses.length; i++) {
- if (this.impulses[i].name === id) {
- this.convolverNode.buffer = this.impulses[i].audioBuffer;
- break;
- }
- }
- }
- };
- p5.Convolver.prototype.dispose = function () {
- // remove all the Impulse Response buffers
- for (var i in this.impulses) {
- this.impulses[i] = null;
- }
- this.convolverNode.disconnect();
- this.concolverNode = null;
- if (typeof this.output !== 'undefined') {
- this.output.disconnect();
- this.output = null;
- }
- if (typeof this.panner !== 'undefined') {
- this.panner.disconnect();
- this.panner = null;
- }
- };
-}(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 () {
- 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.defaults = {
- 'callback': Tone.noOp,
- 'frequency': 1,
- 'lookAhead': 'auto'
- };
- 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.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.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._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;
- }
- };
- Tone.Clock.prototype.getStateAtTime = function (time) {
- return this._state.getStateAtTime(time);
- };
- Tone.Clock.prototype.dispose = function () {
- cancelAnimationFrame(this._loopID);
- Tone.TimelineState.prototype.dispose.call(this);
- this._writable('frequency');
- this.frequency.dispose();
- this.frequency = null;
- this._boundLoop = Tone.noOp;
- this._nextTick = Infinity;
- this.callback = null;
- this._state.dispose();
- this._state = null;
- };
- return Tone.Clock;
-}(Tone_core_Tone, Tone_signal_TimelineSignal);
-var metro;
-metro = function () {
- 'use strict';
- var p5sound = master;
- // requires the Tone.js library's Clock (MIT license, Yotam Mann)
- // https://github.com/TONEnoTONE/Tone.js/
- var Clock = Tone_core_Clock;
- var ac = p5sound.audiocontext;
- // var upTick = false;
- p5.Metro = function () {
- this.clock = new Clock({ 'callback': this.ontick.bind(this) });
- this.syncedParts = [];
- this.bpm = 120;
- // gets overridden by p5.Part
- this._init();
- this.tickCallback = function () {
- };
- };
- var prevTick = 0;
- var tatumTime = 0;
- p5.Metro.prototype.ontick = function (tickTime) {
- var elapsedTime = tickTime - prevTick;
- var secondsFromNow = tickTime - p5sound.audiocontext.currentTime;
- if (elapsedTime - tatumTime <= -0.02) {
- return;
- } else {
- prevTick = tickTime;
- // for all of the active things on the metro:
- for (var i in this.syncedParts) {
- var thisPart = this.syncedParts[i];
- if (!thisPart.isPlaying)
- return;
- thisPart.incrementStep(secondsFromNow);
- // each synced source keeps track of its own beat number
- for (var j in thisPart.phrases) {
- var thisPhrase = thisPart.phrases[j];
- var phraseArray = thisPhrase.sequence;
- var bNum = this.metroTicks % phraseArray.length;
- if (phraseArray[bNum] !== 0 && (this.metroTicks < phraseArray.length || !thisPhrase.looping)) {
- thisPhrase.callback(secondsFromNow, phraseArray[bNum]);
- }
- }
- }
- this.metroTicks += 1;
- this.tickCallback(secondsFromNow);
- }
- };
- p5.Metro.prototype.setBPM = function (bpm, rampTime) {
- var beatTime = 60 / (bpm * this.tatums);
- var now = p5sound.audiocontext.currentTime;
- tatumTime = beatTime;
- 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) {
- return this.clock.getRate() / this.tatums * 60;
- };
- p5.Metro.prototype._init = function () {
- this.metroTicks = 0;
- };
- // clear existing synced parts, add only this one
- p5.Metro.prototype.resetSync = function (part) {
- this.syncedParts = [part];
- };
- // push a new synced part to the array
- p5.Metro.prototype.pushSync = function (part) {
- this.syncedParts.push(part);
- };
- 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 (timeFromNow) {
- var t = timeFromNow || 0;
- var now = p5sound.audiocontext.currentTime;
- if (this.clock._oscillator) {
- this.clock.stop(now + t);
- }
- };
- p5.Metro.prototype.beatLength = function (tatums) {
- this.tatums = 1 / tatums / 4;
- };
-}(master, Tone_core_Clock);
-var looper;
-looper = function () {
- 'use strict';
- var p5sound = master;
- var bpm = 120;
- /**
- * Set the global tempo, in beats per minute, for all
- * p5.Parts. This method will impact all active p5.Parts.
- *
- * @param {Number} BPM Beats Per Minute
- * @param {Number} rampTime Seconds from now
- */
- p5.prototype.setBPM = function (BPM, rampTime) {
- bpm = BPM;
- for (var i in p5sound.parts) {
- p5sound.parts[i].setBPM(bpm, rampTime);
- }
- };
- /**
- *
A phrase is a pattern of musical events over time, i.e.
- * a series of notes and rests.
- *
- *
Phrases must be added to a p5.Part for playback, and
- * each part can play multiple phrases at the same time.
- * For example, one Phrase might be a kick drum, another
- * could be a snare, and another could be the bassline.
- *
- *
The first parameter is a name so that the phrase can be
- * modified or deleted later. The callback is a a function that
- * this phrase will call at every step—for example it might be
- * called playNote(value){}. The array determines
- * which value is passed into the callback at each step of the
- * phrase. It can be numbers, an object with multiple numbers,
- * or a zero (0) indicates a rest so the callback won't be called).
- *
- * @class p5.Phrase
- * @constructor
- * @param {String} name Name so that you can access the Phrase.
- * @param {Function} callback The name of a function that this phrase
- * will call. Typically it will play a sound,
- * and accept two parameters: a time at which
- * to play the sound (in seconds from now),
- * and a value from the sequence array. The
- * time should be passed into the play() or
- * start() method to ensure precision.
- * @param {Array} sequence Array of values to pass into the callback
- * at each step of the phrase.
- * @example
- *
- */
- p5.Phrase = function (name, callback, sequence) {
- this.phraseStep = 0;
- this.name = name;
- this.callback = callback;
- /**
- * Array of values to pass into the callback
- * at each step of the phrase. Depending on the callback
- * function's requirements, these values may be numbers,
- * strings, or an object with multiple parameters.
- * Zero (0) indicates a rest.
- *
- * @property sequence
- * @type {Array}
- */
- this.sequence = sequence;
- };
- /**
- *
A p5.Part plays back one or more p5.Phrases. Instantiate a part
- * with steps and tatums. By default, each step represents 1/16th note.
- *
- *
See p5.Phrase for more about musical timing.
- *
- * @class p5.Part
- * @constructor
- * @param {Number} [steps] Steps in the part
- * @param {Number} [tatums] Divisions of a beat (default is 1/16, a quarter note)
- * @example
- *
- */
- p5.Part = function (steps, bLength) {
- this.length = steps || 0;
- // how many beats
- this.partStep = 0;
- this.phrases = [];
- this.isPlaying = false;
- this.noLoop();
- this.tatums = bLength || 0.0625;
- // defaults to quarter note
- this.metro = new p5.Metro();
- this.metro._init();
- this.metro.beatLength(this.tatums);
- this.metro.setBPM(bpm);
- p5sound.parts.push(this);
- this.callback = function () {
- };
- };
- /**
- * Set the tempo of this part, in Beats Per Minute.
- *
- * @method setBPM
- * @param {Number} BPM Beats Per Minute
- * @param {Number} [rampTime] Seconds from now
- */
- p5.Part.prototype.setBPM = function (tempo, rampTime) {
- this.metro.setBPM(tempo, rampTime);
- };
- /**
- * Returns the Beats Per Minute of this currently part.
- *
- * @method getBPM
- * @return {Number}
- */
- p5.Part.prototype.getBPM = function () {
- return this.metro.getBPM();
- };
- /**
- * Start playback of this part. It will play
- * through all of its phrases at a speed
- * determined by setBPM.
- *
- * @method start
- * @param {Number} [time] seconds from now
- */
- p5.Part.prototype.start = function (time) {
- if (!this.isPlaying) {
- this.isPlaying = true;
- this.metro.resetSync(this);
- var t = time || 0;
- this.metro.start(t);
- }
- };
- /**
- * Loop playback of this part. It will begin
- * looping through all of its phrases at a speed
- * determined by setBPM.
- *
- * @method loop
- * @param {Number} [time] seconds from now
- */
- p5.Part.prototype.loop = function (time) {
- this.looping = true;
- // rest onended function
- this.onended = function () {
- this.partStep = 0;
- };
- var t = time || 0;
- this.start(t);
- };
- /**
- * Tell the part to stop looping.
- *
- * @method noLoop
- */
- p5.Part.prototype.noLoop = function () {
- this.looping = false;
- // rest onended function
- this.onended = function () {
- this.stop();
- };
- };
- /**
- * Stop the part and cue it to step 0.
- *
- * @method stop
- * @param {Number} [time] seconds from now
- */
- p5.Part.prototype.stop = function (time) {
- this.partStep = 0;
- this.pause(time);
- };
- /**
- * Pause the part. Playback will resume
- * from the current step.
- *
- * @method pause
- * @param {Number} time seconds from now
- */
- p5.Part.prototype.pause = function (time) {
- this.isPlaying = false;
- var t = time || 0;
- this.metro.stop(t);
- };
- /**
- * Add a p5.Phrase to this Part.
- *
- * @method addPhrase
- * @param {p5.Phrase} phrase reference to a p5.Phrase
- */
- p5.Part.prototype.addPhrase = function (name, callback, array) {
- var p;
- if (arguments.length === 3) {
- p = new p5.Phrase(name, callback, array);
- } else if (arguments[0] instanceof p5.Phrase) {
- p = arguments[0];
- } else {
- throw 'invalid input. addPhrase accepts name, callback, array or a p5.Phrase';
- }
- this.phrases.push(p);
- // reset the length if phrase is longer than part's existing length
- if (p.sequence.length > this.length) {
- this.length = p.sequence.length;
- }
- };
- /**
- * Remove a phrase from this part, based on the name it was
- * given when it was created.
- *
- * @method removePhrase
- * @param {String} phraseName
- */
- p5.Part.prototype.removePhrase = function (name) {
- for (var i in this.phrases) {
- if (this.phrases[i].name === name) {
- this.phrases.splice(i, 1);
- }
- }
- };
- /**
- * Get a phrase from this part, based on the name it was
- * given when it was created. Now you can modify its array.
- *
- * @method getPhrase
- * @param {String} phraseName
- */
- p5.Part.prototype.getPhrase = function (name) {
- for (var i in this.phrases) {
- if (this.phrases[i].name === name) {
- return this.phrases[i];
- }
- }
- };
- /**
- * Get a phrase from this part, based on the name it was
- * given when it was created. Now you can modify its array.
- *
- * @method replaceSequence
- * @param {String} phraseName
- * @param {Array} sequence Array of values to pass into the callback
- * at each step of the phrase.
- */
- p5.Part.prototype.replaceSequence = function (name, array) {
- for (var i in this.phrases) {
- if (this.phrases[i].name === name) {
- this.phrases[i].sequence = array;
- }
- }
- };
- p5.Part.prototype.incrementStep = function (time) {
- if (this.partStep < this.length - 1) {
- this.callback(time);
- this.partStep += 1;
- } else {
- if (!this.looping && this.partStep == this.length - 1) {
- console.log('done');
- // this.callback(time);
- this.onended();
- }
- }
- };
- /**
- * Fire a callback function at every step.
- *
- * @method onStep
- * @param {Function} callback The name of the callback
- * you want to fire
- * on every beat/tatum.
- */
- p5.Part.prototype.onStep = function (callback) {
- this.callback = callback;
- };
- // ===============
- // p5.Score
- // ===============
- /**
- * A Score consists of a series of Parts. The parts will
- * be played back in order. For example, you could have an
- * A part, a B part, and a C part, and play them back in this order
- * new p5.Score(a, a, b, a, c)
- *
- * @class p5.Score
- * @constructor
- * @param {p5.Part} part(s) One or multiple parts, to be played in sequence.
- * @return {p5.Score}
- */
- p5.Score = function () {
- // for all of the arguments
- this.parts = [];
- this.currentPart = 0;
- var thisScore = this;
- for (var i in arguments) {
- this.parts[i] = arguments[i];
- this.parts[i].nextPart = this.parts[i + 1];
- this.parts[i].onended = function () {
- thisScore.resetPart(i);
- playNextPart(thisScore);
- };
- }
- this.looping = false;
- };
- p5.Score.prototype.onended = function () {
- if (this.looping) {
- // this.resetParts();
- this.parts[0].start();
- } else {
- this.parts[this.parts.length - 1].onended = function () {
- this.stop();
- this.resetParts();
- };
- }
- this.currentPart = 0;
- };
- /**
- * Start playback of the score.
- *
- * @method start
- */
- p5.Score.prototype.start = function () {
- this.parts[this.currentPart].start();
- this.scoreStep = 0;
- };
- /**
- * Stop playback of the score.
- *
- * @method stop
- */
- p5.Score.prototype.stop = function () {
- this.parts[this.currentPart].stop();
- this.currentPart = 0;
- this.scoreStep = 0;
- };
- /**
- * Pause playback of the score.
- *
- * @method pause
- */
- p5.Score.prototype.pause = function () {
- this.parts[this.currentPart].stop();
- };
- /**
- * Loop playback of the score.
- *
- * @method loop
- */
- p5.Score.prototype.loop = function () {
- this.looping = true;
- this.start();
- };
- /**
- * Stop looping playback of the score. If it
- * is currently playing, this will go into effect
- * after the current round of playback completes.
- *
- * @method noLoop
- */
- p5.Score.prototype.noLoop = function () {
- this.looping = false;
- };
- p5.Score.prototype.resetParts = function () {
- for (var i in this.parts) {
- this.resetPart(i);
- }
- };
- p5.Score.prototype.resetPart = function (i) {
- this.parts[i].stop();
- this.parts[i].partStep = 0;
- for (var p in this.parts[i].phrases) {
- this.parts[i].phrases[p].phraseStep = 0;
- }
- };
- /**
- * Set the tempo for all parts in the score
- *
- * @param {Number} BPM Beats Per Minute
- * @param {Number} rampTime Seconds from now
- */
- p5.Score.prototype.setBPM = function (bpm, rampTime) {
- for (var i in this.parts) {
- this.parts[i].setBPM(bpm, rampTime);
- }
- };
- function playNextPart(aScore) {
- aScore.currentPart++;
- if (aScore.currentPart >= aScore.parts.length) {
- aScore.scoreStep = 0;
- aScore.onended();
- } else {
- aScore.scoreStep = 0;
- aScore.parts[aScore.currentPart - 1].stop();
- aScore.parts[aScore.currentPart].start();
- }
- }
-}(master);
-var soundRecorder;
-soundRecorder = function () {
- 'use strict';
- var p5sound = master;
- var ac = p5sound.audiocontext;
- /**
- *
Record sounds for playback and/or to save as a .wav file.
- * The p5.SoundRecorder records all sound output from your sketch,
- * or can be assigned a specific source with setInput().
- *
The record() method accepts a p5.SoundFile as a parameter.
- * When playback is stopped (either after the given amount of time,
- * or with the stop() method), the p5.SoundRecorder will send its
- * recording to that p5.SoundFile for playback.
- * var mic, recorder, soundFile;
- * var state = 0;
- *
- * function setup() {
- * background(200);
- * // create an audio in
- * mic = new p5.AudioIn();
- *
- * // prompts user to enable their browser mic
- * mic.start();
- *
- * // create a sound recorder
- * recorder = new p5.SoundRecorder();
- *
- * // connect the mic to the recorder
- * recorder.setInput(mic);
- *
- * // this sound file will be used to
- * // playback & save the recording
- * soundFile = new p5.SoundFile();
- *
- * text('keyPress to record', 20, 20);
- * }
- *
- * function keyPressed() {
- * // make sure user enabled the mic
- * if (state === 0 && mic.enabled) {
- *
- * // record to our p5.SoundFile
- * recorder.record(soundFile);
- *
- * background(255,0,0);
- * text('Recording!', 20, 20);
- * state++;
- * }
- * else if (state === 1) {
- * background(0,255,0);
- *
- * // stop recorder and
- * // send result to soundFile
- * recorder.stop();
- *
- * text('Stopped', 20, 20);
- * state++;
- * }
- *
- * else if (state === 2) {
- * soundFile.play(); // play the result!
- * save(soundFile, 'mySound.wav');
- * state++;
- * }
- * }
- *
- */
- p5.SoundRecorder = function () {
- this.input = ac.createGain();
- this.output = ac.createGain();
- this.recording = false;
- this.bufferSize = 1024;
- this._channels = 2;
- // stereo (default)
- this._clear();
- // initialize variables
- this._jsNode = ac.createScriptProcessor(this.bufferSize, this._channels, 2);
- this._jsNode.onaudioprocess = this._audioprocess.bind(this);
- /**
- * callback invoked when the recording is over
- * @private
- * @type {function(Float32Array)}
- */
- this._callback = function () {
- };
- // connections
- this._jsNode.connect(p5.soundOut._silentNode);
- this.setInput();
- // add this p5.SoundFile to the soundArray
- p5sound.soundArray.push(this);
- };
- /**
- * Connect a specific device to the p5.SoundRecorder.
- * If no parameter is given, p5.SoundRecorer will record
- * all audible p5.sound from your sketch.
- *
- * @method setInput
- * @param {Object} [unit] p5.sound object or a web audio unit
- * that outputs sound
- */
- p5.SoundRecorder.prototype.setInput = function (unit) {
- this.input.disconnect();
- this.input = null;
- this.input = ac.createGain();
- this.input.connect(this._jsNode);
- this.input.connect(this.output);
- if (unit) {
- unit.connect(this.input);
- } else {
- p5.soundOut.output.connect(this.input);
- }
- };
- /**
- * Start recording. To access the recording, provide
- * a p5.SoundFile as the first parameter. The p5.SoundRecorder
- * will send its recording to that p5.SoundFile for playback once
- * recording is complete. Optional parameters include duration
- * (in seconds) of the recording, and a callback function that
- * will be called once the complete recording has been
- * transfered to the p5.SoundFile.
- *
- * @method record
- * @param {p5.SoundFile} soundFile p5.SoundFile
- * @param {Number} [duration] Time (in seconds)
- * @param {Function} [callback] The name of a function that will be
- * called once the recording completes
- */
- p5.SoundRecorder.prototype.record = function (sFile, duration, callback) {
- this.recording = true;
- if (duration) {
- this.sampleLimit = Math.round(duration * ac.sampleRate);
- }
- if (sFile && callback) {
- this._callback = function () {
- this.buffer = this._getBuffer();
- sFile.setBuffer(this.buffer);
- callback();
- };
- } else if (sFile) {
- this._callback = function () {
- this.buffer = this._getBuffer();
- sFile.setBuffer(this.buffer);
- };
- }
- };
- /**
- * Stop the recording. Once the recording is stopped,
- * the results will be sent to the p5.SoundFile that
- * was given on .record(), and if a callback function
- * was provided on record, that function will be called.
- *
- * @method stop
- */
- p5.SoundRecorder.prototype.stop = function () {
- this.recording = false;
- this._callback();
- this._clear();
- };
- p5.SoundRecorder.prototype._clear = function () {
- this._leftBuffers = [];
- this._rightBuffers = [];
- this.recordedSamples = 0;
- this.sampleLimit = null;
- };
- /**
- * internal method called on audio process
- *
- * @private
- * @param {AudioProcessorEvent} event
- */
- p5.SoundRecorder.prototype._audioprocess = function (event) {
- if (this.recording === false) {
- return;
- } else if (this.recording === true) {
- // if we are past the duration, then stop... else:
- if (this.sampleLimit && this.recordedSamples >= this.sampleLimit) {
- this.stop();
- } else {
- // get channel data
- var left = event.inputBuffer.getChannelData(0);
- var right = event.inputBuffer.getChannelData(1);
- // clone the samples
- this._leftBuffers.push(new Float32Array(left));
- this._rightBuffers.push(new Float32Array(right));
- this.recordedSamples += this.bufferSize;
- }
- }
- };
- p5.SoundRecorder.prototype._getBuffer = function () {
- var buffers = [];
- buffers.push(this._mergeBuffers(this._leftBuffers));
- buffers.push(this._mergeBuffers(this._rightBuffers));
- return buffers;
- };
- p5.SoundRecorder.prototype._mergeBuffers = function (channelBuffer) {
- var result = new Float32Array(this.recordedSamples);
- var offset = 0;
- var lng = channelBuffer.length;
- for (var i = 0; i < lng; i++) {
- var buffer = channelBuffer[i];
- result.set(buffer, offset);
- offset += buffer.length;
- }
- return result;
- };
- 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) {
- this.input.disconnect();
- }
- this.input = null;
- this._jsNode = null;
- };
- /**
- * Save a p5.SoundFile as a .wav audio file.
- *
- * @method saveSound
- * @param {p5.SoundFile} soundFile p5.SoundFile that you wish to save
- * @param {String} name name of the resulting .wav file.
- */
- p5.prototype.saveSound = function (soundFile, name) {
- var leftChannel, rightChannel;
- leftChannel = soundFile.buffer.getChannelData(0);
- // handle mono files
- if (soundFile.buffer.numberOfChannels > 1) {
- rightChannel = soundFile.buffer.getChannelData(1);
- } else {
- rightChannel = leftChannel;
- }
- var interleaved = interleave(leftChannel, rightChannel);
- // create the buffer and view to create the .WAV file
- var buffer = new ArrayBuffer(44 + interleaved.length * 2);
- var view = new DataView(buffer);
- // write the WAV container,
- // check spec at: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
- // RIFF chunk descriptor
- writeUTFBytes(view, 0, 'RIFF');
- view.setUint32(4, 36 + interleaved.length * 2, true);
- writeUTFBytes(view, 8, 'WAVE');
- // FMT sub-chunk
- writeUTFBytes(view, 12, 'fmt ');
- view.setUint32(16, 16, true);
- view.setUint16(20, 1, true);
- // stereo (2 channels)
- view.setUint16(22, 2, true);
- view.setUint32(24, 44100, true);
- view.setUint32(28, 44100 * 4, true);
- view.setUint16(32, 4, true);
- view.setUint16(34, 16, true);
- // data sub-chunk
- writeUTFBytes(view, 36, 'data');
- view.setUint32(40, interleaved.length * 2, true);
- // write the PCM samples
- var lng = interleaved.length;
- var index = 44;
- var volume = 1;
- for (var i = 0; i < lng; i++) {
- view.setInt16(index, interleaved[i] * (32767 * volume), true);
- index += 2;
- }
- p5.prototype.writeFile([view], name, 'wav');
- };
- // helper methods to save waves
- function interleave(leftChannel, rightChannel) {
- var length = leftChannel.length + rightChannel.length;
- var result = new Float32Array(length);
- var inputIndex = 0;
- for (var index = 0; index < length;) {
- result[index++] = leftChannel[inputIndex];
- result[index++] = rightChannel[inputIndex];
- inputIndex++;
- }
- return result;
- }
- function writeUTFBytes(view, offset, string) {
- var lng = string.length;
- for (var i = 0; i < lng; i++) {
- view.setUint8(offset + i, string.charCodeAt(i));
- }
- }
-}(sndcore, master);
-var peakdetect;
-peakdetect = function () {
- 'use strict';
- var p5sound = master;
- /**
- *
PeakDetect works in conjunction with p5.FFT to
- * look for onsets in some or all of the frequency spectrum.
- *
- *
- * To use p5.PeakDetect, call update in the draw loop
- * and pass in a p5.FFT object.
- *
- *
- * You can listen for a specific part of the frequency spectrum by
- * setting the range between freq1 and freq2.
- *
- *
- *
threshold is the threshold for detecting a peak,
- * scaled between 0 and 1. It is logarithmic, so 0.1 is half as loud
- * as 1.0.
- *
- *
- * The update method is meant to be run in the draw loop, and
- * frames determines how many loops must pass before
- * another peak can be detected.
- * For example, if the frameRate() = 60, you could detect the beat of a
- * 120 beat-per-minute song with this equation:
- * framesPerPeak = 60 / (estimatedBPM / 60 );
- *
- *
- *
- * Based on example contribtued by @b2renger, and a simple beat detection
- * explanation by Felix Turner.
- *
- *
- * @class p5.PeakDetect
- * @constructor
- * @param {Number} [freq1] lowFrequency - defaults to 20Hz
- * @param {Number} [freq2] highFrequency - defaults to 20000 Hz
- * @param {Number} [threshold] Threshold for detecting a beat between 0 and 1
- * scaled logarithmically where 0.1 is 1/2 the loudness
- * of 1.0. Defaults to 0.35.
- * @param {Number} [framesPerPeak] Defaults to 20.
- * @example
- *
- */
- p5.PeakDetect = function (freq1, freq2, threshold, _framesPerPeak) {
- var framesPerPeak;
- // framesPerPeak determines how often to look for a beat.
- // If a beat is provided, try to look for a beat based on bpm
- this.framesPerPeak = _framesPerPeak || 20;
- this.framesSinceLastPeak = 0;
- this.decayRate = 0.95;
- this.threshold = threshold || 0.35;
- this.cutoff = 0;
- // how much to increase the cutoff
- // TO DO: document this / figure out how to make it accessible
- this.cutoffMult = 1.5;
- this.energy = 0;
- this.penergy = 0;
- // TO DO: document this property / figure out how to make it accessible
- this.currentValue = 0;
- /**
- * isDetected is set to true when a peak is detected.
- *
- * @attribute isDetected
- * @type {Boolean}
- * @default false
- */
- this.isDetected = false;
- this.f1 = freq1 || 40;
- this.f2 = freq2 || 20000;
- // function to call when a peak is detected
- this._onPeak = function () {
- };
- };
- /**
- * The update method is run in the draw loop.
- *
- * Accepts an FFT object. You must call .analyze()
- * on the FFT object prior to updating the peakDetect
- * because it relies on a completed FFT analysis.
- *
- * @method update
- * @param {p5.FFT} fftObject A p5.FFT object
- */
- p5.PeakDetect.prototype.update = function (fftObject) {
- var nrg = this.energy = fftObject.getEnergy(this.f1, this.f2) / 255;
- if (nrg > this.cutoff && nrg > this.threshold && nrg - this.penergy > 0) {
- // trigger callback
- this._onPeak();
- this.isDetected = true;
- // debounce
- this.cutoff = nrg * this.cutoffMult;
- this.framesSinceLastPeak = 0;
- } else {
- this.isDetected = false;
- if (this.framesSinceLastPeak <= this.framesPerPeak) {
- this.framesSinceLastPeak++;
- } else {
- this.cutoff *= this.decayRate;
- this.cutoff = Math.max(this.cutoff, this.threshold);
- }
- }
- this.currentValue = nrg;
- this.penergy = nrg;
- };
- /**
- * onPeak accepts two arguments: a function to call when
- * a peak is detected. The value of the peak,
- * between 0.0 and 1.0, is passed to the callback.
- *
- * @method onPeak
- * @param {Function} callback Name of a function that will
- * be called when a peak is
- * detected.
- * @param {Object} [val] Optional value to pass
- * into the function when
- * a peak is detected.
- * @example
- *
- */
- p5.PeakDetect.prototype.onPeak = function (callback, val) {
- var self = this;
- self._onPeak = function () {
- callback(self.energy, val);
- };
- };
-}(master);
-var gain;
-gain = function () {
- 'use strict';
- var p5sound = master;
- /**
- * A gain node is usefull to set the relative volume of sound.
- * It's typically used to build mixers.
- *
- * @class p5.Gain
- * @constructor
- * @example
- *
- *
- * // load two soundfile and crossfade beetween them
- * var sound1,sound2;
- * var gain1, gain2, gain3;
- *
- * function preload(){
- * soundFormats('ogg', 'mp3');
- * sound1 = loadSound('../_files/Damscray_-_Dancing_Tiger_01');
- * sound2 = loadSound('../_files/beat.mp3');
- * }
- *
- * function setup() {
- * createCanvas(400,200);
- *
- * // create a 'master' gain to which we will connect both soundfiles
- * gain3 = new p5.Gain();
- * gain3.connect();
- *
- * // setup first sound for playing
- * sound1.rate(1);
- * sound1.loop();
- * sound1.disconnect(); // diconnect from p5 output
- *
- * gain1 = new p5.Gain(); // setup a gain node
- * gain1.setInput(sound1); // connect the first sound to its input
- * gain1.connect(gain3); // connect its output to the 'master'
- *
- * sound2.rate(1);
- * sound2.disconnect();
- * sound2.loop();
- *
- * gain2 = new p5.Gain();
- * gain2.setInput(sound2);
- * gain2.connect(gain3);
- *
- * }
- *
- * function draw(){
- * background(180);
- *
- * // calculate the horizontal distance beetween the mouse and the right of the screen
- * var d = dist(mouseX,0,width,0);
- *
- * // map the horizontal position of the mouse to values useable for volume control of sound1
- * var vol1 = map(mouseX,0,width,0,1);
- * var vol2 = 1-vol1; // when sound1 is loud, sound2 is quiet and vice versa
- *
- * gain1.amp(vol1,0.5,0);
- * gain2.amp(vol2,0.5,0);
- *
- * // map the vertical position of the mouse to values useable for 'master volume control'
- * var vol3 = map(mouseY,0,height,0,1);
- * gain3.amp(vol3,0.5,0);
- * }
- *
- *
- */
- p5.Gain = function () {
- this.ac = p5sound.audiocontext;
- this.input = this.ac.createGain();
- this.output = this.ac.createGain();
- // 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.
- *
- * @method setInput
- * @param {Object} src p5.sound / Web Audio object with a sound
- * output.
- */
- p5.Gain.prototype.setInput = function (src) {
- src.connect(this.input);
- };
- /**
- * Send output to a p5.sound or web audio object
- *
- * @method connect
- * @param {Object} unit
- */
- p5.Gain.prototype.connect = function (unit) {
- var u = unit || p5.soundOut.input;
- this.output.connect(u.input ? u.input : u);
- };
- /**
- * Disconnect all output.
- *
- * @method disconnect
- */
- p5.Gain.prototype.disconnect = function () {
- this.output.disconnect();
- };
- /**
- * Set the output level of the gain node.
- *
- * @method amp
- * @param {Number} volume amplitude between 0 and 1.0
- * @param {Number} [rampTime] create a fade that lasts rampTime
- * @param {Number} [timeFromNow] schedule this event to happen
- * seconds from now
- */
- p5.Gain.prototype.amp = function (vol, rampTime, tFromNow) {
- var rampTime = rampTime || 0;
- var tFromNow = tFromNow || 0;
- var now = p5sound.audiocontext.currentTime;
- var currentVol = this.output.gain.value;
- this.output.gain.cancelScheduledValues(now);
- 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 distortion;
-distortion = function () {
- 'use strict';
- var p5sound = master;
- /*
- * Adapted from [Kevin Ennis on StackOverflow](http://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion)
- */
- function makeDistortionCurve(amount) {
- var k = typeof amount === 'number' ? amount : 50;
- var n_samples = 44100;
- var curve = new Float32Array(n_samples);
- var deg = Math.PI / 180;
- var i = 0;
- var x;
- for (; i < n_samples; ++i) {
- x = i * 2 / n_samples - 1;
- curve[i] = (3 + k) * x * 20 * deg / (Math.PI + k * Math.abs(x));
- }
- return curve;
- }
- /**
- * A Distortion effect created with a Waveshaper Node,
- * with an approach adapted from
- * [Kevin Ennis](http://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion)
- *
- * @class p5.Distortion
- * @constructor
- * @param {Number} [amount=0.25] Unbounded distortion amount.
- * Normal values range from 0-1.
- * @param {String} [oversample='none'] 'none', '2x', or '4x'.
- *
- * @return {Object} Distortion object
- */
- p5.Distortion = function (amount, oversample) {
- if (typeof amount === 'undefined') {
- amount = 0.25;
- }
- if (typeof amount !== 'number') {
- throw new Error('amount must be a number');
- }
- if (typeof oversample === 'undefined') {
- oversample = '2x';
- }
- if (typeof oversample !== 'string') {
- throw new Error('oversample must be a String');
- }
- var curveAmount = p5.prototype.map(amount, 0, 1, 0, 2000);
- this.ac = p5sound.audiocontext;
- this.input = this.ac.createGain();
- this.output = this.ac.createGain();
- /**
- * The p5.Distortion is built with a
- *
- * Web Audio WaveShaper Node.
- *
- * @property WaveShaperNode
- * @type {Object} AudioNode
- */
- this.waveShaperNode = this.ac.createWaveShaper();
- this.amount = curveAmount;
- this.waveShaperNode.curve = makeDistortionCurve(curveAmount);
- this.waveShaperNode.oversample = oversample;
- this.input.connect(this.waveShaperNode);
- this.waveShaperNode.connect(this.output);
- this.connect();
- // add to the soundArray
- p5sound.soundArray.push(this);
- };
- p5.Distortion.prototype.process = function (src, amount, oversample) {
- src.connect(this.input);
- this.set(amount, oversample);
- };
- /**
- * Set the amount and oversample of the waveshaper distortion.
- *
- * @method setType
- * @param {Number} [amount=0.25] Unbounded distortion amount.
- * Normal values range from 0-1.
- * @param {String} [oversample='none'] 'none', '2x', or '4x'.
- * @param {String}
- */
- p5.Distortion.prototype.set = function (amount, oversample) {
- if (amount) {
- var curveAmount = p5.prototype.map(amount, 0, 1, 0, 2000);
- this.amount = curveAmount;
- this.waveShaperNode.curve = makeDistortionCurve(curveAmount);
- }
- if (oversample) {
- this.waveShaperNode.oversample = oversample;
- }
- };
- /**
- * Return the distortion amount, typically between 0-1.
- *
- * @method getAmount
- * @return {Number} Unbounded distortion amount.
- * Normal values range from 0-1.
- */
- p5.Distortion.prototype.getAmount = function () {
- return this.amount;
- };
- /**
- * Return the oversampling.
- *
- * @return {String} Oversample can either be 'none', '2x', or '4x'.
- */
- p5.Distortion.prototype.getOversample = function () {
- return this.waveShaperNode.oversample;
- };
- /**
- * Send output to a p5.sound or web audio object
- *
- * @method connect
- * @param {Object} unit
- */
- p5.Distortion.prototype.connect = function (unit) {
- var u = unit || p5.soundOut.input;
- this.output.connect(u);
- };
- /**
- * Disconnect all output.
- *
- * @method disconnect
- */
- p5.Distortion.prototype.disconnect = function () {
- this.output.disconnect();
- };
- p5.Distortion.prototype.dispose = function () {
- var index = p5sound.soundArray.indexOf(this);
- p5sound.soundArray.splice(index, 1);
- this.input.disconnect();
- this.waveShaperNode.disconnect();
- this.input = null;
- this.waveShaperNode = null;
- if (typeof this.output !== 'undefined') {
- this.output.disconnect();
- this.output = null;
- }
- };
-}(master);
-var src_app;
-src_app = function () {
- 'use strict';
- var p5SOUND = sndcore;
- return p5SOUND;
-}(sndcore, master, helpers, errorHandler, panner, soundfile, amplitude, fft, signal, oscillator, env, pulse, noise, audioin, filter, delay, reverb, metro, looper, soundRecorder, peakdetect, gain, distortion);
-}));
\ No newline at end of file
diff --git a/floppies/claudia/noweb/.DS_Store b/floppies/claudia/noweb/.DS_Store
index e99677bd6100353ea8ce007c73532f10a0050acf..c8dc3bd15c48a82d3668dc69ebf659f63af0b296 100644
GIT binary patch
delta 207
zcmZoMXfc@JFUrEez`)4BAi%(o!;r`j&*0CH$B;VtBIELUkPH`t8$%XQJO^1eC*3eO
zIX|}mC=Ub+`hWyhb-DR2E-9rY$qWovzHO@lE5@NN1&caGOp6OLkS*S@`68nk+r$R7
J&Fmb1`2huXFSP&w
delta 227
zcmZoMXfc@JFUrcmz`)4BAi%(o$WX+P$&kuWoKie_BjfUVkPIh74p1x!D1#)MoRgHF
zp97S|CY_UR7@VA+TL6+~5S*`sT~%(ri%UvrNiqY&r3uHo!D`X1vH-dyH$R1tGN7u0
T3}h=0Y~IM|%C?!E<1aq|b*eQO
diff --git a/floppies/claudia/noweb/index.html b/floppies/claudia/noweb/index.html
index ceb4a4e..6d97523 100644
--- a/floppies/claudia/noweb/index.html
+++ b/floppies/claudia/noweb/index.html
@@ -4,9 +4,9 @@
-
-
-
+
+
+
The fine line
@@ -24,9 +24,9 @@
diff --git a/floppies/claudia/noweb/libraries/.DS_Store b/floppies/claudia/noweb/libraries/.DS_Store
deleted file mode 100644
index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 6148
zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3
zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ
zLs35+`xjp>T0The web is much more than just canvas and p5.dom makes it easy to interact
- * with other HTML5 objects, including text, hyperlink, image, input, video,
- * audio, and webcam.
- *
There is a set of creation methods, DOM manipulation methods, and
- * an extended p5.Element that supports a range of HTML elements. See the
- *
- * beyond the canvas tutorial for a full overview of how this addon works.
- *
- *
Methods and properties shown in black are part of the p5.js core, items in
- * blue are part of the p5.dom library. You will need to include an extra file
- * in order to access the blue functions. See the
- * using a library
- * section for information on how to include this library. p5.dom comes with
- * p5 complete or you can download the single file
- *
- * here.
- *
See tutorial: beyond the canvas
- * for more info on how to use this libary.
- *
- * @module p5.dom
- * @submodule p5.dom
- * @for p5.dom
- * @main
- */
-
-(function (root, factory) {
- if (typeof define === 'function' && define.amd)
- define('p5.dom', ['p5'], function (p5) { (factory(p5));});
- else if (typeof exports === 'object')
- factory(require('../p5'));
- else
- factory(root['p5']);
-}(this, function (p5) {
-
-// =============================================================================
-// p5 additions
-// =============================================================================
-
- /**
- * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.'
- * prefixes to specify an ID or class respectively, and none for a tag) and returns it as
- * a p5.Element. If a class or tag name is given with more than 1 element,
- * only the first element will be returned.
- * The DOM node itself can be accessed with .elt.
- * Returns null if none found. You can also specify a container to search within.
- *
- * @method select
- * @param {String} name id, class, or tag name of element to search for
- * @param {String} [container] id, p5.Element, or HTML element to search within
- * @return {Object/p5.Element|Null} p5.Element containing node found
- * @example
- *
- * // these are all valid calls to select()
- * var a = select('#moo');
- * var b = select('#blah', '#myContainer');
- * var c = select('#foo', b);
- * var d = document.getElementById('beep');
- * var e = select('p', d);
- *
- *
- */
- p5.prototype.select = function (e, p) {
- var res = null;
- var container = getContainer(p);
- if (e[0] === '.'){
- e = e.slice(1);
- res = container.getElementsByClassName(e);
- if (res.length) {
- res = res[0];
- } else {
- res = null;
- }
- }else if (e[0] === '#'){
- e = e.slice(1);
- res = container.getElementById(e);
- }else {
- res = container.getElementsByTagName(e);
- if (res.length) {
- res = res[0];
- } else {
- res = null;
- }
- }
- if (res) {
- return wrapElement(res);
- } else {
- return null;
- }
- };
-
- /**
- * Searches the page for elements with the given class or tag name (using the '.' prefix
- * to specify a class and no prefix for a tag) and returns them as p5.Elements
- * in an array.
- * The DOM node itself can be accessed with .elt.
- * Returns an empty array if none found.
- * You can also specify a container to search within.
- *
- * @method selectAll
- * @param {String} name class or tag name of elements to search for
- * @param {String} [container] id, p5.Element, or HTML element to search within
- * @return {Array} Array of p5.Elements containing nodes found
- * @example
- *
- * function setup() {
- * createButton('btn');
- * createButton('2nd btn');
- * createButton('3rd btn');
- * var buttons = selectAll('button');
- *
- * for (var i = 0; i < buttons.length; i++){
- * buttons[i].size(100,100);
- * }
- * }
- *
- *
- * // these are all valid calls to selectAll()
- * var a = selectAll('.moo');
- * var b = selectAll('div');
- * var c = selectAll('button', '#myContainer');
- * var d = select('#container');
- * var e = selectAll('p', d);
- * var f = document.getElementById('beep');
- * var g = select('.blah', f);
- *
- *
- */
- p5.prototype.selectAll = function (e, p) {
- var arr = [];
- var res;
- var container = getContainer(p);
- if (e[0] === '.'){
- e = e.slice(1);
- res = container.getElementsByClassName(e);
- } else {
- res = container.getElementsByTagName(e);
- }
- if (res) {
- for (var j = 0; j < res.length; j++) {
- var obj = wrapElement(res[j]);
- arr.push(obj);
- }
- }
- return arr;
- };
-
- /**
- * Helper function for select and selectAll
- */
- function getContainer(p) {
- var container = document;
- if (typeof p === 'string' && p[0] === '#'){
- p = p.slice(1);
- container = document.getElementById(p) || document;
- } else if (p instanceof p5.Element){
- container = p.elt;
- } else if (p instanceof HTMLElement){
- container = p;
- }
- return container;
- }
-
- /**
- * Helper function for getElement and getElements.
- */
- function wrapElement(elt) {
- if(elt.tagName === "INPUT" && elt.type === "checkbox") {
- var converted = new p5.Element(elt);
- converted.checked = function(){
- if (arguments.length === 0){
- return this.elt.checked;
- } else if(arguments[0]) {
- this.elt.checked = true;
- } else {
- this.elt.checked = false;
- }
- return this;
- };
- return converted;
- } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") {
- return new p5.MediaElement(elt);
- } else {
- return new p5.Element(elt);
- }
- }
-
- /**
- * Removes all elements created by p5, except any canvas / graphics
- * elements created by createCanvas or createGraphics.
- * Event handlers are removed, and element is removed from the DOM.
- * @method removeElements
- * @example
- *
- * function setup() {
- * createCanvas(100, 100);
- * createDiv('this is some text');
- * createP('this is a paragraph');
- * }
- * function mousePressed() {
- * removeElements(); // this will remove the div and p, not canvas
- * }
- *
- *
- */
- p5.prototype.removeElements = function (e) {
- for (var i=0; i
- * var myDiv;
- * function setup() {
- * myDiv = createDiv('this is some text');
- * }
- *
- */
-
- /**
- * Creates a <p></p> element in the DOM with given inner HTML. Used
- * for paragraph length text.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createP
- * @param {String} html inner HTML for element created
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var myP;
- * function setup() {
- * myP = createP('this is some text');
- * }
- *
- */
-
- /**
- * Creates a <span></span> element in the DOM with given inner HTML.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createSpan
- * @param {String} html inner HTML for element created
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var mySpan;
- * function setup() {
- * mySpan = createSpan('this is some text');
- * }
- *
- */
- var tags = ['div', 'p', 'span'];
- tags.forEach(function(tag) {
- var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1);
- p5.prototype[method] = function(html) {
- var elt = document.createElement(tag);
- elt.innerHTML = typeof html === undefined ? "" : html;
- return addElement(elt, this);
- }
- });
-
- /**
- * Creates an <img> element in the DOM with given src and
- * alternate text.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createImg
- * @param {String} src src path or url for image
- * @param {String} [alt] alternate text to be used if image does not load
- * @param {Function} [successCallback] callback to be called once image data is loaded
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var img;
- * function setup() {
- * img = createImg('http://p5js.org/img/asterisk-01.png');
- * }
- *
- */
- p5.prototype.createImg = function() {
- var elt = document.createElement('img');
- var args = arguments;
- var self;
- var setAttrs = function(){
- self.width = elt.offsetWidth || elt.width;
- self.height = elt.offsetHeight || elt.height;
- if (args.length > 1 && typeof args[1] === 'function'){
- self.fn = args[1];
- self.fn();
- }else if (args.length > 1 && typeof args[2] === 'function'){
- self.fn = args[2];
- self.fn();
- }
- };
- elt.src = args[0];
- if (args.length > 1 && typeof args[1] === 'string'){
- elt.alt = args[1];
- }
- elt.onload = function(){
- setAttrs();
- }
- self = addElement(elt, this);
- return self;
- };
-
- /**
- * Creates an <a></a> element in the DOM for including a hyperlink.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createA
- * @param {String} href url of page to link to
- * @param {String} html inner html of link element to display
- * @param {String} [target] target where new link should open,
- * could be _blank, _self, _parent, _top.
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var myLink;
- * function setup() {
- * myLink = createA('http://p5js.org/', 'this is a link');
- * }
- *
- */
- p5.prototype.createA = function(href, html, target) {
- var elt = document.createElement('a');
- elt.href = href;
- elt.innerHTML = html;
- if (target) elt.target = target;
- return addElement(elt, this);
- };
-
- /** INPUT **/
-
-
- /**
- * Creates a slider <input></input> element in the DOM.
- * Use .size() to set the display length of the slider.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createSlider
- * @param {Number} min minimum value of the slider
- * @param {Number} max maximum value of the slider
- * @param {Number} [value] default value of the slider
- * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value)
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * var slider;
- * function setup() {
- * slider = createSlider(0, 255, 100);
- * slider.position(10, 10);
- * slider.style('width', '80px');
- * }
- *
- * function draw() {
- * var val = slider.value();
- * background(val);
- * }
- *
- */
- p5.prototype.createButton = function(label, value) {
- var elt = document.createElement('button');
- elt.innerHTML = label;
- elt.value = value;
- if (value) elt.value = value;
- return addElement(elt, this);
- };
-
- /**
- * Creates a checkbox <input></input> element in the DOM.
- * Calling .checked() on a checkbox returns if it is checked or not
- *
- * @method createCheckbox
- * @param {String} [label] label displayed after checkbox
- * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- */
- p5.prototype.createSelect = function(mult) {
- var elt = document.createElement('select');
- if (mult){
- elt.setAttribute('multiple', 'true');
- }
- var self = addElement(elt, this);
- self.option = function(name, value){
- var opt = document.createElement('option');
- opt.innerHTML = name;
- if (arguments.length > 1)
- opt.value = value;
- else
- opt.value = name;
- elt.appendChild(opt);
- };
- self.selected = function(value){
- var arr = [];
- if (arguments.length > 0){
- for (var i = 0; i < this.elt.length; i++){
- if (value.toString() === this.elt[i].value){
- this.elt.selectedIndex = i;
- }
- }
- return this;
- }else{
- if (mult){
- for (var i = 0; i < this.elt.selectedOptions.length; i++){
- arr.push(this.elt.selectedOptions[i].value);
- }
- return arr;
- }else{
- return this.elt.value;
- }
- }
- };
- return self;
- };
-
- /**
- * Creates a radio button <input></input> element in the DOM.
- * The .option() method can be used to set options for the radio after it is
- * created. The .value() method will return the currently selected option.
- *
- * @method createRadio
- * @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
- * @example
- *
- * var radio;
- *
- * function setup() {
- * radio = createRadio();
- * radio.option('apple', 1);
- * radio.option('bread', 2);
- * radio.option('juice', 3);
- * radio.style('width', '60px');
- * textAlign(CENTER);
- * }
- *
- * function draw() {
- * background(200);
- * var val = radio.value();
- * if (val) {
- * text('item cost is $'+val, width/2, height/2);
- * }
- * }
- *
- */
- p5.prototype.createRadio = function() {
- var radios = document.querySelectorAll("input[type=radio]");
- var count = 0;
- if(radios.length > 1){
- var length = radios.length;
- var prev=radios[0].name;
- var current = radios[1].name;
- count = 1;
- for(var i = 1; i < length; i++) {
- current = radios[i].name;
- if(prev != current){
- count++;
- }
- prev = current;
- }
- }
- else if (radios.length == 1){
- count = 1;
- }
- var elt = document.createElement('div');
- var self = addElement(elt, this);
- var times = -1;
- self.option = function(name, value){
- var opt = document.createElement('input');
- opt.type = 'radio';
- opt.innerHTML = name;
- if (arguments.length > 1)
- opt.value = value;
- else
- opt.value = name;
- opt.setAttribute('name',"defaultradio"+count);
- elt.appendChild(opt);
- if (name){
- times++;
- var ran = Math.random().toString(36).slice(2);
- var label = document.createElement('label');
- opt.setAttribute('id', "defaultradio"+count+"-"+times);
- label.htmlFor = "defaultradio"+count+"-"+times;
- label.appendChild(document.createTextNode(name));
- elt.appendChild(label);
- }
- return opt;
- };
- self.selected = function(){
- var length = this.elt.childNodes.length;
- if(arguments.length == 1) {
- for (var i = 0; i < length; i+=2){
- if(this.elt.childNodes[i].value == arguments[0])
- this.elt.childNodes[i].checked = true;
- }
- return this;
- } else {
- for (var i = 0; i < length; i+=2){
- if(this.elt.childNodes[i].checked == true)
- return this.elt.childNodes[i].value;
- }
- }
- };
- self.value = function(){
- var length = this.elt.childNodes.length;
- if(arguments.length == 1) {
- for (var i = 0; i < length; i+=2){
- if(this.elt.childNodes[i].value == arguments[0])
- this.elt.childNodes[i].checked = true;
- }
- return this;
- } else {
- for (var i = 0; i < length; i+=2){
- if(this.elt.childNodes[i].checked == true)
- return this.elt.childNodes[i].value;
- }
- return "";
- }
- };
- return self
- };
-
- /**
- * Creates an <input></input> element in the DOM for text input.
- * Use .size() to set the display length of the box.
- * Appends to the container node if one is specified, otherwise
- * appends to body.
- *
- * @method createInput
- * @param {Number} [value] default value of the input box
- * @param {String} [type] type of text, ie text, password etc. Defaults to text
- * @return {Object/p5.Element} pointer to p5.Element holding created node
- * @example
- *
- * function setup(){
- * var inp = createInput('');
- * inp.input(myInputEvent);
- * }
- *
- * function myInputEvent(){
- * console.log('you are typing: ', this.value());
- * }
- *
- *
- */
- p5.prototype.createInput = function(value, type) {
- var elt = document.createElement('input');
- elt.type = type ? type : 'text';
- if (value) elt.value = value;
- return addElement(elt, this);
- };
-
- /**
- * Creates an <input></input> element in the DOM of type 'file'.
- * This allows users to select local files for use in a sketch.
- *
- * @method createFileInput
- * @param {Function} [callback] callback function for when a file loaded
- * @param {String} [multiple] optional to allow multiple files selected
- * @return {Object/p5.Element} pointer to p5.Element holding created DOM element
- * @example
- * var input;
- * var img;
- *
- * function setup() {
- * input = createFileInput(handleFile);
- * input.position(0, 0);
- * }
- *
- * function draw() {
- * if (img) {
- * image(img, 0, 0, width, height);
- * }
- * }
- *
- * function handleFile(file) {
- * print(file);
- * if (file.type === 'image') {
- * img = createImg(file.data);
- * img.hide();
- * }
- * }
- */
- p5.prototype.createFileInput = function(callback, multiple) {
-
- // Is the file stuff supported?
- if (window.File && window.FileReader && window.FileList && window.Blob) {
- // Yup, we're ok and make an input file selector
- var elt = document.createElement('input');
- elt.type = 'file';
-
- // If we get a second argument that evaluates to true
- // then we are looking for multiple files
- if (multiple) {
- // Anything gets the job done
- elt.multiple = 'multiple';
- }
-
- // Function to handle when a file is selected
- // We're simplifying life and assuming that we always
- // want to load every selected file
- function handleFileSelect(evt) {
- // These are the files
- var files = evt.target.files;
- // Load each one and trigger a callback
- for (var i = 0; i < files.length; i++) {
- var f = files[i];
- var reader = new FileReader();
- function makeLoader(theFile) {
- // Making a p5.File object
- var p5file = new p5.File(theFile);
- return function(e) {
- p5file.data = e.target.result;
- callback(p5file);
- };
- };
- reader.onload = makeLoader(f);
-
- // Text or data?
- // This should likely be improved
- if (f.type.indexOf('text') > -1) {
- reader.readAsText(f);
- } else {
- reader.readAsDataURL(f);
- }
- }
- }
-
- // Now let's handle when a file was selected
- elt.addEventListener('change', handleFileSelect, false);
- return addElement(elt, this);
- } else {
- console.log('The File APIs are not fully supported in this browser. Cannot create element.');
- }
- };
-
-
- /** VIDEO STUFF **/
-
- function createMedia(pInst, type, src, callback) {
- var elt = document.createElement(type);
-
- // allow src to be empty
- var src = src || '';
- if (typeof src === 'string') {
- src = [src];
- }
- for (var i=0; ithis
- * page for further information about supported formats.
- *
- * @method createVideo
- * @param {String|Array} src path to a video file, or array of paths for
- * supporting different browsers
- * @param {Object} [callback] callback function to be called upon
- * 'canplaythrough' event fire, that is, when the
- * browser can play the media, and estimates that
- * enough data has been loaded to play the media
- * up to its end without having to stop for
- * further buffering of content
- * @return {Object/p5.Element} pointer to video p5.Element
- */
- p5.prototype.createVideo = function(src, callback) {
- return createMedia(this, 'video', src, callback);
- };
-
- /** AUDIO STUFF **/
-
- /**
- * Creates a hidden HTML5 <audio> element in the DOM for simple audio
- * playback. Appends to the container node if one is specified,
- * otherwise appends to body. The first parameter
- * can be either a single string path to a audio file, or an array of string
- * paths to different formats of the same audio. This is useful for ensuring
- * that your audio can play across different browsers, as each supports
- * different formats. See this
- * page for further information about supported formats.
- *
- * @method createAudio
- * @param {String|Array} src path to an audio file, or array of paths for
- * supporting different browsers
- * @param {Object} [callback] callback function to be called upon
- * 'canplaythrough' event fire, that is, when the
- * browser can play the media, and estimates that
- * enough data has been loaded to play the media
- * up to its end without having to stop for
- * further buffering of content
- * @return {Object/p5.Element} pointer to audio p5.Element
- */
- p5.prototype.createAudio = function(src, callback) {
- return createMedia(this, 'audio', src, callback);
- };
-
-
- /** CAMERA STUFF **/
-
- p5.prototype.VIDEO = 'video';
- p5.prototype.AUDIO = 'audio';
-
- navigator.getUserMedia = navigator.getUserMedia ||
- navigator.webkitGetUserMedia ||
- navigator.mozGetUserMedia ||
- navigator.msGetUserMedia;
-
- /**
- *
Creates a new <video> element that contains the audio/video feed
- * from a webcam. This can be drawn onto the canvas using video().
- *
More specific properties of the feed can be passing in a Constraints object.
- * See the
- * W3C
- * spec for possible properties. Note that not all of these are supported
- * by all browsers.
- *
Security note: A new browser security specification requires that getUserMedia,
- * which is behind createCapture(), only works when you're running the code locally,
- * or on HTTPS. Learn more here
- * and here.
- *
- * @method createCapture
- * @param {String|Constant|Object} type type of capture, either VIDEO or
- * AUDIO if none specified, default both,
- * or a Constraints object
- * @param {Function} callback function to be called once
- * stream has loaded
- * @return {Object/p5.Element} capture video p5.Element
- * @example
- *
- */
- p5.prototype.createCapture = function() {
- var useVideo = true;
- var useAudio = true;
- var constraints;
- var cb;
- for (var i=0; i
- * var h2 = createElement('h2','im an h2 p5.element!');
- *
- */
- p5.prototype.createElement = function(tag, content) {
- var elt = document.createElement(tag);
- if (typeof content !== 'undefined') {
- elt.innerHTML = content;
- }
- return addElement(elt, this);
- };
-
-
-// =============================================================================
-// p5.Element additions
-// =============================================================================
- /**
- *
- * Adds specified class to the element.
- *
- * @for p5.Element
- * @method addClass
- * @param {String} class name of class to add
- * @return {Object/p5.Element}
- * @example
- *
- * var div = createDiv('div');
- * div.addClass('myClass');
- *
- */
- p5.Element.prototype.addClass = function(c) {
- if (this.elt.className) {
- // PEND don't add class more than once
- //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
- //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
- this.elt.className = this.elt.className+' '+c;
- //}
- } else {
- this.elt.className = c;
- }
- return this;
- }
-
- /**
- *
- * Removes specified class from the element.
- *
- * @method removeClass
- * @param {String} class name of class to remove
- * @return {Object/p5.Element}
- */
- p5.Element.prototype.removeClass = function(c) {
- var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
- this.elt.className = this.elt.className.replace(regex, '');
- this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
- return this;
- }
-
- /**
- *
- * Attaches the element as a child to the parent specified.
- * Accepts either a string ID, DOM node, or p5.Element.
- * If no argument is specified, an array of children DOM nodes is returned.
- *
- * @method child
- * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
- * to add to the current element
- * @return {p5.Element}
- * @example
- *
- * var div0 = createDiv('this is the parent');
- * var div1 = createDiv('this is the child');
- * div0.child(div1); // use p5.Element
- *
- *
- * var div0 = createDiv('this is the parent');
- * var div1 = createDiv('this is the child');
- * div1.id('apples');
- * div0.child('apples'); // use id
- *
- *
- * var div0 = createDiv('this is the parent');
- * var elt = document.getElementById('myChildDiv');
- * div0.child(elt); // use element from page
- *
- */
- p5.Element.prototype.child = function(c) {
- if (typeof c === 'undefined'){
- return this.elt.childNodes
- }
- if (typeof c === 'string') {
- if (c[0] === '#') {
- c = c.substring(1);
- }
- c = document.getElementById(c);
- } else if (c instanceof p5.Element) {
- c = c.elt;
- }
- this.elt.appendChild(c);
- return this;
- };
-
- /**
- * Centers a p5 Element either vertically, horizontally,
- * or both, relative to its parent or according to
- * the body if the Element has no parent. If no argument is passed
- * the Element is aligned both vertically and horizontally.
- *
- * @param {String} align passing 'vertical', 'horizontal' aligns element accordingly
- * @return {Object/p5.Element} pointer to p5.Element
- * @example
- *
- * function setup() {
- * var div = createDiv('').size(10,10);
- * div.style('background-color','orange');
- * div.center();
- *
- * }
- *
- */
- p5.Element.prototype.center = function(align) {
- var style = this.elt.style.display;
- var hidden = this.elt.style.display === 'none';
- var parentHidden = this.parent().style.display === 'none';
- var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
-
- if (hidden) this.show();
-
- this.elt.style.display = 'block';
- this.position(0,0);
-
- if (parentHidden) this.parent().style.display = 'block';
-
- var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
- var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
- var y = pos.y;
- var x = pos.x;
-
- if (align === 'both' || align === undefined){
- this.position(wOffset/2, hOffset/2);
- }else if (align === 'horizontal'){
- this.position(wOffset/2, y);
- }else if (align === 'vertical'){
- this.position(x, hOffset/2);
- }
-
- this.style('display', style);
-
- if (hidden) this.hide();
-
- if (parentHidden) this.parent().style.display = 'none';
-
- return this;
- };
-
- /**
- *
- * If an argument is given, sets the inner HTML of the element,
- * replacing any existing html. If true is included as a second
- * argument, html is appended instead of replacing existing html.
- * If no arguments are given, returns
- * the inner HTML of the element.
- *
- * @for p5.Element
- * @method html
- * @param {String} [html] the HTML to be placed inside the element
- * @param {boolean} [append] whether to append HTML to existing
- * @return {Object/p5.Element|String}
- * @example
- *
- * var div = createDiv('').size(100,100);
- * div.html('hi');
- *
- *
- * var div = createDiv('Hello ').size(100,100);
- * div.html('World', true);
- *
- */
- p5.Element.prototype.html = function() {
- if (arguments.length === 0) {
- return this.elt.innerHTML;
- } else if (arguments[1]) {
- this.elt.innerHTML += arguments[0];
- return this;
- } else {
- this.elt.innerHTML = arguments[0];
- return this;
- }
- };
-
- /**
- *
- * Sets the position of the element relative to (0, 0) of the
- * window. Essentially, sets position:absolute and left and top
- * properties of style. If no arguments given returns the x and y position
- * of the element in an object.
- *
- * @method position
- * @param {Number} [x] x-position relative to upper left of window
- * @param {Number} [y] y-position relative to upper left of window
- * @return {Object/p5.Element}
- * @example
- *
- * function setup() {
- * var cnv = createCanvas(100, 100);
- * // positions canvas 50px to the right and 100px
- * // below upper left corner of the window
- * cnv.position(50, 100);
- * }
- *
- */
- p5.Element.prototype.position = function() {
- if (arguments.length === 0){
- return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
- }else{
- this.elt.style.position = 'absolute';
- this.elt.style.left = arguments[0]+'px';
- this.elt.style.top = arguments[1]+'px';
- this.x = arguments[0];
- this.y = arguments[1];
- return this;
- }
- };
-
- /* Helper method called by p5.Element.style() */
- p5.Element.prototype._translate = function(){
- this.elt.style.position = 'absolute';
- // 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)';
- } else if (arguments.length > 2) {
- this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
- if (arguments.length === 3) {
- this.elt.parentElement.style.perspective = '1000px';
- } else {
- this.elt.parentElement.style.perspective = arguments[3]+'px';
- }
- }
- // add any extra transform styling back on end
- this.elt.style.transform += transform;
- return this;
- };
-
- /* 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){
- this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
- }else if (arguments.length === 2){
- this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
- }else if (arguments.length === 3){
- this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
- this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
- this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
- }
- // add remaining transform back on
- this.elt.style.transform += transform;
- return this;
- };
-
- /**
- * Sets the given style (css) property (1st arg) of the element with the
- * given value (2nd arg). If a single argument is given, .style()
- * returns the value of the given property; however, if the single argument
- * is given in css syntax ('text-align:center'), .style() sets the css
- * appropriatly. .style() also handles 2d and 3d css transforms. If
- * the 1st arg is 'rotate', 'translate', or 'position', the following arguments
- * accept Numbers as values. ('translate', 10, 100, 50);
- *
- * @method style
- * @param {String} property property to be set
- * @param {String|Number|p5.Color} [value] value to assign to property
- * @param {String|Number} [value] value to assign to property (rotate/translate)
- * @param {String|Number} [value] value to assign to property (rotate/translate)
- * @param {String|Number} [value] value to assign to property (translate)
- * @return {String|Object/p5.Element} value of property, if no value is specified
- * or p5.Element
- * @example
- *
- * var myDiv = createDiv("I like pandas.");
- * myDiv.style("font-size", "18px");
- * myDiv.style("color", "#ff0000");
- *
- *
- * var col = color(25,23,200,50);
- * 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);
- *
- */
- p5.Element.prototype.style = function(prop, val) {
- var self = this;
-
- if (val instanceof p5.Color) {
- val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')'
- }
-
- if (typeof val === 'undefined') {
- if (prop.indexOf(':') === -1) {
- var styles = window.getComputedStyle(self.elt);
- var style = styles.getPropertyValue(prop);
- return style;
- } else {
- var attrs = prop.split(';');
- for (var i = 0; i < attrs.length; i++) {
- var parts = attrs[i].split(':');
- if (parts[0] && parts[1]) {
- this.elt.style[parts[0].trim()] = parts[1].trim();
- }
- }
- }
- } else {
- if (prop === 'rotate' || prop === 'translate' || prop === 'position'){
- var trans = Array.prototype.shift.apply(arguments);
- var f = this[trans] || this['_'+trans];
- f.apply(this, arguments);
- } else {
- this.elt.style[prop] = val;
- if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
- var numVal = val.replace(/\D+/g, '');
- this[prop] = parseInt(numVal, 10); // pend: is this necessary?
- }
- }
- }
- return this;
- };
-
-
- /**
- *
- * Adds a new attribute or changes the value of an existing attribute
- * on the specified element. If no value is specified, returns the
- * value of the given attribute, or null if attribute is not set.
- *
- * @method attribute
- * @param {String} attr attribute to set
- * @param {String} [value] value to assign to attribute
- * @return {String|Object/p5.Element} value of attribute, if no value is
- * specified or p5.Element
- * @example
- *
- * var myDiv = createDiv("I like pandas.");
- * myDiv.attribute("align", "center");
- *
- */
- p5.Element.prototype.removeAttribute = function(attr) {
- this.elt.removeAttribute(attr);
- return this;
- };
-
-
- /**
- * Either returns the value of the element if no arguments
- * given, or sets the value of the element.
- *
- * @method value
- * @param {String|Number} [value]
- * @return {String|Object/p5.Element} value of element if no value is specified or p5.Element
- * @example
- *
- * // 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('this is a div');
- * div.hide();
- *
- */
- p5.Element.prototype.hide = function() {
- this.elt.style.display = 'none';
- return this;
- };
-
- /**
- *
- * Sets the width and height of the element. AUTO can be used to
- * only adjust one dimension. If no arguments given returns the width and height
- * of the element in an object.
- *
- * @method size
- * @param {Number} [w] width of the element
- * @param {Number} [h] height of the element
- * @return {Object/p5.Element}
- * @example
- *
- * var div = createDiv('this is a div');
- * div.size(100, 100);
- *
- */
- p5.Element.prototype.size = function(w, h) {
- if (arguments.length === 0){
- return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
- }else{
- var aW = w;
- var aH = h;
- var AUTO = p5.prototype.AUTO;
- if (aW !== AUTO || aH !== AUTO) {
- if (aW === AUTO) {
- aW = h * this.width / this.height;
- } else if (aH === AUTO) {
- aH = w * this.height / this.width;
- }
- // set diff for cnv vs normal div
- if (this.elt instanceof HTMLCanvasElement) {
- var j = {};
- var k = this.elt.getContext('2d');
- for (var prop in k) {
- j[prop] = k[prop];
- }
- this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
- this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
- this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
- this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
- for (var prop in j) {
- this.elt.getContext('2d')[prop] = j[prop];
- }
- } else {
- this.elt.style.width = aW+'px';
- this.elt.style.height = aH+'px';
- this.elt.width = aW;
- this.elt.height = aH;
- this.width = aW;
- this.height = aH;
- }
-
- this.width = this.elt.offsetWidth;
- this.height = this.elt.offsetHeight;
-
- if (this._pInst) { // main canvas associated with p5 instance
- if (this._pInst._curElement.elt === this.elt) {
- this._pInst._setProperty('width', this.elt.offsetWidth);
- this._pInst._setProperty('height', this.elt.offsetHeight);
- }
- }
- }
- return this;
- }
- };
-
- /**
- * Removes the element and deregisters all listeners.
- * @method remove
- * @example
- *
- * var myDiv = createDiv('this is some text');
- * myDiv.remove();
- *
- */
- p5.Element.prototype.remove = function() {
- // deregister events
- for (var ev in this._events) {
- this.elt.removeEventListener(ev, this._events[ev]);
- }
- if (this.elt.parentNode) {
- this.elt.parentNode.removeChild(this.elt);
- }
- delete(this);
- };
-
-
-
-// =============================================================================
-// p5.MediaElement additions
-// =============================================================================
-
-
- /**
- * Extends p5.Element to handle audio and video. In addition to the methods
- * of p5.Element, it also contains methods for controlling media. It is not
- * called directly, but p5.MediaElements are created by calling createVideo,
- * createAudio, and createCapture.
- *
- * @class p5.MediaElement
- * @constructor
- * @param {String} elt DOM node that is wrapped
- * @param {Object} [pInst] pointer to p5 instance
- */
- p5.MediaElement = function(elt, pInst) {
- p5.Element.call(this, elt, pInst);
-
- var self = this;
- this.elt.crossOrigin = 'anonymous';
-
- this._prevTime = 0;
- this._cueIDCounter = 0;
- this._cues = [];
- this._pixelDensity = 1;
-
- /**
- * Path to the media element source.
- *
- * @property src
- * @return {String} src
- */
- Object.defineProperty(self, 'src', {
- get: function() {
- var firstChildSrc = self.elt.children[0].src;
- var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
- var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc;
- return ret;
- },
- set: function(newValue) {
- for (var i = 0; i < self.elt.children.length; i++) {
- self.elt.removeChild(self.elt.children[i]);
- }
- var source = document.createElement('source');
- source.src = newValue;
- elt.appendChild(source);
- self.elt.src = newValue;
- },
- });
-
- // private _onended callback, set by the method: onended(callback)
- self._onended = function() {};
- self.elt.onended = function() {
- self._onended(self);
- }
- };
- p5.MediaElement.prototype = Object.create(p5.Element.prototype);
-
-
-
-
- /**
- * Play an HTML5 media element.
- *
- * @method play
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.play = function() {
- if (this.elt.currentTime === this.elt.duration) {
- this.elt.currentTime = 0;
- }
-
- if (this.elt.readyState > 1) {
- this.elt.play();
- } else {
- // in Chrome, playback cannot resume after being stopped and must reload
- this.elt.load();
- this.elt.play();
- }
- return this;
- };
-
- /**
- * Stops an HTML5 media element (sets current time to zero).
- *
- * @method stop
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.stop = function() {
- this.elt.pause();
- this.elt.currentTime = 0;
- return this;
- };
-
- /**
- * Pauses an HTML5 media element.
- *
- * @method pause
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.pause = function() {
- this.elt.pause();
- return this;
- };
-
- /**
- * Set 'loop' to true for an HTML5 media element, and starts playing.
- *
- * @method loop
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.loop = function() {
- this.elt.setAttribute('loop', true);
- this.play();
- return this;
- };
- /**
- * Set 'loop' to false for an HTML5 media element. Element will stop
- * when it reaches the end.
- *
- * @method noLoop
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.noLoop = function() {
- this.elt.setAttribute('loop', false);
- return this;
- };
-
-
- /**
- * Set HTML5 media element to autoplay or not.
- *
- * @method autoplay
- * @param {Boolean} autoplay whether the element should autoplay
- * @return {Object/p5.Element}
- */
- p5.MediaElement.prototype.autoplay = function(val) {
- this.elt.setAttribute('autoplay', val);
- return this;
- };
-
- /**
- * Sets volume for this HTML5 media element. If no argument is given,
- * returns the current volume.
- *
- * @param {Number} [val] volume between 0.0 and 1.0
- * @return {Number|p5.MediaElement} current volume or p5.MediaElement
- * @method volume
- */
- p5.MediaElement.prototype.volume = function(val) {
- if (typeof val === 'undefined') {
- return this.elt.volume;
- } else {
- this.elt.volume = val;
- }
- };
-
- /**
- * If no arguments are given, returns the current playback speed of the
- * element. The speed parameter sets the speed where 2.0 will play the
- * element twice as fast, 0.5 will play at half the speed, and -1 will play
- * the element in normal speed in reverse.(Note that not all browsers support
- * backward playback and even if they do, playback might not be smooth.)
- *
- * @method speed
- * @param {Number} [speed] speed multiplier for element playback
- * @return {Number|Object/p5.MediaElement} current playback speed or p5.MediaElement
- */
- p5.MediaElement.prototype.speed = function(val) {
- if (typeof val === 'undefined') {
- return this.elt.playbackRate;
- } else {
- this.elt.playbackRate = val;
- }
- };
-
- /**
- * If no arguments are given, returns the current time of the element.
- * If an argument is given the current time of the element is set to it.
- *
- * @method time
- * @param {Number} [time] time to jump to (in seconds)
- * @return {Number|Object/p5.MediaElement} current time (in seconds)
- * or p5.MediaElement
- */
- p5.MediaElement.prototype.time = function(val) {
- if (typeof val === 'undefined') {
- return this.elt.currentTime;
- } else {
- this.elt.currentTime = val;
- }
- };
-
- /**
- * Returns the duration of the HTML5 media element.
- *
- * @method duration
- * @return {Number} duration
- */
- p5.MediaElement.prototype.duration = function() {
- return this.elt.duration;
- };
- p5.MediaElement.prototype.pixels = [];
- p5.MediaElement.prototype.loadPixels = function() {
- if (!this.canvas) {
- this.canvas = document.createElement('canvas');
- this.drawingContext = this.canvas.getContext('2d');
- }
- if (this.loadedmetadata) { // wait for metadata for w/h
- if (this.canvas.width !== this.elt.width) {
- this.canvas.width = this.elt.width;
- this.canvas.height = this.elt.height;
- this.width = this.canvas.width;
- this.height = this.canvas.height;
- }
- this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
- p5.Renderer2D.prototype.loadPixels.call(this);
- }
- return this;
- }
- p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
- if (this.loadedmetadata) { // wait for metadata
- p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
- }
- return this;
- }
- p5.MediaElement.prototype.get = function(x, y, w, h){
- if (this.loadedmetadata) { // wait for metadata
- return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
- } else if (typeof x === 'undefined') {
- return new p5.Image(1, 1);
- } else if (w > 1) {
- return new p5.Image(x, y, w, h);
- } else {
- return [0, 0, 0, 255];
- }
- };
- p5.MediaElement.prototype.set = function(x, y, imgOrCol){
- if (this.loadedmetadata) { // wait for metadata
- p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
- }
- };
- p5.MediaElement.prototype.copy = function(){
- p5.Renderer2D.prototype.copy.apply(this, arguments);
- };
- p5.MediaElement.prototype.mask = function(){
- this.loadPixels();
- p5.Image.prototype.mask.apply(this, arguments);
- };
- /**
- * Schedule an event to be called when the audio or video
- * element reaches the end. If the element is looping,
- * this will not be called. The element is passed in
- * as the argument to the onended callback.
- *
- * @method onended
- * @param {Function} callback function to call when the
- * soundfile has ended. The
- * media element will be passed
- * in as the argument to the
- * callback.
- * @return {Object/p5.MediaElement}
- * @example
- *
- */
- p5.MediaElement.prototype.onended = function(callback) {
- this._onended = callback;
- return this;
- };
-
-
- /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
-
- /**
- * Send the audio output of this element to a specified audioNode or
- * p5.sound object. If no element is provided, connects to p5's master
- * output. That connection is established when this method is first called.
- * All connections are removed by the .disconnect() method.
- *
- * This method is meant to be used with the p5.sound.js addon library.
- *
- * @method connect
- * @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API,
- * or an object from the p5.sound library
- */
- p5.MediaElement.prototype.connect = function(obj) {
- var audioContext, masterOutput;
-
- // if p5.sound exists, same audio context
- if (typeof p5.prototype.getAudioContext === 'function') {
- audioContext = p5.prototype.getAudioContext();
- masterOutput = p5.soundOut.input;
- } else {
- try {
- audioContext = obj.context;
- masterOutput = audioContext.destination
- } catch(e) {
- throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
- }
- }
-
- // create a Web Audio MediaElementAudioSourceNode if none already exists
- if (!this.audioSourceNode) {
- this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
-
- // connect to master output when this method is first called
- this.audioSourceNode.connect(masterOutput);
- }
-
- // connect to object if provided
- if (obj) {
- if (obj.input) {
- this.audioSourceNode.connect(obj.input);
- } else {
- this.audioSourceNode.connect(obj);
- }
- }
-
- // otherwise connect to master output of p5.sound / AudioContext
- else {
- this.audioSourceNode.connect(masterOutput);
- }
-
- };
-
- /**
- * Disconnect all Web Audio routing, including to master output.
- * This is useful if you want to re-route the output through
- * audio effects, for example.
- *
- * @method disconnect
- */
- p5.MediaElement.prototype.disconnect = function() {
- if (this.audioSourceNode) {
- this.audioSourceNode.disconnect();
- } else {
- throw 'nothing to disconnect';
- }
- };
-
-
- /*** SHOW / HIDE CONTROLS ***/
-
- /**
- * Show the default MediaElement controls, as determined by the web browser.
- *
- * @method showControls
- */
- p5.MediaElement.prototype.showControls = function() {
- // must set style for the element to show on the page
- this.elt.style['text-align'] = 'inherit';
- this.elt.controls = true;
- };
-
- /**
- * Hide the default mediaElement controls.
- *
- * @method hideControls
- */
- p5.MediaElement.prototype.hideControls = function() {
- this.elt.controls = false;
- };
-
- /*** SCHEDULE EVENTS ***/
-
- /**
- * Schedule events to trigger every time a MediaElement
- * (audio/video) reaches a playback cue point.
- *
- * Accepts a callback function, a time (in seconds) at which to trigger
- * the callback, and an optional parameter for the callback.
- *
- * Time will be passed as the first parameter to the callback function,
- * and param will be the second parameter.
- *
- *
- * @method addCue
- * @param {Number} time Time in seconds, relative to this media
- * element's playback. For example, to trigger
- * an event every time playback reaches two
- * seconds, pass in the number 2. This will be
- * passed as the first parameter to
- * the callback function.
- * @param {Function} callback Name of a function that will be
- * called at the given time. The callback will
- * receive time and (optionally) param as its
- * two parameters.
- * @param {Object} [value] An object to be passed as the
- * second parameter to the
- * callback function.
- * @return {Number} id ID of this cue,
- * useful for removeCue(id)
- * @example
- *