gibberish-dsp
Version:
Gibberish is designed to be an optimized API for audio synthesis using per-sample techniques.
124 lines (96 loc) • 4.3 kB
JavaScript
let g = require( 'genish.js' ),
effect = require( './effect.js' )
module.exports = function( Gibberish ) {
let proto = Object.create( effect )
let Shuffler = inputProps => {
let bufferShuffler = Object.create( proto ),
bufferSize = 88200
const props = Object.assign( {}, Shuffler.defaults, effect.defaults, inputProps )
let out
bufferShuffler.__createGraph = function() {
let isStereo = false
if( out === undefined ) {
isStereo = typeof props.input.isStereo !== 'undefined' ? props.input.isStereo : true
}else{
isStereo = out.input.isStereo
//out.isStereo = isStereo
}
const phase = g.accum( 1,0,{ shouldWrap: false })
const input = g.in( 'input' ),
inputGain = g.in( 'inputGain' ),
__leftInput = isStereo ? input[ 0 ] : input,
__rightInput = isStereo ? input[ 1 ] : null,
leftInput = g.mul( __leftInput, inputGain ),
rightInput = g.mul( __rightInput, inputGain ),
rateOfShuffling = g.in( 'rate' ),
chanceOfShuffling = g.in( 'chance' ),
reverseChance = g.in( 'reverseChance' ),
repitchChance = g.in( 'repitchChance' ),
repitchMin = g.in( 'repitchMin' ),
repitchMax = g.in( 'repitchMax' )
let pitchMemory = g.history(1)
let shouldShuffleCheck = g.eq( g.mod( phase, rateOfShuffling ), 0 )
let isShuffling = g.memo( g.sah( g.lt( g.noise(), chanceOfShuffling ), shouldShuffleCheck, 0 ) )
// if we are shuffling and on a repeat boundary...
let shuffleChanged = g.memo( g.and( shouldShuffleCheck, isShuffling ) )
let shouldReverse = g.lt( g.noise(), reverseChance ),
reverseMod = g.switch( shouldReverse, -1, 1 )
let pitch = g.ifelse(
g.and( shuffleChanged, g.lt( g.noise(), repitchChance ) ),
g.memo( g.mul( g.add( repitchMin, g.mul( g.sub( repitchMax, repitchMin ), g.noise() ) ), reverseMod ) ),
reverseMod
)
// only switch pitches on repeat boundaries
pitchMemory.in( g.switch( shuffleChanged, pitch, pitchMemory.out ) )
let fadeLength = g.memo( g.div( rateOfShuffling, 100 ) ),
fadeIncr = g.memo( g.div( 1, fadeLength ) )
const bufferL = g.data( bufferSize )
const bufferR = isStereo ? g.data( bufferSize ) : null
let readPhase = g.accum( pitchMemory.out, 0, { shouldWrap:false })
let stutter = g.wrap( g.sub( g.mod( readPhase, bufferSize ), 22050 ), 0, bufferSize )
let normalSample = g.peek( bufferL, g.accum( 1, 0, { max:88200 }), { mode:'simple' })
let stutterSamplePhase = g.switch( isShuffling, stutter, g.mod( readPhase, bufferSize ) )
let stutterSample = g.memo( g.peek(
bufferL,
stutterSamplePhase,
{ mode:'samples' }
) )
let stutterShouldFadeIn = g.and( shuffleChanged, isShuffling )
let stutterPhase = g.accum( 1, shuffleChanged, { shouldWrap: false })
let fadeInAmount = g.memo( g.div( stutterPhase, fadeLength ) )
let fadeOutAmount = g.div( g.sub( rateOfShuffling, stutterPhase ), g.sub( rateOfShuffling, fadeLength ) )
let fadedStutter = g.ifelse(
g.lt( stutterPhase, fadeLength ),
g.memo( g.mul( g.switch( g.lt( fadeInAmount, 1 ), fadeInAmount, 1 ), stutterSample ) ),
g.gt( stutterPhase, g.sub( rateOfShuffling, fadeLength ) ),
g.memo( g.mul( g.gtp( fadeOutAmount, 0 ), stutterSample ) ),
stutterSample
)
let outputL = g.mix( normalSample, fadedStutter, isShuffling )
let pokeL = g.poke( bufferL, leftInput, g.mod( g.add( phase, 44100 ), 88200 ) )
let panner = g.pan( outputL, outputL, g.in( 'pan' ) )
bufferShuffler.graph = [ panner.left, panner.right ]
}
bufferShuffler.__createGraph()
bufferShuffler.__requiresRecompilation = [ 'input' ]
out = Gibberish.factory(
bufferShuffler,
bufferShuffler.graph,
['fx','shuffler'],
props
)
return out
}
Shuffler.defaults = {
input:0,
rate:22050,
chance:.25,
reverseChance:.5,
repitchChance:.5,
repitchMin:.5,
repitchMax:2,
pan:.5,
mix:.5
}
return Shuffler
}