Less of a code suggestion and more of a documentation suggestion.

The way I personally find it easiest to program is the “find the pattern” method. Basically, I’ll come up with an idea of what I want to do and then try to find examples of something doing that thing. I think it would help out if the docs could include some examples in the form of links out to scripts or maybe even commits that implement certain features or do certain things. For example, I think this kind of thing is super useful:

I’ve been thinking of doing something similar for showing how to work with note states in the new clock system (clock.run with params, clock.sleep for note length, using a counter (and why you need it) to hold note states maintaining a buffer of on notes, etc.). i.e. this snippet which I’m still making sure is solid first

local noteCounter = 1
local heldNotes = {}

function pulseNote(freq, gateLength)
  local currentNote = noteCounter
  noteCounter = noteCounter + 1
  engine.start(currentNote, freq)
  clock.sleep(gateLength)
  engine.stop(currentNote)
end

function playNote (noteNum, gateType, cur)
  local freq = MusicUtil.note_num_to_freq(noteNum)
  -- print(cur.nextStep .. ':' .. cur.nextStage ..' - play note ' .. freq .. 'hz of gate type ' .. gateType)
  if gateType == 'pulse' then
      clock.run(pulseNote, freq, cur.gateLength)
  elseif gateType == 'hold' then
    local currentNote = noteCounter
    noteCounter = noteCounter + 1
    table.insert(heldNotes, currentNote)
    engine.start(currentNote, freq)
  end
end

function stopHeldNote ()
  local noteToStop = table.remove(heldNotes)
  if noteToStop ~= nil then
    engine.stop(noteToStop)
  end
end

Maybe we need a place to house this kind of stuff that’s not just threads on lines.

And I will say I, love the procedural style of working with lua in norns/crow that a lot of scripts use. I wish engines were the same…every time I try to open a supercollider file to make some change I end up closing it more confused than when I started :sweat_smile:

EDIT: one more reason I think this could be good is that it could make it easy to find the most up to date way of doing things (provided the docs are kept up to date)

2 Likes

thiswhat are some of those things?

what i’m thinking from a docs standpoint is that we identify the common “I want to do this thing that is totally already implemented elsewhere” stuff and create/enhance the lib entries to make it easy to include in your script. so then documentation of these entries becomes an API guide. I think the original norns scripting docs still fulfill their original purpose – to get you thinking about musical code and show you how to get from small idea to instrument relatively quickly.

where we’re growing to (i think) is identifying the building blocks for complex ideas and helping folks recycle common elements.

exciting to see the energy around this!

2 Likes

i would love to contribute although i’ve hit a blocker of not finding the right workflow/setup - do you ssh in direct to Norns and make edits there? Or run a Norns setup in Docker?

I’ve always admired how SuperCollider had a bunch of examples in their docs, which are also available in a window right inside the editor. Looks like LDocs (the lua doc-writing tool that norns uses) has that ability based on this (also a plug for the lua functional lib). We could embed the code examples straight in the API page.

4 Likes

