-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathflora.lua
executable file
·287 lines (233 loc) · 9.89 KB
/
flora.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
277
278
279
280
281
282
283
284
285
286
287
---flora
-- v2.1.1
-- lines: llllllll.co/t/40261
--
-- k1+k2: show/hide instructions
------------------------------
-- todo list:
-- add keyboard control for updating sentences/rulesets
-- credits: Brian Crabtree (@tehn), Dan Derks(@dan_derks), Daniel Shiffman, Eli Fieldsteel, Mark Wheeler (@markwheeler), Tom Armitage (@infovore), Tyler Etters (@tyleretters)
-- source code and documentation: https://github.com/jaseknighter/flora
------------------------------
include "flora/lib/includes"
------------------------------
-- lfo code
------------------------------
lfo_lattice = Lattice:new{
auto = true,
meter = 4,
ppqn = 96
}
lfo_pattern = lfo_lattice:new_sprocket{
action = function(t)
if initializing == false then
local lfo_slope1 = lfo[1].slope
local lfo_slope2 = lfo[2].slope
local lfo1_volts_min = params:get("1lfo_volts_min")
local lfo1_volts_max = params:get("1lfo_volts_max")
local lfo2_volts_min = params:get("2lfo_volts_min")
local lfo2_volts_max = params:get("2lfo_volts_max")
local lfo_val1 = util.linlin(-1,1,lfo1_volts_min,lfo1_volts_max,lfo_slope1)
local lfo_val2 = util.linlin(-1,1,lfo2_volts_min,lfo2_volts_max,lfo_slope2)
play_crow_lfos("1lfo", lfo_val1)
play_midi_cc_lfos("1lfo", lfo[1].slope)
play_crow_lfos("2lfo", lfo_val2)
play_midi_cc_lfos("2lfo", lfo[2].slope)
end
end,
division = 1/16,
enabled = true
}
play_crow_lfos = function(source,volts)
for i=1,4,1 do
local output_crow = params:get("output_crow"..i)
if source == "1lfo" and output_crow == 6 then -- lfo1 output
crow.output[i].slew = params:get("1lfo_slew")/1000
crow.output[i].volts = volts
crow.output[i]()
elseif source == "2lfo" and output_crow == 7 then -- lfo2 output
crow.output[i].slew = params:get("2lfo_slew")/1000
crow.output[i].volts = volts
crow.output[i]()
end
end
end
play_midi_cc_lfos = function(source,volts)
local play_midi_lfo1 = params:get("1play_midi_lfo_cc")
local play_midi_lfo2 = params:get("2play_midi_lfo_cc")
local cc_val = math.floor(util.linlin(-5,10,1,127,volts))
if source == "1lfo" and play_midi_lfo1 == 2 then
-- local slew = params:get("lz_x_slew")/1000
local cc = params:get("1midi_lfo_cc")
local ch = params:get("1midi_lfo_chan")
ch = ch > 0 and ch or nil
if midi_out_device then
midi_out_device:cc(cc, cc_val, ch)
end
-- print("x",cc_val)
end
if source == "2lfo" and play_midi_lfo2 == 2 then
-- local slew = params:get("lz_y_slew")/1000
local cc = params:get("2midi_lfo_cc")
local ch = params:get("2midi_lfo_chan")
ch = ch > 0 and ch or nil
if midi_out_device then
midi_out_device:cc(cc, cc_val, ch)
end
end
end
------------------------------
-- init
------------------------------
function init()
-- set sensitivity of the encoders
norns.enc.sens(1,6)
norns.enc.sens(2,6)
norns.enc.sens(3,6)
nb:init()
pages = UI.Pages.new(0, num_pages)
-- look for a 16n device
for i=16, 1, -1
do
if midi.devices[i] then
if midi.devices[i].name == "16n" then device_16n = midi.devices[i] end
end
end
-- if default_to_community_garden then
-- l_system_instructions = l_system_instructions_community
-- else
-- l_system_instructions = l_system_instructions_default
-- end
for i=1,num_plants,1
do
envelopes[i] = envelope:new(i,tt.env_arrays)
envelopes[i].init(num_plants,true)
local active = i == 1 and true or false
envelopes[i].set_active(active)
local initial_plant_instruction_num = i == 1 and INITIAL_PLANT_INSTRUCTIONS_1 or INITIAL_PLANT_INSTRUCTIONS_2
plants[i] = plant:new(i,initial_plant_instruction_num,envelopes[i].graph_nodes)
end
plants[1].set_active(true)
parameters.add_params(plants)
build_scale()
for i=1,num_plants,1
do
plants[i].setup(plants[i].get_current_instruction())
end
modify.init()
water.init()
garden.init()
-- for lib/hnds
lfo_types = {"sine", "square", "s+h"}
lfo_index = nil
redraw_timer()
page_scroll(1)
-- polls.init()
for i=1,#midi.vports,1
do
if midi.vports[i].device and midi.vports[i].device.name == "16n" then
-- print("found 16n")
device_16n = midi.vports[i].device
end
end
----------- pset sequencer code starts here -------------
-- plant modulation exclusion group
local pset_param_exclusions_plant = {"plant1_instructions","plant2_instructions","plant1_angle","plant2_angle"}
-- plow exclusion group
local pset_param_exclusions_plow = {"num_plow1_controls","num_plow2_controls","plow1_max_level","plow1_max_time","plow2_max_level","plow2_max_time","randomize_env_probability1","time_probability1","level_probability1","curve_probability1","time_modulation1","level_modulation1","curve_modulation1","randomize_env_probability2","time_probability2","level_probability2","curve_probability2","time_modulation2","level_modulation2","curve_modulation2"}
for i=1, MAX_ENVELOPE_NODES, 1
do
table.insert(pset_param_exclusions_plow, "plow1_time" .. i)
table.insert(pset_param_exclusions_plow, "plow2_time" .. i)
table.insert(pset_param_exclusions_plow, "plow1_level" .. i)
table.insert(pset_param_exclusions_plow, "plow2_level" .. i)
table.insert(pset_param_exclusions_plow, "plow1_curve" .. i)
table.insert(pset_param_exclusions_plow, "plow2_curve" .. i)
end
-- plow modulation exclusion group
local pset_param_exclusions_plow_modulation = {"randomize_env_probability1","time_probability1","level_probability1","curve_probability1","time_modulation1","level_modulation1","curve_modulation1"}
-- water exclusion group
local pset_param_exclusions_water = {"amp","rqmin","rqmax","note_scalar","num_active_cf_scalars","cfs1","cfs2","cfs3","cfs4","plant_1_note_duration","plant_2_note_duration","num_note_frequencies","tempo_scalar_offset","nf_numerator1","nf_denominator1","nf_offset1","nf_numerator2","nf_denominator2","nf_offset2","nf_numerator3","nf_denominator3","nf_offset3","nf_numerator4","nf_denominator4","nf_offset4","nf_numerator5","nf_denominator5","nf_offset5","nf_numerator6","nf_denominator6","nf_offset6"}
-- nav exclusion group
local pset_param_exclusions_nav = {"page_turner", "active_plant_switcher"}
-- i/o exclusion group
local pset_param_exclusions_inputs_outputs = {"midi_device","plant1_cc_channel","plant2_cc_channel","plow1_cc_channel","plow2_cc_channel","water_cc_channel","output_midi","midi_out_device","midi_out_channel1","midi_out_channel2","output_crow1","output_crow2","output_crow3","output_crow4","output_jf","jf_mode","output_wdel_ks","wdel_mix","wdel_time_short","wdel_time_long","wdel_feedback","wdel_filter","wdel_clock","wdel_clock_ratio_div","wdel_clock_ratio_mul","wdel_freeze","wdel_frequency","wdel_mod_rate","wdel_mod_amount","wdel_freeze","output_wsyn","wsyn_ar_mode","wsyn_vel","wsyn_curve","wsyn_ramp","wsyn_fm_index","wsyn_fm_env","wsyn_fm_ratio_num","wsyn_fm_ratio_den","wsyn_lpg_time","wsyn_lpg_symmetry","wsyn_pluckylog","wsyn_randomize","wsyn_init","wtape_timestamp","wtape_seek","wtape_record","wtape_play","wtape_reverse","wtape_loop_active","wtape_echo_mode","wtape_loop_start","wtape_loop_end","wtape_loop_next","wtape_loop_next_trigger","wtape_loop_scale_mult","wtape_speed","wtape_freq","wtape_erase_strength","wtape_monitor_level","wtape_rec_level"}
-- root note & scale exclusion group
local pset_param_exclusions_root_note_scale = {"scale_mode", "root_note"}
-- table for exclusion group table names
local pset_exclusion_tables = {pset_param_exclusions_plant,pset_param_exclusions_plow,pset_param_exclusions_plow_modulation,pset_param_exclusions_water,pset_param_exclusions_nav,pset_param_exclusions_inputs_outputs, pset_param_exclusions_root_note_scale}
-- table for exclusion group labels
local pset_exclusion_table_labels = {"plant","plow","plow mod","water", "nav", "i/o", "root note/scale"}
-- init the plant generation sequencer
p_gen_seq.init()
-- init the pset sequencer passing pset exclusion info
pset_seq.init(pset_exclusion_tables, pset_exclusion_table_labels)
----------- pset sequencer code ends here -------------
save_load.init()
sharer:new()
sharer:init()
clock.run(init_done)
end
function init_done()
clock.sleep(0.5)
function process_crow_input_stream(v)
local x = math.abs(v)
if params:get("input_crow2") == 2 then
params:set("note_scalar",math.floor(x))
end
end
midi_out_device = midi.connect(params:get("midi_out_device"))
midi_out_channel1 = params:get("midi_out_channel1")
midi_out_channel2 = params:get("midi_out_channel2")
midi_out_channel_tt = params:get("midi_out_channel_tt")
crow.input[2].stream = process_crow_input_stream
crow.input[2].mode("stream", 0.1)
lfo_lattice:start()
lfo.init()
tt.init()
tinta_envelope = envelope:new(1)
tinta_envelope.init(1,false)
screen.clear()
initializing = false
end
--------------------------
-- encoders and keys
--------------------------
function enc(n, delta)
encoders_and_keys.enc(n, delta)
end
function key(n,z)
encoders_and_keys.key(n, z)
end
--------------------------
-- redraw
--------------------------
function redraw_timer()
redrawtimer = metro.init(function()
local status = norns.menu.status()
-- local menu_status = true
if status == false and menu_status == false and initializing == false then
if screen_dirty then
flora_pages.draw_pages()
screen_dirty = false
elseif pages.index < 4 then
flora_pages.draw_pages()
elseif pages.index == 7 then
screen.clear()
screen_dirty = true
end
end
if menu_status == true and status == false then
menu_status = false
screen_dirty = true
elseif menu_status == false and status == true then
menu_status = true
params:get_midi_devices()
_menu.rebuild_params()
end
end, SCREEN_FRAMERATE, -1)
redrawtimer:start()
end
function cleanup ()
all_notes_off()
end