Norns: scripting

Are you trying to append to an existing file? If so, try opening the file with a, or a+ instead of w?

Here is some Lua file I/O info, hope it helps!

@darwingrosse, @Justmat thanks for the input! The solution I settled on was “r+”. If I wanted to make an endless amount of saveable patterns, I’d definitely use append. I kind of ripped these two functions out of your scripts, so thanks @Justmat

so just wrangling a dev environment that lets me work on supercollider without using the wifi (which is super flakey for me but I know that that is being rewritten)

In order to use screen to connect over the serial port - follow instructions elsewhere. ie:

screen /dev/usb.tty- 115200

then in the screen if you want full screen and colour I needed to do:

stty rows 54 columns 181
export TERM=xterm-256color

then vim is working full screen with syntax highlighting! win :slight_smile:

.bashrc has all kinds of things in it - will need to review :wink: including messing with the TERM - not sure why I needed to set it

this is on a mac but should be similar elsewhere

(I’m too lazy to figure out using maiden over serial - presume some kind of tunnel would do the trick - if anyone knows how to do it - please share! )

(also worth noting - the usb connection directly from my Mac keeps the battery charged. Which is nice)

(I also installed tmux - which works nicely as well )

(edit - also installed this into my vim so I have syntax highlighting https://github.com/supercollider/scvim - I’ve not bothered trying to get the interactivity to work because I don’t need it here )

(last edit - if you are connecting with something that wants to use the bottom line for status (like mincom for example) just reduce the rows in the stty command)

(really the last edit - if you are running on the terminal (as above or via ssh):

go into norns. make a copy of start.sh - I called mine dstart.sh and edit it to say

cd “$(dirname “$0”)”
./crone.sh &
./matron.sh &

  • ie take out the pipe to dev null for the logging - that way you can see all the logging on the terminal
    )
6 Likes

Thanks for the tips on how to change stty rows and columns, and to get colour. Will try it out.

I prefer using serial (Putty on Windows), sometimes coupled with a ssh window if i need to debug both lua and sc simultaneously. Hotspot works better than going thru my wifi network.

I’m quite sure maiden only works over HTTP right now.

you can tunnel http over anything (my head of engineering at work has a script to tunnel it over DNS which he used to use on trains instead of paying for wifi!!!)

yeah - if I have the hotspot open I can’t access the internet but the serial thing actually seems pretty good now

1 Like

posting here instead of code review because it’s not actually producing sound yet, but would welcome anyone’s guidance, i don’t know anyone interested in coding offline…

i’m working on a small function that captures midi input within a period of time and converts it to a scale that can be used by other sequencer or generative scripts. ideally this is something that could be popped in with minimal editing to any such script that is using the markeats library for scales.

this could be a nice and intuitive-for-pianists alternative to scrolling through scales/modes or tweaking the transpose knob, and could also allow you to quickly and musically change key ‘on the fly’

that was my idea, here is how i did it:

music = require 'mark_eats/musicutil'

chordsList = {}
timeSet = 0
timeDone = 0

--midi function 
m = midi.connect()
m.event = function(data)
  local d = midi.to_msg(data)
  if d.type == "note_on" then
    noteBuffer(d.note)
    timeDone = util.time()
  end
end

--note function 
function noteBuffer(aNote)
  timeSet = util.time()
  for k, v in pairs (chordsList) do --ignore duplicate notes 
    if aNote == v then
    return
    end
  end 
  if timeSet - timeDone < .5 then --set window for note input presses 
    table.insert(chordsList, aNote)
  else -- clears table and starts new buffer 
    print('Initializing buffer')
    for k, v in pairs(chordsList) do chordsList[k]=nil end
    table.insert(chordsList, aNote)
  end
  capturedNotes = music.note_nums_to_names(chordsList)
  print(#chordsList .. '-size buffer of notes ' )
  for k, v in pairs(capturedNotes) do 
    print(v)
  end
end

i guess my only question is, does this feel like i’m on the right track? what should i be worried about?

it runs for me, but it’s split between the m.event function and the notebuffer function in a pretty confusing way. seems like it could be simplified. my next stop is to try to get it running with the sequencer in study 4.

3 Likes

this looks great!

to me the only confusing thing is why you put timeDone = util.time() inside the m.event()… it seems like you should just put it at the end of the noteBuffer function and it’d do the same thing, but feel more tidy?

or it could be further optimized by just storing a single time, of the last event. call it timeLast = util.time() and make it the last line of the noteBuffer function. then you can get rid of timeSet, and just do:

if util.time() - timeLast < 0.5 then -- set window for note input presses
1 Like

3 posts were merged into an existing topic: Norns: code review

Is there a way to change the ppq of BeatClock without editing the actual lua file? Trying to integrate BeatClock into ekombi this morning, but need a much higher ppq than 24. I’m trying to find a way to do this from my script.

Working on an FM modulation matrix. I have a grid and a Norns. I would like to press button (1,3) on the grid to enable encoder 1 to control parameter “frequency 1”. I would like to press button (1,4) on the grid to enable encoder 1 to control parameter “amplitude 1”.

I got stuck when I observed the function enc(num, delta) as a function that is executed when the script is loaded and cannot be redefined after the script runs.

Is it possible to give an encoder a new function when the script is loaded and function g.event(x,y,z) is called?

update: my roommate rubber ducked with me and she suggested a global state that the grid keys modify. This works, but the way I did it is very ugly. But yea! Modal encoders that change the [hz,phase,amp] array for five of the six oscillators.

I’m tempted to put together a PR but I don’t really want this to be “earthsea with FM”. I think it can be a unique interface to FM synthesis.

quick thought (and untested) but maybe try something like:

local _enc = function(num, delta)
  -- do actual handling here
end

function enc(num, delta)
  _enc(num,delta)
end

i think you should be able to reassign _enc on the fly and get what you’re after?

4 Likes

this is the correct way to do this @ppqq

I realized I wanted to dynamically call a function name based on some global state, not override the definition of enc(). StackOverflow to the rescue.

Tables of functions are pretty cool and easy to understand.

3 Likes

loving all the new scripts in the update! especially molly the polly! sounds insane.

made a quick hack of @Justmat’s step sequencer to distinguish the black keys from the white keys on the grid keybed:

4 changes to the script:

1: add new variable with the others (not sure if this is needed or just good hygiene?)

local black_keys = {}

2: add new function to get table of coordinates whose notes are sharp/flat

local function keybed_lights()
  for y = 3, 8 do
    for x = 1, 16 do
      local note = music_util.note_num_to_name(note_from_grid(x, y), 1)
      if string.find(note, "%#") then
        table.insert(black_keys, {x, y})
      end
    end
  end
  return black_keys
end

3: add this to init() to store the table on run

black_keys = keybed_lights()

4: add this to grid_redraw to display the keys

  for index, t in pairs(black_keys) do 
      g.led(t[1], t[2], 2)
    end

i still don’t have a very good grasp on how to use github or what the etiquette is for posting minimal modifications of others’ work there, but let me know if this kind of stuff shouldn’t live on lllllll.co!

5 Likes

Nice! This seems really useful. I have been preparing a small update for Paths, so I’ll be sure to include it.

4 Likes

Is there a way to change the step of a ParamSet or to use floats instead of integers? So if I want to use a parameter to change the value of something from 0.0 1.0 with the steps being 0.01, how would I do that?

Also, in regards to Polls, is there a way to pass Symbols or Strings between a CroneEngine and LUA?

I’m going deeper into the params/preset system. I noticed an artifact called fm7.pmap. The contents are keys enumerating all params I exposed through supercollider and values which all read “-1”.

What do these values mean?

no. you can currently send a single float, or a blob of bytes (which is passed to lua as an array; afaik only the VU meters use this - see AudioContext.)

we could add format strings to polls, as we have for commands. i didn’t feel this was necessary enough to introduce the attendant complexity immediately. specifically i wonder why you would ever need to generate a string from SC - personally i would just have a little engine-specific lua code to convert a float value. (the main reason i would want poll formats is to send multiple float values in a single packet.)

in any case:

technically yes. paramset / params accept arbitrary deltas:

and param subclasses can customize how they handle deltas.

but in the menu, delta is fixed (or rather is a function of encoder sensitivity, which is fixed in the menu… or something like that.)

i agree that finer param control in the menu would be great. (how? dunno. on aleph we had two param value knobs - coarse with acceleration, fine without. here would need a mod key or something. up to y’all.) but you can also roll your own custom param UI in a script, affecting the exact same param data.

1 Like

pmap is param map = midi map

-1 means unassigned

re: param delta ranges, yes we should figure out a way to refine the design for granularity

1 Like

if I may, (might be too soon to make a ticket on github) what do you think about renaming the extension to “.midimap”?