UNPKG

gibber.core.lib

Version:

small set of core objects and functions for gibber

289 lines (245 loc) 9.96 kB
module.exports = function( Gibber ) { const addValuesFilters = (seq,key,target) => { const values = seq.values // XXX support [1/8,1/4,1/16].rnd( 1/16, 2 ) syntax // to always play 1/16th notes twice if( values.randomFlag ) { values.addFilter( ( args,ptrn ) => { const range = ptrn.values.length - 1 const idx = Math.round( Math.random() * range ) return [ ptrn.values[ idx ], 1, idx ] }) for( let i = 0; i < values.randomArgs.length; i+=2 ) { values.repeat( values.randomArgs[ i ], values.randomArgs[ i + 1 ] ) } } // trigger autotrig patterns if( key === 'note' || key === 'chord' || key === 'trigger' || key === 'notef' || key === 'pickplay' ) { values.addFilter( ( args,ptrn ) => { if( ptrn.seq.target.autotrig !== undefined ) { for( let s of ptrn.seq.target.autotrig ) { s.fire() } } return args }) } } const addTimingFilters = function( seq,key,renderMode ) { const __timings = seq.timings if( __timings.randomFlag ) { __timings.addFilter( ( args,ptrn ) => { const range = ptrn.values.length - 1 const idx = Math.round( Math.random() * range ) return [ ptrn.values[ idx ], 1, idx ] }) for( let i = 0; i < __timings.randomArgs.length; i+=2 ) { __timings.repeat( __timings.randomArgs[ i ], __timings.randomArgs[ i + 1 ] ) } } const filter = renderMode === 'Audio' ? (args,ptrn) => { if( typeof args[0] === 'number' ) { args[0] = Gibberish.Clock.time( args[0] ) } return args } : (args,ptrn) => { if( typeof args[0] === 'number' ) { args[0] = Gibber.Clock.time( args[0] ) } return args } seq.timings.addFilter( (args,ptrn)=>{ if( args[0] === 0 ) { ptrn.seq.stop() console.warn( 'sequencer attempting to fire with a time of zero; this will result in an infinite loop, so the sequencer has been stopped.' ) } return args }) seq.timings.addFilter( filter ) } const Seq = function( props ) { let __values = props.values const __timings = props.timings const delay = props.delay const target = props.target const key = props.key const priority = props.priority let rate = props.rate || 1 let density = props.density || 1 let autotrig = false const render = props.render || 'Audio' const Gibberish = Gibber.Audio.Gibberish !== undefined ? Gibber.Audio.Gibberish : null if( __values.type === 'gen' ) { __values = __values.render() } if( Array.isArray( __values ) && __values.length <= 0 ) throw Error('arrays passed to sequences must have at least one value inside of them') // convert to pattern if needed and render const values = Array.isArray( __values ) ? Gibber.Pattern( ...__values ).render() : typeof __values === 'function' && __values.isPattern ? __values.render() : __values.requiresRender ? __values : Gibber.Pattern( __values ).render() // if an array of values is passed, let users call pattern method on that array, for example: // a.note.seq( b=[0,1,2,3], 1/4 ) // b.transpose.seq( 1,1 ) // also set random flag if needed if( Array.isArray( __values ) ) { Object.assign( __values, values ) __values.addFilter = values.addFilter.bind( values ) __values.removeFilter = values.removeFilter.bind( values ) __values.inspect = values.inspect.bind( values ) if( __values.randomFlag !== undefined ) values.randomFlag = __values.randomFlag if( __values.randomArgs !== undefined ) values.randomArgs = __values.randomArgs } else if( typeof __values === 'object' && __values.type==='gen' ) { props.values.addFilter = values.addFilter.bind( values ) props.values.removeFilter = values.removeFilter.bind( values ) props.values.inspect = values.inspect.bind( values ) } // process time values if( target !== undefined ) { if( Gibber[ render ].timeProps[ target.name ] !== undefined && Gibber[ render ].timeProps[ target.name ].indexOf( key ) !== -1 ) { const filter = render === 'Audio' ? (args,ptrn) => { args[0] = Gibberish.Clock.time( args[0] ) return args } : (args,ptrn) => { args[0] = Gibber.Audio.Clock.time( args[0] ) return args } values.addFilter( filter ) } } const timings = Array.isArray( __timings ) ? Gibber.Pattern( ...__timings ).render() : typeof __timings === 'function' && __timings.isPattern ? __timings.render() : __timings === undefined || __timings === null ? null : __timings.requiresRender ? __timings : Gibber.Pattern( __timings ).render() if( timings === null ) autotrig = true if( Array.isArray( __timings ) ) { Object.assign( __timings, timings ) __timings.addFilter = timings.addFilter.bind( timings ) if( __timings.randomFlag !== undefined ) timings.randomFlag = __timings.randomFlag if( __timings.randomArgs !== undefined ) timings.randomArgs = __timings.randomArgs } if( autotrig === false ) { timings.output = { time:'time', shouldExecute:0 } timings.density = 1 // XXX delay annotations so that they occur after values annotations have occurred. There might // need to be more checks for this flag in the various annotation update files... right now // the check is only in createBorderCycle.js. timings.__delayAnnotations = true } // if an array is passed to the seq, enable users to call pattern methods on array //if( Array.isArray( __timings ) ) Object.assign( __timings, timings ) const clear = render === 'Audio' ? function() { this.stop() if( this.values !== undefined && this.values.clear !== undefined ) { this.values.clear() } if( this.timings !== undefined && this.timings !== null && this.timings.clear !== undefined ) this.timings.clear() if( Gibberish.mode === 'worklet' ) { const idx = Seq.sequencers.indexOf( seq ) seq.stop() const __seq = Seq.sequencers.splice( idx, 1 )[0] if( __seq !== undefined ) { __seq.stop() } } } : function() { this.stop() if( this.values !== undefined && this.values.clear !== undefined ) this.values.clear() if( this.timings !== undefined && this.timings !== null && this.timings.clear !== undefined ) this.timings.clear() const idx = Seq.sequencers.indexOf( seq ) const __seq = Seq.sequencers.splice( idx, 1 )[0] if( __seq !== undefined ) { __seq.stop() } } values.__patternType = 'values' if( timings !== null ) timings.__patternType = 'timings' //const offsetRate = Gibberish.binops.Mul(rate, Gibber.Clock.AudioClock ) // XXX need to fix so that we can use the clock rate as the base // XXX need to abstract this so that a graphics sequencer could also be called... const seq = Gibber.Audio.Gibberish.Sequencer({ values, timings, density, target, key, priority, rate:1/*Gibber.Clock.AudioClock*/, clear, autotrig, mainthreadonly:props.mainthreadonly }) if( values.setSeq ) values.setSeq( seq ) addValuesFilters( seq,key ) if( autotrig === false ) { addTimingFilters( seq,key,render ) if( timings.setSeq ) timings.setSeq( seq ) }else{ if( target !== undefined ) { if( target.autotrig === undefined ) { target.autotrig = [] Gibber.Audio.Gibberish.worklet.port.postMessage({ address:'property', name:'autotrig', value:[], object:target.id }) } // object name key value if( Gibber.Audio.Gibberish.mode === 'worklet' ) { Gibber.Audio.Gibberish.worklet.port.postMessage({ address:'addObjectToProperty', name:'autotrig', object:target.id, key:target.autotrig.length, value:seq.id }) target.autotrig.push( seq ) } }else{ throw Error('you must define timings for any sequence without a target') } } //Gibberish.proxyEnabled = false //Gibber.Ugen.createProperty( seq, 'density', timings, [], Gibber ) //Gibberish.proxyEnabled = true Seq.sequencers.push( seq ) // if x.y.seq() etc. // standalone === false is most common use case if( props.standalone === false ) { // required ternary because pattern methods don't have __ prefix const targetProp = target[ '__' + key ] === undefined ? target[ key ] : target[ '__' + key ] const prevSeq = targetProp.sequencers[ props.number ] if( prevSeq !== undefined ) { prevSeq.clear(); } // XXX you have to add a method that does all this shit on the worklet. crap. targetProp.sequencers[ props.number ] = seq targetProp[ props.number ] = seq //target.__sequencers.push( seq ) if( typeof delay !== 'function' ) { seq.start( Gibber.Audio.Clock.time( delay ) ) } else { delay.seqs.push( seq ) } } Gibber.publish( 'new sequence', seq ) return seq } Seq.sequencers = [] Seq.clear = function() { Seq.sequencers.forEach( seq => seq.clear() ) //for( let i = Seq.sequencers.length - 1; i >= 0; i-- ) { // Seq.sequencers[ i ].clear() //} Seq.sequencers = [] } Seq.DNR = -987654321 return Seq }