gibberish-dsp
Version:
Gibberish is designed to be an optimized API for audio synthesis using per-sample techniques.
125 lines (101 loc) • 3.94 kB
JavaScript
const g = require( 'genish.js' ),
instrument = require( './instrument.js' ),
__wavefold = require( '../fx/wavefolder.dsp.js' )
const genish = g
module.exports = function( Gibberish ) {
const wavefold = __wavefold( Gibberish )[1]
const Complex = inputProps => {
const syn = Object.create( instrument )
const frequency = g.in( 'frequency' ),
loudness = g.in( 'loudness' ),
triggerLoudness = g.in( '__triggerLoudness' ),
glide = g.max( 1, g.in( 'glide' ) ),
slidingFreq = g.slide( frequency, glide, glide ),
attack = g.in( 'attack' ),
decay = g.in( 'decay' ),
sustain = g.in( 'sustain' ),
sustainLevel = g.in( 'sustainLevel' ),
release = g.in( 'release' ),
pregain = g.in( 'pregain' ),
postgain= g.in( 'postgain' ),
bias = g.in( 'bias' )
const props = Object.assign( {}, Complex.defaults, inputProps )
Object.assign( syn, props )
syn.__createGraph = function() {
const osc = Gibberish.oscillators.factory( syn.waveform, slidingFreq, syn.antialias )
const env = Gibberish.envelopes.factory(
props.useADSR,
props.shape,
attack, decay,
sustain, sustainLevel,
release,
props.triggerRelease
)
const saturation = g.in('saturation')
// below doesn't work as it attempts to assign to release property triggering codegen...
// syn.release = ()=> { syn.env.release() }
{
'use jsdsp'
let oscWithEnv = osc * env * loudness * triggerLoudness,
panner
let foldedOsc = wavefold( wavefold( wavefold( wavefold( bias + oscWithEnv * (pregain * env) * .333 ) ) ) )
foldedOsc = g.tanh( foldedOsc * .6 ) * postgain
// 16 is an unfortunate empirically derived magic number...
const baseCutoffFreq = g.in('cutoff') * ( frequency / ( g.gen.samplerate / 16 ) )
const cutoff = g.min( baseCutoffFreq * g.pow( 2, g.in('filterMult') * loudness * triggerLoudness ) * env, .995 )
const filteredOsc = Gibberish.filters.factory( foldedOsc, cutoff, saturation, props )
let complexWithGain = filteredOsc * g.in( 'gain' )
// XXX ugly, ugly hack
if( props.filterModel !== 2 ) complexWithGain = complexWithGain * saturation
if( syn.panVoices === true ) {
panner = g.pan( complexWithGain, complexWithGain, g.in( 'pan' ) )
syn.graph = [ panner.left, panner.right ]
}else{
syn.graph = complexWithGain
}
syn.env = env
syn.osc = osc
syn.filter = filteredOsc
}
}
syn.__requiresRecompilation = [ 'waveform', 'antialias', 'filterModel','filterMode', 'useADSR', 'shape' ]
syn.__createGraph()
const out = Gibberish.factory( syn, syn.graph, ['instruments', 'complex'], props )
return out
}
Complex.defaults = {
waveform:'triangle',
attack: 44,
decay: 22050,
sustain:44100,
sustainLevel:.6,
release:22050,
useADSR:false,
shape:'exponential',
triggerRelease:false,
gain: .5,
pulsewidth:.25,
frequency:220,
pan: .5,
antialias:true,
panVoices:false,
loudness:1,
__triggerLoudness:1,
glide:1,
saturation:1,
filterMult:2,
Q:.25,
cutoff:.5,
//filterType:1,
filterModel:1,
filterMode:0,
isStereo:false,
pregain:4,
postgain:1,
bias:0
}
// do not include velocity, which shoudl always be per voice
let PolyComplex = Gibberish.PolyTemplate( Complex, ['frequency','attack','decay','pulsewidth','pan','gain','glide', 'saturation', 'filterMult', 'Q', 'cutoff', 'resonance', 'antialias', 'filterModel', 'waveform', 'filterMode', '__triggerLoudness', 'loudness', 'pregain', 'postgain', 'bias'] )
PolyComplex.defaults = Complex.defaults
return [ Complex, PolyComplex ]
}