-
-
Save trentgill/4c36851dbd33f2a987b7560750e9abd0 to your computer and use it in GitHub Desktop.
-- spacetime | |
-- norns study 3 | |
-- | |
-- ENC 1 - sweep filter | |
-- ENC 2 - select edit position | |
-- ENC 3 - choose command | |
-- KEY 3 - randomize command set | |
-- | |
-- spacetime is a weird function sequencer. | |
-- it plays a note on each step. | |
-- each step is a symbol for the action. | |
-- + = increase note | |
-- - = decrease note | |
-- < = go to bottom note | |
-- > = go to top note | |
-- * = random note | |
-- M = fast metro | |
-- m = slow metro | |
-- # = jump random position | |
-- | |
-- augment/change this script with new functions! | |
engine.name = "PolyPerc" | |
note = 40 | |
position = 1 | |
steps = {1,1,1,1 ,1,1,1,1 ,1,1,1,1 ,1,1,1,1} | |
steps_len = #steps | |
edit = 1 | |
-- helpers to index into commands table | |
label = 1 | |
act = 2 | |
commands = | |
{ { "+", function inc() note = util.clamp(note + 5, 40, 120) end } | |
, { "-", function dec() note = util.clamp(note - 5, 40, 120) end } | |
, { "<", function bottom() note = 40 end } | |
, { ">", function top() note = 120 end } | |
, { "*", function rand() note = math.random(80) + 40 end } | |
, { "M", function metrofast() counter.time = 0.125 end } | |
, { "m", function metroslow() counter.time = 0.25 end } | |
, { "#", function positionrand() position = math.random(steps_len) end } | |
} | |
commands_len = #commands | |
function init() | |
params:add_control("cutoff",controlspec.new(50,5000,'exp',0,555,'hz')) | |
params:set_action("cutoff", function(x) engine.cutoff(x) end) | |
counter = metro.alloc(count, 0.125, -1) | |
counter:start() | |
end | |
function count() | |
position = (position % steps_len) + 1 | |
commands[steps[position]].act() | |
engine.hz(midi_to_hz(note)) | |
redraw() | |
end | |
function redraw() | |
screen.clear() | |
for i=1,steps_len do | |
screen.level((i == edit) and 15 or 2) | |
screen.move(i*8-8,40) | |
screen.text(commands[steps[i]].label) | |
if i == position then | |
screen.move(i*8-8, 45) | |
screen.line_rel(6,0) | |
screen.stroke() | |
end | |
end | |
screen.update() | |
end | |
function enc(n,d) | |
if n == 1 then | |
params:delta("cutoff",d) | |
elseif n == 2 then | |
edit = util.clamp(edit + d, 1, steps_len) | |
elseif n == 3 then | |
steps[edit] = util.clamp(steps[edit]+d, 1, commands_len) | |
end | |
redraw() | |
end | |
function key(n,z) | |
if n==3 and z==1 then | |
randomize_steps() | |
end | |
end | |
function midi_to_hz(note) | |
return (440 / 32) * (2 ^ ((note - 9) / 12)) | |
end | |
function randomize_steps() | |
for i=1,steps_len do | |
steps[i] = math.random(commands_len) | |
end | |
end |
Just a note for people coming here who hadn't seen this before (and I'm thinking this is how this works in lua, it's how it works in JS)
screen.level((i == edit) and 15 or 2) makes use of programming techniques known as "take right" and "take left". When many items are and'ed together, the right-most term that is truthy is given. So if i == 2 is evaluated to be true, the next term's truthiness is evaluated. Since non-zero numbers are truth-y in lua, 15 is returned.
"or" works similarly but the left most truth-y item is returned. you can think of this expression like this: ((i == 2) and 15) or 2
. If ((i == 2) and 15)
returns false, the truthiness of the next value is checked. And since non-zero numbers are truthy as mentioned above, 2 is returned.
@catfact
thanks for the pointer on #
. had no idea about the performance there. excuse the globals everywhere too (was trying not to refactor the whole study).
point taken on the all-caps. it was a holdover from the study which used allcaps to identify the table-lengths. i just reused them and didn't really think about it. updated now to all lowercase with the idea that they don't need to be constant, and there's nothing stopping someone making the command list change at runtime.
speaking of convention, i just read the LuaStyleGuide again which covers both the allcaps constants, but also the idiom explained by @jlmitch5. worth the 10minute read!
real minor stuff:
the
#
operator in lua iterates over the table, it's not efficient. if the table is constant size, i'd just store the size once.i'm little confused by the all-caps variable names. convention would imply these are constants. but
COMMANDS
is a constant andSTEPS
isn't.