Norns: studies

Wondering if this has happened to anyone else here or if someone can point out what I may have done wrong here but last night my first day with Norns I wanted to start the first study “many tomorrows”.

I was able to get into maiden and create a new script, name it, then add the first block of code from the study. However once I ran it in Matron I got no sound and my Norns pretty much became unresponsive except for encoder 2. When I turned encoder 2 it allowed me to scroll though the page I was on but that was about it.

Now what I’m wondering is could this have happened because I went into maiden while a script was locally loaded on Norns? Forgive my lack of knowledge here as Norns will be my first foray into programming.

For about two hours I thought I had bricked or ruined Norns. After rebooting my computer I was able to have control over it again.

@prnts this sounds like a wifi bug that we’re working on. if it happens again and still isn’t responsive after a minute, shut down the norns via the emergency switch on the bottom and reboot. (use this bottom switch only if there’s a crash-- use SLEEP from the menu normally, which does a clean shutdown.

EDIT: also see @Olivier’s post below

2 Likes

This has happened to me periodically when going through the studies or creating my own scripts. When Norns becomes unresponsive while I’m working in Maiden (and without any clear error reported), simply switching to and running any other stable script directly from Maiden gets things back up and running immediately.

3 Likes

all studies now have syntax highlighting! extra readable.

13 Likes

I loved this study so much that I decided to write something similiar using processing (hope you don’t mind).
But I expanded it to use 4 tracks (for four midi channels) and added few custom operators like for example:
‘C’ - change random operator in next track
‘R’ - rotate right next track
here is recording from such session

After all this experimentation now I know that I will definetly need to start saving money for Norns :stuck_out_tongue:

12 Likes

I combined Spacetime with @burn’s KarplusRings engine. I also made it so that button 2 resets all steps to default.

-- spacetime with rings
-- norns study 3
--
-- ENC 1 - change brightness
-- ENC 2 - select edit position
-- ENC 3 - choose command
-- KEY 2 - return commands to default
-- 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!
-- this mod adds burn's 
-- excellent KarplusRings 
-- engine

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

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

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
  act[step[position]]()
  engine.hz(midi_to_hz(note))
  redraw()
end

function redraw()
  screen.clear()
  
  for i = 1,16 do
    screen.level((i == edit) and 15 or 2)
    screen.move(i*8-8,40)
    screen.text(label[step[i]])
    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("brightness", d)
  elseif n == 2 then
    edit = util.clamp(edit + d, 1, STEPS)
  elseif n ==3 then
    step[edit] = util.clamp(step[edit]+d, 1, COMMANDS)
  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
    step[i] = math.random(COMMANDS)
  end
end

function init_steps()
  for i= 1,16 do
    step[i] = 1
  end
end
15 Likes

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
13 Likes

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!

25 Likes

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?

8 Likes

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.

16 Likes

Could someone explain that line of Spacetime script:

act[step[position]]()

??

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)

4 Likes

Ok i get it. Thank you!

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

1 Like

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.

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!

17 Likes

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

the midi library is basically done, though there are suggestions to add more convenience functions (filtering messages by type, etc). if you’re curious in the guts: https://github.com/monome/norns/pull/476

the study is not complete but coming. (we’re in azores this week playing music at a huge circus festival with @instantjuggler)

again thank you for your patience and thanks to everyone helping shape the decisions in how the code works!

12 Likes

That feeling when you think something might arrive today, but you’re not sure, but you keep checking anyway.

12 Likes

perhaps you’re seeing all of the code being added to the midi branch on github :slight_smile:

8 Likes