UNPKG

gibberish-dsp

Version:

Gibberish is designed to be an optimized API for audio synthesis using per-sample techniques.

1,868 lines (1,438 loc) 528 kB
(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(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({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":33}],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":33}],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":33}],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":19,"./div.js":24,"./env.js":25,"./gen.js":33,"./gte.js":35,"./ifelseif.js":38,"./lt.js":41,"./memo.js":45,"./mul.js":51,"./neq.js":52,"./peek.js":57,"./poke.js":61,"./sub.js":72,"./utilities.js":78}],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":33}],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 ) { out.node.port.postMessage({ key:'set', idx:releaseAccum.inputs[0].inputs[1].memory.value.idx, value:0 }) }else{ 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":19,"./div.js":24,"./env.js":25,"./gen.js":33,"./gtp.js":36,"./ifelseif.js":38,"./lt.js":41,"./mul.js":51,"./neq.js":52,"./not.js":54,"./param.js":56,"./peek.js":57,"./poke.js":61,"./sub.js":72}],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":33}],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":33}],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":33}],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":33,"./history.js":37,"./mul.js":51,"./sub.js":72}],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{ if( gen.memory && gen.memory.heap ) gen.memory.heap[ ugen.memory.value.idx ] = ugen.max } } ugen.memory = { value: { length:1, idx:null } } return ugen } },{"./gen.js":33}],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":33}],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":33}],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":30,"./gen.js":33,"./memo.js":45,"./sub.js":72}],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":33}],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 ) gen.memory.heap[ this.memory.value.idx ] = this.initialValue 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 = Object.assign( { initialValue: 0, shouldWrap:true }, properties ) Object.assign( ugen, { min: min, max: max, initialValue: defaults.initialValue, 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() { //console.log( 'counter value', this.memory.value.idx, gen.memory.heap[ this.memory.value.idx ], gen.memory ) if( this.memory.value.idx !== null ) { return gen.memory.heap[ this.memory.value.idx ] } }, set( v ) { if( this.memory.value.idx !== null ) { //console.log( 'settting counter', v ) 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":33}],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":19,"./gen.js":33,"./mul.js":51,"./peek.js":57,"./phasor.js":59}],18:[function(require,module,exports){ 'use strict' const gen = require( './gen.js' ), accum= require( './phasor.js' ), data = require( './data.js' ), peek = require( './peek.js' ), mul = require( './mul.js' ), add = require( './add.js' ), phasor=require( './phasor.js') const proto = { basename:'cycleN', 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 = mul( add( 1, peek( gen.globals.cycle, phasor( frequency, reset, props )) ), .5 ) ugen.name = 'cycle' + gen.getUID() return ugen } },{"./add.js":5,"./data.js":19,"./gen.js":33,"./mul.js":51,"./peek.js":57,"./phasor.js":59}],19:[function(require,module,exports){ 'use strict' const gen = require('./gen.js'), utilities = require( './utilities.js' ), peek = require('./peek.js'), poke = require('./poke.js') const proto = { basename:'data', globals: {}, memo:{}, gen() { let idx //console.log( 'data name:', this.name, proto.memo ) //debugger if( gen.memo[ this.name ] === undefined ) { let ugen = this gen.requestMemory( this.memory, this.immutable ) idx = this.memory.values.idx if( this.buffer !== undefined ) { 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' if( this.name.indexOf('data') === -1 ) { proto.memo[ this.name ] = idx }else{ gen.memo[ this.name ] = idx } }else{ //console.log( 'using gen data memo', proto.memo[ this.name ] ) 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??? //if( proto.memo[ x ] === undefined ) { buffer = { length: y > 1 ? y : 1 } // XXX what??? shouldLoad = true //}else{ //buffer = proto.memo[ x ] //} }else if( x instanceof Float32Array ) { buffer = x }else if( x instanceof Uint8Array ) { buffer = x }else if( x instanceof AudioBuffer ) { buffer = x.getChannelData(0) } ugen = Object.create( proto ) Object.assign( ugen, { buffer, name: proto.basename + gen.getUID(), dim: buffer !== undefined ? buffer.length : 1, // XXX how do we dynamically allocate this? channels : 1, onload: properties !== undefined ? properties.onload || null : null, //then( fnc ) { // ugen.onload = fnc // return ugen //}, immutable: properties !== undefined && properties.immutable === true ? true : false, load( filename, __resolve ) { let promise = utilities.loadSample( filename, ugen ) promise.then( _buffer => { proto.memo[ x ] = _buffer ugen.name = filename ugen.memory.values.length = ugen.dim = _buffer.length gen.requestMemory( ugen.memory, ugen.immutable ) gen.memory.heap.set( _buffer, ugen.memory.values.idx ) if( typeof ugen.onload === 'function' ) ugen.onload( _buffer ) __resolve( ugen ) }) }, memory : { values: { length:buffer !== undefined ? buffer.length : 1, idx:null } } }, properties ) 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 ) } }) } } } let returnValue if( shouldLoad === true ) { returnValue = new Promise( (resolve,reject) => { //ugen.load( x, resolve ) let promise = utilities.loadSample( x, ugen ) promise.then( _buffer => { proto.memo[ x ] = _buffer ugen.memory.values.length = ugen.dim = _buffer.length ugen.buffer = _buffer //gen.once( 'memory init', ()=> { // console.log( "CALLED", ugen.memory ) // gen.requestMemory( ugen.memory, ugen.immutable ) // gen.memory.heap.set( _buffer, ugen.memory.values.idx ) // if( typeof ugen.onload === 'function' ) ugen.onload( _buffer ) //}) resolve( ugen ) }) }) }else if( proto.memo[ x ] !== undefined ) { gen.once( 'memory init', ()=> { gen.requestMemory( ugen.memory, ugen.immutable ) gen.memory.heap.set( ugen.buffer, ugen.memory.values.idx ) if( typeof ugen.onload === 'function' ) ugen.onload( ugen.buffer ) }) returnValue = ugen }else{ returnValue = ugen } return returnValue } },{"./gen.js":33,"./peek.js":57,"./poke.js":61,"./utilities.js":78}],20:[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":33,"./history.js":37,"./memo.js":45,"./mul.js":51,"./sub.js":72}],21:[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":33,"./history.js":37,"./mul.js":51,"./t60.js":74}],22:[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":19,"./gen.js":33,"./memo.js":45,"./peek.js":57,"./poke.js":61,"./sub.js":72,"./wrap.js":80}],23:[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":33,"./history.js":37,"./sub.js":72}],24:[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":33}],25:[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":19,"./gen":33,"./peek":57,"./phasor":59,"./windows":79}],26:[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":33}],27:[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":33}],28:[function(require,module,exports){ /** * Copyright 2018 Google LLC * * 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. */ // originally from: // https://github.com/GoogleChromeLabs/audioworklet-polyfill // I am modifying it to accept variable buffer sizes // and to get rid of some strange global initialization that seems required to use it // with browserify. Also, I added changes to fix a bug in Safari for the AudioWorkletProcessor // property not having a prototype (see:https://github.com/GoogleChromeLabs/audioworklet-polyfill/pull/25) // TODO: Why is there an iframe involved? (realm.js) const Realm = require( './realm.js' ) const AWPF = function( self = window, bufferSize = 4096 ) { const PARAMS = [] let nextPort if (typeof AudioWorkletNode !== 'function' || !("audioWorklet" in AudioContext.prototype)) { self.AudioWorkletNode = function AudioWorkletNode (context, name, options) { const processor = getProcessorsForContext(context)[name]; const outputChannels = options && options.outputChannelCount ? options.outputChannelCount[0] : 2; const scriptProcessor = context.createScriptProcessor( bufferSize, 2, outputChannels); scriptProcessor.parameters = new Map(); if (processor.properties) { for (let i = 0; i < processor.properties.length; i++) { const prop = processor.properties[i]; const node = context.createGain().gain; node.value = prop.defaultValue; // @TODO there's no good way to construct the proxy AudioParam here scriptProcessor.parameters.set(prop.name, node); } } const mc = new MessageChannel(); nextPort = mc.port2; const inst = new processor.Processor(options || {}); nextPort = null; scriptProcessor.port = mc.port1; scriptProcessor.processor = processor; scriptProcessor.instance = inst; scriptProcessor.onaudioprocess = onAudioProcess; return scriptProcessor; }; Object.defineProperty((self.AudioContext || self.webkitAudioContext).prototype, 'audioWorklet', { get () { return this.$$audioWorklet || (this.$$audioWorklet = new self.AudioWorklet(this)); } }); /* XXX - ADDED TO OVERCOME PROBLEM IN SAFARI WHERE AUDIOWORKLETPROCESSOR PROTOTYPE IS NOT AN OBJECT */ const AudioWorkletProcessor = function() { this.port = nextPort } AudioWorkletProcessor.prototype = {} self.AudioWorklet = class AudioWorklet { constructor (audioContext) { this.$$context = audioContext; } addModule (url, options) { return fetch(url).then(r => { if (!r.ok) throw Error(r.status); return r.text(); }).then( code => { const context = { sampleRate: this.$$context.sampleRate, currentTime: this.$$context.currentTime, AudioWorkletProcessor, registerProcessor: (name, Processor) => { const processors = getProcessorsForContext(this.$$context); processors[name] = { realm, context, Processor, properties: Processor.parameterDescriptors || [] }; } }; context.self = context; const realm = new Realm(context, document.documentElement); realm.exec(((options && options.transpile) || String)(code)); return null; }); } }; } function onAudioProcess (e) { const parameters = {}; let index = -1; this.parameters.forEach((value, key) => { const arr = PARAMS[++index] || (PARAMS[index] = new Float32Array(this.bufferSize)); // @TODO proper values here if possible arr.fill(value.value); parameters[key] = arr; }); this.processor.realm.exec( 'self.sampleRate=sampleRate=' + this.context.sampleRate + ';' + 'self.currentTime=currentTime=' + this.context.currentTime ); const inputs = channelToArray(e.inputBuffer); const outputs = channelToArray(e.outputBuffer); this.instance.process([inputs], [outputs], parameters); } function channelToArray (ch) { const out = []; for (let i = 0; i < ch.numberOfChannels; i++) { out[i] = ch.getChannelData(i); } return out; } function getProcessorsForContext (audioContext) { return audioContext.$$processors || (audioContext.$$processors = {}); } } module.exports = AWPF },{"./realm.js":29}],29:[function(require,module,exports){ /** * Copyright 2018 Google LLC * * 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. */ module.exports = function Realm (scope, parentElement) { const frame = document.createElement('iframe'); frame.style.cssText = 'position:absolute;left:0;top:-999px;width:1px;height:1px;'; parentElement.appendChild(frame); const win = frame.contentWindow; const doc = win.document; let vars = 'var window,$hook'; for (const i in win) { if (!(i in scope) && i !== 'eval') { vars += ','; vars += i; } } for (const i in scope) { vars += ','; vars += i; vars += '=self.'; vars += i; } const script = doc.createElement('script'); script.appendChild(doc.createTextNode( `function $hook(self,console) {"use strict"; ${vars};return function() {return eval(arguments[0])}}` )); doc.body.appendChild(script); this.exec = win.$hook.call(scope, scope, console); } },{}],30:[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":33}],31:[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":33}],32:[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":33}],33:[function(require,module,exports){ 'use strict' /* gen.js * * low-level code generation for unit generators * */ const MemoryHelper = require( 'memory-helper' ) const EE = require( 'events' ).EventEmitter const gen = { accum:0, getUID() { return this.accum++ }, debug:false, samplerate: 44100, // change on audiocontext creation shouldLocalize: false, graph:null, alwaysReturnArrays: 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( reques