Norns: scripting

norns

#160

you can load any sample programmatically using engine.read(v, filename), where v is the voice number and filename is the path to the sample you want to load.


#161

thanks, I stupidly did not try with full path.


#162

How to iterate over a table with strings as keys?

If I use numbers as keys in a table, I can use e.g. for i=1,4 do to look into the 4 fields of a table. Very convenient for me.

But how do I iterate over a table which uses strings as keys (like name, pitch, velo, length)? The use of such strings as keys would make my code easier to read, but makes it harder to access the table via loops like for…do (unless there’s a trick I have not found yet).

Please, can somebody help me here?


#163

you want pairs:

table = { ... } 

for k,v in pairs(table) do 
    .....
end

Another edit since the first one was rubbish - pairs is what you want because it’s good when you are using a table like a map - if you are doing other things - like using it as an array then ipairs will come in useful


#164

Thanks much!

Does
for k,v in pairs(table) do
mean that
if there is a k(ey) for which a v(alue) exists,
do something,
then move on to the next key?

What happens in case I use this on a table with numbers as keys, does this mean that if there’s a table with some keys missing (like {[1]=23, [3]=4, [5]=6}), it will not do something for those keys without values?

In addition I am also confused by the terms “map” and “array”. Where is the difference between the two in layperson’s terms? For me an array is a (2-dimensional) table in which I can access a cell in the table with its x and y coordinates.


#165

My understanding is that pairs is good when there is a key, value pair and ipairs will help when there isn’t a key

map (or dict or dictionary) normally would mean a data structure with key value pairs

array is just list of stuff (with as you say one or more dimensions)


#166

Be careful with ipairs, though, as it terminates once it finds a nil-value. So if you define an array like

a = {}
a[1] = 1
a[3] = 3

then use

for k,v in ipairs(a) do
    print(v)
end

you’ll only get 1 printed out, because ipairs terminates when it finds that a[2] = nil. Pairs doesn’t do that.


#167

ah - good to know that!


#168

@junklight & @x2mirko: Thanks much, that helped a lot!


#169

Came up with something fun… dynamically loading presets.

Step 1 - make a few different presets for a script like foulplay (make duplicates of the original). I made one of each for the 3 different drumkits in audio/common (i just edited the pset file by hand to define the samples for each channel)

local presets = {
    "okyeron/foulplay606.pset",
    "okyeron/foulplay808.pset",
    "okyeron/foulplay909.pset"
} 

Step 2 - and then maybe a function to load one of those:

function loadpset(filepath)
  local file = io.open(data_dir .. filepath, "r")
  if file then
    print("pset found: "..filepath)
    params:read(filepath)
  end
end

Step 3 - of course remember to call this in init() so you have one at the start

loadpset(presets[3])

Step 4 - Then you can load the different presets while the script is running. For example in function gridkey(x, y, state) I could set a specific grid button to load that preset:

 if x == 3 and y == 6 and state==1 then -- make grid 6-3 select preset 1
   loadpset(presets[1])
  end

Step 5 - Fun!


#170

Following up on this.

You can also load a preset that has just a subset of the available settings and it won’t change other parameters.

For example (with foulplay), if you loaded the following pset while running, it would just change bpm and delay settings - and not change the samples you have loaded.

"bpm": 170
"1: delay send": -0
"2: delay send": -0
"3: delay send": -0
"delay time": 0.5
"delay feedback": 0.9
"delay level": -1.0

So you could have FX presets mapped to a button or triggered by an event!


#171

Script produces unexpected low frequency rumbling

I worked on expanding the AWAKE script the last couple of weeks. It all worked fine, until I changed two lines of codes (lines 321 and 322) from
one.pos = 1
two.pos = 1
to
one.pos = 0
two.pos = 0
and ran the script again from maiden.

From this moment on, the script only produces a thumping low frequency noise on the right side and silence on the left side, instead of a simple square wave melody. I tried to set both values back to 1, still thumping noise, comment both lines out, still thumping noise, so I can’t get the script back to work.

Even worse and more mysterious: Several previous versions of this script (which I did not edit in today’ session!) now also only produce this thumping noise.

Loading tehn’s original version of AWAKE works normally, but almost all of my edited versions of the AWAKE script are ruined. When selecting them while tehn’s original AWAKE is running, a crack is heard, then comes the low frequency thumping noise. When I then load a healthy AWAKE, it takes a second or 2 until the “normal” sound comes back, first on the right side, then on the left side.

Restarting via sleep while running a healthy AWAKE does not help, and it does not matter if a grid and/or wifi nub is connected.

Please, does anybody have any idea what could have caused this and how I can fix this?

I don’t have a USB stick with me, so I can’t re-install the system. Is there a way to re-install the system without a USB stick? I do have Cyberduck installed, if that helps.

