UNPKG

gibberish-dsp

Version:

Gibberish is designed to be an optimized API for audio synthesis using per-sample techniques.

209 lines (168 loc) 7.16 kB
const g = require( 'genish.js' ), filter = require( './filter.js' ) const genish = g module.exports = function( Gibberish ) { Gibberish.genish.diodeZDF = ( input, __Q, __freq, saturation, isStereo=false ) => { const iT = 1 / g.gen.samplerate, kz1 = g.history(0), kz2 = g.history(0), kz3 = g.history(0), kz4 = g.history(0) let ka1 = 1.0, ka2 = 0.5, ka3 = 0.5, ka4 = 0.5, kindx = 0 const freq = g.mul( g.max(.005, g.min( __freq, .995)), genish.gen.samplerate / 2 ) //const freq = g.max(.005, g.min( __freq, .995)) // XXX this is where the magic number hapens for Q... const Q = g.memo( g.add( .5, g.mul( __Q, g.add( 5, g.sub( 5, g.mul( g.div( freq, 20000 ), 5 ) ) ) ) ) ) // kwd = 2 * $M_PI * acf[kindx] const kwd = g.memo( g.mul( Math.PI * 2, freq ) ) // kwa = (2/iT) * tan(kwd * iT/2) const kwa =g.memo( g.mul( 2/iT, g.tan( g.mul( kwd, iT/2 ) ) ) ) // kG = kwa * iT/2 const kg = g.memo( g.mul( kwa, iT/2 ) ) const kG4 = g.memo( g.mul( .5, g.div( kg, g.add( 1, kg ) ) ) ) const kG3 = g.memo( g.mul( .5, g.div( kg, g.sub( g.add( 1, kg ), g.mul( g.mul( .5, kg ), kG4 ) ) ) ) ) const kG2 = g.memo( g.mul( .5, g.div( kg, g.sub( g.add( 1, kg ), g.mul( g.mul( .5, kg ), kG3 ) ) ) ) ) const kG1 = g.memo( g.div( kg, g.sub( g.add( 1, kg ), g.mul( kg, kG2 ) ) ) ) const kGAMMA = g.memo( g.mul( g.mul( kG4, kG3 ) , g.mul( kG2, kG1 ) ) ) const kSG1 = g.memo( g.mul( g.mul( kG4, kG3 ), kG2 ) ) const kSG2 = g.memo( g.mul( kG4, kG3) ) const kSG3 = kG4 let kSG4 = 1.0 // kk = 4.0*(kQ - 0.5)/(25.0 - 0.5) const kalpha = g.memo( g.div( kg, g.add(1.0, kg) ) ) const kbeta1 = g.memo( g.div( 1.0, g.sub( g.add( 1, kg ), g.mul( kg, kG2 ) ) ) ) const kbeta2 = g.memo( g.div( 1.0, g.sub( g.add( 1, kg ), g.mul( g.mul( .5, kg ), kG3 ) ) ) ) const kbeta3 = g.memo( g.div( 1.0, g.sub( g.add( 1, kg ), g.mul( g.mul( .5, kg ), kG4 ) ) ) ) const kbeta4 = g.memo( g.div( 1.0, g.add( 1, kg ) ) ) const kgamma1 = g.memo( g.add( 1, g.mul( kG1, kG2 ) ) ) const kgamma2 = g.memo( g.add( 1, g.mul( kG2, kG3 ) ) ) const kgamma3 = g.memo( g.add( 1, g.mul( kG3, kG4 ) ) ) const kdelta1 = kg const kdelta2 = g.memo( g.mul( 0.5, kg ) ) const kdelta3 = g.memo( g.mul( 0.5, kg ) ) const kepsilon1 = kG2 const kepsilon2 = kG3 const kepsilon3 = kG4 const klastcut = freq //;; feedback inputs const kfb4 = g.memo( g.mul( kbeta4 , kz4.out ) ) const kfb3 = g.memo( g.mul( kbeta3, g.add( kz3.out, g.mul( kfb4, kdelta3 ) ) ) ) const kfb2 = g.memo( g.mul( kbeta2, g.add( kz2.out, g.mul( kfb3, kdelta2 ) ) ) ) //;; feedback process const kfbo1 = g.memo( g.mul( kbeta1, g.add( kz1.out, g.mul( kfb2, kdelta1 ) ) ) ) const kfbo2 = g.memo( g.mul( kbeta2, g.add( kz2.out, g.mul( kfb3, kdelta2 ) ) ) ) const kfbo3 = g.memo( g.mul( kbeta3, g.add( kz3.out, g.mul( kfb4, kdelta3 ) ) ) ) const kfbo4 = kfb4 const kSIGMA = g.memo( g.add( g.add( g.mul( kSG1, kfbo1 ), g.mul( kSG2, kfbo2 ) ), g.add( g.mul( kSG3, kfbo3 ), g.mul( kSG4, kfbo4 ) ) ) ) //const kSIGMA = 1 //;; non-linear processing //if (knlp == 1) then // kin = (1.0 / tanh(ksaturation)) * tanh(ksaturation * kin) //elseif (knlp == 2) then // kin = tanh(ksaturation * kin) //endif // //const kin = input let kin = isStereo === true ? g.add( input[0], input[1] ) : input//g.memo( g.mul( g.div( 1, g.tanh( saturation ) ), g.tanh( g.mul( saturation, input ) ) ) ) kin = g.tanh( g.mul( saturation, kin ) ) const kun = g.div( g.sub( kin, g.mul( Q, kSIGMA ) ), g.add( 1, g.mul( Q, kGAMMA ) ) ) //const kun = g.div( 1, g.add( 1, g.mul( Q, kGAMMA ) ) ) //(kin - kk * kSIGMA) / (1.0 + kk * kGAMMA) //;; 1st stage let kxin = g.memo( g.add( g.add( g.mul( kun, kgamma1 ), kfb2), g.mul( kepsilon1, kfbo1 ) ) ) // (kun * kgamma1 + kfb2 + kepsilon1 * kfbo1) let kv = g.memo( g.mul( g.sub( g.mul( ka1, kxin ), kz1.out ), kalpha ) ) //kv = (ka1 * kxin - kz1) * kalpha let klp = g.add( kv, kz1.out ) //klp = kv + kz1 kz1.in( g.add( klp, kv ) ) //kz1 = klp + kv //;; 2nd stage //kxin = (klp * kgamma2 + kfb3 + kepsilon2 * kfbo2) //kv = (ka2 * kxin - kz2) * kalpha //klp = kv + kz2 //kz2 = klp + kv kxin = g.memo( g.add( g.add( g.mul( klp, kgamma2 ), kfb3), g.mul( kepsilon2, kfbo2 ) ) ) // (kun * kgamma1 + kfb2 + kepsilon1 * kfbo1) kv = g.memo( g.mul( g.sub( g.mul( ka2, kxin ), kz2.out ), kalpha ) ) //kv = (ka1 * kxin - kz1) * kalpha klp = g.add( kv, kz2.out ) //klp = kv + kz1 kz2.in( g.add( klp, kv ) ) //kz1 = klp + kv //;; 3rd stage //kxin = (klp * kgamma3 + kfb4 + kepsilon3 * kfbo3) //kv = (ka3 * kxin - kz3) * kalpha //klp = kv + kz3 //kz3 = klp + kv kxin = g.memo( g.add( g.add( g.mul( klp, kgamma3 ), kfb4), g.mul( kepsilon3, kfbo3 ) ) ) // (kun * kgamma1 + kfb2 + kepsilon1 * kfbo1) kv = g.memo( g.mul( g.sub( g.mul( ka3, kxin ), kz3.out ), kalpha ) ) //kv = (ka1 * kxin - kz1) * kalpha klp = g.add( kv, kz3.out ) //klp = kv + kz1 kz3.in( g.add( klp, kv ) ) //kz1 = klp + kv //;; 4th stage //kv = (ka4 * klp - kz4) * kalpha //klp = kv + kz4 //kz4 = klp + kv // (kun * kgamma1 + kfb2 + kepsilon1 * kfbo1) kv = g.memo( g.mul( g.sub( g.mul( ka4, kxin ), kz4.out ), kalpha ) ) //kv = (ka1 * kxin - kz1) * kalpha klp = g.add( kv, kz4.out ) //klp = kv + kz1 kz4.in( g.add( klp, kv ) ) //kz1 = klp + kv if( isStereo ) { //let polesR = g.data([ 0,0,0,0 ], 1, { meta:true }), // rezzR = g.clamp( g.mul( polesR[3], rez ) ), // outputR = g.sub( input[1], rezzR ) //polesR[0] = g.add( polesR[0], g.mul( g.add( g.mul(-1, polesR[0] ), outputR ), cutoff )) //polesR[1] = g.add( polesR[1], g.mul( g.add( g.mul(-1, polesR[1] ), polesR[0] ), cutoff )) //polesR[2] = g.add( polesR[2], g.mul( g.add( g.mul(-1, polesR[2] ), polesR[1] ), cutoff )) //polesR[3] = g.add( polesR[3], g.mul( g.add( g.mul(-1, polesR[3] ), polesR[2] ), cutoff )) //let right = g.switch( isLowPass, polesR[3], g.sub( outputR, polesR[3] ) ) //returnValue = [left, right] }else{ // returnValue = klp } //returnValue = klp return klp } const DiodeZDF = inputProps => { const zdf = Object.create( filter ) const props = Object.assign( {}, DiodeZDF.defaults, filter.defaults, inputProps ) const isStereo = props.input.isStereo Object.assign( zdf, props ) const __out = Gibberish.factory( zdf, Gibberish.genish.diodeZDF( g.in('input'), g.in('Q'), g.in('cutoff'), g.in('saturation'), isStereo ), ['filters','Filter24TB303'], props ) return __out } DiodeZDF.defaults = { input:0, Q: .65, saturation: 1, cutoff:.5 } return DiodeZDF }