├── .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 |
13 |
--------------------------------------------------------------------------------
/ArrowOn.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 | 
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 |
--------------------------------------------------------------------------------