-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathextrovert-self-monomefuncs.lua
More file actions
407 lines (320 loc) · 13.5 KB
/
Copy pathextrovert-self-monomefuncs.lua
File metadata and controls
407 lines (320 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
return {
-- Parse a Monome button press
parseButtonPress = function(self, x, y, s)
if y == (self.gridy - 1) then -- Parse page-row commands
self:parsePageButton(x, s)
elseif y == self.gridy then -- Parse control-row commands
self:parseCommandButton(x, s) -- Toggle global command flags
else -- Parse sequence-button commands
self:parseSeqButton(x, y, s)
end
pd.post("Monome cmd: " .. x .. " " .. y .. " " .. s)
end,
-- Parse an incoming control-row command from the Monome
parseCommandButton = function(self, x, s)
local light = 1 -- Stays set to 1 if the button is to be lit; else will be set to 0
local flagbool = true -- Sets flags to true if the key is pressed; sets them to false if they are unpressed
if s == 0 then -- On up-keystrokes...
light = 0 -- The button will be darkened
flagbool = false -- The flag will be set to false
end
-- Empty swap and pageswap storage on any upstroke or downstroke
self.swap = false
self.pageswap = false
if x == 1 then -- Parse OFF button
self.ctrlflags.off = flagbool
elseif x == 2 then -- Parse PITCH-SHIFT button
self.ctrlflags.pitch = flagbool
self:sendVisibleSeqRows()
elseif x == 3 then -- Parse LOOP button
self.ctrlflags.loop = flagbool
if self.ctrlflags.pitch then
self:sendVisibleSeqRows()
end
elseif x == 4 then -- Parse SWAP button
self.ctrlflags.swap = flagbool
elseif rangeCheck(x, 5, self.gridx) then -- Parse GATE buttons
-- Left to right on 8 width: 1, 2, 4, 8
-- Left to right on 16 width: 1, 2, 4, 8, 16, 16, 16, etc
-- If this is a down keystroke...
if flagbool then
local key = math.min(self.gridx, math.max(1, (2 ^ (x - 4)) / 2))
-- If a "change global gate" command is given, advance the global gate-value by the gate-button value.
if self.ctrlflags.off and self.ctrlflags.swap then
self.tick = (((self.tick + ((self.longticks / self.gridx) * key)) - 1) % self.longticks) + 1
else -- Else, if this is a regular gate-button-press...
self.ctrlflags.gate = key -- Set the GATE command to the corresponding key-value
for i = 5, self.gridx do -- Turn LEDs on and off, based on which button is held
sendLED(i - 1, self.gridy - 1, ((x == i) and 1) or 0)
end
end
else -- Else, if this is an up keystroke...
-- Unset the GATE value
self.ctrlflags.gate = false
-- Revert to displaying the GATE counter
self:sendGateCountButtons()
end
end
-- If this wasn't a GATE button, send the LED straightforwardly
if x < 5 then
sendLED(x - 1, self.gridy - 1, light)
end
end,
-- Parse an incoming page-row command from the Monome
parsePageButton = function(self, x, s)
if s == 1 then -- On down-keystrokes...
if self.ctrlflags.off and self.ctrlflags.pitch and self.ctrlflags.loop then -- If OFF, PITCH, and LOOP are held...
-- Reset all SCATTER values for every sequence on the page
self:ctrlPageOffScatter(x)
self:sendVisibleSeqRows()
elseif self.ctrlflags.pitch and self.ctrlflags.loop then -- If both PITCH and LOOP are held...
-- Set a scatter-bit for every sequence on the page
self:ctrlPageScatter(self.page, x)
self:sendVisibleSeqRows()
elseif self.ctrlflags.off and self.ctrlflags.pitch then -- If both OFF and PITCH are held...
-- Reset all PITCH values for every sequence on the page
self:ctrlPageOffPitch(x)
self:sendVisibleSeqRows()
elseif self.ctrlflags.off then -- If OFF is held...
-- If GATE is also held, send PAGE-GATE-OFF command. Else send PAGE-OFF command.
if self.ctrlflags.gate then
self:ctrlPageGateOff(x)
else
self:ctrlPageOff(x)
end
elseif self.ctrlflags.pitch then -- If PITCH is held...
-- Set a pitch-bit for every sequence on the active page
self:ctrlPagePitch(self.page, x)
self:sendVisibleSeqRows()
elseif self.ctrlflags.swap then -- If OFF is not held, but SWAP is held...
-- If GATE is also held, send PAGE-GATE-SWAP command. Else send PAGE-SWAP command.
if self.ctrlflags.gate then
self:ctrlPageGateSwap(x)
self:updateSeqPage(x)
else
self:ctrlPageSwap(x)
end
elseif self.ctrlflags.gate then -- If neither OFF nor SWAP is held, but GATE is held, then send PAGE-GATE command.
self:ctrlPageGate(x)
else -- If neither OFF, SWAP, nor GATE are held...
-- If the page was double-clicked, tab into overview mode. Else, set overview mode to false, and tab to the given page.
if self.page == x then
self.overview = not self.overview
else
self.overview = false
self.page = x
end
self:sendPageRow() -- Send the page-row butons to the Monome.
end
self:sendMetaGrid() -- Send the seq-rows to the Monome, for any mode.
if self.ctrlflags.gate then
self:updateSeqPage(x) -- Update on-screen GUI to reflect pending GATE
end
end
end,
-- Parse an incoming sequence-row command from the Monome
parseSeqButton = function(self, x, y, s)
-- If this isn't a down-keystroke, abort function
if s ~= 1 then
return nil
end
local snum = 1 -- Sequence number
local col = 1 -- Column is 1, by default
if self.overview then -- In overview mode...
snum = y + ((x - 1) * (self.gridy - 2)) -- Convert an overview button into its snum sequence
else -- In beatslice-view mode...
snum = y + ((self.page - 1) * (self.gridy - 2)) -- Convert y row, and page value, into a sequence-key
col = x -- Match the col-value to the column of the button that has been pressed
end
if self.ctrlflags.off and self.ctrlflags.pitch and self.ctrlflags.loop then -- If OFF, PITCH, and LOOP are held, send OFF-SCATTER command.
self:ctrlPressOffScatter(snum)
self:sendScatterRow(snum)
elseif self.ctrlflags.pitch and self.ctrlflags.loop then -- If PITCH and LOOP are held, send SCATTER command.
self:ctrlPressScatter(snum, col)
self:sendScatterRow(snum)
elseif self.ctrlflags.off and self.ctrlflags.pitch then -- If OFF and PITCH are held, send PRESS-OFF-PITCH command.
self:ctrlPressOffPitch(snum)
self:sendPitchRow(snum)
elseif self.ctrlflags.pitch then -- Else if PITCH is held, send PRESS-PITCH command.
self:ctrlPressPitch(snum, col)
self:sendPitchRow(snum)
elseif self.ctrlflags.loop then -- Else if LOOP is held, send PRESS-LOOP command.
self:ctrlPressLoop(snum, col)
else -- Else, check for commands that interact with GATE
self:ctrlGate(snum) -- Apply the global gate-value to the sequence, whatever the global gate-value is
if self.ctrlflags.off then -- Else if OFF is held, send PRESS-OFF command.
self:ctrlPressOff(snum)
elseif self.ctrlflags.swap then -- Else if SWAP is held, send PRESS-SWAP command.
self:ctrlPressSwap(snum)
else -- Else, if no control-buttons are held (aside from GATE, optionally), send a PRESS command.
if self.ctrlflags.gate then -- If GATE is held, send the PRESS as a TRIG command, to prevent accidental offsets
self:ctrlPressTrig(snum, col)
else -- Else, if GATE isn't held, send a regular PRESS command
self:ctrlPress(snum, col)
end
end
end
if self.ctrlflags.gate then
self:updateSeqButton(snum) -- Update on-screen GUI to reflect pending GATE
end
end,
-- Spoof a series of x,y button presses, as held in a list
parseVirtualButtonPress = function(self, ...)
local arg = {...}
for s = 1, 0, -1 do -- Spoof all downstrokes, and then spoof all upstrokes, to allow keychords
for i = 1, #arg, 2 do
self:parseButtonPress(arg[i], arg[i + 1], s)
end
end
end,
-- Send the binary counting buttons for current tick position as related to GATE values
sendGateCountButtons = function(self)
-- Get the offset position of the last binary-counting button
local offset = 0
local multi = 1
while (multi * 2) <= self.gridx do
offset = offset + 1
multi = multi * 2
end
-- Based on tick position within the longest seq's bounds, display a binary value within the GATE buttons
local chunk = self.longticks / self.gridx
local cols = math.ceil(self.tick / chunk)
for i = 5 + offset, 5, -1 do
local igate = math.min(self.gridx, math.max(1, (2 ^ (i - 4)) / 2))
if (cols - igate) >= 0 then
sendLED(i - 1, self.gridy - 1, 1)
cols = cols - igate
else
sendLED(i - 1, self.gridy - 1, 0)
end
end
end,
-- Refresh the sequence-buttons, in different ways depending on self.overview
sendMetaGrid = function(self)
local s = 1 -- Change overview-button to on
if self.overview then -- Overview Mode...
self:sendOverviewSeqButtons()
else -- Not Overview Mode...
s = 0 -- Change overview-button to off
self:sendVisibleSeqRows()
end
end,
-- Refresh a sequence's buttons, in different ways depending on self.overview
sendMetaSeqRow = function(self, s)
if self.overview then -- Overview Mode...
self:sendOverviewSeqLED(s)
else -- Not Overview Mode...
self:sendSeqRowIfVisible(s)
end
end,
-- Send all sequences to the Monome's buttons in overview mode
sendOverviewSeqButtons = function(self)
for i = 1, self.gridx * (self.gridy - 2) do
self:sendOverviewSeqLED(i)
end
end,
-- Send a single LED button representing an entire sequence, for overview-mode
sendOverviewSeqLED = function(self, s)
sendLED( -- Send the LED information to the Monome apparatus... (Note: this translates keys into columns aligned with their corresponding page buttons)
math.floor((s - 1) / (self.gridy - 2)), -- Grab button's page value, translated into X
(s - 1) % (self.gridy - 2), -- Grab button's on-page position, translated to Y
(self.seq[s].pointer and 1) or 0 -- Grab activity value, translated from boolean to 0/1
)
end,
-- Send the page-command row a new set of button data
sendPageRow = function(self)
if self.overview then
self:sendSimpleRow(false, self.gridy - 2, true)
else
self:sendSimpleRow(self.page - 1, self.gridy - 2, false)
end
end,
-- Send a sequence's SCATTER row
sendScatterRow = function(self, s)
self:sendBoolRow(s, self.seq[s].stab)
end,
-- Send a sequence's PITCH row
sendPitchRow = function(self, s)
self:sendBoolRow(s, self.seq[s].ptab)
end,
-- Send a row containing a sequence's boolean pitch-values
sendBoolRow = function(self, s, bools)
local yrow = (s - 1) % (self.gridy - 2)
self:sendBoolTabRow(yrow, bools)
end,
-- Send the Monome button-data for a single visible sequence-row
sendSeqRow = function(self, s)
local yrow = (s - 1) % (self.gridy - 2)
local subpoint = self.seq[s].pointer and (math.ceil(self.seq[s].pointer / (#self.seq[s].tick / self.gridx)) - 1)
self:sendSimpleRow(subpoint, yrow)
end,
-- Check whether a single sequence-row would be visible, before sending its Monome button-data
sendSeqRowIfVisible = function(self, s)
if rangeCheck((s - 1), (self.page - 1) * (self.gridy - 2), (self.page * (self.gridy - 2)) - 1) then -- If the sequence is upon a currently-visible page...
self:sendSeqRow(s) -- Send the sequence's Monome GUI row
end
end,
-- Send a row whose LED-bytes correspond to a table of booleans
sendBoolTabRow = function(self, yrow, bools)
local rowbytes = {0, yrow} -- These bytes mean: "this command is offset by 0 spaces, and affects row number yrow"
if self.prefs.monome.osctype == 0 then
rowbytes = {yrow} -- If in MonomeSerial communications mode, remove the X-offset value from the rowbytes table so that the byte sequence is properly formed
end
for b = 0, self.gridx - 8, 8 do
local outbyte = 0
for i = 1, 8 do
local k = b + i
if bools[k] then
outbyte = outbyte + math.max(1, 2 ^ (i - 1))
end
end
table.insert(rowbytes, outbyte)
end
pd.send("extrovert-monome-out-row", "list", rowbytes) -- Send the row-out command to the Puredata Monome-row apparatus
end,
-- Send a row containing only one lit button to the Monome apparatus (incoming values should be 0-indexed!)
-- If xpoint is set to false, then a blank row is sent.
sendSimpleRow = function(self, xpoint, yrow, invert)
invert = invert or false
local rowbytes = {0, yrow} -- These bytes mean: "this command is offset by 0 spaces, and affects row number yrow"
if self.prefs.monome.osctype == 0 then
rowbytes = {yrow} -- If in MonomeSerial communications mode, remove the X-offset value from the rowbytes table so that the byte sequence is properly formed
end
-- Generate a series of bytes, each holding the on-off values for an 8-button slice of the relevant row
for b = 0, self.gridx - 8, 8 do
local pbyte = (invert and 255) or 0
-- If an xpoint was given...
if xpoint then
-- If the xpoint is within this row-chunk, highlight it based on Overview Mode style
if rangeCheck(xpoint, b, b + 7) then
pbyte = 2 ^ (xpoint - b)
if invert then -- If the style is inverted, invert the byte
pbyte = 255 - pbyte
end
end
end
-- Put the 8-button byte into the row-bytes table
table.insert(rowbytes, pbyte)
end
pd.send("extrovert-monome-out-row", "list", rowbytes) -- Send the row-out command to the Puredata Monome-row apparatus
end,
-- Send the Monome button-data for all visible sequence-rows
sendVisibleSeqRows = function(self)
for i = ((self.page - 1) * (self.gridy - 2)) + 1, self.page * (self.gridy - 2) do
if self.ctrlflags.pitch and self.ctrlflags.loop then
self:sendScatterRow(i)
elseif self.ctrlflags.pitch then
self:sendPitchRow(i)
else
self:sendSeqRow(i)
end
end
end,
-- Initialize the parameters of the Puredata Monome apparatus
startMonome = function(self)
pd.send("extrovert-osc-type", "float", {self.prefs.monome.osctype})
pd.send("extrovert-osc-in-port", "float", {self.prefs.monome.osclisten})
pd.send("extrovert-osc-out-port", "float", {self.prefs.monome.oscsend})
pd.post("Initialized Monome settings")
end,
}