Norns: studies

norns

#64

Here’s another fun Spacetime edit. I added in a gate row. Encoder 1 navigates between the command row and the gate row. One fun thing is that the gate row always runs sequentially while the command row does its own thing.

Lua is a lot of fun (aside from the 1 indexing blergh). This only took about 20 minutes to get the second row added in. When I find time, I’d like to break the metro commands out of the command row and into a dedicated clock div row. I would like to also add in the ability to change each row’s length. It would be Kria’s weird cousin.

Maybe MIDI Out once I get comfortable…

-- spacetime deluxe
-- with rings
-- from norns study 3
--
-- ENC 1 - change row
-- ENC 2 - select edit position
-- ENC 3 - edit current step
-- KEY 2 - return current row to default
-- KEY 3 - randomize current row
--
-- 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
--
-- top row: functions
-- bottom row: gates

-- augment/change this script 
-- with new functions!
-- this mod adds burn's 
-- excellent KarplusRings 
-- engine

engine.name = "KarplusRings"
local cs = require 'controlspec'

numRows = 2
activeRow = 1

note = 40
position = 1
step = {1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1}
STEPS = 16
edit = 1

gatePosition = 1
gate = {2,1,2,2, 2,1,2,1, 2,2,1,2, 2,1,2,1}
gateLabel = {"_", "X"}

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
function metrofast() counter.time = 0.125 end
function metroslow() counter.time = 0.25 end
function positionrand() position = math.random(STEPS) end

act = {inc, dec, bottom, top, rand, metrofast, metroslow, positionrand}
COMMANDS = 8
label = {"+", "-", "<", ">", "*", "M", "m", "#"}

function init()
  cs.AMP = cs.new(0,1,'lin',0,0.75,'')
  params:add_control("amp",cs.AMP)
  params:set_action("amp",
  function(x) engine.amp(x) end)
  engine.amp(0.75)

  cs.DECAY = cs.new(0.1,15,'lin',0,3.6,'s')
  params:add_control("damping",cs.DECAY)
  params:set_action("damping",
  function(x) engine.decay(x) end)

  cs.COEF = cs.new(0,1,'lin',0,0.11,'')
  params:add_control("brightness",cs.COEF)
  params:set_action("brightness",
  function(x) engine.coef(x) end)

  cs.LPF_FREQ = cs.new(100,10000,'lin',0,3600,'')
  params:add_control("lpf_freq",cs.LPF_FREQ)
  params:set_action("lpf_freq",
  function(x) engine.lpf_freq(x) end)
  engine.lpf_freq(3600.0)

  cs.LPF_GAIN = cs.new(0,3.2,'lin',0,0.5,'')
  params:add_control("lpf_gain",cs.LPF_GAIN)
  params:set_action("lpf_gain",
  function(x) engine.lpf_gain(x) end)

  cs.BPF_FREQ = cs.new(100,10000,'lin',0,1200,'')
  params:add_control("bpf_freq",cs.BPF_FREQ)
  params:set_action("bpf_freq",
  function(x) engine.bpf_freq(x) end)
  engine.bpf_freq(1200.0)

  cs.BPF_RES = cs.new(0,4,'lin',0,0.5,'')
  params:add_control("bpf_res",cs.BPF_RES)
  params:set_action("bpf_res",
  function(x) engine.bpf_res(x) end)

  counter = metro.alloc(count, 0.125, -1)
  counter:start()
end

function count()
  position = (position % STEPS) + 1
  gatePosition = (gatePosition % STEPS) + 1
  
  act[step[position]]()
  
  if gate[gatePosition] == 2 then
    engine.hz(midi_to_hz(note))
  end
  
  redraw()
end

function redraw()
  screen.clear()
  
  for i = 1,16 do
    screen.level((i == edit and activeRow == 1) and 15 or 2)
    screen.move(i*8-8,10)
    screen.text(label[step[i]])
    
    screen.level((i == edit and activeRow == 2) and 15 or 2)
    screen.move(i*8-8, 30)
    screen.text(gateLabel[gate[i]])
    
    if i == position then
      screen.move(i*8-8, 15)
      screen.line_rel(6,0)
      screen.stroke()
    end
      
    if i == gatePosition then
      screen.move(i*8-8, 35)
      screen.line_rel(6,0)
      screen.stroke()
    end
  end
  
  screen.update()
end

function enc(n,d)
  if n == 1 then
    activeRow = util.clamp(activeRow + d, 1, numRows)
  elseif n == 2 then
    edit = util.clamp(edit + d, 1, STEPS)
  elseif n ==3 then
    if activeRow == 1 then
      step[edit] = util.clamp(step[edit]+d, 1, COMMANDS)
    elseif activeRow == 2 then
      gate[edit] = util.clamp(gate[edit]+d, 1, 2)
    end
  end
  redraw()
end

function key(n,z)
  if n == 2 and z == 1 then
    init_steps()
  end
  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,16 do
    if activeRow == 1 then step[i] = math.random(COMMANDS) end
    if activeRow == 2 then gate[i] = math.random(2) end
  end
end

function init_steps()
  for i= 1,16 do
    if activeRow == 1 then step[i] = 1 end
    if activeRow == 2 then gate[i] = 1 end
  end
end

#65

THIS WEEK the study is getting pushed to next week, so i can work on some code and get a norns update out to you all. (and finish building/shipping second batch).

thanks for your patience, you’ll like the update!


#66

I just added a dust pull request so that pre-written studies will be included for people who don’t want to program.

There are some other goodies in the pull request, including a super-deluxe version of spacetime :slight_smile:

Will there be a new study today?


#67

the last two weeks have been focused on refining code— we decided the device management (grid/midi/etc) needed some care (substantial change) before i introduce a study. the new method is much more dynamic. thanks for your patience!

still on track for a friday update.


#68

Could someone explain that line of Spacetime script:

act[step[position]]()

??


#69

you can read this as

act_idx = step[position]
act[act_idx]()

so lets say act_idx = 1 , which refers to inc, (in act array)
so act[1]() becomes inc(), so calls the inc() function.

key point being the action (function) being called depends on the value of the step at position.
(similar thing is done with label, but labels are just strings, rather than calling a function)


#70

Ok i get it. Thank you!


#71

Many Happy Wednesdays you big puffy frog. I love you.


#72

Any chance of a new study tomorrow? I’ll be stuck at home with some time to devote to fleshing out MIDI in my scripts.


#73

i’m just back from a week traveling, where i thought i’d make further progress on the midi library but i forgot how the west coast sunshine actually can prevent getting work done.

so once we have the midi library completed (this week’s imperative) i’ll get the study put together.

thanks for your patience!


#74

No worries! Looking forward to it when it’s ready.