UNPKG

gibberwocky

Version:

music sequencing for Live and Max/MSP

1,537 lines (1,192 loc) 2.76 MB
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ const Queue = require( './priorityqueue.js' ) let Gibber = null let Scheduler = { currentTime : performance.now(), queue: new Queue( ( a, b ) => a.time - b.time ), visualizationTime: { init:true, base:0, phase:0, }, init( __Gibber ) { Gibber = __Gibber window.requestAnimationFrame( this.onAnimationFrame ) }, clear() { this.queue.data.length = 0 }, add( func, offset, idx ) { let time = this.currentTime + offset this.queue.push({ func, time }) return time }, runSoon( evt ) { try{ evt.func() }catch(e) { console.log( 'annotation error:', e.toString() ) } }, run( timestamp, dt ) { let nextEvent = this.queue.peek() //if( nextEvent === undefined ) return if( nextEvent !== undefined && this.queue.length > 0 && nextEvent.time <= timestamp ) { // remove event this.queue.pop() setTimeout( ()=> this.runSoon( nextEvent ) ) //try{ // nextEvent.func() //}catch( e ) { // console.log( e ) // Gibber.Environment.error( 'annotation error:', e.toString() ) //} // call recursively this.run( timestamp ) } if( Gibber.Environment.codeMarkup.waveform.widgets.dirty === true ) { Gibber.Environment.codeMarkup.waveform.drawWidgets() } }, onAnimationFrame( timestamp ) { window.requestAnimationFrame( this.onAnimationFrame ) const diff = timestamp - this.currentTime this.currentTime = timestamp this.visualizationTime.phase += diff this.run( timestamp, diff ) }, updateVisualizationTime( ms ) { if( this.visualizationTime.init === true ) { this.visualizationTime.base += ms this.visualizationTime.phase = 0 }else{ this.visualizationTime.init = true } }, } Scheduler.onAnimationFrame = Scheduler.onAnimationFrame.bind( Scheduler ) module.exports = Scheduler },{"./priorityqueue.js":42}],2:[function(require,module,exports){ const Utility = require( '../../utility.js' ) const $ = Utility.create module.exports = function( Marker ) { 'use strict' const ArrayExpression = function( patternNode, state, seq, patternType, container=null, index=0, isLookup=false ) { if( patternNode.processed === true ) return const cm = state.cm const track = seq.object const patternObject = seq[ patternType ] const [ patternName, start, end ] = Marker._getNamesAndPosition( patternNode, state, patternType, index ) const cssName = patternName patternObject.markers = [] if( track.markup === undefined ) Marker.prepareObject( track ) let count = 0 for( let element of patternNode.elements ) { let cssClassName = patternName + '_' + count, elementStart = Object.assign( {}, start ), elementEnd = Object.assign( {}, end ), marker elementStart.ch = element.loc.start.column// + Marker.offset.horizontal elementEnd.ch = element.loc.end.column // + Marker.offset.horizontal if( element.type === 'BinaryExpression' ) { marker = cm.markText( elementStart, elementEnd, { 'className': cssClassName + ' annotation', startStyle: 'annotation-no-right-border', endStyle: 'annotation-no-left-border', //inclusiveLeft:true, inclusiveRight:true }) // create specific border for operator: top, bottom, no sides const divStart = Object.assign( {}, elementStart ) const divEnd = Object.assign( {}, elementEnd ) divStart.ch += 1 divEnd.ch -= 1 const marker2 = cm.markText( divStart, divEnd, { className:cssClassName + '_binop annotation-binop' }) patternObject.markers.push( marker, marker2 ) }else if (element.type === 'UnaryExpression' ) { marker = cm.markText( elementStart, elementEnd, { 'className': cssClassName + ' annotation', //inclusiveLeft: true, //inclusiveRight: true }) let start2 = Object.assign( {}, elementStart ) start2.ch += 1 let marker2 = cm.markText( elementStart, start2, { 'className': cssClassName + ' annotation-no-right-border', //inclusiveLeft: true, //inclusiveRight: true }) let marker3 = cm.markText( start2, elementEnd, { 'className': cssClassName + ' annotation-no-left-border', //inclusiveLeft: true, //inclusiveRight: true }) patternObject.markers.push( marker, marker2, marker3 ) }else if( element.type === 'ArrayExpression' ) { marker = cm.markText( elementStart, elementEnd, { 'className': cssClassName + ' annotation', //inclusiveLeft:true, inclusiveRight:true, startStyle:'annotation-left-border-start', endStyle: 'annotation-right-border-end', }) // mark opening array bracket const arrayStart_start = Object.assign( {}, elementStart ) const arrayStart_end = Object.assign( {}, elementStart ) arrayStart_end.ch += 1 cm.markText( arrayStart_start, arrayStart_end, { className:cssClassName + '_start' }) // mark closing array bracket const arrayEnd_start = Object.assign( {}, elementEnd ) const arrayEnd_end = Object.assign( {}, elementEnd ) arrayEnd_start.ch -=1 const marker2 = cm.markText( arrayEnd_start, arrayEnd_end, { className:cssClassName + '_end' }) patternObject.markers.push( marker, marker2 ) }else{ marker = cm.markText( elementStart, elementEnd, { 'className': cssClassName + ' annotation', //inclusiveLeft:true, inclusiveRight:true }) patternObject.markers.push( marker ) } if( track.markup.textMarkers[ patternName ] === undefined ) track.markup.textMarkers[ patternName ] = [] track.markup.textMarkers[ patternName ][ count ] = marker if( track.markup.cssClasses[ patternName ] === undefined ) track.markup.cssClasses[ patternName ] = [] track.markup.cssClasses[ patternName ][ count ] = cssClassName count++ } let highlighted = { className:null, isArray:false }, cycle = Marker._createBorderCycleFunction( patternName, patternObject ) patternObject.patternType = patternType patternObject.patternName = patternName patternObject.update = () => { let className = '.' + patternName className += '_' + patternObject.update.currentIndex if( highlighted.className !== className ) { // remove any previous annotations for this pattern if( highlighted.className !== null ) { if( highlighted.isArray === false && highlighted.className ) { $( highlighted.className ).remove( 'annotation-border' ) }else{ $( highlighted.className ).remove( 'annotation-array' ) $( highlighted.className + '_start' ).remove( 'annotation-border-left' ) $( highlighted.className + '_end' ).remove( 'annotation-border-right' ) if( $( highlighted.className + '_binop' ).length > 0 ) { $( highlighted.className + '_binop' ).remove( 'annotation-binop-border' ) } } } // add annotation for current pattern element const values = isLookup === false ? patternObject.values : patternObject._values if( Array.isArray( values[ patternObject.update.currentIndex ] ) ) { $( className ).add( 'annotation-array' ) $( className + '_start' ).add( 'annotation-border-left' ) $( className + '_end' ).add( 'annotation-border-right' ) highlighted.isArray = true }else{ $( className ).add( 'annotation-border' ) if( $( className + '_binop' ).length > 0 ) { $( className + '_binop' ).add( 'annotation-binop-border' ) } highlighted.isArray = false } highlighted.className = className cycle.clear() }else{ cycle() } } // check to see if a clear function already exists and save reference // XXX should clear be saved somewhere else... on the update function? let __clear = null if( patternObject.clear !== undefined ) __clear = patternObject.clear patternObject.clear = () => { if( highlighted.className !== null ) { $( highlighted.className ).remove( 'annotation-border' ) } cycle.clear() patternObject.markers.forEach( marker => marker.clear() ) if( __clear !== null ) __clear() } Marker._addPatternFilter( patternObject ) patternObject._onchange = () => { Marker._updatePatternContents( patternObject, patternName, track ) } } return ArrayExpression } },{"../../utility.js":49}],3:[function(require,module,exports){ module.exports = function( Marker ) { 'use strict' // 1/4, 1/8 etc. const BinaryExpression = function( patternNode, state, seq, patternType,container=null, index=0 ) { if( patternNode.processed === true ) return const cm = state.cm const seqTarget = seq.object const patternObject = seq[ patternType ] const [ className, start, end ] = Marker._getNamesAndPosition( patternNode, state, patternType, index ) const cssName = className const marker = cm.markText( start, end, { 'className': cssName + ' annotation annotation-border' , startStyle: 'annotation-no-right-border', endStyle: 'annotation-no-left-border', //inclusiveLeft:true, //inclusiveRight:true } ) if( seqTarget.markup === undefined ) Marker.prepareObject( seqTarget ) seqTarget.markup.textMarkers[ className ] = marker const divStart = Object.assign( {}, start ) const divEnd = Object.assign( {}, end ) divStart.ch += 1 divEnd.ch -= 1 const marker2 = cm.markText( divStart, divEnd, { className:'annotation-binop-border' }) if( seqTarget.markup.cssClasses[ className ] === undefined ) seqTarget.markup.cssClasses[ className ] = [] seqTarget.markup.cssClasses[ className ][ index ] = cssName patternObject.marker = marker Marker.finalizePatternAnnotation( patternObject, className, seqTarget, marker ) } return BinaryExpression } },{}],4:[function(require,module,exports){ module.exports = function( Marker ) { 'use strict' // CallExpression denotes an array (or other object) that calls a method, like .rnd() // could also represent an anonymous function call, like Rndi(19,40) const CallExpression = function( patternNode, state, seq, patternType, index=0 ) { if( patternNode.processed === true ) return var args = Array.prototype.slice.call( arguments, 0 ) if( patternNode.callee.type === 'MemberExpression' && patternNode.callee.object.type === 'ArrayExpression' ) { args[ 0 ] = patternNode.callee.object args[ 0 ].offset = Marker.offset Marker.patternMarkupFunctions.ArrayExpression( ...args ) } else if (patternNode.callee.type === 'Identifier' ) { // function like Euclid or gen~d Marker.patternMarkupFunctions.Identifier( ...args ) } } return CallExpression } },{}],5:[function(require,module,exports){ const __Identifier = function( Marker ) { const mark = function( node, state, patternType, seqNumber ) { const [ className, start, end ] = Marker._getNamesAndPosition( node, state, patternType, seqNumber ) const cssName = className + '_' + seqNumber const commentStart = end // we define the comment range as being one character, this // only defines the range of characters that will be replaced // by the eventual longer comment string. const commentEnd = Object.assign( {}, commentStart ) commentEnd.ch += 1 const line = end.line const lineTxt = state.cm.getLine( line ) let ch = end.ch // for whatever reason, sometimes the ch value leads to // an undefined final character in the string. In that case, // work back until we find the actual final character. let lastChar = lineTxt[ ch ] while( lastChar === undefined ) { ch-- lastChar = lineTxt[ ch ] } // different replacements are used for use in sequencers, when a callexpression // creating a wavepattern is often followed by a comma, vs when a wavepattern is // assigned to a variable, when no comma is present if( lastChar === ',' ) { state.cm.replaceRange( ' ,', { line, ch:ch }, { line, ch:ch + 1 } ) }else if( lastChar === ')' ){ state.cm.replaceRange( ') ', { line, ch:ch }, { line, ch:ch + 1 } ) }else{ state.cm.replaceRange( lastChar + ' ', { line, ch:ch }, { line, ch:ch + 1 } ) } const marker = state.cm.markText( commentStart, commentEnd, { className }) return [ marker, className ] } // Typically this is used with named functions. For example, if you store an // Arp in the variable 'a' and pass 'a' into a sequence, 'a' is the Identifier // and this function will be called to mark up the associated pattern. const Identifier = function( patternNode, state, seq, patternType, containerNode, seqNumber ) { if( patternNode.processed === true ) return const cm = state.cm const track = seq.object const patternObject = seq[ patternType ] const [ marker, className ] = mark( patternNode, state, patternType, seqNumber ) // WavePatterns can also be passed as named functions; make sure we forward // these to the appropriate markup functions if( patternObject.type === 'WavePattern' || patternObject.isGen ) { //|| patternObject.type === 'Lookup' ) { if( patternObject.widget === undefined ) { // if wavepattern is inlined to .seq Marker.processGen( containerNode, cm, track, patternObject, seq ) }else{ patternObject.update = Marker.patternUpdates.anonymousFunction( patternObject, marker, className, cm, track ) } }else{ let updateName = typeof patternNode.callee !== 'undefined' ? patternNode.callee.name : patternNode.name // this doesn't work for variables storing lookups, as there's no array to highlight // if( patternObject.type === 'Lookup' ) updateName = 'Lookup' if( Marker.patternUpdates[ updateName ] ) { if( updateName !== 'Lookup' ) { patternObject.update = Marker.patternUpdates[ updateName ]( patternObject, marker, className, cm, track, patternNode ) }else{ Marker.patternUpdates[ updateName ]( patternObject, marker, className, cm, track, patternNode, patternType, seqNumber ) } } else { patternObject.update = Marker.patternUpdates.anonymousFunction( patternObject, marker, className, cm, track ) } patternObject.patternName = className // store value changes in array and then pop them every time the annotation is updated patternObject.update.value = [] if( updateName !== 'Lookup' ) Marker._addPatternFilter( patternObject ) } patternObject.marker = marker } return Identifier } module.exports = __Identifier },{}],6:[function(require,module,exports){ module.exports = function( Marker ) { // Marker.patternMarkupFunctions[ valuesNode.type ]( valuesNode, state, seq, 'values', container, seqNumber ) const Literal = function( patternNode, state, seq, patternType, container=null, index=0 ) { if( patternNode.processed === true ) return const cm = state.cm const seqTarget = seq.object const patternObject = seq[ patternType ] const [ className, start, end ] = Marker._getNamesAndPosition( patternNode, state, patternType, index ) const cssName = className const marker = cm.markText( start, end, { 'className': cssName + ' annotation-border', //inclusiveLeft: true, //inclusiveRight: true }) if( seqTarget.markup === undefined ) Marker.prepareObject( seqTarget ) seqTarget.markup.textMarkers[ className ] = marker if( seqTarget.markup.cssClasses[ className ] === undefined ) seqTarget.markup.cssClasses[ className ] = [] seqTarget.markup.cssClasses[ className ][ index ] = cssName patternObject.marker = marker Marker.finalizePatternAnnotation( patternObject, className, seqTarget, marker ) } return Literal } },{}],7:[function(require,module,exports){ module.exports = function( Marker ) { // for negative literals e.g. -10 const UnaryExpression = function( patternNode, state, seq, patternType, container=null, index=0 ) { if( patternNode.processed === true ) return const cm = state.cm const seqTarget = seq.object const patternObject = seq[ patternType ] const [ className, start, end ] = Marker._getNamesAndPosition( patternNode, state, patternType, index ) const cssName = className marker = cm.markText( start, end, { 'className': cssName + ' annotation', //inclusiveLeft: true, //inclusiveRight: true }) if( seqTarget.markup === undefined ) Marker.prepareObject( seqTarget ) seqTarget.markup.textMarkers[ className ] = marker if( seqTarget.markup.cssClasses[ className ] === undefined ) seqTarget.markup.cssClasses[ className ] = [] seqTarget.markup.cssClasses[ className ][ index ] = cssName let start2 = Object.assign( {}, start ) start2.ch += 1 let marker2 = cm.markText( start, start2, { 'className': cssName + ' annotation-no-right-border', //inclusiveLeft: true, //inclusiveRight: true }) let marker3 = cm.markText( start2, end, { 'className': cssName + ' annotation-no-left-border', //inclusiveLeft: true, //inclusiveRight: true }) patternObject.marker = marker Marker.finalizePatternAnnotation( patternObject, className ) } return UnaryExpression } },{}],8:[function(require,module,exports){ const Utility = require( '../../utility.js' ) const $ = Utility.create const EuclidAnnotation = require( '../update/euclidAnnotation.js' ) module.exports = function( node, cm, track, objectName, state, cb ) { const Marker = Gibber.Environment.codeMarkup const steps = node.arguments[ 0 ].properties const Identifier = Marker.patternMarkupFunctions.Identifier track.markup.textMarkers[ 'step' ] = [] track.markup.textMarkers[ 'step' ].children = [] const hexSteps = window[ objectName ] const objectClassName = objectName + '_steps' let count = 0 for( let key in steps ) { let step = steps[ key ].value if( step && step.value ) { // ensure it is a correctly formed step step.loc.start.line += Marker.offset.vertical - 1 step.loc.end.line += Marker.offset.vertical - 1 step.loc.start.ch = step.loc.end.column - 1 step.loc.end.ch = step.loc.end.column const pattern = hexSteps.seqs[ steps[ key ].key.value ].timings // we estimate whether or not a comma was used to separate between // key / value pairs. If there's more than one pattern and this // isn't the last time through the for loop, we assume there is a // comma (otherwise an error would occur). const useComma = count++ != steps.length - 1 && steps.length > 1 if( useComma === true ) { // move off of end quote to comma step.loc.start.ch += 1 step.loc.end.ch += 1 // replace comma with a comma and a space cm.replaceRange( ", ", step.loc.start, step.loc.end ) step.loc.start.ch += 1 step.loc.end.ch += 1 }else{ // replace end quote with a quote and a space cm.replaceRange( "' ", step.loc.start, step.loc.end ) step.loc.start.ch += 1 step.loc.end.ch += 1 } let className = objectClassName + '_' + key let marker = cm.markText( step.loc.start, step.loc.end, { className } ) pattern.update = EuclidAnnotation( pattern, marker, className, cm, track ) pattern.patternName = className // store value changes in array and then pop them every time the annotation is updated pattern.update.value = [] Marker._addPatternFilter( pattern ) pattern.marker = marker /* patternObject._onchange = () => { let delay = Utility.beatsToMs( 1, Gibber.Scheduler.bpm ) Gibber.Environment.animationScheduler.add( () => { marker.doc.replaceRange( patternObject.values.join(''), step.loc.start, step.loc.end ) mark( step, key, cm, track ) }, delay ) } */ } } } },{"../../utility.js":49,"../update/euclidAnnotation.js":13}],9:[function(require,module,exports){ const Utility = require( '../../utility.js' ) const $ = Utility.create module.exports = function( node, cm, track, objectName, vOffset=0 ) { let timelineNodes = node.arguments[ 0 ].elements //console.log( timelineNodes ) track.markup.textMarkers[ 'score' ] = [] for( let i = 0; i < timelineNodes.length; i+=2 ) { let timeNode = timelineNodes[ i ], functionNode = timelineNodes[ i + 1 ] functionNode.loc.start.line += vOffset - 1 functionNode.loc.end.line += vOffset - 1 functionNode.loc.start.ch = functionNode.loc.start.column functionNode.loc.end.ch = functionNode.loc.end.column let marker = cm.markText( functionNode.loc.start, functionNode.loc.end, { className:`score${i/2}` } ) track.markup.textMarkers[ 'score' ][ i/2 ] = marker } let lastClass = 'score0' $( '.' + lastClass ).add( 'scoreCurrentIndex' ) // TODO: global object usage is baaaad methinks? window[ objectName ].onadvance = ( idx ) => { $( '.' + lastClass ).remove( 'scoreCurrentIndex' ) lastClass = `score${idx}` $( '.' + lastClass ).add( 'scoreCurrentIndex' ) } } },{"../../utility.js":49}],10:[function(require,module,exports){ const Utility = require( '../../utility.js' ) const $ = Utility.create module.exports = function( node, cm, track, objectName, state, cb ) { const Marker = Gibber.Environment.codeMarkup // tsk tsk tsk global... const steps = node.arguments[ 0 ].properties track.markup.textMarkers[ 'step' ] = [] track.markup.textMarkers[ 'step' ].children = [] const mark = ( _step, _key, _cm, _track ) => { for( let i = 0; i < _step.value.length; i++ ) { let pos = { loc:{ start:{}, end:{}} } Object.assign( pos.loc.start, _step.loc.start ) Object.assign( pos.loc.end , _step.loc.end ) pos.loc.start.ch += i pos.loc.end.ch = pos.loc.start.ch + 1 let posMark = _cm.markText( pos.loc.start, pos.loc.end, { className:`step_${_key}_${i}` }) _track.markup.textMarkers.step[ _key ].pattern[ i ] = posMark } } for( let key in steps ) { let step = steps[ key ].value if( step && step.value ) { // ensure it is a correctly formed step step.loc.start.line += Marker.offset.vertical - 1 step.loc.end.line += Marker.offset.vertical - 1 step.loc.start.ch = step.loc.start.column + 1 step.loc.end.ch = step.loc.end.column - 1 let marker = cm.markText( step.loc.start, step.loc.end, { className:`step${key}` } ) track.markup.textMarkers.step[ key ] = marker track.markup.textMarkers.step[ key ].pattern = [] mark( step, key, cm, track ) let count = 0, span, update, _key = steps[ key ].key.value, patternObject = window[ objectName ].seqs[ _key ].values update = () => { let currentIdx = update.currentIndex // count++ % step.value.length if( span !== undefined ) { span.remove( 'euclid0' ) span.remove( 'euclid1' ) } let spanName = `.step_${key}_${currentIdx}`, currentValue = patternObject.update.value.pop() //step.value[ currentIdx ] span = $( spanName ) if( currentValue !== Gibber.Seq.DO_NOT_OUTPUT ) { span.add( 'euclid1' ) setTimeout( ()=> { span.remove( 'euclid1' ) }, 50 ) } span.add( 'euclid0' ) } patternObject._onchange = () => { let delay = Utility.beatsToMs( 1, Gibber.Scheduler.bpm ) Gibber.Environment.animationScheduler.add( () => { marker.doc.replaceRange( patternObject.values.join(''), step.loc.start, step.loc.end ) mark( step, key, cm, track ) }, delay ) } patternObject.update = update patternObject.update.value = [] Marker._addPatternFilter( patternObject ) } } } },{"../../utility.js":49}],11:[function(require,module,exports){ module.exports = ( patternObject, marker, className, cm ) => { patternObject.commentMarker = marker let update = () => { if( !patternObject.commentMarker ) return let patternValue = '' + patternObject.update.value.pop() if( patternValue.length > 8 ) patternValue = patternValue.slice(0,8) let val ='/* ' + patternValue + ' */', pos = patternObject.commentMarker.find(), end = Object.assign( {}, pos.to ) //pos.from.ch += 1 end.ch = pos.from.ch + val.length //pos.from.ch += 1 cm.replaceRange( val, pos.from, pos.to ) if( patternObject.commentMarker ) patternObject.commentMarker.clear() patternObject.commentMarker = cm.markText( pos.from, end, { className, atomic:false }) } patternObject.clear = () => { try{ let commentPos = patternObject.commentMarker.find() //commentPos.to.ch -= 1 // XXX wish I didn't have to do this cm.replaceRange( '', commentPos.from, commentPos.to ) patternObject.commentMarker.clear() delete patternObject.commentMarker } catch( e ) {} // yes, I just did that XXX } return update } },{}],12:[function(require,module,exports){ const Utility = require( '../../utility.js' ) const $ = Utility.create module.exports = function( classNamePrefix, patternObject ) { let modCount = 0, lastBorder = null, lastClassName = null const cycle = function( isArray = false ) { let className = '.' + classNamePrefix, border = 'top' // accommodate arrays if( patternObject.values.length > 1 || patternObject.type === 'Lookup' ) { className += '_' + patternObject.update.currentIndex } switch( modCount++ % 4 ) { case 1: border = 'right'; break; case 2: border = 'bottom'; break; case 3: border = 'left'; break; } // for a pattern holding arrays... like for chord() if( isArray === true ) { switch( border ) { case 'left': $( className ).remove( 'annotation-' + lastBorder + '-border-cycle' ) $( className + '_start' ).add( 'annotation-left-border-cycle' ) break; case 'right': $( className ).remove( 'annotation-' + lastBorder + '-border-cycle' ) $( className + '_end' ).add( 'annotation-right-border-cycle' ) break; case 'top': $( className ).add( 'annotation-top-border-cycle' ) $( className+'_start' ).remove( 'annotation-left-border-cycle' ) $( className+'_start' ).add( 'annotation-top-border-cycle' ) $( className+'_end' ).add( 'annotation-top-border-cycle' ) break; case 'bottom': $( className ).add( 'annotation-bottom-border-cycle' ) $( className+'_end' ).remove( 'annotation-right-border-cycle' ) $( className+'_end' ).add( 'annotation-bottom-border-cycle' ) $( className+'_start' ).add( 'annotation-bottom-border-cycle' ) break; default: $( className ).add( 'annotation-' + border + '-border-cycle' ) $( className+'_start' ).remove( 'annotation-' + border + '-border-cycle' ) $( className+'_end' ).remove( 'annotation-' + border + '-border-cycle' ) break; } }else{ $( className ).remove( 'annotation-' + border + '-border' ) $( className ).add( 'annotation-' + border + '-border-cycle' ) if( lastBorder !== null ) { $( className ).remove( 'annotation-' + lastBorder + '-border-cycle' ) $( className ).add( 'annotation-' + lastBorder + '-border' ) } } lastBorder = border lastClassName = className } cycle.clear = function() { modCount = 1 if( lastClassName !== null ) { $( lastClassName ).remove( 'annotation-left-border' ) $( lastClassName ).remove( 'annotation-left-border-cycle' ) $( lastClassName ).remove( 'annotation-right-border' ) $( lastClassName ).remove( 'annotation-right-border-cycle' ) $( lastClassName ).remove( 'annotation-top-border' ) $( lastClassName ).remove( 'annotation-top-border-cycle' ) $( lastClassName ).remove( 'annotation-bottom-border' ) $( lastClassName ).remove( 'annotation-bottom-border-cycle' ) } lastBorder = null } return cycle } },{"../../utility.js":49}],13:[function(require,module,exports){ const Utility = require( '../../utility.js' ) const $ = Utility.create module.exports = ( patternObject, marker, className, cm, track ) => { let val ='/* ' + patternObject.values.join('') + ' */', pos = marker.find(), end = Object.assign( {}, pos.to ), annotationStartCh = pos.from.ch + 3, annotationEndCh = annotationStartCh + 1, memberAnnotationStart = Object.assign( {}, pos.from ), memberAnnotationEnd = Object.assign( {}, pos.to ), initialized = false, markStart = null, commentMarker, currentMarker, chEnd end.ch = pos.from.ch + val.length pos.to.ch -= 1 cm.replaceRange( val, pos.from, pos.to ) patternObject.commentMarker = cm.markText( pos.from, end, { className, atomic:false }) track.markup.textMarkers[ className ] = {} let mark = () => { // first time through, use the position given to us by the parser let range,start, end if( initialized === false ) { memberAnnotationStart.ch = annotationStartCh memberAnnotationEnd.ch = annotationEndCh initialized = true }else{ // after the first time through, every update to the pattern store the current // position of the first element (in markStart) before replacing. Use this to generate position // info. REPLACING TEXT REMOVES TEXT MARKERS. range = markStart start = range.from memberAnnotationStart.ch = start.ch memberAnnotationEnd.ch = start.ch + 1 } for( let i = 0; i < patternObject.values.length; i++ ) { track.markup.textMarkers[ className ][ i ] = cm.markText( memberAnnotationStart, memberAnnotationEnd, { 'className': `${className}_${i}` } ) memberAnnotationStart.ch += 1 memberAnnotationEnd.ch += 1 } if( start !== undefined ) { start.ch -= 3 end = Object.assign({}, start ) end.ch = memberAnnotationEnd.ch + 3 patternObject.commentMarker = cm.markText( start, end, { className, atomic:true }) } } mark() // XXX: there's a bug when you sequence pattern transformations, and then insert newlines ABOVE the annotation let count = 0, span, update, activeSpans = [] update = () => { let currentIdx = count++ % patternObject.values.length if( span !== undefined ) { span.remove( 'euclid0' ) } let spanName = `.${className}_${currentIdx}`, currentValue = patternObject.values[ currentIdx ] span = $( spanName ) // deliberate == if( currentValue == 1 ) { span.add( 'euclid1' ) activeSpans.push( span ) setTimeout( ()=> { activeSpans.forEach( _span => _span.remove( 'euclid1' ) ) activeSpans.length = 0 }, 50 ) }else{ span.add( 'euclid0' ) } } patternObject._onchange = () => { let delay = Utility.beatsToMs( 1, Gibber.Scheduler.bpm ) markStart = track.markup.textMarkers[ className ][ 0 ].find() Gibber.Environment.animationScheduler.add( () => { for( let i = 0; i < patternObject.values.length; i++ ) { let markerCh = track.markup.textMarkers[ className ][ i ], pos = markerCh.find() marker.doc.replaceRange( '' + patternObject.values[ i ], pos.from, pos.to ) } mark() }, delay ) } patternObject.clear = () => { const commentPos = patternObject.commentMarker.find() // if this gets called twice... if( commentPos === undefined ) return cm.replaceRange( '', commentPos.from, { line:commentPos.to.line, ch:commentPos.to.ch+1 } ) patternObject.commentMarker.clear() } return update } },{"../../utility.js":49}],14:[function(require,module,exports){ module.exports = ( patternObject, marker, className, cm, track, patternNode, patternType, seqNumber ) => { Gibber.Environment.codeMarkup.processGen( patternNode, cm, null, patternObject, null, -1 ) patternNode.arguments[1].offset = patternNode.offset Gibber.Environment.codeMarkup.patternMarkupFunctions.ArrayExpression( patternNode.arguments[1], cm.__state, { object:patternObject, [ patternType ]: patternObject }, patternType, null, seqNumber, true ) } },{}],15:[function(require,module,exports){ module.exports = function( Marker ) { const strip = function( unstripped ) { //const unstripped = node.property.type === 'Identifier' ? node.property.name : node.property.raw ) const stripped = unstripped[0] === '"' || unstripped[0] === "'" ? unstripped.slice(1,-1) : unstripped return stripped } const seqSuffixes = ['once', 'repeat', 'stop'] return { Literal( node, state, cb ) { state.push( node.value ) }, Identifier( node, state, cb ) { state.push( strip( node.name ) ) }, AssignmentExpression( expression, state, cb ) { // the only assignments we're interested in for annotation purposes are // wavepatterns / gen(ish) expressions, and standalone objects like // Steps / Arp / Scores. Everything else we can ignore e.g. bass = tracks[0] // first check to see if the right operand is a callexpression if( expression.right.type === 'CallExpression' ) { // if standalone object (Steps, Arp, Score etc.) if( Marker.standalone[ expression.right.callee.name ] ) { const obj = window[ expression.left.name ] if( obj.markup === undefined ) Marker.prepareObject( obj ) Marker.standalone[ expression.right.callee.name ]( expression.right, state.cm, obj, expression.left.name, state, cb ) }else{ // if it's a gen~ object we need to store a reference so that we can create wavepattern // annotations when appropriate. const left = expression.left const right= expression.right Marker.globalIdentifiers[ left.name ] = right // XXX does this need a track object? passing null... // Marker.processGen( expression, state.cm, null) } } }, CallExpression( node, state, cb ) { cb( node.callee, state ) let endIdx = state.length - 1 const end = state[ endIdx ] const foundSequence = state.indexOf( 'seq' ) > -1 // end === 'seq' const isMessage = state.indexOf( 'message' ) > -1 // check to see if .once, .repeat, or .stop is appended to call to .seq if( seqSuffixes.indexOf( end ) > -1 ) { endIdx-- if( seqSuffixes.indexOf( node.callee.property.name ) > -1 ) return //node = node.callee.property } if( isMessage === false ) { if( foundSequence === true ){ const hasSeqNumber = node.arguments.length > 2 let seqNumber = 0 if( hasSeqNumber === true ) { seqNumber = node.arguments[2].raw } const seq = Marker.getObj( state.slice( 0, endIdx ), true, seqNumber ) Marker.markPatternsForSeq( seq, node.arguments, state, cb, node, seqNumber ) }else{ Marker.processGen( node, state.cm, null, null, null, state.indexOf('seq') > -1 ? 0 : -1 ) } }else{ if( foundSequence === true ){ const hasSeqNumber = node.arguments.length > 2 let seqNumber = 0 if( hasSeqNumber === true ) { seqNumber = node.arguments[2].raw } state[0] = 'message' state[1] = node.arguments[0].value state[2] = 'seq' let seq if( node.arguments.length > 1 ) { // this is the call to .seq state[1] = node.callee.object.arguments[0].value seq = window.message( node.callee.object.arguments[0].value ) Marker.markPatternsForSeq( seq, node.arguments, state, cb, node, seqNumber ) }else{ // this is the call to message(), which has one argument, the message prefix seq = window.message( node.arguments[0].value ) } } } }, MemberExpression( node, state, cb ) { // XXX why was this here? //if( node.object.name === 'tracks' ) state.length = 0 // for any member name, make sure to get rid of potential quotes surrounding it using // the strip function. if( node.object.type !== 'Identifier' ) { if( node.property ) { const unstripped = node.property.type === 'Identifier' ? node.property.name : node.property.raw state.unshift( strip( unstripped ) ) } cb( node.object, state ) }else{ if( node.property !== undefined ) { // if the objects is an array member, e.g. tracks[0] state.unshift( strip( node.property.raw || node.property.name ) ) } state.unshift( strip( node.object.name ) ) } }, } } },{}],16:[function(require,module,exports){ const COLORS = { FILL:'rgba(46,50,53,1)', STROKE:'#aaa', DOT:'rgba(89, 151, 198, 1)'//'rgba(0,0,255,1)' } let Gibber = null const Waveform = { widgets: { dirty:false }, createWaveformWidget( line, closeParenStart, ch, isAssignment, node, cm, patternObject, track, isSeq=true ) { let widget = document.createElement( 'canvas' ) widget.padding = 40 widget.waveWidth = 60 widget.ctx = widget.getContext('2d') widget.style.display = 'inline-block' widget.style.verticalAlign = 'middle' widget.style.height = '1.1em' widget.style.width = ((widget.padding * 2 + widget.waveWidth) * window.devicePixelRation ) + 'px' widget.style.backgroundColor = 'transparent' widget.style.margin = '0 1em' //widget.style.borderLeft = '1px solid #666' //widget.style.borderRight = '1px solid #666' widget.setAttribute( 'width', widget.padding * 2 + widget.waveWidth ) widget.setAttribute( 'height', 13 ) widget.ctx.fillStyle = COLORS.FILL widget.ctx.strokeStyle = COLORS.STROKE widget.ctx.font = '10px monospace' widget.ctx.lineWidth = 1 widget.gen = patternObject !== null ? patternObject : Gibber.__gen.gen.lastConnected.shift() widget.values = [] widget.storage = [] widget.min = 10000 widget.max = -10000 if( widget.gen === null || widget.gen === undefined ) { if( node.expression !== undefined && node.expression.type === 'AssignmentExpression' ) { isAssignment = true widget.gen = window[ node.expression.left.name ] if( widget.gen.widget !== undefined ) { widget.gen.widget.parentNode.removeChild( widget.gen.widget ) } widget.gen.widget = widget }else if( node.type === 'CallExpression' ) { const state = cm.__state if( node.callee.name !== 'Lookup' ) { const objName = `${state[0]}` const track = window.signals[0]//window[ objName ][ state[1] ] let wave if( state.length > 2 ) { wave = track[ node.callee.object.property.value][ node.arguments[2].value ] }else{ wave = track() } if( wave !== undefined && wave.values.type === 'WavePattern' ) { widget.gen = wave.values widget.gen.paramID += '_' + node.arguments[2].value //widget.gen.widget = widget } isAssignment = true }else{ widget.gen = patternObject } //if( seq !== undefined && seq.timings.type === 'WavePattern' ) { //} } }else{ if( widget.gen.widget !== undefined && widget.gen.widget !== widget ) { isAssignment = true //widget.gen = window[ node.expression.left.name ] } } //Gibber.__gen.gen.lastConnected = null //for( let i = 0; i < 120; i++ ) widget.values[ i ] = 0 let replaced = false //if( isAssignment === false && isSeq === false ) { // if( widget.gen !== null ) { // let oldWidget = Waveform.widgets[ widget.gen.paramID ] // if( oldWidget !== undefined ) { // //oldWidget.parentNode.removeChild( oldWidget ) // console.log( 'replaced' ) // widget = oldWidget // replaced = true // } // } //} if( replaced === false ) { widget.mark = cm.markText({ line, ch:ch }, { line, ch:ch+1 }, { replacedWith:widget }) widget.mark.__clear = widget.mark.clear widget.mark.clear = function() { const pos = widget.mark.find() if( pos === undefined ) return widget.mark.__clear() if( isSeq === true ) { // only replace for waveforms inside of a .seq() call cm.replaceRange( '', { line:pos.from.line, ch:pos.from.ch }, { line:pos.from.line, ch:pos.to.ch } ) } } } widget.clear = ()=> widget.mark.clear() if( widget.gen !== null ) { //console.log( 'paramID = ', widget.gen.paramID ) Waveform.widgets[ widget.gen.paramID ] = widget widget.gen.widget = widget } if( patternObject !== null ) { patternObject.mark = widget.mark if( patternObject === Gibber.Gen.lastConnected[0] ) Gibber.Gen.lastConnected.shift() } widget.onclick = ()=> { widget.min = Infinity widget.max = -Infinity widget.storage.length = 0 } }, clear() { for( let key in Waveform.widgets ) { let widget = Waveform.widgets[ key ] if( typeof widget === 'object' ) { widget.mark.clear() //widget.parentNode.removeChild( widget ) } } Waveform.widgets = { dirty:false } }, // currently called when a network snapshot message is received providing ugen state.. // needs to also be called for wavepatterns. updateWidget( id, __value, isFromMax = true ) { const widget = typeof id !== 'object' ? Waveform.widgets[ id ] : id if( widget === undefined ) return let value = parseFloat( __value ) // XXX why does beats generate a downward ramp? if( isFromMax ) value = 1 - value if( typeof widget.values[76] !== 'object' ) { widget.values[ 76 ] = value widget.storage.push( value ) } if( widget.storage.length > 120 ) { widget.max = Math.max.apply( null, widget.storage ) widget.min = Math.min.apply( null, widget.storage ) widget.storage.length = 0 } else if( value > widget.max ) { widget.max = value }else if( value < widget.min ) { widget.min = value } widget.values.shift() Waveform.widgets.dirty = true }, // called by animation scheduler if Waveform.widgets.dirty === true drawWidgets() { Waveform.widgets.dirty = false const drawn = [] for( let key in Waveform.widgets ) { if( key === 'dirty' ) continue const widget = Waveform.widgets[ key ] // ensure that a widget does not get drawn more // than once per frame if( drawn.indexOf( widget ) !== -1 ) continue if( typeof widget === 'object' && widget.ctx !== undefined ) { widget.ctx.fillStyle = COLORS.FILL widget.ctx.fillRect( 0,0, widget.width, widget.height ) // draw left border widget.ctx.beginPath() widget.ctx.moveTo( widget.padding + .5, 0.5 ) widget.ctx.lineTo( widget.padding + .5, widget.height + .5 ) widget.ctx.stroke() // draw right border widget.ctx.beginPath() widget.ctx.moveTo( widget.padding + widget.waveWidth + .5, .5 ) widget.ctx.lineTo( widget.padding + widget.waveWidth + .5, widget.height + .5 ) widget.ctx.stroke() // draw waveform widget.ctx.beginPath() widget.ctx.moveTo( widget.padding, widget.height / 2 + 1 ) const range = widget.max - widget.min const wHeight = widget.height * .85 + .45 for( let i = 0, len = widget.waveWidth; i < len; i++ ) { const data = widget.values[ i ] const shouldDrawDot = typeof data === 'object' const value = shouldDrawDot ? data.value : data const scaledValue = ( value - widget.min ) / range const yValue = scaledValue * (wHeight) - .5 if( shouldDrawDot === true ) { widget.ctx.fillStyle = COLORS.DOT widget.ctx.fillRect( i + widget.padding -1, wHeight - yValue - 1.5, 3, 3) widget.ctx.lineTo( i + widget.padding + .5, wHeight - yValue - 1.5 ) }else{ widget.ctx.lineTo( i + widget.padding + .5, wHeight - yValue ) } } widget.ctx.stroke() // draw min/max widget.ctx.fillStyle = COLORS.STROKE widget.ctx.textAlign = 'right' widget.ctx.fillText( widget.min.toFixed(2), widget.padding - 2, widget.height ) widget.ctx.textAlign = 'left' widget.ctx.fillText( widget.max.toFixed(2), widget.waveWidth + widget.padding + 2, widget.height / 2 ) // draw corners widget.ctx.beginPath() widget.ctx.moveTo( .5, 3.5 ) widget.ctx.lineTo( .5, .5 ) widget.ctx.lineTo( 3.5, .5) widget.ctx.moveTo( .5, widget.height - 3.5 ) widget.ctx.lineTo( .5, widget.height - .5 ) widget.ctx.lineTo( 3.5, widget.height - .5 ) const right = widget.padding * 2 + widget.waveWidth - .5 widget.ctx.moveTo( right, 3.5 ) widget.ctx.lineTo( right, .5 ) widget.ctx.lineTo( right - 3, .5 ) widget.ctx.moveTo( right, widget.height - 3.5 ) widget.ctx.lineTo( right, widget.height - .5 ) widget.ctx.lineTo( right - 3, widget.height - .5 ) widget.ctx.stroke() drawn.push( widget ) } } } } module.exports = function( __Gibber ) { Gibber = __Gibber return Waveform } },{}],17:[function(require,module,exports){ module.exports = function( Gibber ) { let Arp = function( chord = [0,2,4,6], octaves = 1, pattern = 'updown2' ) { let notes, arp if( typeof chord === 'string' ) { // TODO: doesn't work... numbers can't be MIDI numbers because they go through scale conversion let _chord = Gibber.Theory.Chord.create( chord ) chord = _chord.notes } notes = Gibber.Pattern.apply( null, chord.slice( 0 ) ) if( pattern === 'down' ) notes.reverse() let maxLength = notes.values.length * octaves, dir = pattern !== 'down' ? 'up' : 'down' arp = ()=> { arp.phase++ if( arp.phase >= maxLength ) { arp.phase = 0 } const range = arp.notes.end - arp.notes.start if( arp.phase !== 0 && arp.phase % range === 0 ) { if( dir === 'up' ) { if( arp.octave < octaves ) { arp.octave += 1 }else{ if( pattern === 'up' ) { arp.octave = 1 }else{ dir = 'down' notes.reverse() } } }else{ if( arp.octave > 1 ) { arp.octave += -1 }else{ if( pattern === 'down' ) { arp.octave = octaves } else { dir = 'up' notes.reverse() } } } } let octaveMod, note = arp.notes() //note = Gibber.Theory.Note.convertToMIDI( note ) for( let i = 1; i < arp.octave; i++ ) { note += 7 } let methodNames = [ 'rotate','switch','invert','reset', 'flip', 'transpose','reverse','shuffle','scale', 'store', 'range', 'set' ] for( let key of methodNames ) { arp[ key ] = notes[ key ].bind( notes ) Gibber.addSequencingToMethod( arp, key ) } //arp.transpose = notes.transpose.bind( notes ) //arp.reset = notes.reset.bind( notes ) //arp.reverse = notes.reverse.bind( notes ) //arp.rotate = notes.rotate.bind( notes ) return note } arp.octave = 0 arp.phase = -1 arp.notes = notes return arp } Arp.patterns = { up( array ) { return array }, down( array ) { return array.reverse() }, updown( array ) { let _tmp = array.slice( 0 ) _tmp.reverse() return array.concat( _tmp ) }, updown2( array ) { // do not repeat highest and lowest notes var tmp = array.slice( 0 ) tmp.pop() tmp.reverse() tmp.pop() return array.concat( tmp ) } } return Arp } },{}],18:[function(require,module,exports){ module.exports = function( Gibber ) { let Pattern = Gibber.Pattern const numberToBinaryArray = num => { let str = Number( num ).toString( 2 ) while( str.length < 8 ) { str = '0' + str } // need to use parseFloat an