Skip to content

Instantly share code, notes, and snippets.

@trentgill
Last active July 5, 2019 14:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trentgill/4c36851dbd33f2a987b7560750e9abd0 to your computer and use it in GitHub Desktop.
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
@catfact
Copy link

catfact commented Jul 8, 2018

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 and STEPS isn't.

@jlmitch5
Copy link

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.

@trentgill
Copy link
Author

@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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment