genish.js
Version:
118 lines (96 loc) • 3.9 kB
JavaScript
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
}