UNPKG

gibber.lib

Version:

standalone libraries from Gibber to include in web pages

1,579 lines (1,324 loc) 1.68 MB
!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Gibber=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ (function (global){ !function (root, factory) { if (typeof define === "function" && define.amd) { define([], factory); } else if (typeof exports === "object") { module.exports = factory(); } else { root.Gibberish = factory(); } }(this, function () { /**#Gibberish - Miscellaneous Gibberish is the main object used to manage the audio graph and perform codegen functions. All constructors are also inside of the Gibberish object. Gibberish can automatically generate an appropriate web audio callback for you; if you want to use this you must execute the Gibberish.init() command before creating any Gibberish ugens. ## Example Usage## `// make a sine wave Gibberish.init(); a = new Gibberish.Sine().connect();` ## Constructor **param** *bufferSize*: Integer. Default 1024. The size of the buffer to be calculated. Since JavaScript is single-threaded, setting exceedingly large values for this will yield to stuttering in graphics and user interface performance. - - - - **/ /**###Gibberish.audioFiles : property Array. Anytime an audiofile is loaded (normally using the Sampler ugen) the resulting sample buffer is stored in this array so that it can be immediately recalled. **/ /**###Gibberish.callback : property String. Whenever Gibberish performs code generation the resulting callback is stored here. **/ /**###Gibberish.out : property Object. The is the 'master' bus that everything eventually gets routed to if you're using the auto-generated calback. This bus is initialized in the call to Gibberish.init. **/ /**###Gibberish.dirtied : property Array. A list of objects that need to be codegen'd **/ /**###Gibberish.isDirty : property Booelan. Whether or codegen should be performed. **/ /**###Gibberish.codeblock : property Array. During codegen, each ugen's codeblock is inserted into this array. Once all the ugens have codegen'd, the array is concatenated to form the callback. **/ /**###Gibberish.upvalues : property Array. Each ugen's callback function is stored in this array; the contents of the array become upvalues to the master callback function when it is codegen'd. **/ /**###Gibberish.debug : property Boolean. Default false. When true, the callbackString is printed to the console whenever a codegen is performed **/ /**###Gibberish.memo : property Object. Used in the codegen process to make sure codegen for each ugen is only performed once. **/ var Gibberish = { memo : {}, codeblock : [], analysisCodeblock : [], analysisUgens : [], dirtied : [], id : 0, isDirty : false, // whether or not callback needs to codegen'd out : null, // main output bus debug : false, callback : '', audioFiles : {}, sequencers : [], callbackArgs : ['input'], // names of function arguments for main audio callback callbackObjects : [], // ugen function callbacks used in main audio callback analysisCallbackArgs : [], analysisCallbackObjects : [], onBlock: null, /**###Gibberish.createCallback : method Perform codegen on all dirty ugens and re-create the audio callback. This method is called automatically in the default Gibberish sample loop whenever Gibberish.isDirty is true. **/ createCallback : function() { this.memo = {}; this.codeblock.length = 0; this.callbackArgs.length = 0; this.callbackObjects.length = 0; this.analysisCallbackArgs.length = 0; /* generate code for dirty ugens */ /*for(var i = 0; i < this.dirtied.length; i++) { this.dirtied[i].codegen(); }*/ this.dirtied.length = 0; this.codestring = '' this.args = ['input'] this.memo = {}; this.out.codegen() var codeblockStore = this.codeblock.slice(0) // we must push these here because the callback arguments are at the start of the string, // but we have to wait to codegen the analysis ugens until after their targets have been codegen'd if(this.analysisUgens.length > 0) { this.analysisCodeblock.length = 0; for(var i = 0; i < this.analysisUgens.length; i++) { this.analysisCallbackArgs.push( this.analysisUgens[i].analysisSymbol ) } } this.args = this.args.concat( this.callbackArgs ) this.args = this.args.concat( this.analysisCallbackArgs ) /* concatenate code for all ugens */ //this.memo = {}; this.codestring += codeblockStore.join('\t') //this.codeblock.join("\t"); this.codestring += "\n\t"; /* analysis codeblock */ if(this.analysisUgens.length > 0) { this.analysisCodeblock.length = 0; for(var i = 0; i < this.analysisUgens.length; i++) { this.codeblock.length = 0; this.analysisUgens[i].analysisCodegen(); /* if(this.codestring !== 'undefined' ) { this.codestring += this.codeblock.join(""); this.codestring += "\n\t"; this.analysisCodeblock.push ( this.analysisUgens[i].analysisCodegen() ); } */ } this.codestring += this.analysisCodeblock.join('\n\t'); this.codestring += '\n\t'; } this.codestring += 'return ' + this.out.variable +';\n'; this.callbackString = this.codestring; if( this.debug ) console.log( this.callbackString ); return [this.args, this.codestring]; }, /**###Gibberish.audioProcess : method The default audio callback used in Webkit browsers. This callback starts running as soon as Gibberish.init() is called. param **Audio Event** : Object. The HTML5 audio event object. **/ audioProcess : function(e){ var bufferL = e.outputBuffer.getChannelData(0), bufferR = e.outputBuffer.getChannelData(1), input = e.inputBuffer.getChannelData(0), me = Gibberish, callback = me.callback, sequencers = me.sequencers, out = Gibberish.out.callback, objs = me.callbackObjects.slice(0), callbackArgs, callbackBody, _callback, val if( me.onBlock !== null ) me.onBlock( me.context ) objs.unshift(0) for(var i = 0, _bl = e.outputBuffer.length; i < _bl; i++){ for(var j = 0; j < sequencers.length; j++) { sequencers[j].tick(); } if(me.isDirty) { _callback = me.createCallback(); try{ callback = me.callback = new Function( _callback[0], _callback[1] ) }catch( e ) { console.error( "ERROR WITH CALLBACK : \n\n", _callback ) } me.isDirty = false; objs = me.callbackObjects.slice(0) objs.unshift(0) } //console.log( "CB", callback ) objs[0] = input[i] val = callback.apply( null, objs ); bufferL[i] = val[0]; bufferR[i] = val[1]; } }, /**###Gibberish.audioProcessFirefox : method The default audio callback used in Firefox. This callback starts running as soon as Gibberish.init() is called. param **Sound Data** : Object. The buffer of audio data to be filled **/ audioProcessFirefox : function(soundData) { // callback for firefox var me = Gibberish, callback = me.callback, sequencers = me.sequencers, objs = me.callbackObjects.slice(0), _callback objs.unshift(0) for (var i=0, size=soundData.length; i<size; i+=2) { for(var j = 0; j < sequencers.length; j++) { sequencers[j].tick(); } if(me.isDirty) { _callback = me.createCallback(); try { callback = me.callback = new Function( _callback[0], _callback[1] ) }catch( e ) { console.error( 'ERROR WITH CALLBACK : \n\n', callback ) } me.isDirty = false; objs = me.callbackObjects.slice(0) objs.unshift(0) } var val = callback.apply(null, objs); soundData[i] = val[0]; soundData[i+1] = val[1]; } }, /**###Gibberish.clear : method Remove all objects from Gibberish graph and perform codegen... kills all running sound and CPU usage. **/ clear : function() { this.out.inputs.length = 0; this.analysisUgens.length = 0; this.sequencers.length = 0; this.callbackArgs.length = 2 this.callbackObjects.length = 1 Gibberish.dirty(this.out); }, /**###Gibberish.dirty : method Tell Gibberish a ugen needs to be codegen'd and mark the entire callback as needing regeneration param **Ugen** : Object. The ugen that is 'dirtied'... that has a property value changed. **/ dirty : function(ugen) { if(typeof ugen !== 'undefined') { var found = false; for(var i = 0; i < this.dirtied.length; i++) { if(this.dirtied[i].variable === ugen.variable) found = true; } if(!found) { this.isDirty = true; this.dirtied.push(ugen); } }else{ this.isDirty = true; } }, /**###Gibberish.generateSymbol : method Generate a unique symbol for a given ugen using its name and a unique id number. param **name** : String. The name of the ugen; for example, reverb, delay etc. **/ generateSymbol : function(name) { return name + "_" + this.id++; }, // as taken from here: https://wiki.mozilla.org/Audio_Data_API#Standardization_Note // only the number of channels is changed in the audio.mozSetup() call /**###Gibberish.AudioDataDestination : method Used to generate callback for Firefox. param **sampleRate** : String. The sampleRate for the audio callback to run at. NOT THE BUFFER SIZE. param **readFn** : Function. The audio callback to use. **/ AudioDataDestination : function(sampleRate, readFn) { // for Firefox Audio Data API // Initialize the audio output. var audio = new Audio(); audio.mozSetup(2, sampleRate); var currentWritePosition = 0; var prebufferSize = sampleRate / 2; // buffer 500ms var tail = null, tailPosition; // The function called with regular interval to populate // the audio output buffer. setInterval(function() { var written; // Check if some data was not written in previous attempts. if(tail) { written = audio.mozWriteAudio(tail.subarray(tailPosition)); currentWritePosition += written; tailPosition += written; if(tailPosition < tail.length) { // Not all the data was written, saving the tail... return; // ... and exit the function. } tail = null; } // Check if we need add some data to the audio output. var currentPosition = audio.mozCurrentSampleOffset(); var available = currentPosition + prebufferSize - currentWritePosition; if(available > 0) { // Request some sound data from the callback function. var soundData = new Float32Array(available); readFn(soundData); // Writting the data. written = audio.mozWriteAudio(soundData); currentPosition = audio.mozCurrentSampleOffset(); if(written < soundData.length) { // Not all the data was written, saving the tail. tail = soundData; tailPosition = written; } currentWritePosition += written; } }, 100); }, /**###Gibberish.AudioDataDestination : method Create a callback and start it running. Note that in iOS audio callbacks can only be created in response to user events. Thus, in iOS this method assigns an event handler to the HTML body that creates the callback as soon as the body is touched; at that point the event handler is removed. **/ init : function() { // TODO: GET A BETTER TEST FOR THIS. The problem is that browserify adds a process object... not sure how robust // testing for the presence of the version property will be var isNode = typeof global !== 'undefined', bufferSize = typeof arguments[0] === 'undefined' ? 1024 : arguments[0], audioContext, start if( typeof webkitAudioContext !== 'undefined' ) { audioContext = webkitAudioContext }else if ( typeof AudioContext !== 'undefined' ) { audioContext = AudioContext } // we will potentially delay start of audio until touch of screen for iOS devices start = function() { if( typeof audioContext !== 'undefined' ) { if( document && document.documentElement && 'ontouchstart' in document.documentElement ) { window.removeEventListener('touchstart', start); if('ontouchstart' in document.documentElement){ // required to start audio under iOS 6 var mySource = Gibberish.context.createBufferSource(); mySource.connect(Gibberish.context.destination); mySource.noteOn(0); } } }else{ alert('Your browser does not support javascript audio synthesis. Please download a modern web browser that is not Internet Explorer.') } if( Gibberish.onstart ) Gibberish.onstart() } Gibberish.context = new audioContext(); Gibberish.node = Gibberish.context.createScriptProcessor(bufferSize, 2, 2, Gibberish.context.sampleRate); Gibberish.node.onaudioprocess = Gibberish.audioProcess; Gibberish.node.connect(Gibberish.context.destination); Gibberish.out = new Gibberish.Bus2(); Gibberish.out.codegen(); // make sure bus is first upvalue so that clearing works correctly Gibberish.dirty(Gibberish.out); if( document && document.documentElement && 'ontouchstart' in document.documentElement ) { window.addEventListener('touchstart', start); }else{ start(); } return this; }, /**###Gibberish.makePanner : method Create and return an object that can be used to pan a stereo source. **/ // makePanner : function() { // var sin = Math.sin; // var cos = Math.cos; // var sqrtTwoOverTwo = Math.sqrt(2) / 2; // // var f = function(val, pan, array) { // var isObject = typeof val === 'object'; // var l = isObject ? val[0] : val; // var r = isObject ? val[1] : val; // // array[0] = l * (sqrtTwoOverTwo * (cos(pan) - sin(pan)) ); // array[1] = r * (sqrtTwoOverTwo * (cos(pan) + sin(pan)) ); // // return array; // }; // // return f; // }, makePanner : function() { // thanks to grrrwaaa for this // create pan curve arrays (once-only): var panTableL = [], panTableR = []; var sqrtTwoOverTwo = Math.sqrt(2) / 2; for( var i = 0; i < 1024; i++ ) { var pan = -1 + ( i / 1024 ) * 2; panTableL[i] = (sqrtTwoOverTwo * (Math.cos(pan) - Math.sin(pan)) ); panTableR[i] = (sqrtTwoOverTwo * (Math.cos(pan) + Math.sin(pan)) ); } return function(val, pan, output) { var isObject = typeof val === 'object', l = isObject ? val[0] : val, r = isObject ? val[1] : val, _index, index, frac, index2, val1, val2; _index = ((pan + 1) * 1023) / 2 index = _index | 0 frac = _index - index; index = index & 1023; index2 = index === 1023 ? 0 : index + 1; val1 = panTableL[index]; val2 = panTableL[index2]; output[0] = ( val1 + ( frac * (val2 - val1) ) ) * l; val1 = panTableR[index]; val2 = panTableR[index2]; output[1] = ( val1 + ( frac * (val2 - val1) ) ) * r; return output; } }, // IMPORTANT: REMEMBER THIS IS OVERRIDDEN IN GIBBER defineUgenProperty : function(key, initValue, obj) { var prop = obj.properties[key] = { value: initValue, binops: [], parent : obj, name : key, }; Object.defineProperty(obj, key, { configurable: true, get: function() { return prop.value }, set: function(val) { prop.value = val; Gibberish.dirty(obj); }, }); }, /**###Gibberish.polyInit : method For ugens with polyphony, add metaprogramming that passes on property changes to the 'children' of the polyphonic object. Polyphonic ugens in Gibberish are just single instances that are routed into a shared bus, along with a few special methods for voice allocation etc. param **Ugen** : Object. The polyphonic ugen **/ polyInit : function(ugen) { ugen.mod = ugen.polyMod; ugen.removeMod = ugen.removePolyMod; ugen.voicesClear = function() { if( ugen.children.length > 0 ) { for( var i = 0; i < ugen.children.length; i++ ) { ugen.children[ i ].disconnect() } ugen.children.length = 0 ugen.voiceCount = 0 } } for(var key in ugen.polyProperties) { (function(_key) { var value = ugen.polyProperties[_key]; Object.defineProperty(ugen, _key, { configurable: true, get : function() { return value; }, set : function(val) { value = val; for(var i = 0; i < ugen.children.length; i++) { ugen.children[i][_key] = value; } }, }); })(key); } var maxVoices = ugen.maxVoices Object.defineProperty( ugen, 'maxVoices', { get: function() { return maxVoices }, set: function(v) { maxVoices = v; this.voicesClear(); this.initVoices() } }) }, /**###Gibberish.interpolate : method Similiar to makePanner, this method returns a function that can be used to linearly interpolate between to values. The resulting function takes an array and a floating point position index and returns a value. **/ interpolate : function(arr, phase){ var index = phase | 0, // round down index2 = index + 1 > arr.length - 1 ? 0 : index + 1; frac = phase - index; return arr[index] + frac * (arr[index2] - arr[index]); }, pushUnique : function(item, array) { var obj = item; var shouldAdd = true; for(var j = 0; j < array.length; j++) { if(obj === array[j]) { shouldAdd = false; break; } } if(shouldAdd) { array.push(obj); } }, export : function(key, obj) { for(var _key in Gibberish[key]) { //console.log("exporting", _key, "from", key); obj[_key] = Gibberish[key][_key]; } }, /**###Gibberish.ugen : method Creates a prototype object that is used by all ugens. **/ ugen : function() { Gibberish.extend(this, { /**#Ugen - Miscellaneous The prototype object that all ugens inherit from **/ /**###Ugen.processProperties : method Used to assign and process arguments passed to the constructor functions of ugens. param **argumentList** : Array. A list of arguments (may be a single dictionary) passed to a ugen constructor. **/ processProperties : function(args){ if(typeof arguments[0][0] === 'object' && typeof arguments[0][0].type === 'undefined' && !Array.isArray(arguments[0][0]) && arguments[0][0].name !== 'op') { var dict = arguments[0][0]; for(var key in dict) { if(typeof dict[key] !== 'undefined') { if(typeof this.properties[key] === 'object' && typeof this.properties[key].binops !== 'undefined') { this.properties[key].value = dict[key]; }else{ this[key] = dict[key]; } } } }else{ var i = 0; for(var key in this.properties) { if(typeof this.properties[key] === 'object' && typeof this.properties[key].binops !== 'undefined') { if(typeof arguments[0][i] !== 'undefined'){ this.properties[key].value = arguments[0][i++]; } }else{ if(typeof arguments[0][i] !== 'undefined') { this.properties[key] = arguments[0][i++]; } } } } return this; }, valueOf: function() { this.codegen() //console.log( "VALUEOF", this.variable ) return this.variable }, /**###Ugen.codegen : method Generates output code (as a string) used inside audio callback **/ codegen : function() { var s = '', v = null, initialized = false; if(Gibberish.memo[this.symbol]) { //console.log("MEMO" + this.symbol); return Gibberish.memo[this.symbol]; }else{ // we generate the symbol and use it to create our codeblock, but only if the ugen doesn't already have a variable assigned. // since the memo is cleared every time the callback is created, we need to check to see if this exists. v = this.variable ? this.variable : Gibberish.generateSymbol('v'); Gibberish.memo[this.symbol] = v; this.variable = v; } s += 'var ' + v + " = " + this.symbol + "("; for(var key in this.properties) { var property = this.properties[key]; var value = ''; //if(this.name === "single_sample_delay") { console.log( "SSD PROP" + key ); } if( Array.isArray( property.value ) ) { if(property.value.length === 0) value = 0; // primarily for busses for(var i = 0; i < property.value.length; i++) { var member = property.value[i]; if( typeof member === 'object' ) { value += member !== null ? member.valueOf() : 'null'; }else{ if(typeof property.value === 'function') { value += property.value(); }else{ value += property.value; } } value += i < property.value.length - 1 ? ', ' : ''; } }else if( typeof property.value === 'object' ) { if( property.value !== null) { value = property.value.codegen ? property.value.valueOf() : property.value } }else if( property.name !== 'undefined'){ if(typeof property.value === 'function') { value = property.value(); }else{ value = property.value; } } if(property.binops.length != 0) { for( var k = 0; k < property.binops.length; k++) { s += '(' // all leading parenthesis... } for(var j = 0; j < property.binops.length; j++) { var op = property.binops[j], val; if( typeof op.ugen === 'number') { val = op.ugen; }else{ val = op.ugen !== null ? op.ugen.valueOf() : 'null'; } if(op.binop === "=") { s = s.replace(value, ""); s += val; }else if(op.binop === "++"){ s += ' + Math.abs(' + val + ')'; }else{ if( j === 0) s+= value s += " " + op.binop + " " + val + ")"; } } }else{ s += value } s += ", "; } if(s.charAt(s.length - 1) === " ") s = s.slice(0, -2); // remove trailing spaces s += ");\n"; this.codeblock = s; if( Gibberish.codeblock.indexOf( this.codeblock ) === -1 ) Gibberish.codeblock.push( this.codeblock ) if( Gibberish.callbackArgs.indexOf( this.symbol ) === -1 && this.name !== 'op') { Gibberish.callbackArgs.push( this.symbol ) } if( Gibberish.callbackObjects.indexOf( this.callback ) === -1 && this.name !== 'op' ) { Gibberish.callbackObjects.push( this.callback ) } this.dirty = false; return v; }, /**###Ugen.defineUgenProperty : method Creates getters and setters for ugen properties that automatically dirty the ugen whenever the property value is changed. param **key** : String. The name of a property to add getter / setters for. param **value** : Any. The initival value to set the property to **/ /**###Ugen.init : method Initialize ugen by calling defineUgenProperty for every key in the ugen's properties dictionary, generating a unique id for the ugen and various other small tasks. **/ init : function() { if(!this.initalized) { this.symbol = Gibberish.generateSymbol(this.name); this.codeblock = null; this.variable = null; } if(typeof this.properties === 'undefined') { this.properties = {}; } if(!this.initialized) { this.destinations = []; for(var key in this.properties) { Gibberish.defineUgenProperty(key, this.properties[key], this); } } if(arguments.length > 0 && typeof arguments[0][0] === 'object' && arguments[0][0].type === 'undefined') { var options = arguments[0][0]; for(var key in options) { this[key] = options[key]; } } this.initialized = true; return this; }, /**###Ugen.mod : method Modulate a property of a ugen on a per-sample basis. param **key** : String. The name of the property to modulate param **value** : Any. The object or number value to modulate the property with param **op** : String. Default "+". The operation to perform. Can be +,-,*,/,= or ++. ++ adds and returns the absolute value. **/ mod : function(name, value, op) { var property = this.properties[ name ]; var mod = { ugen:value, binop:op }; property.binops.push( mod ); Gibberish.dirty( this ); }, /**###Ugen.removeMod : method Remove a modulation from a ugen. param **key** : String. The name of the property to remove the modulation from param **arg** : Number or Object. Optional. This determines which modulation to remove if more than one are assigned to the property. If this argument is undefined, all modulations are removed. If the argument is a number, the number represents a modulation in the order that they were applied (an array index). If the argument is an object, it removes a modulation that is using a matching object as the modulator. **/ removeMod : function(name, arg) { if(typeof arg === 'undefined' ) { this.properties[name].binops.length = 0; }else if(typeof arg === 'number') { this.properties[name].binops.splice(arg, 1); }else if(typeof arg === 'object') { for(var i = 0, j = this.properties[name].binops.length; i < j; i++) { if(this.properties[name].binops[i].ugen === arg) { this.properties[name].binops.splice(i, 1); } } }; Gibberish.dirty( this ); }, /**###Ugen.polyMod : method Applies a modulation to all children of a polyphonic ugen param **key** : String. The name of the property to modulate param **value** : Any. The object or number value to modulate the property with param **op** : String. Default "+". The operation to perform. Can be +,-,*,/,= or ++. ++ adds and returns the absolute value. **/ polyMod : function(name, modulator, type) { for(var i = 0; i < this.children.length; i++) { this.children[i].mod(name, modulator, type); } Gibberish.dirty(this); }, /**###Ugen.removePolyMod : method Removes a modulation from all children of a polyphonic ugen. The arguments param **arg** : Number or Object. Optional. This determines which modulation to remove if more than one are assigned to the property. If this argument is undefined, all modulations are removed. If the argument is a number, the number represents a modulation in the order that they were applied (an array index). If the argument is an object, it removes a modulation that is using a matching object as the modulator. **/ removePolyMod : function() { var args = Array.prototype.slice.call(arguments, 0); if(arguments[0] !== "amp" && arguments[0] !== "pan") { for(var i = 0; i < this.children.length; i++) { this.children[i].removeMod.apply(this.children[i], args); } }else{ this.removeMod.apply(this, args); } Gibberish.dirty(this); }, smooth : function(property, amount) { var op = new Gibberish.OnePole(); this.mod(property, op, "="); }, /**###Ugen.connect : method Connect the output of a ugen to a bus. param **bus** : Bus ugen. Optional. The bus to connect the ugen to. If no argument is passed the ugen is connect to Gibberish.out. Gibberish.out is automatically created when Gibberish.init() is called and can be thought of as the master stereo output for Gibberish. **/ connect : function(bus, position) { if(typeof bus === 'undefined') bus = Gibberish.out; if(this.destinations.indexOf(bus) === -1 ){ bus.addConnection( this, 1, position ); this.destinations.push( bus ); } return this; }, /**###Ugen.send : method Send an arbitrary amount of output to a bus param **bus** : Bus ugen. The bus to send the ugen to. param **amount** : Float. The amount of signal to send to the bus. **/ send : function(bus, amount) { if(this.destinations.indexOf(bus) === -1 ){ bus.addConnection( this, amount ); this.destinations.push( bus ); }else{ bus.adjustSendAmount(this, amount); } return this; }, /**###Ugen.disconnect : method Disconnect a ugen from a bus (or all busses). This stops all audio and signal processing for the ugen. param **bus** : Bus ugen. Optional. The bus to disconnect the ugen from. If this argument is undefined the ugen will be disconnected from all busses. **/ disconnect : function(bus, tempDisconnect ) { // tempDisconnect is used to do a short disconnect and reconnect var idx if( !tempDisconnect ) { /*if( this.children ) { for(var i = 0; i < this.children.length; i++) { this.children[i].disconnect( this ) } }else if( typeof this.input === 'object' ) { this.input.disconnect( null, tempDisconnect ) }*/ /*var idx = Gibberish.callbackArgs.indexOf( this.symbol ) Gibberish.callbackArgs.splice(idx, 1) idx = Gibberish.callbackObjects.indexOf( this.callback ) Gibberish.callbackObjects.splice(idx, 1)*/ } if( !bus ) { for(var i = 0; i < this.destinations.length; i++) { this.destinations[i].removeConnection( this ); } this.destinations = []; }else{ idx = this.destinations.indexOf(bus); if(idx > -1) { this.destinations.splice(idx, 1); } bus.removeConnection( this ); } Gibberish.dirty( this ) return this; }, }); }, }; Array2 = function() { this.length = 0; }; Array2.prototype = []; Array2.prototype.remove = function(arg, searchDeep) { // searchDeep when true removes -all- matches, when false returns first one found. searchDeep = typeof searchDeep === 'undefined' ? true : searchDeep; if(typeof arg === "undefined") { // clear all for(var i = 0; i < this.length; i++) { delete this[i]; } this.length = 0; }else if(typeof arg === "number") { this.splice(arg,1); }else if(typeof arg === "string"){ // find named member and remove var removeMe = []; for(var i = 0; i < this.length; i++) { var member = this[i]; if(member.type === arg || member.name === arg) { if(!searchDeep) { this.splice(i,1); return; }else{ removeMe.push(i); } } } for(var i = 0; i < removeMe.length; i++) { this.splice( removeMe[i], 1); } }else if(typeof arg === "object") { var idx = this.indexOf(arg); while(idx > -1) { this.splice(idx,1); idx = this.indexOf(arg); } } if(this.parent) Gibberish.dirty(this.parent); }; Array2.prototype.get = function(arg) { if(typeof arg === "number") { return this[arg]; }else if(typeof arg === "string"){ // find named member and remove for(var i = 0; i < this.length; i++) { var member = this[i]; if(member.name === arg) { return member; } } }else if(typeof arg === "object") { var idx = this.indexOf(arg); if(idx > -1) { return this[idx]; } } return null; }; Array2.prototype.replace = function(oldObj, newObj) { newObj.parent = this; newObj.input = oldObj.input; if(typeof oldObj != "number") { var idx = this.indexOf(oldObj); if(idx > -1) { this.splice(idx, 1, newObj); } }else{ this.splice(oldObj, 1, newObj); } if(this.parent) Gibberish.dirty(this.parent); }; Array2.prototype.insert = function(v, pos) { v.parent = this; this.input = this.parent; if(Array.isArray(v)) { for(var i = 0; i < v.length; i++) { this.splice(pos + i, 0, v[i]); } }else{ this.splice(pos,0,v); } if(this.parent) Gibberish.dirty(this.parent); }; Array2.prototype.add = function() { for(var i = 0; i < arguments.length; i++) { arguments[i].parent = this; arguments[i].input = this.parent; //console.log(this.parent, this.parent.channels); //if(typeof this.parent.channels === "number") { //console.log("CHANGING CHANNELS"); //arguments[i].channels = this.parent.channels; //} this.push(arguments[i]); } //console.log("ADDING ::: this.parent = ", this.parent) if(this.parent) { console.log("DIRTYING"); Gibberish.dirty(this.parent); } }; var rnd = Math.random; Gibberish.rndf = function(min, max, number, canRepeat) { canRepeat = typeof canRepeat === "undefined" ? true : canRepeat; if(typeof number === "undefined" && typeof min != "object") { if(arguments.length == 1) { max = arguments[0]; min = 0; }else if(arguments.length == 2) { min = arguments[0]; max = arguments[1]; }else{ min = 0; max = 1; } var diff = max - min, r = Math.random(), rr = diff * r return min + rr; }else{ var output = []; var tmp = []; if(typeof number === "undefined") { number = max || min.length; } for(var i = 0; i < number; i++) { var num; if(typeof arguments[0] === "object") { num = arguments[0][rndi(0, arguments[0].length - 1)]; }else{ if(canRepeat) { num = Gibberish.rndf(min, max); }else{ num = Gibberish.rndf(min, max); while(tmp.indexOf(num) > -1) { num = Gibberish.rndf(min, max); } tmp.push(num); } } output.push(num); } return output; } }; Gibberish.Rndf = function() { var _min, _max, quantity, random = Math.random, canRepeat; if(arguments.length === 0) { _min = 0; _max = 1; }else if(arguments.length === 1) { _max = arguments[0]; _min = 0; }else if(arguments.length === 2) { _min = arguments[0]; _max = arguments[1]; }else if(arguments.length === 3) { _min = arguments[0]; _max = arguments[1]; quantity = arguments[2]; }else{ _min = arguments[0]; _max = arguments[1]; quantity = arguments[2]; canRepeat = arguments[3]; } return function() { var value, min, max, range; min = typeof _min === 'function' ? _min() : _min max = typeof _max === 'function' ? _max() : _max if( typeof quantity === 'undefined') { value = Gibberish.rndf( min, max ) }else{ value = Gibberish.rndf( min, max, quantity, canRepeat ) } return value; } }; Gibberish.rndi = function( min, max, number, canRepeat ) { var range; if(arguments.length === 0) { min = 0; max = 1; }else if(arguments.length === 1) { max = arguments[0]; min = 0; }else if( arguments.length === 2 ){ min = arguments[0]; max = arguments[1]; }else{ min = arguments[0]; max = arguments[1]; number = arguments[2]; canRepeat = arguments[3]; } range = max - min if( range < number ) canRepeat = true if( typeof number === 'undefined' ) { range = max - min return Math.round( min + Math.random() * range ); }else{ var output = []; var tmp = []; for(var i = 0; i < number; i++) { var num; if(canRepeat) { num = Gibberish.rndi(min, max); }else{ num = Gibberish.rndi(min, max); while(tmp.indexOf(num) > -1) { num = Gibberish.rndi(min, max); } tmp.push(num); } output.push(num); } return output; } }; Gibberish.Rndi = function() { var _min, _max, quantity, random = Math.random, round = Math.round, canRepeat, range; if(arguments.length === 0) { _min = 0; _max = 1; }else if(arguments.length === 1) { _max = arguments[0]; _min = 0; }else if(arguments.length === 2) { _min = arguments[0]; _max = arguments[1]; }else if(arguments.length === 3) { _min = arguments[0]; _max = arguments[1]; quantity = arguments[2]; }else{ _min = arguments[0]; _max = arguments[1]; quantity = arguments[2]; canRepeat = arguments[3]; } range = _max - _min if( typeof quantity === 'number' && range < quantity ) canRepeat = true return function() { var value, min, max, range; min = typeof _min === 'function' ? _min() : _min max = typeof _max === 'function' ? _max() : _max if( typeof quantity === 'undefined') { value = Gibberish.rndi( min, max ) }else{ value = Gibberish.rndi( min, max, quantity, canRepeat ) } return value; } }; Gibberish.extend = function(destination, source) { for (var property in source) { var keys = property.split("."); if(source[property] instanceof Array && source[property].length < 100) { // don't copy large array buffers destination[property] = source[property].slice(0); if(property === "fx") { destination[property].parent = source[property].parent; } }else if (typeof source[property] === "object" && source[property] !== null && !(source[property] instanceof Float32Array) ) { destination[property] = destination[property] || {}; arguments.callee(destination[property], source[property]); } else { destination[property] = source[property]; } } return destination; }; Function.prototype.clone=function(){ return eval('['+this.toString()+']')[0]; }; String.prototype.format = function(i, safe, arg) { function format() { var str = this, len = arguments.length + 1; for (i = 0; i < len; arg = arguments[i++]) { safe = arg; //typeof arg === 'object' ? JSON.stringify(arg) : arg; str = str.replace(RegExp('\\{' + (i - 1) + '\\}', 'g'), safe); } return str; } format.native = String.prototype.format; return format; }(); Gibberish.future = function(func, time) { var seq = new Gibberish.Sequencer({ values:[ function(){}, function() { func(); seq.stop(); seq.disconnect(); } ], durations:[ time ] }).start() seq.cancel = function() { seq.stop(); seq.disconnect(); } return seq } Gibberish.Proxy = function() { var value = 0; Gibberish.extend(this, { name: 'proxy', type: 'effect', properties : {}, callback : function() { return value; }, }).init(); this.input = arguments[0]; value = this.input.parent[ this.input.name ]; delete this.input.parent[ this.input.name ]; this.input.parent.properties[ this.input.name ].value = this; Object.defineProperty( this.input.parent, this.input.name, { get : function(){ return value; }, set : function(_value) { value = _value; } }); Gibberish.dirty(this.input.parent); }; Gibberish.Proxy.prototype = new Gibberish.ugen(); Gibberish.Proxy2 = function() { var input = arguments[0], name = arguments[1], phase = 0 Gibberish.extend( this, { name: 'proxy2', type: 'effect', properties : { }, callback : function() { var v = input[ name ] // if( phase++ % 44100 === 0 ) console.log( v, input, name) return Array.isArray( v ) ? ( v[0] + v[1] + v[2] ) / 3 : v }, }).init(); this.getInput = function() { return input } this.setInput = function( v ) { input = v } this.getName = function() { return name } this.setName = function( v ) { name = v } }; Gibberish.Proxy2.prototype = new Gibberish.ugen(); Gibberish.Proxy3 = function() { var input = arguments[0], name = arguments[1], phase = 0 Gibberish.extend( this, { name: 'proxy3', type: 'effect', properties : { }, callback : function() { var v = input[ name ] //if( phase++ % 44100 === 0 ) console.log( v, input, name) return v || 0 }, }) this.init(); this.codegen = function() { // if(Gibberish.memo[this.symbol]) { // return Gibberish.memo[this.symbol]; // } console.log(" CALLED ") if( ! this.variable ) this.variable = Gibberish.generateSymbol('v'); Gibberish.callbackArgs.push( this.symbol ) Gibberish.callbackObjects.push( this.callback ) this.codeblock = "var " + this.variable + " = " + this.symbol + "(" + input.properties[ name ].codegen() + ");\n" } }; Gibberish.Proxy3.prototype = new Gibberish.ugen(); Gibberish.oscillator = function() { this.type = 'oscillator'; this.oscillatorInit = function() { this.fx = new Array2; this.fx.parent = this; return this; } }; Gibberish.oscillator.prototype = new Gibberish.ugen(); Gibberish._oscillator = new Gibberish.oscillator(); /**#Gibberish.Table - Oscillator An wavetable oscillator. ## Example Usage## `// fill the wavetable with random samples Gibberish.init(); a = new Gibberish.Table(); var t = [] for( var i = 0; i < 1024; i++ ) { t[ i ] = Gibberish.rndf(-1,1) } a.setTable( t ) a.connect() ` - - - - **/ /**###Gibberish.Table.frequency : property Number. From 20 - 20000 hz. **/ /**###Gibberish.Table.amp : property Number. A linear value specifying relative amplitude, ostensibly from 0..1 but can be higher, or lower when used for modulation. **/ Gibberish.Wavetable = function() { var phase = 0, table = null, tableFreq = Gibberish.context.sampleRate / 1024, signHistory = 0, flip = 0; this.properties = { frequency : 440, amp : .25, sync: 0 }; /**###Gibberish.Wavetable.setTable : method Assign an array representing one cycle of a waveform to use. param **table** Float32Array. Assign an array to be used as the wavetable. **/ this.getTable = function() { return table; } this.setTable = function(_table) { table = _table; tableFreq = Gibberish.context.sampleRate / table.length } this.getTableFreq = function() { return tableFreq } this.setTableFreq = function( v ) { tableFreq = v; } this.getPhase = function() { return phase } this.setPhase = function(v) { phase = v } /**###Gibberish.Wavetable.callback : method Returns a single sample of output. param **frequency** Number. The frequency to be used to calculate output. param **amp** Number. The amplitude to be used to calculate output. **/ this.callback = function(frequency, amp, sync) { var index, frac, index2, val1, val2, sign; phase += frequency / tableFreq; while(phase >= 1024) phase -= 1024; index = phase | 0; frac = phase - index; index = index & 1023; index2 = index === 1023 ? 0 : index + 1; val1 = table[index]; val2 = table[index2]; // sign = typeof sync == 'number' ? sync ? sync < 0 ? -1 : 1 : isNaN(sync) ? NaN : 0 : NaN; // if( sign !== signHistory && sign !== 0) { // flip++ // // if( flip === 2 ){ // phase = 0 // flip = 0 // } // //console.log( "FLIP", sign, signHistory, count, sync ) // } // // if( sign !== 0 ) signHistory = sign return ( val1 + ( frac * (val2 - val1) ) ) * amp; } } Gibberish.Wavetable.prototype = Gibberish._oscillator; Gibberish.Table = function( table ) { this.__proto__ = new Gibberish.Wavetable(); this.name = 'table'; var pi_2 = Math.PI * 2 if( typeof table === 'undefined' ) { table = new Float32Array(1024); for(var i = 1024; i--;) { table[i] = Math.sin( (i / 1024) * pi_2); } } this.setTable( table ); this.init(); this.oscillatorInit(); //this.processProperties( arguments ); } Gibberish.asmSine = function (stdlib, foreign, heap) { "use asm"; var sin = stdlib.Math.sin; var phase = 0.0; var out = new stdlib.Float32Array(heap); var floor = stdlib.Math.floor; var tableFreq = 0.0; function init() { var i = 1024; var j = 1024.0; var test = 0.0; for (; i = (i - 1) | 0; ) { j = j - 1.0; out[i >> 2] = +(sin( +(j / 1024.0) * 6.2848)); } tableFreq = 44100.0 / 1024.0; } function gen(freq, amp, sr) { freq = +freq; amp = +amp; sr = +sr; var index = 0.0, index1 = 0, index2 = 0, frac = 0.0, val1 = 0.0, val2 = 0.0; phase = +(phase + freq / tableFreq); if(phase >= 1024.0) phase = +(phase - 1024.0); index = +floor(phase); frac = phase - index; index1 = (~~index); if((index1 | 0) == (1024 | 0)) { index2 = 0 } else { index2 = (index1 + 1) | 0; } val1 = +out[ index1 >> 2 ]; val2 = +out[ index2 >> 2 ]; return +((val1 + (frac * (val2 - val1))) * amp); } function get(idx) { idx = idx|0; return +out[idx >> 2]; } return { init:init, gen: gen, get: get, } }; /* phase += frequency / tableFreq; while(phase >= 1024) phase -= 1024; index = phase | 0; frac = phase - index; index = index & 1023; index2 = index === 1023 ? 0 : index + 1; val1 = table[index]; val2 = table[index2]; return ( val1 + ( frac * (val2 - val1) ) ) * amp; */ /*function gen (freq, amp, sr) { freq = +freq; amp = +amp; sr = +sr; phase = +(phase + +(+(freq / sr) * 3.14159 * 2.0)); return +(+sin(phase) * amp); }*/ //var pi_2 = (3.14159 * 2.0); Gibberish.asmSine2 = function () { this.properties = { frequency:440.0, amp:.5, sr: Gibberish.context.sampleRate } this.name = 'sine' var buf = new ArrayBuffer(4096); var asm = Gibberish.asmSine(window, null, buf); asm.init(); this.getTable = function() { return buf; } this.get = asm.get; this.callback = asm.gen; this.init(); this.oscillatorInit(); this.processProperties( arguments ); return this; } Gibberish.asmSine2.prototype = Gibberish._oscillator; /**#Gibberish.Sine - Oscillator A sinewave calculated on a per-sample basis. ## Example Usage## `// make a sine wave Gibberish.init(); a = new Gibberish.Sine().connect();` - - - - **/ /**###Gibberish.Sine.frequency : property Number. From 20 - 20000 hz. **/ /**###Gibberish.Sine.amp : property Number. A linear value specifying relative amplitude, ostensibly from 0..1 but can be higher, or lower when used for modulation. **/ Gibberish.Sine = function() { this.__proto__ = new Gibberish.Wavetable(); this.name = 'sine'; var pi_2 = Math.PI * 2, table = new Float32Array(1024); for(var i = 1024; i--;) { table[i] = Math.sin( (i / 1024) * pi_2); } this.setTable( table ); this.init( arguments ); this.oscillatorInit(); this.processProperties( arguments ); }; /**#Gibberish.Sine2 - Oscillator A sinewave calculated on a per-sample basis that can be panned. ## Example Usage## `// make a sine wave Gibberish.init(); a = new Gibberish.Sine2(880, .5, -.25).connect();` - - - - **/ /**###Gibberish.Sine2.frequency : property Number. From 20 - 20000 hz. **/ /**###Gibberish.Sine2.amp : property Number. A linear value specifying relative amplitude, ostensibly from 0..1 but can be higher, or lower when used for modulation. **/ /**###Gibberish.Sine2.pan : property Number. -1..1. The position of the sinewave in the stereo spectrum **/ Gibberish.Sine2 = function() { this.__proto__ = new Gibberish.Sine(); this.name = "sine2"; var sine = this.__proto__.callback, panner = Gibberish.makePanner(), output = [0,0]; /**###Gibberish.Sine2.callback : method Returns a stereo sample of output as an array. param **frequency** Number. The frequency to be used to calculate output. param **amp** Number. The amplitude to be used to calculate output. param **pan** Number. The position in the stereo spectrum of the signal. **/ this.callback = function(frequency, amp, pan) { var out = sine(frequency, amp); output = panner(out, pan, output); return output; } this.init(); this.oscillatorInit(); Gibberish.defineUgenProp