UNPKG

gibber.core.lib

Version:

small set of core objects and functions for gibber

177 lines (141 loc) 4.93 kB
module.exports = function( Gibber ) { const Steps = { type:'Steps', create( _steps, target ) { const stepseq = Object.create( Steps ) stepseq.seqs = {} for( let _key in _steps ) { let values = _steps[ _key ] const parsedKey = parseInt( _key ) const key = isNaN( parsedKey ) ? _key : parsedKey let usesStringValues = false if( values.isPattern !== true ) { if( Array.isArray( values ) ) { values = Gibber.Pattern( ...values ) }else if( typeof values === 'string' ) { values = values.split('') usesStringValues = true }else{ values = Gibber.Pattern( values ) } } const seq = Gibber.Seq({ values: usesStringValues ? values : key, timings: usesStringValues ? [ 1 / values.length ] : values, 'key': target.__isEnsemble !== true ? 'note' : 'trigger', target: target.__isEnsemble ? target[ target[ key ].name ] : target, priority:0 }) const onlyUsesVelocity = typeof key === 'string' if( usesStringValues ) { seq.values.addFilter( new Function( 'args', 'ptrn', `let sym = args[ 0 ], velocity = parseInt( sym, 16 ) / 15 if( isNaN( velocity ) ) { velocity = sym === 'x' ? 1 : sym === 'X' ? 1.5 : 0 } // TODO: is there a better way to get access to beat, beatOffset and scheduler? if( velocity !== 0 ) { ${ onlyUsesVelocity ? '' :'ptrn.seq.target.__triggerLoudness = velocity' } } args[ 0 ] = sym === '.' ? -987654321 : ${typeof key === 'string' ? 'velocity' : key } return args `) ) } stepseq.seqs[ _key ] = seq stepseq[ _key ] = usesStringValues ? seq.values : seq.timings } stepseq.start() stepseq.addPatternMethods() return stepseq }, /* two parts: * 1. The easy part, make methods that can be called from the main thread * and run over every seq instance in the step sequencer * 2. The hard part, make an object that lives in the audio thread * and can be sequenced. It needs references to all sequencers in the * step sequencer. */ addPatternMethods() { // XXX shouldn't use audio id by default... sigh const id = Gibber.Audio.Gibberish.utilities.getUID() // store ids of all controlled sequencers const seqIds = [] for( let key in this.seqs ) { seqIds.push( this.seqs[ key ].id ) } // this object will be transferred to audio thread const obj = { id, seqIds } groupMethodNames.forEach( name => { // EASY PART this[ name ] = function( ...args ) { for( let key in this.seqs ) { this.seqs[ key ].values[ name ].apply( this, args ) } } this[ name ].sequencers = [] this[ name ].seq = ( values, timings, number = 0, delay = 0 ) => { const s = Gibber.Seq({ values, timings, target:this.__wrapped, key:name, priority:1, }).start() this[ name ].sequencers.push( s ) // needed for annotations this[ name ][ number ] = s return this } // store function body to create function in audio thread representation of steps // needs to be one line for stringify / parsing // XXX ugh arguments? is there ever more than one argument? obj[ name ] = `for( let seq of this.seqs ) { seq.values.${name}.apply( this, arguments ); seq.timings.${name}.apply( this, arguments )}` }) // HARD PART // code to be evaluated in audio thread // 1. create a new object, steps, bassed on stringifying obj // 2. add all of the transform methods // 3. store all controlled sequencers in steps.seqs after getting references // from Gibberish.ugens const code = `const steps = JSON.parse( \`${JSON.stringify(obj)}\` ) const methods = ${JSON.stringify( groupMethodNames ) } steps.seqs = steps.seqIds.map( id => Gibberish.ugens.get( id ) ) for( let method of methods ) { steps[ method ] = new Function( steps[ method ] ) } Gibberish.ugens.set( steps.id, steps )` Gibber.Audio.Gibberish.worklet.port.postMessage({ address:'eval', code }) this.__wrapped = obj }, start() { for( let key in this.seqs ) { this.seqs[ key ].start() } }, stop() { for( let key in this.seqs ) { this.seqs[ key ].stop() } }, clear() { this.stop() for( let key in this.seqs ) { this.seqs[ key ].timings.clear() } } } const groupMethodNames = [ 'rotate', 'reverse', 'transpose', 'range', 'shuffle', 'scale', 'repeat', 'store', 'reset','flip', 'invert', 'set', 'double' ] return Steps.create }