UNPKG

genish.js

Version:
118 lines (96 loc) 3.9 kB
'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 }