-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathpedalboard.lua
276 lines (246 loc) · 7.18 KB
/
pedalboard.lua
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
-- Pedalboard: Chainable FX
--
-- E1 changes page
--
-- Main Page: The Board
-- E2 changes focused slot
-- E3 changes pedal
-- E3 doesn't take effect til K3
-- K2 jumps to pedal page
-- K2 + E2 re-orders pedals
-- K2 + E3 changes wet/dry
-- K2 + K3 toggles bypass
-- When adding/changing pedals,
-- K2+K3 defaults to bypassed
--
-- Pedal Pages
-- E2 / E3 change dial values
-- K2 / K3 cycle thru dial pairs
-- Hold K2 tap K3 for tap tempo
-- (where appropriate)
--
-- Mod Matrix Page
-- E2 scrolls vertically
-- E3 changes values
-- K2 / K3 move left / right
--
-- Arc (optional)
-- "Follow" mode:
-- On The Board,
-- each enc controls wet/dry.
-- On Pedal Pages,
-- E1,2,3 change dial values
-- E4 changes wet/dry.
-- On Mod Matrix,
-- E1,2,3,4 change whatever
-- is currently focused.
-- "Fixed" mode:
-- Use the params page
-- to choose which params
-- are controlled by which enc
--
-- Crow (optional)
-- Use the params page
-- to choose which params
-- are controlled
-- by which crow input
--
--
-- v2.3.1 @21echoes
engine.name = "Pedalboard"
local UI = require "ui"
local encoders = require "encoders"
local Board = include("lib/ui/board")
local ModMatrix = include("lib/ui/modmatrix")
local ScreenState = include("lib/ui/util/screen_state")
local Arcify = include("lib/ui/util/arcify")
local Crowify = include("lib/ui/util/crowify")
-- Pages UI management
local pages
local pages_table
-- Variables for the render loop
local SCREEN_FRAMERATE = 15
local screen_refresh_metro
-- User's initial audio settings
local initital_monitor_level
local initital_reverb_onoff
local initital_compressor_onoff
-- Arc control of parameters
local arcify = nil
-- Crow control of parameters
local crowify = nil
function init()
-- Setup our overall rendering style
screen.level(15)
screen.aa(0)
screen.line_width(1)
-- Some pedals have requirements that may not be satisfied. Check for them now
Board:add_optional_pedals_if_ready()
-- Start setting up params (delegate to the Board class)
params:add_separator("Pedalboard")
Board:add_params()
ModMatrix:add_params(Board.pedal_classes)
setup_arcify()
setup_crowify()
params:bang()
-- Turn off the built-in monitoring, reverb, etc.
initital_monitor_level = params:get('monitor_level')
params:set('monitor_level', -math.huge)
initital_reverb_onoff = params:get('reverb')
params:set('reverb', 1) -- 1 is OFF
initital_compressor_onoff = params:get('compressor')
params:set('compressor', 1) -- 1 is OFF
-- Set up pages
pages_table = {
Board:new(
add_page,
insert_page_at_index,
remove_page,
swap_page,
set_page_index
),
ModMatrix:new(),
}
pages = UI.Pages.new(1, #pages_table)
pages_table[1]:enter(arcify)
_set_encoder_sensitivities()
-- Render loop
screen_refresh_metro = metro.init()
screen_refresh_metro.event = render_loop
screen_refresh_metro:start(1 / SCREEN_FRAMERATE)
end
-- Interactions
function key(n, z)
-- All key presses are routed to the current page's class.
local screen_dirty = false
if current_page() then screen_dirty = current_page():key(n, z) end
ScreenState.mark_screen_dirty(screen_dirty)
end
function enc(n, delta)
if n == 1 then
-- E1 changes page
pages:set_index_delta(util.clamp(delta, -1, 1), false)
if current_page() then current_page():enter(arcify) end
_set_encoder_sensitivities()
ScreenState.mark_screen_dirty(true)
else
-- Other encoders are routed to the current page's class
local screen_dirty = false
if current_page() then screen_dirty = current_page():enc(n, delta) end
ScreenState.mark_screen_dirty(screen_dirty)
end
end
-- Render
function render_loop()
if ScreenState.is_screen_dirty() then
ScreenState.mark_screen_dirty(false)
redraw()
end
end
function redraw()
screen.clear()
-- Redraw both our content and the current page's content
if pages then pages:redraw() end
if current_page() then current_page():redraw() end
screen.update()
end
function cleanup()
-- deinitialization
metro.free(screen_refresh_metro.id)
screen_refresh_metro = nil
-- I'm not particularly sure on the details of Lua memory management, so we go a little overboard here maybe
for i, page in ipairs(pages_table) do
pages_table[i]:cleanup()
pages_table[i] = nil
end
pages_table = nil
pages = nil
-- Put user's audio settings back where they were
params:set('monitor_level', initital_monitor_level)
params:set('reverb', initital_reverb_onoff)
params:set('compressor', initital_compressor_onoff)
end
-- Utils
function current_page()
if pages_table == nil or pages == nil then return nil end
return pages_table[pages.index]
end
function modmatrix_page()
return pages_table[#pages_table]
end
function set_page_index(new_page_index)
pages:set_index(new_page_index)
if current_page() then current_page():enter(arcify) end
_set_encoder_sensitivities()
ScreenState.mark_screen_dirty(true)
end
function add_page(page_instance)
-- ModMatrix is always the last page, so we insert one before last
local index = #pages_table
table.insert(pages_table, index, page_instance)
modmatrix_page():add_pedal(page_instance, index - 1)
pages = UI.Pages.new(1, #pages_table)
ScreenState.mark_screen_dirty(true)
end
function insert_page_at_index(index, page_instance)
table.insert(pages_table, index, page_instance)
modmatrix_page():add_pedal(page_instance, index - 1)
pages = UI.Pages.new(1, #pages_table)
ScreenState.mark_screen_dirty(true)
end
function remove_page(index, cleanup)
cleanup = cleanup == nil and true or cleanup
page = table.remove(pages_table, index)
if cleanup then
page:cleanup()
end
modmatrix_page():remove_pedal(index - 1)
pages = UI.Pages.new(1, #pages_table)
ScreenState.mark_screen_dirty(true)
end
function swap_page(index, page_instance)
local old_pedal = pages_table[index]
pages_table[index] = page_instance
modmatrix_page():remove_pedal(index - 1)
modmatrix_page():add_pedal(page_instance, index - 1)
old_pedal:cleanup()
ScreenState.mark_screen_dirty(true)
end
function _set_encoder_sensitivities()
-- 1 sensitivity should be a bit slower
norns.enc.sens(1, 5)
-- Set the E2 and E3 sensitivities to be quite slower on the Board, otherwise just a bit slower
norns.enc.sens(2, pages.index == 1 and 5 or 2)
norns.enc.sens(3, pages.index == 1 and 6 or 2)
end
function setup_arcify()
params:add_group("Arc", 5)
params:add({
id = "arc_mode",
name = "Arc Follow Mode",
type = "option",
options = {"Follow Screen", "Fixed"},
})
arcify = Arcify.new()
for pedal_index, pedal in ipairs(Board.pedal_classes) do
for i, param_id in ipairs(pedal._param_ids_flat) do
arcify:register(param_id)
end
end
ModMatrix:arcify_register(arcify)
arcify:add_params()
end
function setup_crowify()
-- Crowify adds its own group
crowify = Crowify.new()
for pedal_index, pedal in ipairs(Board.pedal_classes) do
for i, param_id in ipairs(pedal._param_ids_flat) do
crowify:register(param_id)
end
end
ModMatrix:crowify_register(crowify)
crowify:add_params()
end
-- TODO: how do we manage upgrading MiUgens versions?
-- probably: a mi-ugens-version.txt file
-- but... what do we do when that's missing? or when it's *newer* than what we expect?