UNPKG

gibberwocky

Version:

music sequencing for Live and Max/MSP

1,948 lines (1,474 loc) 742 kB
const window = {}; let Gibberish = null; let initialized = false; (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Gibberish = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { name:'abs', gen() { let out, inputs = gen.getInputs( this ) const isWorklet = gen.mode === 'worklet' const ref = isWorklet ? '' : 'gen.' if( isNaN( inputs[0] ) ) { gen.closures.add({ [ this.name ]: isWorklet ? 'Math.abs' : Math.abs }) out = `${ref}abs( ${inputs[0]} )` } else { out = Math.abs( parseFloat( inputs[0] ) ) } return out } } module.exports = x => { let abs = Object.create( proto ) abs.inputs = [ x ] return abs } },{"./gen.js":30}],2:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { basename:'accum', gen() { let code, inputs = gen.getInputs( this ), genName = 'gen.' + this.name, functionBody gen.requestMemory( this.memory ) gen.memory.heap[ this.memory.value.idx ] = this.initialValue functionBody = this.callback( genName, inputs[0], inputs[1], `memory[${this.memory.value.idx}]` ) //gen.closures.add({ [ this.name ]: this }) gen.memo[ this.name ] = this.name + '_value' return [ this.name + '_value', functionBody ] }, callback( _name, _incr, _reset, valueRef ) { let diff = this.max - this.min, out = '', wrap = '' /* three different methods of wrapping, third is most expensive: * * 1: range {0,1}: y = x - (x | 0) * 2: log2(this.max) == integer: y = x & (this.max - 1) * 3: all others: if( x >= this.max ) y = this.max -x * */ // must check for reset before storing value for output if( !(typeof this.inputs[1] === 'number' && this.inputs[1] < 1) ) { if( this.resetValue !== this.min ) { out += ` if( ${_reset} >=1 ) ${valueRef} = ${this.resetValue}\n\n` //out += ` if( ${_reset} >=1 ) ${valueRef} = ${this.min}\n\n` }else{ out += ` if( ${_reset} >=1 ) ${valueRef} = ${this.min}\n\n` //out += ` if( ${_reset} >=1 ) ${valueRef} = ${this.initialValue}\n\n` } } out += ` var ${this.name}_value = ${valueRef}\n` if( this.shouldWrap === false && this.shouldClamp === true ) { out += ` if( ${valueRef} < ${this.max } ) ${valueRef} += ${_incr}\n` }else{ out += ` ${valueRef} += ${_incr}\n` // store output value before accumulating } if( this.max !== Infinity && this.shouldWrapMax ) wrap += ` if( ${valueRef} >= ${this.max} ) ${valueRef} -= ${diff}\n` if( this.min !== -Infinity && this.shouldWrapMin ) wrap += ` if( ${valueRef} < ${this.min} ) ${valueRef} += ${diff}\n` //if( this.min === 0 && this.max === 1 ) { // wrap = ` ${valueRef} = ${valueRef} - (${valueRef} | 0)\n\n` //} else if( this.min === 0 && ( Math.log2( this.max ) | 0 ) === Math.log2( this.max ) ) { // wrap = ` ${valueRef} = ${valueRef} & (${this.max} - 1)\n\n` //} else if( this.max !== Infinity ){ // wrap = ` if( ${valueRef} >= ${this.max} ) ${valueRef} -= ${diff}\n\n` //} out = out + wrap + '\n' return out }, defaults : { min:0, max:1, resetValue:0, initialValue:0, shouldWrap:true, shouldWrapMax: true, shouldWrapMin:true, shouldClamp:false } } module.exports = ( incr, reset=0, properties ) => { const ugen = Object.create( proto ) Object.assign( ugen, { uid: gen.getUID(), inputs: [ incr, reset ], memory: { value: { length:1, idx:null } } }, proto.defaults, properties ) if( properties !== undefined && properties.shouldWrapMax === undefined && properties.shouldWrapMin === undefined ) { if( properties.shouldWrap !== undefined ) { ugen.shouldWrapMin = ugen.shouldWrapMax = properties.shouldWrap } } if( properties !== undefined && properties.resetValue === undefined ) { ugen.resetValue = ugen.min } if( ugen.initialValue === undefined ) ugen.initialValue = ugen.min Object.defineProperty( ugen, 'value', { get() { //console.log( 'gen:', gen, gen.memory ) return gen.memory.heap[ this.memory.value.idx ] }, set(v) { gen.memory.heap[ this.memory.value.idx ] = v } }) ugen.name = `${ugen.basename}${ugen.uid}` return ugen } },{"./gen.js":30}],3:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { basename:'acos', gen() { let out, inputs = gen.getInputs( this ) const isWorklet = gen.mode === 'worklet' const ref = isWorklet ? '' : 'gen.' if( isNaN( inputs[0] ) ) { gen.closures.add({ 'acos': isWorklet ? 'Math.acos' :Math.acos }) out = `${ref}acos( ${inputs[0]} )` } else { out = Math.acos( parseFloat( inputs[0] ) ) } return out } } module.exports = x => { let acos = Object.create( proto ) acos.inputs = [ x ] acos.id = gen.getUID() acos.name = `${acos.basename}{acos.id}` return acos } },{"./gen.js":30}],4:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ), mul = require( './mul.js' ), sub = require( './sub.js' ), div = require( './div.js' ), data = require( './data.js' ), peek = require( './peek.js' ), accum = require( './accum.js' ), ifelse = require( './ifelseif.js' ), lt = require( './lt.js' ), bang = require( './bang.js' ), env = require( './env.js' ), add = require( './add.js' ), poke = require( './poke.js' ), neq = require( './neq.js' ), and = require( './and.js' ), gte = require( './gte.js' ), memo = require( './memo.js' ), utilities= require( './utilities.js' ) module.exports = ( attackTime = 44100, decayTime = 44100, _props ) => { const props = Object.assign({}, { shape:'exponential', alpha:5, trigger:null }, _props ) const _bang = props.trigger !== null ? props.trigger : bang(), phase = accum( 1, _bang, { min:0, max: Infinity, initialValue:-Infinity, shouldWrap:false }) let bufferData, bufferDataReverse, decayData, out, buffer //console.log( 'shape:', props.shape, 'attack time:', attackTime, 'decay time:', decayTime ) let completeFlag = data( [0] ) // slightly more efficient to use existing phase accumulator for linear envelopes if( props.shape === 'linear' ) { out = ifelse( and( gte( phase, 0), lt( phase, attackTime )), div( phase, attackTime ), and( gte( phase, 0), lt( phase, add( attackTime, decayTime ) ) ), sub( 1, div( sub( phase, attackTime ), decayTime ) ), neq( phase, -Infinity), poke( completeFlag, 1, 0, { inline:0 }), 0 ) } else { bufferData = env({ length:1024, type:props.shape, alpha:props.alpha }) bufferDataReverse = env({ length:1024, type:props.shape, alpha:props.alpha, reverse:true }) out = ifelse( and( gte( phase, 0), lt( phase, attackTime ) ), peek( bufferData, div( phase, attackTime ), { boundmode:'clamp' } ), and( gte(phase,0), lt( phase, add( attackTime, decayTime ) ) ), peek( bufferDataReverse, div( sub( phase, attackTime ), decayTime ), { boundmode:'clamp' }), neq( phase, -Infinity ), poke( completeFlag, 1, 0, { inline:0 }), 0 ) } const usingWorklet = gen.mode === 'worklet' if( usingWorklet === true ) { out.node = null utilities.register( out ) } // needed for gibberish... getting this to work right with worklets // via promises will probably be tricky out.isComplete = ()=> { if( usingWorklet === true && out.node !== null ) { const p = new Promise( resolve => { out.node.getMemoryValue( completeFlag.memory.values.idx, resolve ) }) return p }else{ return gen.memory.heap[ completeFlag.memory.values.idx ] } } out.trigger = ()=> { if( usingWorklet === true && out.node !== null ) { out.node.port.postMessage({ key:'set', idx:completeFlag.memory.values.idx, value:0 }) }else{ gen.memory.heap[ completeFlag.memory.values.idx ] = 0 } _bang.trigger() } return out } },{"./accum.js":2,"./add.js":5,"./and.js":7,"./bang.js":11,"./data.js":18,"./div.js":23,"./env.js":24,"./gen.js":30,"./gte.js":32,"./ifelseif.js":35,"./lt.js":38,"./memo.js":42,"./mul.js":48,"./neq.js":49,"./peek.js":54,"./poke.js":56,"./sub.js":66,"./utilities.js":72}],5:[function(require,module,exports){ 'use strict' const gen = require('./gen.js') const proto = { basename:'add', gen() { let inputs = gen.getInputs( this ), out='', sum = 0, numCount = 0, adderAtEnd = false, alreadyFullSummed = true if( inputs.length === 0 ) return 0 out = ` var ${this.name} = ` inputs.forEach( (v,i) => { if( isNaN( v ) ) { out += v if( i < inputs.length -1 ) { adderAtEnd = true out += ' + ' } alreadyFullSummed = false }else{ sum += parseFloat( v ) numCount++ } }) if( numCount > 0 ) { out += adderAtEnd || alreadyFullSummed ? sum : ' + ' + sum } out += '\n' gen.memo[ this.name ] = this.name return [ this.name, out ] } } module.exports = ( ...args ) => { const add = Object.create( proto ) add.id = gen.getUID() add.name = add.basename + add.id add.inputs = args return add } },{"./gen.js":30}],6:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ), mul = require( './mul.js' ), sub = require( './sub.js' ), div = require( './div.js' ), data = require( './data.js' ), peek = require( './peek.js' ), accum = require( './accum.js' ), ifelse = require( './ifelseif.js' ), lt = require( './lt.js' ), bang = require( './bang.js' ), env = require( './env.js' ), param = require( './param.js' ), add = require( './add.js' ), gtp = require( './gtp.js' ), not = require( './not.js' ), and = require( './and.js' ), neq = require( './neq.js' ), poke = require( './poke.js' ) module.exports = ( attackTime=44, decayTime=22050, sustainTime=44100, sustainLevel=.6, releaseTime=44100, _props ) => { let envTrigger = bang(), phase = accum( 1, envTrigger, { max: Infinity, shouldWrap:false, initialValue:Infinity }), shouldSustain = param( 1 ), defaults = { shape: 'exponential', alpha: 5, triggerRelease: false, }, props = Object.assign({}, defaults, _props ), bufferData, decayData, out, buffer, sustainCondition, releaseAccum, releaseCondition const completeFlag = data( [0] ) bufferData = env({ length:1024, alpha:props.alpha, shift:0, type:props.shape }) sustainCondition = props.triggerRelease ? shouldSustain : lt( phase, add( attackTime, decayTime, sustainTime ) ) releaseAccum = props.triggerRelease ? gtp( sub( sustainLevel, accum( div( sustainLevel, releaseTime ) , 0, { shouldWrap:false }) ), 0 ) : sub( sustainLevel, mul( div( sub( phase, add( attackTime, decayTime, sustainTime ) ), releaseTime ), sustainLevel ) ), releaseCondition = props.triggerRelease ? not( shouldSustain ) : lt( phase, add( attackTime, decayTime, sustainTime, releaseTime ) ) out = ifelse( // attack lt( phase, attackTime ), peek( bufferData, div( phase, attackTime ), { boundmode:'clamp' } ), // decay lt( phase, add( attackTime, decayTime ) ), peek( bufferData, sub( 1, mul( div( sub( phase, attackTime ), decayTime ), sub( 1, sustainLevel ) ) ), { boundmode:'clamp' }), // sustain and( sustainCondition, neq( phase, Infinity ) ), peek( bufferData, sustainLevel ), // release releaseCondition, //lt( phase, attackTime + decayTime + sustainTime + releaseTime ), peek( bufferData, releaseAccum, //sub( sustainLevel, mul( div( sub( phase, attackTime + decayTime + sustainTime), releaseTime ), sustainLevel ) ), { boundmode:'clamp' } ), neq( phase, Infinity ), poke( completeFlag, 1, 0, { inline:0 }), 0 ) const usingWorklet = gen.mode === 'worklet' if( usingWorklet === true ) { out.node = null utilities.register( out ) } out.trigger = ()=> { shouldSustain.value = 1 envTrigger.trigger() } // needed for gibberish... getting this to work right with worklets // via promises will probably be tricky out.isComplete = ()=> { if( usingWorklet === true && out.node !== null ) { const p = new Promise( resolve => { out.node.getMemoryValue( completeFlag.memory.values.idx, resolve ) }) return p }else{ return gen.memory.heap[ completeFlag.memory.values.idx ] } } out.release = ()=> { shouldSustain.value = 0 // XXX pretty nasty... grabs accum inside of gtp and resets value manually // unfortunately envTrigger won't work as it's back to 0 by the time the release block is triggered... if( usingWorklet && out.node !== null ) { console.log( 'worklet??' ) out.node.port.postMessage({ key:'set', idx:releaseAccum.inputs[0].inputs[1].memory.value.idx, value:0 }) }else{ console.log( 'non-worklet...' ) gen.memory.heap[ releaseAccum.inputs[0].inputs[1].memory.value.idx ] = 0 } } return out } },{"./accum.js":2,"./add.js":5,"./and.js":7,"./bang.js":11,"./data.js":18,"./div.js":23,"./env.js":24,"./gen.js":30,"./gtp.js":33,"./ifelseif.js":35,"./lt.js":38,"./mul.js":48,"./neq.js":49,"./not.js":51,"./param.js":53,"./peek.js":54,"./poke.js":56,"./sub.js":66}],7:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ) let proto = { basename:'and', gen() { let inputs = gen.getInputs( this ), out out = ` var ${this.name} = (${inputs[0]} !== 0 && ${inputs[1]} !== 0) | 0\n\n` gen.memo[ this.name ] = `${this.name}` return [ `${this.name}`, out ] }, } module.exports = ( in1, in2 ) => { let ugen = Object.create( proto ) Object.assign( ugen, { uid: gen.getUID(), inputs: [ in1, in2 ], }) ugen.name = `${ugen.basename}${ugen.uid}` return ugen } },{"./gen.js":30}],8:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { basename:'asin', gen() { let out, inputs = gen.getInputs( this ) const isWorklet = gen.mode === 'worklet' const ref = isWorklet ? '' : 'gen.' if( isNaN( inputs[0] ) ) { gen.closures.add({ 'asin': isWorklet ? 'Math.sin' : Math.asin }) out = `${ref}asin( ${inputs[0]} )` } else { out = Math.asin( parseFloat( inputs[0] ) ) } return out } } module.exports = x => { let asin = Object.create( proto ) asin.inputs = [ x ] asin.id = gen.getUID() asin.name = `${asin.basename}{asin.id}` return asin } },{"./gen.js":30}],9:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { basename:'atan', gen() { let out, inputs = gen.getInputs( this ) const isWorklet = gen.mode === 'worklet' const ref = isWorklet ? '' : 'gen.' if( isNaN( inputs[0] ) ) { gen.closures.add({ 'atan': isWorklet ? 'Math.atan' : Math.atan }) out = `${ref}atan( ${inputs[0]} )` } else { out = Math.atan( parseFloat( inputs[0] ) ) } return out } } module.exports = x => { let atan = Object.create( proto ) atan.inputs = [ x ] atan.id = gen.getUID() atan.name = `${atan.basename}{atan.id}` return atan } },{"./gen.js":30}],10:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ), history = require( './history.js' ), mul = require( './mul.js' ), sub = require( './sub.js' ) module.exports = ( decayTime = 44100 ) => { let ssd = history ( 1 ), t60 = Math.exp( -6.907755278921 / decayTime ) ssd.in( mul( ssd.out, t60 ) ) ssd.out.trigger = ()=> { ssd.value = 1 } return sub( 1, ssd.out ) } },{"./gen.js":30,"./history.js":34,"./mul.js":48,"./sub.js":66}],11:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { gen() { gen.requestMemory( this.memory ) let out = ` var ${this.name} = memory[${this.memory.value.idx}] if( ${this.name} === 1 ) memory[${this.memory.value.idx}] = 0 ` gen.memo[ this.name ] = this.name return [ this.name, out ] } } module.exports = ( _props ) => { let ugen = Object.create( proto ), props = Object.assign({}, { min:0, max:1 }, _props ) ugen.name = 'bang' + gen.getUID() ugen.min = props.min ugen.max = props.max const usingWorklet = gen.mode === 'worklet' if( usingWorklet === true ) { ugen.node = null utilities.register( ugen ) } ugen.trigger = () => { if( usingWorklet === true && ugen.node !== null ) { ugen.node.port.postMessage({ key:'set', idx:ugen.memory.value.idx, value:ugen.max }) }else{ gen.memory.heap[ ugen.memory.value.idx ] = ugen.max } } ugen.memory = { value: { length:1, idx:null } } return ugen } },{"./gen.js":30}],12:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ) let proto = { basename:'bool', gen() { let inputs = gen.getInputs( this ), out out = `${inputs[0]} === 0 ? 0 : 1` //gen.memo[ this.name ] = `gen.data.${this.name}` //return [ `gen.data.${this.name}`, ' ' +out ] return out } } module.exports = ( in1 ) => { let ugen = Object.create( proto ) Object.assign( ugen, { uid: gen.getUID(), inputs: [ in1 ], }) ugen.name = `${ugen.basename}${ugen.uid}` return ugen } },{"./gen.js":30}],13:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { name:'ceil', gen() { let out, inputs = gen.getInputs( this ) const isWorklet = gen.mode === 'worklet' const ref = isWorklet ? '' : 'gen.' if( isNaN( inputs[0] ) ) { gen.closures.add({ [ this.name ]: isWorklet ? 'Math.ceil' : Math.ceil }) out = `${ref}ceil( ${inputs[0]} )` } else { out = Math.ceil( parseFloat( inputs[0] ) ) } return out } } module.exports = x => { let ceil = Object.create( proto ) ceil.inputs = [ x ] return ceil } },{"./gen.js":30}],14:[function(require,module,exports){ 'use strict' let gen = require('./gen.js'), floor= require('./floor.js'), sub = require('./sub.js'), memo = require('./memo.js') let proto = { basename:'clip', gen() { let code, inputs = gen.getInputs( this ), out out = ` var ${this.name} = ${inputs[0]} if( ${this.name} > ${inputs[2]} ) ${this.name} = ${inputs[2]} else if( ${this.name} < ${inputs[1]} ) ${this.name} = ${inputs[1]} ` out = ' ' + out gen.memo[ this.name ] = this.name return [ this.name, out ] }, } module.exports = ( in1, min=-1, max=1 ) => { let ugen = Object.create( proto ) Object.assign( ugen, { min, max, uid: gen.getUID(), inputs: [ in1, min, max ], }) ugen.name = `${ugen.basename}${ugen.uid}` return ugen } },{"./floor.js":27,"./gen.js":30,"./memo.js":42,"./sub.js":66}],15:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { basename:'cos', gen() { let out, inputs = gen.getInputs( this ) const isWorklet = gen.mode === 'worklet' const ref = isWorklet ? '' : 'gen.' if( isNaN( inputs[0] ) ) { gen.closures.add({ 'cos': isWorklet ? 'Math.cos' : Math.cos }) out = `${ref}cos( ${inputs[0]} )` } else { out = Math.cos( parseFloat( inputs[0] ) ) } return out } } module.exports = x => { let cos = Object.create( proto ) cos.inputs = [ x ] cos.id = gen.getUID() cos.name = `${cos.basename}{cos.id}` return cos } },{"./gen.js":30}],16:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { basename:'counter', gen() { let code, inputs = gen.getInputs( this ), genName = 'gen.' + this.name, functionBody if( this.memory.value.idx === null ) gen.requestMemory( this.memory ) functionBody = this.callback( genName, inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], `memory[${this.memory.value.idx}]`, `memory[${this.memory.wrap.idx}]` ) gen.memo[ this.name ] = this.name + '_value' if( gen.memo[ this.wrap.name ] === undefined ) this.wrap.gen() return [ this.name + '_value', functionBody ] }, callback( _name, _incr, _min, _max, _reset, loops, valueRef, wrapRef ) { let diff = this.max - this.min, out = '', wrap = '' // must check for reset before storing value for output if( !(typeof this.inputs[3] === 'number' && this.inputs[3] < 1) ) { out += ` if( ${_reset} >= 1 ) ${valueRef} = ${_min}\n` } out += ` var ${this.name}_value = ${valueRef};\n ${valueRef} += ${_incr}\n` // store output value before accumulating if( typeof this.max === 'number' && this.max !== Infinity && typeof this.min !== 'number' ) { wrap = ` if( ${valueRef} >= ${this.max} && ${loops} > 0) { ${valueRef} -= ${diff} ${wrapRef} = 1 }else{ ${wrapRef} = 0 }\n` }else if( this.max !== Infinity && this.min !== Infinity ) { wrap = ` if( ${valueRef} >= ${_max} && ${loops} > 0) { ${valueRef} -= ${_max} - ${_min} ${wrapRef} = 1 }else if( ${valueRef} < ${_min} && ${loops} > 0) { ${valueRef} += ${_max} - ${_min} ${wrapRef} = 1 }else{ ${wrapRef} = 0 }\n` }else{ out += '\n' } out = out + wrap return out } } module.exports = ( incr=1, min=0, max=Infinity, reset=0, loops=1, properties ) => { let ugen = Object.create( proto ), defaults = { initialValue: 0, shouldWrap:true } if( properties !== undefined ) Object.assign( defaults, properties ) Object.assign( ugen, { min: min, max: max, value: defaults.initialValue, uid: gen.getUID(), inputs: [ incr, min, max, reset, loops ], memory: { value: { length:1, idx: null }, wrap: { length:1, idx: null } }, wrap : { gen() { if( ugen.memory.wrap.idx === null ) { gen.requestMemory( ugen.memory ) } gen.getInputs( this ) gen.memo[ this.name ] = `memory[ ${ugen.memory.wrap.idx} ]` return `memory[ ${ugen.memory.wrap.idx} ]` } } }, defaults ) Object.defineProperty( ugen, 'value', { get() { if( this.memory.value.idx !== null ) { return gen.memory.heap[ this.memory.value.idx ] } }, set( v ) { if( this.memory.value.idx !== null ) { gen.memory.heap[ this.memory.value.idx ] = v } } }) ugen.wrap.inputs = [ ugen ] ugen.name = `${ugen.basename}${ugen.uid}` ugen.wrap.name = ugen.name + '_wrap' return ugen } },{"./gen.js":30}],17:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ), accum= require( './phasor.js' ), data = require( './data.js' ), peek = require( './peek.js' ), mul = require( './mul.js' ), phasor=require( './phasor.js') let proto = { basename:'cycle', initTable() { let buffer = new Float32Array( 1024 ) for( let i = 0, l = buffer.length; i < l; i++ ) { buffer[ i ] = Math.sin( ( i / l ) * ( Math.PI * 2 ) ) } gen.globals.cycle = data( buffer, 1, { immutable:true } ) } } module.exports = ( frequency=1, reset=0, _props ) => { if( typeof gen.globals.cycle === 'undefined' ) proto.initTable() const props = Object.assign({}, { min:0 }, _props ) const ugen = peek( gen.globals.cycle, phasor( frequency, reset, props )) ugen.name = 'cycle' + gen.getUID() return ugen } },{"./data.js":18,"./gen.js":30,"./mul.js":48,"./peek.js":54,"./phasor.js":55}],18:[function(require,module,exports){ 'use strict' let gen = require('./gen.js'), utilities = require( './utilities.js' ), peek = require('./peek.js'), poke = require('./poke.js') let proto = { basename:'data', globals: {}, gen() { let idx if( gen.memo[ this.name ] === undefined ) { let ugen = this gen.requestMemory( this.memory, this.immutable ) idx = this.memory.values.idx try { gen.memory.heap.set( this.buffer, idx ) }catch( e ) { console.log( e ) throw Error( 'error with request. asking for ' + this.buffer.length +'. current index: ' + gen.memoryIndex + ' of ' + gen.memory.heap.length ) } //gen.data[ this.name ] = this //return 'gen.memory' + this.name + '.buffer' gen.memo[ this.name ] = idx }else{ idx = gen.memo[ this.name ] } return idx }, } module.exports = ( x, y=1, properties ) => { let ugen, buffer, shouldLoad = false if( properties !== undefined && properties.global !== undefined ) { if( gen.globals[ properties.global ] ) { return gen.globals[ properties.global ] } } if( typeof x === 'number' ) { if( y !== 1 ) { buffer = [] for( let i = 0; i < y; i++ ) { buffer[ i ] = new Float32Array( x ) } }else{ buffer = new Float32Array( x ) } }else if( Array.isArray( x ) ) { //! (x instanceof Float32Array ) ) { let size = x.length buffer = new Float32Array( size ) for( let i = 0; i < x.length; i++ ) { buffer[ i ] = x[ i ] } }else if( typeof x === 'string' ) { buffer = { length: y > 1 ? y : gen.samplerate * 60 } // XXX what??? shouldLoad = true }else if( x instanceof Float32Array ) { buffer = x } ugen = Object.create( proto ) Object.assign( ugen, { buffer, name: proto.basename + gen.getUID(), dim: buffer.length, // XXX how do we dynamically allocate this? channels : 1, onload: null, then( fnc ) { ugen.onload = fnc return ugen }, immutable: properties !== undefined && properties.immutable === true ? true : false, load( filename ) { let promise = utilities.loadSample( filename, ugen ) promise.then( ( _buffer )=> { ugen.memory.values.length = ugen.dim = _buffer.length ugen.onload() }) }, memory : { values: { length:buffer.length, idx:null } } }) if( shouldLoad ) ugen.load( x ) if( properties !== undefined ) { if( properties.global !== undefined ) { gen.globals[ properties.global ] = ugen } if( properties.meta === true ) { for( let i = 0, length = ugen.buffer.length; i < length; i++ ) { Object.defineProperty( ugen, i, { get () { return peek( ugen, i, { mode:'simple', interp:'none' } ) }, set( v ) { return poke( ugen, v, i ) } }) } } } return ugen } },{"./gen.js":30,"./peek.js":54,"./poke.js":56,"./utilities.js":72}],19:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ), history = require( './history.js' ), sub = require( './sub.js' ), add = require( './add.js' ), mul = require( './mul.js' ), memo = require( './memo.js' ) module.exports = ( in1 ) => { let x1 = history(), y1 = history(), filter //History x1, y1; y = in1 - x1 + y1*0.9997; x1 = in1; y1 = y; out1 = y; filter = memo( add( sub( in1, x1.out ), mul( y1.out, .9997 ) ) ) x1.in( in1 ) y1.in( filter ) return filter } },{"./add.js":5,"./gen.js":30,"./history.js":34,"./memo.js":42,"./mul.js":48,"./sub.js":66}],20:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ), history = require( './history.js' ), mul = require( './mul.js' ), t60 = require( './t60.js' ) module.exports = ( decayTime = 44100, props ) => { let properties = Object.assign({}, { initValue:1 }, props ), ssd = history ( properties.initValue ) ssd.in( mul( ssd.out, t60( decayTime ) ) ) ssd.out.trigger = ()=> { ssd.value = 1 } return ssd.out } },{"./gen.js":30,"./history.js":34,"./mul.js":48,"./t60.js":68}],21:[function(require,module,exports){ 'use strict' const gen = require( './gen.js' ), data = require( './data.js' ), poke = require( './poke.js' ), peek = require( './peek.js' ), sub = require( './sub.js' ), wrap = require( './wrap.js' ), accum= require( './accum.js'), memo = require( './memo.js' ) const proto = { basename:'delay', gen() { let inputs = gen.getInputs( this ) gen.memo[ this.name ] = inputs[0] return inputs[0] }, } const defaults = { size: 512, interp:'none' } module.exports = ( in1, taps, properties ) => { const ugen = Object.create( proto ) let writeIdx, readIdx, delaydata if( Array.isArray( taps ) === false ) taps = [ taps ] const props = Object.assign( {}, defaults, properties ) const maxTapSize = Math.max( ...taps ) if( props.size < maxTapSize ) props.size = maxTapSize delaydata = data( props.size ) ugen.inputs = [] writeIdx = accum( 1, 0, { max:props.size, min:0 }) for( let i = 0; i < taps.length; i++ ) { ugen.inputs[ i ] = peek( delaydata, wrap( sub( writeIdx, taps[i] ), 0, props.size ),{ mode:'samples', interp:props.interp }) } ugen.outputs = ugen.inputs // XXX ugh, Ugh, UGH! but i guess it works. poke( delaydata, in1, writeIdx ) ugen.name = `${ugen.basename}${gen.getUID()}` return ugen } },{"./accum.js":2,"./data.js":18,"./gen.js":30,"./memo.js":42,"./peek.js":54,"./poke.js":56,"./sub.js":66,"./wrap.js":74}],22:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ), history = require( './history.js' ), sub = require( './sub.js' ) module.exports = ( in1 ) => { let n1 = history() n1.in( in1 ) let ugen = sub( in1, n1.out ) ugen.name = 'delta'+gen.getUID() return ugen } },{"./gen.js":30,"./history.js":34,"./sub.js":66}],23:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') const proto = { basename:'div', gen() { let inputs = gen.getInputs( this ), out=` var ${this.name} = `, diff = 0, numCount = 0, lastNumber = inputs[ 0 ], lastNumberIsUgen = isNaN( lastNumber ), divAtEnd = false inputs.forEach( (v,i) => { if( i === 0 ) return let isNumberUgen = isNaN( v ), isFinalIdx = i === inputs.length - 1 if( !lastNumberIsUgen && !isNumberUgen ) { lastNumber = lastNumber / v out += lastNumber }else{ out += `${lastNumber} / ${v}` } if( !isFinalIdx ) out += ' / ' }) out += '\n' gen.memo[ this.name ] = this.name return [ this.name, out ] } } module.exports = (...args) => { const div = Object.create( proto ) Object.assign( div, { id: gen.getUID(), inputs: args, }) div.name = div.basename + div.id return div } },{"./gen.js":30}],24:[function(require,module,exports){ 'use strict' let gen = require( './gen' ), windows = require( './windows' ), data = require( './data' ), peek = require( './peek' ), phasor = require( './phasor' ), defaults = { type:'triangular', length:1024, alpha:.15, shift:0, reverse:false } module.exports = props => { let properties = Object.assign( {}, defaults, props ) let buffer = new Float32Array( properties.length ) let name = properties.type + '_' + properties.length + '_' + properties.shift + '_' + properties.reverse + '_' + properties.alpha if( typeof gen.globals.windows[ name ] === 'undefined' ) { for( let i = 0; i < properties.length; i++ ) { buffer[ i ] = windows[ properties.type ]( properties.length, i, properties.alpha, properties.shift ) } if( properties.reverse === true ) { buffer.reverse() } gen.globals.windows[ name ] = data( buffer ) } let ugen = gen.globals.windows[ name ] ugen.name = 'env' + gen.getUID() return ugen } },{"./data":18,"./gen":30,"./peek":54,"./phasor":55,"./windows":73}],25:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ) let proto = { basename:'eq', gen() { let inputs = gen.getInputs( this ), out out = this.inputs[0] === this.inputs[1] ? 1 : ` var ${this.name} = (${inputs[0]} === ${inputs[1]}) | 0\n\n` gen.memo[ this.name ] = `${this.name}` return [ `${this.name}`, out ] }, } module.exports = ( in1, in2 ) => { let ugen = Object.create( proto ) Object.assign( ugen, { uid: gen.getUID(), inputs: [ in1, in2 ], }) ugen.name = `${ugen.basename}${ugen.uid}` return ugen } },{"./gen.js":30}],26:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { name:'exp', gen() { let out, inputs = gen.getInputs( this ) const isWorklet = gen.mode === 'worklet' const ref = isWorklet? '' : 'gen.' if( isNaN( inputs[0] ) ) { gen.closures.add({ [ this.name ]: isWorklet ? 'Math.exp' : Math.exp }) out = `${ref}exp( ${inputs[0]} )` } else { out = Math.exp( parseFloat( inputs[0] ) ) } return out } } module.exports = x => { let exp = Object.create( proto ) exp.inputs = [ x ] return exp } },{"./gen.js":30}],27:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { name:'floor', gen() { let out, inputs = gen.getInputs( this ) if( isNaN( inputs[0] ) ) { //gen.closures.add({ [ this.name ]: Math.floor }) out = `( ${inputs[0]} | 0 )` } else { out = inputs[0] | 0 } return out } } module.exports = x => { let floor = Object.create( proto ) floor.inputs = [ x ] return floor } },{"./gen.js":30}],28:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { basename:'fold', gen() { let code, inputs = gen.getInputs( this ), out out = this.createCallback( inputs[0], this.min, this.max ) gen.memo[ this.name ] = this.name + '_value' return [ this.name + '_value', out ] }, createCallback( v, lo, hi ) { let out = ` var ${this.name}_value = ${v}, ${this.name}_range = ${hi} - ${lo}, ${this.name}_numWraps = 0 if(${this.name}_value >= ${hi}){ ${this.name}_value -= ${this.name}_range if(${this.name}_value >= ${hi}){ ${this.name}_numWraps = ((${this.name}_value - ${lo}) / ${this.name}_range) | 0 ${this.name}_value -= ${this.name}_range * ${this.name}_numWraps } ${this.name}_numWraps++ } else if(${this.name}_value < ${lo}){ ${this.name}_value += ${this.name}_range if(${this.name}_value < ${lo}){ ${this.name}_numWraps = ((${this.name}_value - ${lo}) / ${this.name}_range- 1) | 0 ${this.name}_value -= ${this.name}_range * ${this.name}_numWraps } ${this.name}_numWraps-- } if(${this.name}_numWraps & 1) ${this.name}_value = ${hi} + ${lo} - ${this.name}_value ` return ' ' + out } } module.exports = ( in1, min=0, max=1 ) => { let ugen = Object.create( proto ) Object.assign( ugen, { min, max, uid: gen.getUID(), inputs: [ in1 ], }) ugen.name = `${ugen.basename}${ugen.uid}` return ugen } },{"./gen.js":30}],29:[function(require,module,exports){ 'use strict' let gen = require( './gen.js' ) let proto = { basename:'gate', controlString:null, // insert into output codegen for determining indexing gen() { let inputs = gen.getInputs( this ), out gen.requestMemory( this.memory ) let lastInputMemoryIdx = 'memory[ ' + this.memory.lastInput.idx + ' ]', outputMemoryStartIdx = this.memory.lastInput.idx + 1, inputSignal = inputs[0], controlSignal = inputs[1] /* * we check to see if the current control inputs equals our last input * if so, we store the signal input in the memory associated with the currently * selected index. If not, we put 0 in the memory associated with the last selected index, * change the selected index, and then store the signal in put in the memery assoicated * with the newly selected index */ out = ` if( ${controlSignal} !== ${lastInputMemoryIdx} ) { memory[ ${lastInputMemoryIdx} + ${outputMemoryStartIdx} ] = 0 ${lastInputMemoryIdx} = ${controlSignal} } memory[ ${outputMemoryStartIdx} + ${controlSignal} ] = ${inputSignal} ` this.controlString = inputs[1] this.initialized = true gen.memo[ this.name ] = this.name this.outputs.forEach( v => v.gen() ) return [ null, ' ' + out ] }, childgen() { if( this.parent.initialized === false ) { gen.getInputs( this ) // parent gate is only input of a gate output, should only be gen'd once. } if( gen.memo[ this.name ] === undefined ) { gen.requestMemory( this.memory ) gen.memo[ this.name ] = `memory[ ${this.memory.value.idx} ]` } return `memory[ ${this.memory.value.idx} ]` } } module.exports = ( control, in1, properties ) => { let ugen = Object.create( proto ), defaults = { count: 2 } if( typeof properties !== undefined ) Object.assign( defaults, properties ) Object.assign( ugen, { outputs: [], uid: gen.getUID(), inputs: [ in1, control ], memory: { lastInput: { length:1, idx:null } }, initialized:false }, defaults ) ugen.name = `${ugen.basename}${gen.getUID()}` for( let i = 0; i < ugen.count; i++ ) { ugen.outputs.push({ index:i, gen: proto.childgen, parent:ugen, inputs: [ ugen ], memory: { value: { length:1, idx:null } }, initialized:false, name: `${ugen.name}_out${gen.getUID()}` }) } return ugen } },{"./gen.js":30}],30:[function(require,module,exports){ 'use strict' /* gen.js * * low-level code generation for unit generators * */ let MemoryHelper = require( 'memory-helper' ) let gen = { accum:0, getUID() { return this.accum++ }, debug:false, samplerate: 44100, // change on audiocontext creation shouldLocalize: false, globals:{ windows: {}, }, mode:'worklet', /* closures * * Functions that are included as arguments to master callback. Examples: Math.abs, Math.random etc. * XXX Should probably be renamed callbackProperties or something similar... closures are no longer used. */ closures: new Set(), params: new Set(), inputs: new Set(), parameters: new Set(), endBlock: new Set(), histories: new Map(), memo: {}, //data: {}, /* export * * place gen functions into another object for easier reference */ export( obj ) {}, addToEndBlock( v ) { this.endBlock.add( ' ' + v ) }, requestMemory( memorySpec, immutable=false ) { for( let key in memorySpec ) { let request = memorySpec[ key ] //console.log( 'requesting ' + key + ':' , JSON.stringify( request ) ) if( request.length === undefined ) { console.log( 'undefined length for:', key ) continue } request.idx = gen.memory.alloc( request.length, immutable ) } }, createMemory( amount, type ) { const mem = MemoryHelper.create( mem, type ) }, /* createCallback * * param ugen - Head of graph to be codegen'd * * Generate callback function for a particular ugen graph. * The gen.closures property stores functions that need to be * passed as arguments to the final function; these are prefixed * before any defined params the graph exposes. For example, given: * * gen.createCallback( abs( param() ) ) * * ... the generated function will have a signature of ( abs, p0 ). */ createCallback( ugen, mem, debug = false, shouldInlineMemory=false, memType = Float64Array ) { let isStereo = Array.isArray( ugen ) && ugen.length > 1, callback, channel1, channel2 if( typeof mem === 'number' || mem === undefined ) { mem = MemoryHelper.create( mem, memType ) } //console.log( 'cb memory:', mem ) this.memory = mem this.outputIdx = this.memory.alloc( 2, true ) this.memo = {} this.endBlock.clear() this.closures.clear() this.inputs.clear() this.params.clear() //this.globals = { windows:{} } this.parameters.clear() this.functionBody = " 'use strict'\n" if( shouldInlineMemory===false ) { this.functionBody += this.mode === 'worklet' ? " var memory = this.memory\n\n" : " var memory = gen.memory\n\n" } // call .gen() on the head of the graph we are generating the callback for //console.log( 'HEAD', ugen ) for( let i = 0; i < 1 + isStereo; i++ ) { if( typeof ugen[i] === 'number' ) continue //let channel = isStereo ? ugen[i].gen() : ugen.gen(), let channel = isStereo ? this.getInput( ugen[i] ) : this.getInput( ugen ), body = '' // if .gen() returns array, add ugen callback (graphOutput[1]) to our output functions body // and then return name of ugen. If .gen() only generates a number (for really simple graphs) // just return that number (graphOutput[0]). body += Array.isArray( channel ) ? channel[1] + '\n' + channel[0] : channel // split body to inject return keyword on last line body = body.split('\n') //if( debug ) console.log( 'functionBody length', body ) // next line is to accommodate memo as graph head if( body[ body.length -1 ].trim().indexOf('let') > -1 ) { body.push( '\n' ) } // get index of last line let lastidx = body.length - 1 // insert return keyword body[ lastidx ] = ' memory[' + (this.outputIdx + i) + '] = ' + body[ lastidx ] + '\n' this.functionBody += body.join('\n') } this.histories.forEach( value => { if( value !== null ) value.gen() }) const returnStatement = isStereo ? ` return [ memory[${this.outputIdx}], memory[${this.outputIdx + 1}] ]` : ` return memory[${this.outputIdx}]` this.functionBody = this.functionBody.split('\n') if( this.endBlock.size ) { this.functionBody = this.functionBody.concat( Array.from( this.endBlock ) ) this.functionBody.push( returnStatement ) }else{ this.functionBody.push( returnStatement ) } // reassemble function body this.functionBody = this.functionBody.join('\n') // we can only dynamically create a named function by dynamically creating another function // to construct the named function! sheesh... // if( shouldInlineMemory === true ) { this.parameters.add( 'memory' ) } let paramString = '' if( this.mode === 'worklet' ) { for( let name of this.parameters.values() ) { paramString += name + ',' } paramString = paramString.slice(0,-1) } const separator = this.parameters.size !== 0 && this.inputs.size > 0 ? ', ' : '' let inputString = '' if( this.mode === 'worklet' ) { for( let ugen of this.inputs.values() ) { inputString+= ugen.name + ',' } inputString = inputString.slice(0,-1) } let buildString = this.mode === 'worklet' ? `return function( ${inputString} ${separator} ${paramString} ){ \n${ this.functionBody }\n}` : `return function gen( ${ [...this.parameters].join(',') } ){ \n${ this.functionBody }\n}` if( this.debug || debug ) console.log( buildString ) callback = new Function( buildString )() // assign properties to named function for( let dict of this.closures.values() ) { let name = Object.keys( dict )[0], value = dict[ name ] callback[ name ] = value } for( let dict of this.params.values() ) { let name = Object.keys( dict )[0], ugen = dict[ name ] Object.defineProperty( callback, name, { configurable: true, get() { return ugen.value }, set(v){ ugen.value = v } }) //callback[ name ] = value } callback.members = this.closures callback.data = this.data callback.params = this.params callback.inputs = this.inputs callback.parameters = this.parameters//.slice( 0 ) callback.isStereo = isStereo //if( MemoryHelper.isPrototypeOf( this.memory ) ) callback.memory = this.memory.heap this.histories.clear() return callback }, /* getInputs * * Called by each individual ugen when their .gen() method is called to resolve their various inputs. * If an input is a number, return the number. If * it is an ugen, call .gen() on the ugen, memoize the result and return the result. If the * ugen has previously been memoized return the memoized value. * */ getInputs( ugen ) { return ugen.inputs.map( gen.getInput ) }, getInput( input ) { let isObject = typeof input === 'object', processedInput if( isObject ) { // if input is a ugen... //console.log( input.name, gen.memo[ input.name ] ) if( gen.memo[ input.name ] ) { // if it has been memoized... processedInput = gen.memo[ input.name ] }else if( Array.isArray( input ) ) { gen.getInput( input[0] ) gen.getInput( input[1] ) }else{ // if not memoized generate code if( typeof input.gen !== 'function' ) { console.log( 'no gen found:', input, input.gen ) } let code = input.gen() //if( code.indexOf( 'Object' ) > -1 ) console.log( 'bad input:', input, code ) if( Array.isArray( code ) ) { if( !gen.shouldLocalize ) { gen.functionBody += code[1] }else{ gen.codeName = code[0] gen.localizedCode.push( code[1] ) } //console.log( 'after GEN' , this.functionBody ) processedInput = code[0] }else{ processedInput = code } } }else{ // it input is a number processedInput = input } return processedInput }, startLocalize() { this.localizedCode = [] this.shouldLocalize = true }, endLocalize() { this.shouldLocalize = false return [ this.codeName, this.localizedCode.slice(0) ] }, free( graph ) { if( Array.isArray( graph ) ) { // stereo ugen for( let channel of graph ) { this.free( channel ) } } else { if( typeof graph === 'object' ) { if( graph.memory !== undefined ) { for( let memoryKey in graph.memory ) { this.memory.free( graph.memory[ memoryKey ].idx ) } } if( Array.isArray( graph.inputs ) ) { for( let ugen of graph.inputs ) { this.free( ugen ) } } } } } } module.exports = gen },{"memory-helper":141}],31:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { basename:'gt', gen() { let out, inputs = gen.getInputs( this ) out = ` var ${this.name} = ` if( isNaN( this.inputs[0] ) || isNaN( this.inputs[1] ) ) { out += `(( ${inputs[0]} > ${inputs[1]}) | 0 )` } else { out += inputs[0] > inputs[1] ? 1 : 0 } out += '\n\n' gen.memo[ this.name ] = this.name return [this.name, out] } } module.exports = (x,y) => { let gt = Object.create( proto ) gt.inputs = [ x,y ] gt.name = gt.basename + gen.getUID() return gt } },{"./gen.js":30}],32:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { name:'gte', gen() { let out, inputs = gen.getInputs( this ) out = ` var ${this.name} = ` if( isNaN( this.inputs[0] ) || isNaN( this.inputs[1] ) ) { out += `( ${inputs[0]} >= ${inputs[1]} | 0 )` } else { out += inputs[0] >= inputs[1] ? 1 : 0 } out += '\n\n' gen.memo[ this.name ] = this.name return [this.name, out] } } module.exports = (x,y) => { let gt = Object.create( proto ) gt.inputs = [ x,y ] gt.name = 'gte' + gen.getUID() return gt } },{"./gen.js":30}],33:[function(require,module,exports){ 'use strict' let gen = require('./gen.js') let proto = { name:'gtp', gen() { let out, inputs = gen.getInputs( this ) if( isNaN( this.inputs[0] ) || isNaN( this.inputs[1] ) ) { out = `(${inputs[ 0 ]} * ( ( ${inputs[0]} > ${inputs[1]} ) | 0 ) )` } else { out = inputs[0] * ( ( inputs[0] > inputs[1] ) | 0 ) } return out } } module.exports = (x,y) => { let gtp = Object.create( proto ) gtp.inputs = [ x,y ] return gtp } },{"./gen.js":30}],34:[function(require,module,exports){ 'use strict' let gen = require('./gen