├── .gitignore ├── ArrowOff.svg ├── ArrowOn.svg ├── M4L.MapButtonMultiSingle.maxpat ├── README.md ├── midi-js-fn-8.js ├── midi-thru-js-fn.amxd ├── screenshot.png ├── stringFormat.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /ArrowOff.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ArrowOn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | midi thru js fn 2 | ==================== 3 | 4 | about 5 | ------------- 6 | a Max for Live device for writing javascript in Ableton 7 | ![screenshot](./screenshot.png) 8 | 9 | documentation 10 | -------------- 11 | 12 | ### global properties 13 | 14 | #### tempo (float) 15 | the current tempo of the live set 16 | 17 | #### timesig ([int numerator, int denominator]) 18 | the current time signature of the live set 19 | 20 | #### grid (object) 21 | An object of the form: 22 | ``` 23 | grid: 24 | bar: float 25 | beat: float 26 | sxtn: float 27 | tick: float 28 | ``` 29 | representing the current location of the playhead 30 | 31 | #### state (object) 32 | An empty object that can be used to store values across script evaluations. 33 | 34 | 35 | #### out(port, value) 36 | output a value to a mapper 37 | 38 | #### setTimeout(fn, ms) 39 | wait for the specified time in milliseconds, then run the given function 40 | 41 | #### wait(ms, fn) 42 | An alias for setTimeout 43 | 44 | #### rand(low = 0, hi = 1, asInt = false) 45 | returns a random value between low and high. Defaults to a random value between 0 and 1 46 | 47 | #### int(value) 48 | round a number down to an integer 49 | 50 | #### scale(value, s1, s2, e1, e2) 51 | linearly scale a value from range s1 to s2, to range e1 to e2 52 | 53 | #### valscale(value, e1, e2) 54 | linearly scale a value between 0 and 1 to range e1 to e2 55 | 56 | #### scaletoval(value, s1, s2) 57 | linearly scale a value between s1 and s2 to a float between 0 and 1 58 | 59 | #### valtosteps(value, steps) 60 | convert a continuous value between 0 and 1 to a discrete number of steps 61 | 62 | #### valtomidi(value) 63 | convert a value between 0 and 1 to an int between 0 and 127 64 | 65 | #### miditoval(value) 66 | convert an int between 0 and 127 to a float between 0 and 1 67 | 68 | #### ftom(freq) 69 | convert a freqency in hertz to the corresponding midi note number 70 | 71 | #### mtof(midiNoteNumber) 72 | convert a midi note number to a frequency in hertz 73 | 74 | #### clamp(val, min, max) 75 | confine a value to range min to max 76 | 77 | #### snap(note, scale) 78 | force a midi value to the nearest midi value in a scale. Scales are defined as an array of ints between 0 and 11 representing the twelve chromatic notes of western music theory. 79 | 80 | ### Array extensions 81 | #### [].foreach(fn) 82 | run a function on each item in an array modifying it in place. 83 | 84 | #### [].map(fn) 85 | map each item in an array to a new value and return the new array. 86 | 87 | #### [].range(len) 88 | returns an array of ints from 0 to len - 1 89 | 90 | #### [].zeros(len) 91 | returns an array of zeros of length len 92 | -------------------------------------------------------------------------------- /midi-js-fn-8.js: -------------------------------------------------------------------------------- 1 | inlets = 2 2 | outlets = 9 3 | 4 | 5 | // utils (b/c max is dumb and the include fn doesn't work for frozen devices) 6 | // utils 7 | 8 | var console = { log: function (val) { outlet(1, Number(val)) } } 9 | var log = console.log 10 | var out = outlet 11 | 12 | var int = function (value) { 13 | return Math.floor(Number(value)) 14 | } 15 | 16 | var setTimeout = function (fn, ms) { 17 | var tsk = new Task(fn) 18 | tsk.schedule(ms) 19 | } 20 | 21 | var wait = function (ms, fn) { 22 | setTimeout(fn, ms) 23 | } 24 | 25 | Array.prototype.foreach = function (fn) { 26 | for (var i in this) { 27 | if (this.hasOwnProperty(i)) { 28 | fn(this[i], Number(i), this) 29 | } 30 | } 31 | return this 32 | } 33 | 34 | Array.prototype.map = function (fn) { 35 | var toRet = [] 36 | for (var i in this) { 37 | if (this.hasOwnProperty(i)) { 38 | toRet.push(fn(this[i], Number(i), this)) 39 | } 40 | } 41 | return toRet 42 | } 43 | 44 | Array.prototype.range = function (len) { 45 | len = len || 0 46 | var toRet = [] 47 | for (var i = 0; i < len; i++) { 48 | toRet.push(i) 49 | } 50 | return toRet 51 | } 52 | 53 | Array.prototype.zeros = function (len) { 54 | len = len || 0 55 | var toRet = [] 56 | for (var i = 0; i < len; i++) { 57 | toRet.push(0) 58 | } 59 | return toRet 60 | } 61 | 62 | var rand = function (low, hi, asInt) { 63 | low = low || 0 64 | hi = hi || 1 65 | var min = !!asInt ? (hi - low) + 1 : (hi - low) 66 | var val = low + (Math.random() * min) 67 | return (asInt ? Math.floor(val) : val) 68 | } 69 | 70 | var scale = function (value, s1, s2, e1, e2) { 71 | return (value - s1) * (e2 - e1) / (s2 - s1) + e1 72 | } 73 | 74 | var valscale = function (value, e1, e2) { 75 | return scale(value, 0.0, 1.0, e1, e2) 76 | } 77 | 78 | var scaletoval = function (value, s1, s2) { 79 | return scale(value, s1, s2, 0.0, 1.0) 80 | } 81 | 82 | var valtosteps = function (value, steps) { 83 | return scaletoval(int(valscale(value, 0, steps-1)), 0, steps-1) 84 | } 85 | 86 | var valtomidi = function (value) { 87 | return Math.floor(valscale(value, 0, 127)) 88 | } 89 | 90 | var miditoval = function (value) { 91 | return scale(value, 0, 127, 0, 1) 92 | } 93 | 94 | var ftom = function (freq) { 95 | // m = 12*log2(fm/440 Hz) + 69 96 | return Math.round((12 * Math.log2(freq/440)) + 69) 97 | } 98 | 99 | var mtof = function (m) { 100 | // fm = 2(m−69)/12(440 Hz). 101 | return Math.pow(2, (m - 69)/12) * 440 102 | } 103 | 104 | var clamp = function (val, min, maxi) { 105 | if (val > maxi) { 106 | return maxi 107 | } 108 | if (val < min) { 109 | return min 110 | } 111 | return val 112 | } 113 | 114 | var snap = function (note, scale) { 115 | var value = note % 12 116 | var octave = Math.floor(note / 12) 117 | var curr = scale[0] 118 | for (var i in scale) { 119 | var val = scale[i] 120 | if (Math.abs(value - val) < Math.abs(value - curr)) { 121 | curr = val 122 | } 123 | } 124 | return curr + (12 * (octave + 1)) 125 | } 126 | 127 | // end utils 128 | 129 | 130 | 131 | 132 | var self = this 133 | var val1 = 0 134 | var val2 = 0 135 | var val3 = 0 136 | var val4 = 0 137 | var val5 = 0 138 | var val6 = 0 139 | var val7 = 0 140 | var val8 = 0 141 | var state = {} 142 | var usrfn = 'if (isNote) {} else { outlet(1, val1); }' 143 | 144 | var tempo = 120 145 | var timesig = [4, 4] 146 | var songtime = 0.0 147 | /** 148 | song time in seconds, represented as double: 149 | time = beats * 60 / tempo (in bpm) 150 | */ 151 | 152 | var grid = { 153 | bar: 0, 154 | beat: 0, 155 | sxtn: 0, 156 | tick: 0 157 | } 158 | 159 | function set_tempo (t) { 160 | tempo = t 161 | } 162 | 163 | function set_timesig (n, d) { 164 | timesig = [n, d] 165 | } 166 | 167 | function set_songtime (st) { 168 | songtime = st 169 | } 170 | 171 | function set_grid (gs) { 172 | gs = gs.split('.') 173 | grid.bar = Number(gs[0]) 174 | grid.beat = Number(gs[1]) 175 | grid.sxtn = Number(gs[2]) 176 | grid.tick= Number(gs[3]) 177 | } 178 | 179 | function text (txt) { 180 | if (txt[0] === '"' && txt[txt.length-1] === '"') {txt = txt.substr(1, txt.length-2)} 181 | usrfn = txt 182 | } 183 | 184 | function auxvals () { 185 | for (var i in arguments) { 186 | var key = 'val' + ~~(Number(i) + 1) 187 | this[key] = arguments[i] 188 | } 189 | var isNote = false 190 | eval(usrfn) 191 | } 192 | auxvals.immediate = 1 193 | 194 | function midiinput (pitch, velocity, pitchbend) { 195 | var isNote = true 196 | eval(usrfn) 197 | } 198 | midiinput.immediate = 1 199 | -------------------------------------------------------------------------------- /midi-thru-js-fn.amxd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsnelgro/midi-thru-js-fn/d7e413a5122fc3a9693c1c498d57b5af3c683048/midi-thru-js-fn.amxd -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsnelgro/midi-thru-js-fn/d7e413a5122fc3a9693c1c498d57b5af3c683048/screenshot.png -------------------------------------------------------------------------------- /stringFormat.js: -------------------------------------------------------------------------------- 1 | // inlets and outlets 2 | inlets = 1; 3 | outlets = 1; 4 | 5 | var max_string_length = 12; 6 | var msg_formated = ""; 7 | 8 | function text(msg) 9 | { 10 | if(msg.length > max_string_length) 11 | { 12 | msg_formated = msg.substring(0, max_string_length-3); 13 | outlet(0,msg_formated.concat("...")); 14 | } 15 | else{ 16 | outlet(0,msg); 17 | } 18 | } -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | // utils 2 | 3 | var console = { log: function (val) { outlet(1, Number(val)) } } 4 | var log = console.log 5 | var out = outlet 6 | 7 | var setTimeout = function (fn, ms) { 8 | var tsk = new Task(fn) 9 | tsk.schedule(ms) 10 | } 11 | 12 | var wait = function (ms, fn) { 13 | setTimeout(fn, ms) 14 | } 15 | 16 | Array.prototype.foreach = function (fn) { 17 | for (var i in this) { 18 | if (this.hasOwnProperty(i)) { 19 | fn(this[i], Number(i), this) 20 | } 21 | } 22 | return this 23 | } 24 | 25 | Array.prototype.map = function (fn) { 26 | var toRet = [] 27 | for (var i in this) { 28 | if (this.hasOwnProperty(i)) { 29 | toRet.push(fn(this[i], Number(i), this)) 30 | } 31 | } 32 | return toRet 33 | } 34 | 35 | Array.prototype.range = function (len) { 36 | len = len || 0 37 | var toRet = [] 38 | for (var i = 0; i < len; i++) { 39 | toRet.push(i) 40 | } 41 | return toRet 42 | } 43 | 44 | Array.prototype.zeros = function (len) { 45 | len = len || 0 46 | var toRet = [] 47 | for (var i = 0; i < len; i++) { 48 | toRet.push(0) 49 | } 50 | return toRet 51 | } 52 | 53 | var rand = function (low, hi, asInt) { 54 | low = low || 0 55 | hi = hi || 1 56 | var min = !!asInt ? (hi - low) + 1 : (hi - low) 57 | var val = low + (Math.random() * min) 58 | return (asInt ? Math.floor(val) : val) 59 | } 60 | 61 | var scale = function (value, s1, s2, e1, e2) { 62 | return (value - s1) * (e2 - e1) / (s2 - s1) + e1 63 | } 64 | 65 | var valscale = function (value, e1, e2) { 66 | return scale(value, 0.0, 1.0, e1, e2) 67 | } 68 | 69 | var valtomidi = function (value) { 70 | return Math.floor(valscale(value, 0, 127)) 71 | } 72 | 73 | var miditoval = function (value) { 74 | return scale(value, 0, 127, 0, 1) 75 | } 76 | 77 | var ftom = function (freq) { 78 | // m = 12*log2(fm/440 Hz) + 69 79 | return Math.round((12 * Math.log2(freq/440)) + 69) 80 | } 81 | 82 | var mtof = function (m) { 83 | // fm = 2(m−69)/12(440 Hz). 84 | return Math.pow(2, (m - 69)/12) * 440 85 | } 86 | 87 | var clamp = function (val, min, maxi) { 88 | if (val > maxi) { 89 | return maxi 90 | } 91 | if (val < min) { 92 | return min 93 | } 94 | return val 95 | } 96 | 97 | var snap = function (note, scale) { 98 | var value = note % 12 99 | var octave = Math.floor(note / 12) 100 | var curr = scale[0] 101 | for (var i in scale) { 102 | var val = scale[i] 103 | if (Math.abs(value - val) < Math.abs(value - curr)) { 104 | curr = val 105 | } 106 | } 107 | return curr + (12 * (octave + 1)) 108 | } 109 | 110 | // end utils 111 | --------------------------------------------------------------------------------