For those willing to take the risk, I have attached the questionable script (which ran fine before running it after changing 2 lines): Does it produce the low frequency noise on your norns as well?

Thanks much in advance!

my_awake_v2_6.lua.zip (4.8 KB)

And here is maiden’s output after loading said script (looks completely normal to me):
norns.script.load(“jhh/my_awake_v2_6.lua”)
# script load
# cleanup
# script clear
# script run
loading engine; name: PolyPerc

Engine.register_commands; count: 6
— engine commands —
amp (f)
cutoff (f)
gain (f)
hz (f)
pw (f)
release (f)
------
— polls —
amp_in_l
amp_in_r
amp_out_l
amp_out_r
cpu_avg
cpu_peak
pitch_in_l
pitch_in_r
tape_play_pos
tape_rec_dur
------
# script init
grid/seek
reconnecting grid…
grid.reconnect (default)
attaching grid
>> name monome 128
>> cols 16
>> serial m1000235
>> dev userdata: 0x217118
>> rows 8
>> id 1
>> key function: 0xa0c80
reconnecting midi…
READING PMAP


#172

think about what happens on line 180 when pos == 0

if one.data[one.pos] > 0 then engine.hz(freqs[one.data[one.pos]+two.data[two.pos]]) end

hint: freqs and one.data are lua arrays, which use 1-base indexing.

spoiler: i guess what you are hearing is the effect of setting oscillator and filter cutoff frequencies to 0 or something equally unfortunate.

i’m just surprised you aren’t seeing lua errors at runtime, but then i’m just going on visual inspection here.


in general, i don’t recommend using 0-based arrays in lua at all. i know 1-base is strange coming from c-like and system-programming languages, but it is actually common in mathematical programming (matlab, mathematica, julia, cobol, algol, fortan, &c.) we’ve designed all the lua interfaces in norns to use 1-base for efficiency and consistency with other parts of lua.


#173

Thanks for the answer, but I am not sure if this is the root cause, for these reasons:

a) four lines above the line you quoted
if one.data[one.pos] > 0 then engine.hz(freqs[one.data[one.pos]+two.data[two.pos]]) end
is this line:
one.pos = one.pos + 1
Both of which are called by the metro timer, so the 0 value is actually never used as an index for the array, the index is always at least 1.

b) the low frequency noise is present even when I remove the 2 lines I added.

c) the low frequency noise is even present in scripts in which I never added the 2 lines, i.e. scripts I edited yesterday for the last time and which worked perfecty by then. Now, after today’s edit broke the current script, the scripts from yesterday also exhibit this noise although I did not touch them.

Please, can you try run the script on your norns and let me know what happened?


#174

pset’s on my brain today - so I wonder if some parameters got set in your version of the scripts?

Compare data/tehn/awake.pset with your script-name version?


#175

[ed: this. ^ i am 99% sure you have a bad value saved for cutoff, transposition, gain, something. ]

ok, i guess the index is not a problem in this case. still, i would just move the increment to the end of the callback instead of initializing the counter to an unusable value.


#176

try deleting your my_awake_v2.6.pmap file so it regenerates

EDIT - in testing your script, it was quiet or maybe rumbling (couldnt tell in my noisy environment) until I went and changed a bunch of the various parameters and now it seems normal.

EDIT2: it might be a good idea to explicitly create a my_awake_v2.6.pset file and fix the path and uncomment --params:read("tehn/awake.pset") and -- params:bang()

EDIT 3: @zebra - do “bad things” happen if you don’t have a pset file and you use params:get/params:set? Or do those just save as local variables or something?


#177

No such file, found one for an earlier version of my script, deleted it, no effect.

I did not yet create a specific my_awake_v2.6.pset file, but I uncommented both --params:read("tehn/awake.pset") and -- params:bang() and now it is running again. Thank you very much!

I need to really read and understand the study about parameters. The reason why I commented these out was that I did not yet want to use them.

Thanks again for getting me up and running again!

EDIT: It looks like the sole culprit was to comment -- params:bang() out. As soon as this is active again, I get normal sound, regardless of whether or not --params:read("tehn/awake.pset") is commented out or not.

EDIT2: @okyeron & @zebra, you have no idea how happy you made me! Now I can get to sleep, it is 1:30 am over here.


#178

What types of data can I store in a params:set() command?


#179

yes, you can use a metro— this is how the menu code works. something like:

  • on key down, start metro
  • if metro func happens before key up, it’s a long press
  • if key up happens before metro, it’s a short press

keep in mind you can also use util.time() to get system time (where you can use two measurements to get a delta time between events)

yes, this is great! i did the same thing for the top row of the grid in playfair