gibberish-dsp
Version:
Gibberish is designed to be an optimized API for audio synthesis using per-sample techniques.
160 lines (135 loc) • 4.93 kB
JavaScript
const __proxy = require( './workletProxy.js' )
const effectProto = require( './fx/effect.js' )
module.exports = function( Gibberish ) {
const proxy = __proxy( Gibberish )
const factory = function( ugen, graph, __name, values, cb=null, shouldProxy = true ) {
if( Gibberish.mode === 'processor' )
ugen.callback = cb === null ? Gibberish.genish.gen.createCallback( graph, Gibberish.memory, false, true ) : cb
else
ugen.callback = { out:[] }
let name = Array.isArray( __name ) ? __name[ __name.length - 1 ] : __name
Object.assign( ugen, {
//type: 'ugen',
id: values.id || Gibberish.utilities.getUID(),
ugenName: name + '_',
graph: graph,
inputNames: ugen.inputNames || new Set( Gibberish.genish.gen.parameters ),
isStereo: Array.isArray( graph ),
dirty: true,
__properties__:values,
__addresses__:{}
})
ugen.ugenName += ugen.id
if( Gibberish.mode === 'processor' ) {
ugen.callback.ugenName = ugen.ugenName // XXX hacky
ugen.callback.id = ugen.id
}
//console.log( 'ugen name/id:', ugen.ugenName, ugen.id )
//console.log( 'callback name/id:', ugen.callback.ugenName, ugen.callback.id )
for( let param of ugen.inputNames ) {
if( param === 'memory' ) continue
let value = values[ param ],
isNumber = typeof value === 'object' || isNaN( value ) ? false : true,
idx
if( isNumber ) {
idx = Gibberish.memory.alloc( 1 )
Gibberish.memory.heap[ idx ] = value
ugen.__addresses__[ param ] = idx
}
// TODO: do we need to check for a setter?
let desc = Object.getOwnPropertyDescriptor( ugen, param ),
setter
if( desc !== undefined ) {
setter = desc.set
}
Object.defineProperty( ugen, param, {
configurable:true,
get() {
if( isNumber ) {
return Gibberish.memory.heap[ idx ]
}else{
return value
}
},
set( v ) {
//if( param === 'input' ) console.log( 'INPUT:', v, isNumber )
if( value !== v ) {
if( setter !== undefined ) setter( v )
if( typeof v === 'number' ) {
Gibberish.memory.heap[ idx ] = value = v
if( isNumber === false ) Gibberish.dirty( ugen )
isNumber = true
}else{
value = v
/*if( isNumber === true )*/ Gibberish.dirty( ugen )
//console.log( 'switching from number:', param, value )
isNumber = false
}
}
}
})
}
// add bypass
if( effectProto.isPrototypeOf( ugen ) ) {
let value = ugen.bypass
Object.defineProperty( ugen, 'bypass', {
configurable:true,
get() { return value },
set( v ) {
if( value !== v ) {
Gibberish.dirty( ugen )
value = v
}
}
})
}
if( ugen.__requiresRecompilation !== undefined ) {
ugen.__requiresRecompilation.forEach( prop => {
let value = values[ prop ]
let isNumber = !isNaN( value )
Object.defineProperty( ugen, prop, {
configurable:true,
get() {
if( isNumber ) {
let idx = ugen.__addresses__[ prop ]
return Gibberish.memory.heap[ idx ]
}else{
//console.log( 'returning:', prop, value, Gibberish.mode )
return value
}
},
set( v ) {
if( value !== v ) {
if( typeof v === 'number' ) {
let idx = ugen.__addresses__[ prop ]
if( idx === undefined ){
idx = Gibberish.memory.alloc( 1 )
ugen.__addresses__[ prop ] = idx
}
value = values[ prop ] = Gibberish.memory.heap[ idx ] = v
isNumber = true
}else{
value = values[ prop ] = v
isNumber = false
//console.log( 'setting ugen', value, Gibberish.mode )
Gibberish.dirty( ugen )
}
//console.log( 'SETTING REDO GRAPH', prop, Gibberish.mode )
// needed for filterType at the very least, becauae the props
// are reused when re-creating the graph. This seems like a cheaper
// way to solve this problem.
//values[ prop ] = v
this.__redoGraph()
}
}
})
})
}
// will only create proxy if worklets are being used
// otherwise will return unaltered ugen
if( values.shouldAddToUgen === true ) Object.assign( ugen, values )
return shouldProxy ? proxy( __name, values, ugen ) : ugen
}
factory.getUID = () => { return Gibberish.utilities.getUID() }
return factory
}