genish.js
Version:
95 lines (77 loc) • 3.14 kB
JavaScript
'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
}