@nattog probably the easiest is to use maiden (accessible at http://norns.local when norns/your coding computer are on the same wifi), cmd+s/cmd+p to save and send your script to norns.

a more “power-user” workflow would be to use smb and then open dust/code of that norns network drive in your editor of choice. run maiden-repl in a terminal to send the script to norns. vs code is nice because both the terminal and code editor could be in the same window.

you could do it with ssh but that seems a little more primitive to me

One thing I’ve done that works for me is I copied most of norns to my local harddrive. I then setup my IDE to SSH into norns and automatically upload code whenever it’s modified.

The main reason I do this is all of the code is indexed locally so I can more easily do a “global search” if I’m trying to look something up. I also like having it locally so I can still work on things when away from the norns and then test it later.

3 Likes

Thanks - i might try out the power user way! This would work for making edits to say existing parts of the the core lib?

1 Like

do you mind doing a brief write-up of how you’ve done this? i’ve done rsync-based approaches like this before, but typically in a corp environment where the tools team could do most of the heavy lifting for me there :sweat_smile:

1 Like

so as I am porting m18s from crow to norns, these are the things that I have had to do this with:

  • how do I advance the sequencer clock (your post covered it)
  • what are the sound engines I could potentially use for generating notes (searched “list of engines” on lines, got pointed to https://monome.org/docs/norns/app/#synths-and-audio-processing by a post of yours)
  • how do I properly set note lengths (couldn’t find a place where that was solved, so I used the clock api docs, combined with a guess that subsequent parameters to clock.run woudl get passed and params to the function it calls)
  • how do I see a list of monospace fonts (this one, the docs are only a subset of the first few fonts…I had to search font in monome/norns github and then look for commits to find where @zebra added a bunch of fonts to look at the source…foudn out this was a thing by searching “norns fonts” on lines)
  • how do I pan some notes hard left, some hard right…found that the rings engine included some panning, but it’s not pan-per-note. I started looking into the “Pan2” supercollider docs and got overwhelmed on that one, so moved on for the time being
  • what’s best practice for calling redraw (polling on a metro/calling imperatively inside of another function) (I couldn’t quickly find an answer to this one…my guess is the metro, but I’m going with the latter for now just coz it was easier to set up).

I’m finding it pretty difficult to guess what questions someone who has less familiarity with coding (in general) would ask, because they might not know what they want yet.

2 Likes

well, I’m using webstorm to do all of the hard work for me, so I’m not sure how useful my instructions would be for people using something else. I haven’t really used rsync much.

I basically just copied the stuff I wanted locally using scp, and then webstorm has a “deployment” feature where you can configure norns as a remote host, connect to it over SFTP, and map your local path to a “deployment path” on the device.

I only have webstorm because my job pays for it, but I imagine other IDEs have similar features… I’m sure you could do all this with rsync and some good bash-fu though

1 Like

oh yes and this is why i love it! basically i always have two ssh sessions open to norns. all the tools i need are old, established, just work

5 Likes

Could definitely see that. If you have l33t vim skills it most definitely is less primitive than the way I use the code editor hah

I dont think we need to try to answer that question. Rather, documenting for yourself-from-2-days-ago is perhaps far more useful. Not to mention we’re typically best at explaining things to folks with one step less knowledge. It’s turtles all the way down :turtle::turtle::turtle:

Maybe we need a thread of ‘a-ha moments while scripting for norns’? Specifically open to all skill levels.

8 Likes

I’ll happily volunteer to pair program, help with architecture, design patterns, etc.

2 Likes

@jlmitch5 @crim FYI I’ve been looking at a more “power user” way of editing scripts using a remote editing setup.


It’s currently mainly focused on working on scripts but the same idea could also be applied to core libs of course.

I figured most the people interested in this would be on GitHub and chime in there but maybe we should create a topic for this as well? I’d like to get some feedback on what does and doesn’t work to determine what should be improved.

2 Likes

My object-oriented grid lib initially kicked off about ten years ago (Java, over OSC, for the original binary grid) and I now have the Lua port working; I’m hard at work on demos and docs right now. GitHub repo. with various READMEs is here - would be interesting to compare/contrast with yours.

3 Likes

Everything is on GitHub, so I have a repo. of the code on the Mac, for general Lua development and unit testing, and a repo. on the norns. I’ll edit via maiden for actual for on-the-box testing, then when I want to sync I mount the norns via SMB for the git client. (I suppose command-line Git would work fine directly on the norns as well, but I’ve never tried it.)

EDIT: Just tried it now. Command-line Git on norns is fine, with norns-generated SSH key on GitHub, and with ssh-agent running to avoid excessive password typeage.

I just had a quick peek but they look pretty darn different ?

mine has a lot more to do with laying out interaction patterns, display-wise it doesn’t do much of anything fancy

I’m not sure what you mean by interaction patterns - could you explain?

I’ve basically implemented a scene graph for grids/arcs: you create a stack of layers each of which contains LED patterns of varying brightness and opacity, and they’re rendered onto a device. You then shift them around, alter visibility etc. to animate an interface. There’s also some smart-ish press management machinery which ensures that any object receiving a button press will get the release at the same local coordinates, no matter how things shift around in the meantime.

I have a few self-made table functions that might be useful to other people. append, take, drop, and rotate. Did a quick rewrite so that they could fit in tabutil.

function tab.append(t1, t2)
  local new_t = {}
  local i = 1
  for j = 1,  tab.count(t1) do
    new_t[i] = t1[j]
    i = i + 1
  end
  for j = 1, tab.count(t2) do
    new_t[i] = t2[j]
    i = i + 1
  end
  return new_t
end

function tab.take(t, n)
  local new_t = {}
  local len = tab.count(t)
  if n > len then n = len end
  for i=1, n do
    new_t[i] = t[i]
  end
  return new_t
end

function tab.drop(t, n)
  local new_t = {}
  local len = tab.count(t)
  if n > len then return new_t end
  for i=1, len do
    new_t[i] = t[i+n]
  end
  return new_t
end

function tab.rotate(t, n)
  local len = tab.count(t)
  local mod = n % len
  local imod = len - mod
  if mod == 0 then return t end
  local t1 = tab.drop(t, imod)
  local t2 = tab.take(t, imod)
  return tab.append(t1, t2)
end
3 Likes