Norns: scripting

norns

#221

there isn’t an explicit mechanism for doing that but you can manipulate the package cache directly:

-- import
local myfoo = require "foo"

-- un-import 
myfoo = nil
package.loaded["foo"] = nil

#222

stupid thing of the day - a big gauge mapped to encoder turn:


#223

Hey… so the grid and midi study went up, but maybe you still have questions about what device info is available? Like maybe… can I get the height and width of a connected grid?

The grid and midi objects (is that the right term?) are tables with sub tables:

 grid.list     -- contains port and device name
 grid.devices  -- contains port and device name
 grid.vport    -- which device is on which virtual port

So it’s handy to print those and see what’s inside:

tab.print(grid.list)  

This is nice to confirm which device is attached to which port

I like something like this to get details of each attached device:

 print("-Grid device details-")
 for i,v in pairs(grid.devices) do
   tab.print(grid.devices[i])
   print("-")
 end

which prints out:

-Grid device details-
cols	8
rows	8
serial	4676000
id	2
ports	table: 0x14d9e20
key	function: 0x1455310
name	monome 4676000
dev	userdata: 0x70305920

So to answer my question above - the number of columns available on this grid would be grid.devices[2].cols and the rows grid.devices[2].rows

NOTE - the grid.devices table uses an id key which is different from the port key in grid.list. The id would be a number chosen by norns when you plug/unplug a device. The port is going to be the numer you’d use in grid.connect() or as selected from the SYSTEM>DEVICES menu.


#224

doing the studies tonight and playing around some in the process, have a few questions that i don’t want to clutter the thread with. is anyone online that has experience that wouldn’t mind answering a couple coding questions?


#225

Has anyone tried or been experimenting with a morphing wavetable type engine? I’m imagining something like the Synth Tech Cloud Terrarium type morphing… I’ve been playing around with making custom wave table banks and would love to use them via norns.


#226

Is there a way to get the current value of a parameter from a sound engine? While messing around with the studies I was hoping to write the current value of Hz. I’m sure it’s possible that this isn’t necessary but I’m curious nonetheless.


#227

my suggestion is to wrap engine commands in a parameter— see part 3

then you can use

params:get('tempo')

let me know if you were suggesting something else?


#228

Ah, cool! I haven’t worked through study three yet so i’m sure that will help. thanks.


#229

Trying to be able to save 16 grid patterns in a data file, each pattern taking up 136 lines in the .data file. The reading load_pattern() function seems to work fine. But the save_pattern() function only works on the first pattern, if I try to save a pattern when pattern_select = 2 or higher, ekombi.data becomes empty. Am I going wrong with the file:seek method or is there something I’m overlooking?

LOOK HERE
function save_pattern()
  local file = io.open(data_dir .. "gittifer/ekombi.data", "w")
  io.output(file)
  file:seek("set", (pattern_select - 1) * 136)
    for i = 1, 8 do
      io.write(tab.count(track[i]) .. "\n")
      for n=1, tab.count(track[i]) do
        io.write(track[i][tab.count(track[i])][n] .. "\n")
      end
      for n=1, 16 - tab.count(track[i]) do
          io.write("00\n")
      end
    end
  print("SAVE COMPLETE")
  io.close(file)
end

function load_pattern()
  local file = io.open(data_dir .. "gittifer/ekombi.data", "r")
  if file then
    print("datafile found")
    io.input(file)
    file:seek("set", (pattern_select - 1) * 137)
    for i = 1, 8 do
      track[i] = {}
      local tracklen = tonumber(io.read("*line"))
      print(i..":tracklen:"..tracklen)
      for n=1, tracklen do
        track[i][n] = {}
      end
      for j=1, tracklen do
        track[i][tracklen][j] = tonumber(io.read("*line"))
      end
      for m = 1, 16 - tab.count(track[i]) do
        io.read("*line")
      end
    end
    print("LOAD COMPLETE")
    io.close(file)
  end
  -- for i = 1, 8 do reer(i) end
end

#230

Is it because you are opening the file in “w” mode in the save_pattern() routine? According to what I can read, that erases any existing data in the file. “r+” should allow you to update an existing file’s contents, if I’m reading this right…

[ddg]


#231

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!


#232

@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


#233

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
    )

#234

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.


#235

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


#237

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.


#238

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

#239

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


#240

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.


#241

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.