├── gibberwocky_2 Project ├── Icon ├── gibberwocky_2.als └── Ableton Project Info │ └── Project8_1.cfg ├── gibberwocky.demo Project ├── Icon ├── gibberwocky.demo.als └── Ableton Project Info │ └── Project8_1.cfg ├── gibberwocky_master.amxd ├── gibberwocky_midi.amxd ├── js ├── example.js ├── index.js ├── annotations │ ├── update │ │ ├── lookupAnnotation.js │ │ ├── anonymousAnnotation.js │ │ ├── createBorderCycle.js │ │ └── euclidAnnotation.js │ ├── markup │ │ ├── callExpression.js │ │ ├── literal.js │ │ ├── binaryExpression.js │ │ ├── unaryExpression.js │ │ ├── identifier.js │ │ └── arrayExpression.js │ ├── standalone │ │ ├── scoreAnnotation.js │ │ ├── hexStepsAnnotations.js │ │ └── stepsAnnotation.js │ └── visitors.js ├── hex.js ├── lomView.js ├── hexSteps.js ├── automata.js ├── animationScheduler.js ├── steps.js ├── momView.js ├── arp.js ├── priorityqueue.js ├── live.js ├── channel.js ├── utility.js ├── track.js ├── clock.js ├── tabs-standalone.microlib-latest.js ├── euclidean.js ├── max_device.js ├── max.js ├── midi.js ├── vanillatree.js ├── theory.js ├── waveObjects.js ├── communication.js └── gen.js ├── css ├── tabs.microlib.css ├── show-hint.css ├── vanillatree.css ├── gibber.css └── codemirror.css ├── .gitignore ├── LICENSE ├── package.json ├── misc └── theory_thoughts.md ├── dups.sh ├── gibberwocky_expr.js ├── index_template.html ├── README.md ├── gulpfile.js ├── gibberwocky.js └── debug.html /gibberwocky_2 Project/Icon : -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gibberwocky.demo Project/Icon : -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gibberwocky_master.amxd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibber-cc/gibberwocky/HEAD/gibberwocky_master.amxd -------------------------------------------------------------------------------- /gibberwocky_midi.amxd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibber-cc/gibberwocky/HEAD/gibberwocky_midi.amxd -------------------------------------------------------------------------------- /gibberwocky_2 Project/gibberwocky_2.als: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibber-cc/gibberwocky/HEAD/gibberwocky_2 Project/gibberwocky_2.als -------------------------------------------------------------------------------- /gibberwocky.demo Project/gibberwocky.demo.als: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibber-cc/gibberwocky/HEAD/gibberwocky.demo Project/gibberwocky.demo.als -------------------------------------------------------------------------------- /gibberwocky_2 Project/Ableton Project Info/Project8_1.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibber-cc/gibberwocky/HEAD/gibberwocky_2 Project/Ableton Project Info/Project8_1.cfg -------------------------------------------------------------------------------- /gibberwocky.demo Project/Ableton Project Info/Project8_1.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gibber-cc/gibberwocky/HEAD/gibberwocky.demo Project/Ableton Project Info/Project8_1.cfg -------------------------------------------------------------------------------- /js/example.js: -------------------------------------------------------------------------------- 1 | const Examples = { 2 | 3 | live: require( './liveExamples.js' ), 4 | max: require( './maxExamples.js' ), 5 | midi: require( './midiExamples.js' ) 6 | 7 | } 8 | 9 | module.exports = Examples 10 | -------------------------------------------------------------------------------- /js/index.js: -------------------------------------------------------------------------------- 1 | //require( 'babel-polyfill' ) 2 | 3 | let Gibber = require( './gibber.js' ), 4 | useAudioContext = false, 5 | count = 0 6 | 7 | Gibber.init() 8 | window.Gibberwocky = window.Gibber = Gibber 9 | 10 | -------------------------------------------------------------------------------- /js/annotations/update/lookupAnnotation.js: -------------------------------------------------------------------------------- 1 | module.exports = ( patternObject, marker, className, cm, track, patternNode, patternType, seqNumber ) => { 2 | Gibber.Environment.codeMarkup.processGen( patternNode, cm, null, patternObject, null, -1 ) 3 | 4 | patternNode.arguments[1].offset = patternNode.offset 5 | 6 | Gibber.Environment.codeMarkup.patternMarkupFunctions.ArrayExpression( 7 | patternNode.arguments[1], 8 | cm.__state, 9 | { object:patternObject, [ patternType ]: patternObject }, 10 | patternType, 11 | null, 12 | seqNumber, 13 | true 14 | ) 15 | } 16 | 17 | -------------------------------------------------------------------------------- /css/tabs.microlib.css: -------------------------------------------------------------------------------- 1 | .microlib_tabs_nav { 2 | width: 100%; 3 | height: 26px; 4 | } 5 | 6 | .microlib_tabs_nav_item { 7 | float: left; 8 | height: 26px; 9 | padding: 0 10px; 10 | line-height: 26px; 11 | border-right: 1px solid #000; 12 | border-bottom: 1px solid #000; 13 | } 14 | 15 | .microlib_tabs_nav_item:hover, 16 | .microlib_tabs_nav_item.microlib_active { 17 | background: #000; 18 | color:white; 19 | cursor: pointer; 20 | } 21 | 22 | .microlib_tabs_tab { 23 | padding: 5px; 24 | display: none; 25 | } 26 | 27 | .microlib_tabs_tab.microlib_active { 28 | display: block; 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db 38 | 39 | # TextMate # 40 | ############ 41 | .tm_properties 42 | 43 | # vim # 44 | *.swp 45 | *.*~ 46 | *.swo 47 | *.bak 48 | 49 | node_modules 50 | -------------------------------------------------------------------------------- /css/show-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-hints { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | list-style: none; 6 | 7 | margin: 0; 8 | padding: 2px; 9 | 10 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 11 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 12 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 13 | border-radius: 3px; 14 | border: 1px solid silver; 15 | 16 | background: white; 17 | font-size: 90%; 18 | font-family: monospace; 19 | 20 | max-height: 20em; 21 | overflow-y: auto; 22 | } 23 | 24 | .CodeMirror-hint { 25 | margin: 0; 26 | padding: 0 4px; 27 | border-radius: 2px; 28 | max-width: 19em; 29 | overflow: hidden; 30 | white-space: pre; 31 | color: black; 32 | cursor: pointer; 33 | } 34 | 35 | li.CodeMirror-hint-active { 36 | background: #08f; 37 | color: white; 38 | } 39 | -------------------------------------------------------------------------------- /js/annotations/markup/callExpression.js: -------------------------------------------------------------------------------- 1 | module.exports = function( Marker ) { 2 | 'use strict' 3 | 4 | // CallExpression denotes an array (or other object) that calls a method, like .rnd() 5 | // could also represent an anonymous function call, like Rndi(19,40) 6 | const CallExpression = function( patternNode, state, seq, patternType, index=0 ) { 7 | if( patternNode.processed === true ) return 8 | 9 | var args = Array.prototype.slice.call( arguments, 0 ) 10 | 11 | if( patternNode.callee.type === 'MemberExpression' && patternNode.callee.object.type === 'ArrayExpression' ) { 12 | args[ 0 ] = patternNode.callee.object 13 | args[ 0 ].offset = Marker.offset 14 | 15 | Marker.patternMarkupFunctions.ArrayExpression( ...args ) 16 | } else if (patternNode.callee.type === 'Identifier' ) { 17 | // function like Euclid or gen~d 18 | Marker.patternMarkupFunctions.Identifier( ...args ) 19 | } 20 | } 21 | 22 | return CallExpression 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License (MIT) 2 | Copyright (c) 2015 Charlie Roberts 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gibber.max", 3 | "version": "1.0.0", 4 | "description": "Use Gibber inside Max/MSP and Max 4 Live.", 5 | "main": "js/index.js", 6 | "dependencies": { 7 | "browserify": "^12.0.1", 8 | "codemirror": "^5.9.0", 9 | "genish.js": "^0.3.0", 10 | "gulp": "^3.9.0", 11 | "marked": "^0.3.6", 12 | "vinyl-transform": "^1.0.0" 13 | }, 14 | "devDependencies": { 15 | "acorn": "^2.7.0", 16 | "babel-polyfill": "^6.3.14", 17 | "babel-preset-es2015": "^6.3.13", 18 | "babelify": "^7.2.0", 19 | "big.js": "^3.1.3", 20 | "gulp-sourcemaps": "^2.6.1", 21 | "gulp-util": "^3.0.7", 22 | "teoria": "^2.2.0", 23 | "through2": "^2.0.3", 24 | "vinyl-source-stream": "^1.1.0", 25 | "watchify": "^3.6.1" 26 | }, 27 | "scripts": { 28 | "test": "echo \"Error: no test specified\" && exit 1" 29 | }, 30 | "keywords": [ 31 | "gibber", 32 | "max/msp", 33 | "live", 34 | "coding" 35 | ], 36 | "author": "Charlie Roberts (http://www.charlie-roberts.com/)", 37 | "license": "MIT" 38 | } 39 | -------------------------------------------------------------------------------- /misc/theory_thoughts.md: -------------------------------------------------------------------------------- 1 | *Note* 2 | - uses MIDI value as core data 3 | - can translate from string to MIDI (possibly slightly annoying) 4 | - can also provide pitchbend amount for microtonal stuff 5 | 6 | *Chord* 7 | - can build set of notes based on notation and output array of MIDI + pitchbend info 8 | - really annoying 9 | 10 | ([A-Za-z]b?#?)(\d)([a-z]{3})(b?#?\d)* // captures ['c','c#','cb'] [4] ['min'] [7] 11 | 12 | *Temperament* 13 | - Defines number of notes per octave 14 | - Defines MIDI note number + pitchbend value for each note 15 | 16 | *Scale* 17 | - a collection of scale degrees that are then used alongside a Temperament 18 | - has a key signature 19 | - can change root scale degree. notes triggered by scale change based on root, scale, and temperament 20 | - should be able to read tuning files from tune.js 21 | 22 | Example: 23 | a = Scale({ key:'f minor', root:1 }) 24 | a.note( 0 ) // outputs a Note object equivalent to F 25 | a.root = 4 // modulate to Bb major 26 | a.note( 2 ) // outputs a Note object equivalent to D (the third of Bb) 27 | -------------------------------------------------------------------------------- /js/annotations/standalone/scoreAnnotation.js: -------------------------------------------------------------------------------- 1 | const Utility = require( '../../utility.js' ) 2 | const $ = Utility.create 3 | 4 | module.exports = function( node, cm, track, objectName, vOffset=0 ) { 5 | let timelineNodes = node.arguments[ 0 ].elements 6 | //console.log( timelineNodes ) 7 | track.markup.textMarkers[ 'score' ] = [] 8 | 9 | for( let i = 0; i < timelineNodes.length; i+=2 ) { 10 | let timeNode = timelineNodes[ i ], 11 | functionNode = timelineNodes[ i + 1 ] 12 | 13 | functionNode.loc.start.line += vOffset - 1 14 | functionNode.loc.end.line += vOffset - 1 15 | functionNode.loc.start.ch = functionNode.loc.start.column 16 | functionNode.loc.end.ch = functionNode.loc.end.column 17 | 18 | let marker = cm.markText( functionNode.loc.start, functionNode.loc.end, { className:`score${i/2}` } ) 19 | track.markup.textMarkers[ 'score' ][ i/2 ] = marker 20 | 21 | } 22 | 23 | let lastClass = 'score0' 24 | $( '.' + lastClass ).add( 'scoreCurrentIndex' ) 25 | // TODO: global object usage is baaaad methinks? 26 | 27 | window[ objectName ].onadvance = ( idx ) => { 28 | $( '.' + lastClass ).remove( 'scoreCurrentIndex' ) 29 | lastClass = `score${idx}` 30 | $( '.' + lastClass ).add( 'scoreCurrentIndex' ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /js/annotations/update/anonymousAnnotation.js: -------------------------------------------------------------------------------- 1 | module.exports = ( patternObject, marker, className, cm ) => { 2 | patternObject.commentMarker = marker 3 | let update = () => { 4 | if( !patternObject.commentMarker ) return 5 | let patternValue = '' + patternObject.update.value.pop() 6 | 7 | if( patternValue.length > 8 ) patternValue = patternValue.slice(0,8) 8 | 9 | let val ='/* ' + patternValue + ' */', 10 | pos = patternObject.commentMarker.find(), 11 | end = Object.assign( {}, pos.to ) 12 | 13 | //pos.from.ch += 1 14 | end.ch = pos.from.ch + val.length 15 | //pos.from.ch += 1 16 | 17 | cm.replaceRange( val, pos.from, pos.to ) 18 | 19 | if( patternObject.commentMarker ) patternObject.commentMarker.clear() 20 | 21 | patternObject.commentMarker = cm.markText( pos.from, end, { className, atomic:false }) 22 | } 23 | 24 | patternObject.clear = () => { 25 | try{ 26 | let commentPos = patternObject.commentMarker.find() 27 | //commentPos.to.ch -= 1 // XXX wish I didn't have to do this 28 | cm.replaceRange( '', commentPos.from, commentPos.to ) 29 | patternObject.commentMarker.clear() 30 | delete patternObject.commentMarker 31 | } catch( e ) {} // yes, I just did that XXX 32 | } 33 | 34 | return update 35 | } 36 | 37 | -------------------------------------------------------------------------------- /js/annotations/markup/literal.js: -------------------------------------------------------------------------------- 1 | module.exports = function( Marker ) { 2 | // Marker.patternMarkupFunctions[ valuesNode.type ]( valuesNode, state, seq, 'values', container, seqNumber ) 3 | 4 | const Literal = function( patternNode, state, seq, patternType, container=null, index=0 ) { 5 | if( patternNode.processed === true ) return 6 | 7 | const cm = state.cm 8 | const seqTarget = seq.object 9 | const patternObject = seq[ patternType ] 10 | 11 | const [ className, start, end ] = Marker._getNamesAndPosition( patternNode, state, patternType, index ) 12 | const cssName = className 13 | 14 | const marker = cm.markText( start, end, { 15 | 'className': cssName + ' annotation-border', 16 | //inclusiveLeft: true, 17 | //inclusiveRight: true 18 | }) 19 | 20 | if( seqTarget.markup === undefined ) Marker.prepareObject( seqTarget ) 21 | 22 | seqTarget.markup.textMarkers[ className ] = marker 23 | 24 | if( seqTarget.markup.cssClasses[ className ] === undefined ) seqTarget.markup.cssClasses[ className ] = [] 25 | 26 | seqTarget.markup.cssClasses[ className ][ index ] = cssName 27 | 28 | patternObject.marker = marker 29 | Marker.finalizePatternAnnotation( patternObject, className, seqTarget, marker ) 30 | } 31 | 32 | return Literal 33 | 34 | } 35 | -------------------------------------------------------------------------------- /dups.sh: -------------------------------------------------------------------------------- 1 | cp gibbergen0.maxpat gibbergen1.maxpat 2 | cp gibbergen0.maxpat gibbergen2.maxpat 3 | cp gibbergen0.maxpat gibbergen3.maxpat 4 | cp gibbergen0.maxpat gibbergen4.maxpat 5 | cp gibbergen0.maxpat gibbergen5.maxpat 6 | cp gibbergen0.maxpat gibbergen6.maxpat 7 | cp gibbergen0.maxpat gibbergen7.maxpat 8 | cp gibbergen0.maxpat gibbergen8.maxpat 9 | cp gibbergen0.maxpat gibbergen9.maxpat 10 | cp gibbergen0.maxpat gibbergen10.maxpat 11 | cp gibbergen0.maxpat gibbergen11.maxpat 12 | cp gibbergen0.maxpat gibbergen12.maxpat 13 | cp gibbergen0.maxpat gibbergen13.maxpat 14 | cp gibbergen0.maxpat gibbergen14.maxpat 15 | cp gibbergen0.maxpat gibbergen15.maxpat 16 | cp gibbergen0.maxpat gibbergen16.maxpat 17 | cp gibbergen0.maxpat gibbergen17.maxpat 18 | cp gibbergen0.maxpat gibbergen18.maxpat 19 | cp gibbergen0.maxpat gibbergen19.maxpat 20 | cp gibbergen0.maxpat gibbergen20.maxpat 21 | cp gibbergen0.maxpat gibbergen21.maxpat 22 | cp gibbergen0.maxpat gibbergen22.maxpat 23 | cp gibbergen0.maxpat gibbergen23.maxpat 24 | cp gibbergen0.maxpat gibbergen24.maxpat 25 | cp gibbergen0.maxpat gibbergen25.maxpat 26 | cp gibbergen0.maxpat gibbergen26.maxpat 27 | cp gibbergen0.maxpat gibbergen27.maxpat 28 | cp gibbergen0.maxpat gibbergen28.maxpat 29 | cp gibbergen0.maxpat gibbergen29.maxpat 30 | cp gibbergen0.maxpat gibbergen30.maxpat 31 | cp gibbergen0.maxpat gibbergen31.maxpat -------------------------------------------------------------------------------- /gibberwocky_expr.js: -------------------------------------------------------------------------------- 1 | function expr(code) { 2 | // get a reference to a named gen~ in the patcher: 3 | var gen_obj = this.patcher.getnamed("mockturtle"); 4 | 5 | // get a reference to the embedded gen~ patcher: 6 | var gen_patcher = gen_obj.subpatcher(); 7 | 8 | // remove all existing objects: 9 | gen_patcher.apply(function(b) { gen_patcher.remove(b); }); 10 | 11 | // hacky parsing to get the number of inlets from the code: 12 | var ninlets = 1; 13 | code.replace(/in(\d+)/igm, function(m,p) { ninlets = Math.max(ninlets, p); }); 14 | // etc. to get (and remove) the params: 15 | var params = []; 16 | code = code.replace(/Param (p\d+)\(([-+]?[0-9]*\.?[0-9]+)\);/igm, function(m,id,init) { 17 | params.push([id, init]); 18 | return ""; 19 | }); 20 | 21 | // have to remove whitespace from code to use with patcher.newdefault() 22 | code = code.replace(/\s/gi, ""); 23 | var codebox = gen_patcher.newdefault([10, 100, "codebox", "@code", code]); 24 | 25 | // create params: 26 | for (var i=0; i 0 ? 1 : 0 14 | onesAndZeros += (num & 4) > 0 ? 1 : 0 15 | onesAndZeros += (num & 2) > 0 ? 1 : 0 16 | onesAndZeros += (num & 1) > 0 ? 1 : 0 17 | } 18 | 19 | let __onesAndZeros = onesAndZeros.split('') 20 | 21 | let pattern = Gibber.Pattern.apply( null, __onesAndZeros ) 22 | 23 | pattern.time = time 24 | 25 | //let output = { time, shouldExecute: 0 } 26 | 27 | pattern.filters.push( ( args ) => { 28 | let val = args[ 0 ], 29 | idx = args[ 2 ], 30 | output = { time, shouldExecute: 0 } 31 | 32 | output.shouldExecute = val > 0 33 | 34 | args[ 0 ] = output 35 | 36 | return args 37 | }) 38 | 39 | pattern.reseed = ( ...args )=> { 40 | let n, k 41 | 42 | if( Array.isArray( args[0] ) ) { 43 | k = args[0][0] 44 | n = args[0][1] 45 | }else{ 46 | k = args[0] 47 | n = args[1] 48 | } 49 | 50 | if( n === undefined ) n = 16 51 | 52 | out = createStartingArray( n,k ) 53 | let _onesAndZeros = Inner( n,k ) 54 | 55 | pattern.set( _onesAndZeros ) 56 | pattern.time = 1 / n 57 | 58 | // this.checkForUpdateFunction( 'reseed', pattern ) 59 | 60 | return pattern 61 | } 62 | 63 | Gibber.addSequencingToMethod( pattern, 'reseed' ) 64 | 65 | if( typeof rotation === 'number' ) pattern.rotate( rotation ) 66 | 67 | return pattern 68 | } 69 | 70 | return Hex 71 | 72 | } 73 | -------------------------------------------------------------------------------- /js/lomView.js: -------------------------------------------------------------------------------- 1 | require( './vanillatree.js' ) 2 | 3 | let Gibber = null, count = -1 4 | 5 | let lomView = { 6 | tree: null, 7 | 8 | init( _gibber ) { 9 | Gibber = _gibber 10 | this.setup() 11 | this.create() 12 | 13 | count++ 14 | if( count ) 15 | Gibber.log( 'the live object model (lom) has been updated.' ) 16 | }, 17 | 18 | setup() { 19 | document.querySelector( '#lomView' ).innerHTML = '' 20 | 21 | this.tree = new VanillaTree('#lomView', { 22 | placeholder: '' 23 | //contextmenu: [{ 24 | // label: 'Label 1', 25 | // action: function(id) { 26 | // // someAction 27 | // } 28 | //},{ 29 | // label: 'Label 2', 30 | // action: function(id) { 31 | // // someAction 32 | // } 33 | //}] 34 | }) 35 | this.tree.mode = 'live' 36 | //elem.addEventListener( 'vtree-select', function( evt ) { 37 | // console.log( evt, evt.detail ) 38 | //}); 39 | }, 40 | 41 | processTrack( track, id ) { 42 | let trackID = id === undefined ? track.spec.name : id 43 | lomView.tree.add({ label:trackID, id:trackID }) 44 | for( let device of track.devices ) { 45 | let deviceID = device.name // device.title 46 | lomView.tree.add({ label:deviceID, id:deviceID, parent:trackID }) 47 | for( let param of device.parameters ) { 48 | lomView.tree.add({ label:param.name, id:encodeURI( param.name ), parent:deviceID }) 49 | } 50 | } 51 | }, 52 | 53 | create() { 54 | Gibber.Live.tracks.forEach( v => lomView.processTrack( v ) ) 55 | Gibber.Live.returns.forEach( v => lomView.processTrack( v ) ) // 'return ' + v.id ) ) 56 | lomView.processTrack( Gibber.Live.master ) 57 | } 58 | } 59 | 60 | module.exports = lomView 61 | -------------------------------------------------------------------------------- /js/hexSteps.js: -------------------------------------------------------------------------------- 1 | module.exports = function( Gibber ) { 2 | 3 | let Steps = { 4 | type:'HexSteps', 5 | create( _steps, track = Gibber.currentTrack ) { 6 | let stepseq = Object.create( Steps ) 7 | 8 | stepseq.seqs = {} 9 | 10 | for ( let _key in _steps ) { 11 | let values = _steps[ _key ], 12 | key = parseInt( _key ) 13 | 14 | let seq = Gibber.Seq( key, Gibber.Hex( values ), 'midinote', track, 0 ) 15 | seq.trackID = track.id 16 | 17 | stepseq.seqs[ _key ] = seq 18 | stepseq[ _key ] = seq.values 19 | } 20 | 21 | stepseq.start() 22 | stepseq.addPatternMethods() 23 | 24 | return stepseq 25 | }, 26 | 27 | addPatternMethods() { 28 | groupMethodNames.map( (name) => { 29 | this[ name ] = function( ...args ) { 30 | for( let key in this.seqs ) { 31 | this.seqs[ key ].values[ name ].apply( this, args ) 32 | } 33 | } 34 | 35 | Gibber.addSequencingToMethod( this, name, 1 ) 36 | }) 37 | }, 38 | 39 | start() { 40 | for( let key in this.seqs ) { 41 | this.seqs[ key ].start() 42 | } 43 | }, 44 | 45 | stop() { 46 | for( let key in this.seqs ) { 47 | this.seqs[ key ].stop() 48 | } 49 | }, 50 | 51 | clear() { 52 | this.stop() 53 | 54 | for( let key in this.seqs ) { 55 | this.seqs[ key ].timings.clear() 56 | } 57 | }, 58 | 59 | /* 60 | *rotate( amt ) { 61 | * for( let key in this.seqs ) { 62 | * this.seqs[ key ].values.rotate( amt ) 63 | * } 64 | *}, 65 | */ 66 | } 67 | 68 | const groupMethodNames = [ 69 | 'rotate', 'reverse', 'transpose', 'range', 70 | 'shuffle', 'scale', 'repeat', 'switch', 'store', 71 | 'reset','flip', 'invert', 'set' 72 | ] 73 | 74 | return Steps.create 75 | 76 | } 77 | -------------------------------------------------------------------------------- /js/automata.js: -------------------------------------------------------------------------------- 1 | module.exports = function( Gibber ) { 2 | 3 | let Pattern = Gibber.Pattern 4 | 5 | const numberToBinaryArray = num => { 6 | let str = Number( num ).toString( 2 ) 7 | while( str.length < 8 ) { 8 | str = '0' + str 9 | } 10 | 11 | // need to use parseFloat and round due to fpe 12 | return str.split('').map( parseFloat ).map( Math.round ) 13 | } 14 | 15 | let Automata = function( __rule=30, __axiom='00011000', evolutionSpeed=1, playbackSpeed ) { 16 | const axiom = typeof __axiom === 'string' ? __axiom : Number( __axiom ).toString( 2 ), 17 | rule = numberToBinaryArray( __rule ) 18 | 19 | let currentState = axiom.split('').map( parseFloat ).map( Math.round ), 20 | nextState = currentState.slice( 0 ), 21 | pattern = Gibber.Pattern.apply( null, currentState ) 22 | 23 | pattern.time = playbackSpeed === undefined ? 1 / currentState.length : playbackSpeed 24 | 25 | pattern.filters.push( ( args ) => { 26 | let val = args[ 0 ], 27 | idx = args[ 2 ], 28 | output = { time:pattern.time, shouldExecute: 0 } 29 | 30 | output.shouldExecute = val > 0 31 | 32 | args[ 0 ] = output 33 | 34 | return args 35 | }) 36 | 37 | const width = currentState.length 38 | 39 | pattern.evolve = ()=> { 40 | currentState = nextState.slice( 0 ) 41 | 42 | for( let i = 0; i < width; i++ ) { 43 | let sum = '' 44 | sum += i > 0 ? currentState[ i - 1 ] : currentState[ currentState.length - 1 ] 45 | sum += currentState[ i ] 46 | sum += i < width - 1 ? currentState[ i + 1 ] : currentState[ 0 ] 47 | nextState[ i ] = rule[ 7 - Number( '0b'+sum ) ] 48 | } 49 | 50 | pattern.set( nextState ) 51 | } 52 | 53 | Gibber.addSequencingToMethod( pattern, 'evolve' ) 54 | Gibber.Utility.future( ()=> pattern.evolve.seq( 1, evolutionSpeed ), evolutionSpeed ) 55 | 56 | return pattern 57 | } 58 | 59 | return Automata 60 | 61 | } 62 | -------------------------------------------------------------------------------- /js/animationScheduler.js: -------------------------------------------------------------------------------- 1 | const Queue = require( './priorityqueue.js' ) 2 | let Gibber = null 3 | 4 | let Scheduler = { 5 | currentTime : performance.now(), 6 | queue: new Queue( ( a, b ) => a.time - b.time ), 7 | visualizationTime: { 8 | init:true, 9 | base:0, 10 | phase:0, 11 | }, 12 | 13 | init( __Gibber ) { 14 | Gibber = __Gibber 15 | window.requestAnimationFrame( this.onAnimationFrame ) 16 | }, 17 | 18 | clear() { 19 | this.queue.data.length = 0 20 | }, 21 | 22 | add( func, offset, idx ) { 23 | let time = this.currentTime + offset 24 | this.queue.push({ func, time }) 25 | 26 | return time 27 | }, 28 | 29 | runSoon( evt ) { 30 | 31 | try{ 32 | evt.func() 33 | }catch(e) { 34 | console.log( 'annotation error:', e.toString() ) 35 | } 36 | }, 37 | 38 | run( timestamp, dt ) { 39 | let nextEvent = this.queue.peek() 40 | //if( nextEvent === undefined ) return 41 | 42 | if( nextEvent !== undefined && this.queue.length > 0 && nextEvent.time <= timestamp ) { 43 | 44 | // remove event 45 | this.queue.pop() 46 | 47 | setTimeout( ()=> this.runSoon( nextEvent ) ) 48 | //try{ 49 | // nextEvent.func() 50 | //}catch( e ) { 51 | // console.log( e ) 52 | // Gibber.Environment.error( 'annotation error:', e.toString() ) 53 | //} 54 | 55 | // call recursively 56 | this.run( timestamp ) 57 | } 58 | 59 | if( Gibber.Environment.codeMarkup.waveform.widgets.dirty === true ) { 60 | Gibber.Environment.codeMarkup.waveform.drawWidgets() 61 | } 62 | }, 63 | 64 | onAnimationFrame( timestamp ) { 65 | window.requestAnimationFrame( this.onAnimationFrame ) 66 | const diff = timestamp - this.currentTime 67 | this.currentTime = timestamp 68 | this.visualizationTime.phase += diff 69 | 70 | this.run( timestamp, diff ) 71 | }, 72 | 73 | updateVisualizationTime( ms ) { 74 | if( this.visualizationTime.init === true ) { 75 | this.visualizationTime.base += ms 76 | this.visualizationTime.phase = 0 77 | }else{ 78 | this.visualizationTime.init = true 79 | } 80 | }, 81 | 82 | } 83 | 84 | Scheduler.onAnimationFrame = Scheduler.onAnimationFrame.bind( Scheduler ) 85 | 86 | module.exports = Scheduler 87 | -------------------------------------------------------------------------------- /js/steps.js: -------------------------------------------------------------------------------- 1 | module.exports = function( Gibber ) { 2 | 3 | let Steps = { 4 | type:'Steps', 5 | create( _steps, track = Gibber.currentTrack ) { 6 | let stepseq = Object.create( Steps ) 7 | 8 | stepseq.seqs = {} 9 | 10 | // create( values, timings, key, object = null, priority=0 ) 11 | for ( let _key in _steps ) { 12 | let values = _steps[ _key ].split(''), 13 | key = parseInt( _key ) 14 | 15 | //Gibber.addSequencingToMethod( d, 'midinote', 0, d.path+'midinote', 'max' ) 16 | let seq = Gibber.Seq( values, 1 / values.length, 'midinote', track, 0, track.__client ) 17 | seq.trackID = track.id 18 | 19 | seq.values.filters.push( function( args ) { 20 | let sym = args[ 0 ], 21 | velocity = ( parseInt( sym, 16 ) * 8 ) - 1 22 | 23 | if( isNaN( velocity ) ) { 24 | velocity = 0 25 | } 26 | 27 | // TODO: is there a better way to get access to beat, beatOffset and scheduler? 28 | if( velocity !== 0 ) { 29 | //let msg = seq.externalMessages[ 'velocity' ]( velocity, seq.values.beat + seq.values.beatOffset, seq.trackID ) 30 | //seq.values.scheduler.msgs.push( msg ) 31 | seq.velocity( velocity ) 32 | } 33 | 34 | args[ 0 ] = sym === '.' ? Gibber.Seq.DO_NOT_OUTPUT : key 35 | 36 | return args 37 | }) 38 | 39 | stepseq.seqs[ _key ] = seq 40 | stepseq[ _key ] = seq.values 41 | } 42 | 43 | stepseq.start() 44 | stepseq.addPatternMethods() 45 | 46 | return stepseq 47 | }, 48 | 49 | addPatternMethods() { 50 | groupMethodNames.map( (name) => { 51 | this[ name ] = function( ...args ) { 52 | for( let key in this.seqs ) { 53 | this.seqs[ key ].values[ name ].apply( this, args ) 54 | } 55 | } 56 | 57 | Gibber.addSequencingToMethod( this, name, 1 ) 58 | }) 59 | }, 60 | 61 | start() { 62 | for( let key in this.seqs ) { 63 | this.seqs[ key ].start() 64 | } 65 | }, 66 | 67 | stop() { 68 | for( let key in this.seqs ) { 69 | this.seqs[ key ].stop() 70 | } 71 | }, 72 | 73 | clear() { this.stop() }, 74 | 75 | /* 76 | *rotate( amt ) { 77 | * for( let key in this.seqs ) { 78 | * this.seqs[ key ].values.rotate( amt ) 79 | * } 80 | *}, 81 | */ 82 | } 83 | 84 | const groupMethodNames = [ 85 | 'rotate', 'reverse', 'transpose', 'range', 86 | 'shuffle', 'scale', 'repeat', 'switch', 'store', 87 | 'reset','flip', 'invert', 'set' 88 | ] 89 | 90 | return Steps.create 91 | 92 | } 93 | -------------------------------------------------------------------------------- /js/momView.js: -------------------------------------------------------------------------------- 1 | require( './vanillatree.js' ) 2 | 3 | /* NOTE THAT DRAG/DROP CODE IS IN VANILLATREE.JS FILE */ 4 | let Gibber = null, count = -1 5 | 6 | let momView = { 7 | tree: null, 8 | 9 | init( _gibber ) { 10 | Gibber = _gibber 11 | this.setup() 12 | this.create() 13 | 14 | count++ 15 | if( count ) 16 | Gibber.log( 'The Max scene has been updated.' ) 17 | }, 18 | 19 | setup() { 20 | document.querySelector( '#momView' ).innerHTML = '' 21 | 22 | this.tree = new VanillaTree('#momView', { 23 | placeholder: '' 24 | }) 25 | this.tree.mode = 'max' 26 | }, 27 | 28 | processDevice( device, id ) { 29 | momView.tree.add({ label:device.path, id:device.path, parent:'devices' }) 30 | for( let value of device.values ) { 31 | let deviceID = value.name // device.title 32 | momView.tree.add({ label:deviceID, id:deviceID, parent:device.path }) 33 | } 34 | }, 35 | 36 | create() { 37 | let deviceBranch = momView.tree.add({ label:'devices', id:'devices', opened:true }) 38 | for( let deviceName in Gibber.Max.devices ) { 39 | momView.processDevice( Gibber.Max.devices[ deviceName ] ) 40 | } 41 | 42 | let namespaceBranch = momView.tree.add({ label:'messages', id:'message', opened:true }) 43 | if( Gibber.Max.MOM.messages !== undefined ) { 44 | for( let ns of Gibber.Max.MOM.messages ) { 45 | momView.tree.add({ label:ns, id:ns, parent:'message' }) 46 | } 47 | } 48 | 49 | let paramsBranch = momView.tree.add({ label:'params', id:'params', opened:true }) 50 | for( let param of Gibber.Max.MOM.root.params ) { 51 | momView.tree.add({ label:param.varname, id:param.varname, parent:'params', opened:true }) 52 | } 53 | 54 | let receivesBranch = momView.tree.add({ label:'receives', id:'receives', opened:true }) 55 | for( let param in Gibber.Max.MOM.receives ) { 56 | momView.tree.add({ label:param, id:param, parent:'receives', opened:true }) 57 | } 58 | 59 | let signalsBranch = momView.tree.add({ label:'signals', id:'signals', opened:true }) 60 | for( let ns of Gibber.Max.MOM.signals ) { 61 | momView.tree.add({ label:ns, id:ns, parent:'signals' }) 62 | } 63 | 64 | let patchersBranch = momView.tree.add({ label:'patchers', id:'patchers', opened:true }) 65 | for( let ns in Gibber.Max.patchers ) { 66 | momView.tree.add({ label:ns, id:ns, parent:'patchers' }) 67 | const patcher = Gibber.Max.patchers[ ns ] 68 | for( let key in patcher ) { 69 | if( key.indexOf('::') === -1 ) { 70 | if( key !== 'sequences' && key !== '__client' ) { 71 | momView.tree.add({ label:key, id:key, parent:ns }) 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | module.exports = momView 80 | -------------------------------------------------------------------------------- /js/annotations/standalone/hexStepsAnnotations.js: -------------------------------------------------------------------------------- 1 | const Utility = require( '../../utility.js' ) 2 | const $ = Utility.create 3 | const EuclidAnnotation = require( '../update/euclidAnnotation.js' ) 4 | 5 | module.exports = function( node, cm, track, objectName, state, cb ) { 6 | const Marker = Gibber.Environment.codeMarkup 7 | const steps = node.arguments[ 0 ].properties 8 | const Identifier = Marker.patternMarkupFunctions.Identifier 9 | 10 | track.markup.textMarkers[ 'step' ] = [] 11 | track.markup.textMarkers[ 'step' ].children = [] 12 | 13 | const hexSteps = window[ objectName ] 14 | const objectClassName = objectName + '_steps' 15 | 16 | let count = 0 17 | for( let key in steps ) { 18 | let step = steps[ key ].value 19 | 20 | if( step && step.value ) { // ensure it is a correctly formed step 21 | step.loc.start.line += Marker.offset.vertical - 1 22 | step.loc.end.line += Marker.offset.vertical - 1 23 | step.loc.start.ch = step.loc.end.column - 1 24 | step.loc.end.ch = step.loc.end.column 25 | 26 | const pattern = hexSteps.seqs[ steps[ key ].key.value ].timings 27 | 28 | // we estimate whether or not a comma was used to separate between 29 | // key / value pairs. If there's more than one pattern and this 30 | // isn't the last time through the for loop, we assume there is a 31 | // comma (otherwise an error would occur). 32 | const useComma = count++ != steps.length - 1 && steps.length > 1 33 | 34 | if( useComma === true ) { 35 | // move off of end quote to comma 36 | step.loc.start.ch += 1 37 | step.loc.end.ch += 1 38 | 39 | // replace comma with a comma and a space 40 | cm.replaceRange( ", ", step.loc.start, step.loc.end ) 41 | 42 | step.loc.start.ch += 1 43 | step.loc.end.ch += 1 44 | }else{ 45 | // replace end quote with a quote and a space 46 | cm.replaceRange( "' ", step.loc.start, step.loc.end ) 47 | 48 | step.loc.start.ch += 1 49 | step.loc.end.ch += 1 50 | } 51 | 52 | let className = objectClassName + '_' + key 53 | 54 | let marker = cm.markText( step.loc.start, step.loc.end, { className } ) 55 | pattern.update = EuclidAnnotation( pattern, marker, className, cm, track ) 56 | pattern.patternName = className 57 | 58 | // store value changes in array and then pop them every time the annotation is updated 59 | pattern.update.value = [] 60 | 61 | Marker._addPatternFilter( pattern ) 62 | 63 | pattern.marker = marker 64 | /* 65 | 66 | patternObject._onchange = () => { 67 | let delay = Utility.beatsToMs( 1, Gibber.Scheduler.bpm ) 68 | Gibber.Environment.animationScheduler.add( () => { 69 | marker.doc.replaceRange( patternObject.values.join(''), step.loc.start, step.loc.end ) 70 | mark( step, key, cm, track ) 71 | }, delay ) 72 | } 73 | */ 74 | } 75 | } 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /js/annotations/standalone/stepsAnnotation.js: -------------------------------------------------------------------------------- 1 | const Utility = require( '../../utility.js' ) 2 | const $ = Utility.create 3 | 4 | module.exports = function( node, cm, track, objectName, state, cb ) { 5 | const Marker = Gibber.Environment.codeMarkup // tsk tsk tsk global... 6 | const steps = node.arguments[ 0 ].properties 7 | 8 | track.markup.textMarkers[ 'step' ] = [] 9 | track.markup.textMarkers[ 'step' ].children = [] 10 | 11 | const mark = ( _step, _key, _cm, _track ) => { 12 | for( let i = 0; i < _step.value.length; i++ ) { 13 | let pos = { loc:{ start:{}, end:{}} } 14 | Object.assign( pos.loc.start, _step.loc.start ) 15 | Object.assign( pos.loc.end , _step.loc.end ) 16 | pos.loc.start.ch += i 17 | pos.loc.end.ch = pos.loc.start.ch + 1 18 | let posMark = _cm.markText( pos.loc.start, pos.loc.end, { className:`step_${_key}_${i}` }) 19 | _track.markup.textMarkers.step[ _key ].pattern[ i ] = posMark 20 | } 21 | } 22 | 23 | for( let key in steps ) { 24 | let step = steps[ key ].value 25 | 26 | if( step && step.value ) { // ensure it is a correctly formed step 27 | step.loc.start.line += Marker.offset.vertical - 1 28 | step.loc.end.line += Marker.offset.vertical - 1 29 | step.loc.start.ch = step.loc.start.column + 1 30 | step.loc.end.ch = step.loc.end.column - 1 31 | 32 | let marker = cm.markText( step.loc.start, step.loc.end, { className:`step${key}` } ) 33 | track.markup.textMarkers.step[ key ] = marker 34 | 35 | track.markup.textMarkers.step[ key ].pattern = [] 36 | 37 | mark( step, key, cm, track ) 38 | 39 | let count = 0, span, update, 40 | _key = steps[ key ].key.value, 41 | patternObject = window[ objectName ].seqs[ _key ].values 42 | 43 | update = () => { 44 | let currentIdx = update.currentIndex // count++ % step.value.length 45 | 46 | if( span !== undefined ) { 47 | span.remove( 'euclid0' ) 48 | span.remove( 'euclid1' ) 49 | } 50 | 51 | let spanName = `.step_${key}_${currentIdx}`, 52 | currentValue = patternObject.update.value.pop() //step.value[ currentIdx ] 53 | 54 | span = $( spanName ) 55 | 56 | if( currentValue !== Gibber.Seq.DO_NOT_OUTPUT ) { 57 | span.add( 'euclid1' ) 58 | setTimeout( ()=> { span.remove( 'euclid1' ) }, 50 ) 59 | } 60 | 61 | span.add( 'euclid0' ) 62 | } 63 | 64 | patternObject._onchange = () => { 65 | let delay = Utility.beatsToMs( 1, Gibber.Scheduler.bpm ) 66 | Gibber.Environment.animationScheduler.add( () => { 67 | marker.doc.replaceRange( patternObject.values.join(''), step.loc.start, step.loc.end ) 68 | mark( step, key, cm, track ) 69 | }, delay ) 70 | } 71 | 72 | patternObject.update = update 73 | patternObject.update.value = [] 74 | 75 | Marker._addPatternFilter( patternObject ) 76 | } 77 | } 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /js/arp.js: -------------------------------------------------------------------------------- 1 | module.exports = function( Gibber ) { 2 | 3 | let Arp = function( chord = [0,2,4,6], octaves = 1, pattern = 'updown2' ) { 4 | let notes, arp 5 | 6 | if( typeof chord === 'string' ) { 7 | // TODO: doesn't work... numbers can't be MIDI numbers because they go through scale conversion 8 | let _chord = Gibber.Theory.Chord.create( chord ) 9 | chord = _chord.notes 10 | } 11 | 12 | notes = Gibber.Pattern.apply( null, chord.slice( 0 ) ) 13 | 14 | if( pattern === 'down' ) notes.reverse() 15 | 16 | let maxLength = notes.values.length * octaves, 17 | dir = pattern !== 'down' ? 'up' : 'down' 18 | 19 | arp = ()=> { 20 | arp.phase++ 21 | if( arp.phase >= maxLength ) { 22 | arp.phase = 0 23 | } 24 | 25 | const range = arp.notes.end - arp.notes.start 26 | if( arp.phase !== 0 && arp.phase % range === 0 ) { 27 | if( dir === 'up' ) { 28 | if( arp.octave < octaves ) { 29 | arp.octave += 1 30 | }else{ 31 | if( pattern === 'up' ) { 32 | arp.octave = 1 33 | }else{ 34 | dir = 'down' 35 | notes.reverse() 36 | } 37 | } 38 | }else{ 39 | if( arp.octave > 1 ) { 40 | arp.octave += -1 41 | }else{ 42 | if( pattern === 'down' ) { 43 | arp.octave = octaves 44 | } else { 45 | dir = 'up' 46 | notes.reverse() 47 | } 48 | } 49 | } 50 | } 51 | 52 | let octaveMod, 53 | note = arp.notes() 54 | 55 | //note = Gibber.Theory.Note.convertToMIDI( note ) 56 | 57 | for( let i = 1; i < arp.octave; i++ ) { 58 | note += 7 59 | } 60 | 61 | let methodNames = [ 62 | 'rotate','switch','invert','reset', 'flip', 63 | 'transpose','reverse','shuffle','scale', 64 | 'store', 'range', 'set' 65 | ] 66 | 67 | for( let key of methodNames ) { 68 | arp[ key ] = notes[ key ].bind( notes ) 69 | Gibber.addSequencingToMethod( arp, key ) 70 | } 71 | 72 | //arp.transpose = notes.transpose.bind( notes ) 73 | //arp.reset = notes.reset.bind( notes ) 74 | //arp.reverse = notes.reverse.bind( notes ) 75 | //arp.rotate = notes.rotate.bind( notes ) 76 | 77 | return note 78 | } 79 | 80 | arp.octave = 0 81 | arp.phase = -1 82 | arp.notes = notes 83 | 84 | return arp 85 | } 86 | 87 | Arp.patterns = { 88 | up( array ) { 89 | return array 90 | }, 91 | 92 | down( array ) { 93 | return array.reverse() 94 | }, 95 | 96 | updown( array ) { 97 | let _tmp = array.slice( 0 ) 98 | _tmp.reverse() 99 | return array.concat( _tmp ) 100 | }, 101 | 102 | updown2( array ) { // do not repeat highest and lowest notes 103 | var tmp = array.slice( 0 ) 104 | tmp.pop() 105 | tmp.reverse() 106 | tmp.pop() 107 | return array.concat( tmp ) 108 | } 109 | } 110 | 111 | return Arp 112 | 113 | } 114 | -------------------------------------------------------------------------------- /index_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Gibberwocky 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |