Norns: scripting

norns

#120

In order to parameterize SynthDefs you need to specify these parameters (in SC lingo, controls) as synthdef args. Also, format spec (second argument of addCommand) should be ”f” for float in your case.


#121

I took a closer look at your SynthDef. It should be noted that numRes and bells cannot actually be modulated, due to the SynthDef being compiled. SClang simply works differently when it’s executed in a SynthDef UgenGraphFunc. (And now we’re getting into SuperCollider peculiarity territories…)


#122

There’s unfortunately not a lot of docs on the engine parts. It will come eventually. :slight_smile:

Look at TestSine as a reference of a simple, well behaving engine in the meantime, and review the core Crone SC classes. They’re quite simple if you know SC.


#123

@jah Thank you, this was very helpful! I actually based this off of TestSine, but had to make a lot of assumptions based on what I saw. Are there any solutions I could explore to adapt this script, or should I consider making something original with the klank UGEN? For now I’ll be looking through the Crone SC classes.


#124

I’m sure you can get a simple, persistent klank engine running with just a ”density” command (remove ”numRes” and ”bells” commands, they’re better of as regular/static vars since you cannot modulate them). You might need to set the second argument to addCommand to ”f” for it to work.

FWIW I have an engine class in the works (dust/lib/sc/abstractions/CroneGenEngine.sc) that introspects a SynthDef or UgenGraphFunc and propagates arguments as commands removing some of the boilerplate code. But I think it might be better to base simple stuff of TestSine to get the hang of it.


#125

Here’s some troubleshooting tips that may help people working on engines.

I was getting error: AUDIO ENGINE for one of the engines I was working on. To troubleshoot, I found out I can ssh into norns and run:

$ sclang [enter]
sc3> Engine_Example.new(Crone.context)

That starts up the named engine, and in my case ran some initialization code that helped me determine what was going on.

While developing and ssh'd in, it was helpful to restart specific processes and view their logs directly:

~/norns/stop.sh
cd ~/norns
./crone.sh # for SuperCollider logs
./maiden.sh # for lua logs

#126

It’s sort of working, but the behaviour seems strange, for example on earthsea the note cannot trigger by pressing the knob of MIDI Fighter Twister (which I tested with MIDI Monitor on Mac that ensure it’s sending out), only can open up the note by note velocity. I think I need to modify the script a bit to make it perfect.

By the way, norns is a too many possibility instrument, even 16 x 4 knobs are not enough (i.e. hello_gong). I need to spend more times to see, so far I’m very satisfied! :smile:


#127

I made the following modification to earthsea when fiddling with one of my midi controllers (because it wasn’t sending note off the same way, so it’s using velocity 0 to indicate note off)

local function midi_event(data)
  if data[1] == 144 and data[3] > 0 then
    note_on(data[2], data[3])
  elseif data[1] == 144 and data[3] == 0  then
    note_off(data[2])
  elseif data[1] == 176 then
    --cc(data1, data2)
  elseif data[1] == 224 then
    --bend(data1, data2)
  end
end

#128

Why does trying to display local variables in REPL always lead to errors or stange things? I am talking about non-function ones. By the way i if someone could explain to me the advantage of making all my global variables local


#129

Lua: odd table copying behavior

I’m really a novice to Lua and programming, so I guess the following is just a “pilot’s error”.

I want to create a function to “rotate” values in a table, so that I can turn {1, 2, 3, 4} into {4, 1, 2, 3}. I guess there are several ways to accomplish this, but the one I chose involves creating a copy (called copy, surprisingly) of the table original whose content I want to rotate, and then run this code:

  for i = 1, #original do
    if i==1 then
      original[i]=copy[#original]
    else
      original[i]=copy[i-1]
    end

When I do the table copy via this code,

for i = 1, #original do
copy[i] = original[i]
end

everything works as expected, and {1, 2, 3, 4} becomes {4, 1, 2, 3}.

But when I use this shorter bit of code to do the copy action,

copy = original

all of a sudden {1, 2, 3, 4} becomes {4, 4, 4, 4}, which is unusable. At first I thought that the shorter copy action itself is wrong, but interestingly, immediately after this copy action both copy and original contain the same values at the same positions.

Please, can somebody be so kind to explain what is causing this odd behavior?


#130

assigning a table to a variable

copy = original

just puts a reference to original in copy - NOT a copy so you now have two variables pointing at the same table.

Frankly if you want to rotate a table I’d just insert the last element at the front of the queue and then remove the last element - two steps + knowing the length of the table

a = {1,2,3,4}
table.insert(a,1,a[#a])
table.remove(a,#a)

and if you need a copy then you can copy it (or if you want a copy of the original do that first and then rotate the other table)

Copying has unexpected traps - what if one of the elements is a table etc. etc and as ever simple dumb code is preferable to complex code. Obviously my code isn’t thread safe because at one point the table has an extra unwanted element but I think lua only has co-operative multithreading so that shouldn’t be an issue


#131

locals can only be accessed in their own scope. (the chunk, file, or function that defined them.) the REPL has its own scope and in fact its own environment which shadows the global environment. so the downside of using local in scripts is that you simply can’t see those variables in the REPL.

the upside of using locals is that it is bad to pollute the global namespace from your script. specifically it can lead to two pitfalls, of which we’ve already seen plenty: 1) you can easily clobber vital global vars used by the nonrns system itself, possibly preventing norns from starting if you do this in the default script. 2) you can easily accidentally use globals defined in previously run scripts, leading to weird bugs and indeterminate behavior.

we’ve discussed sandboxing of scripts to make both these issues go away, and clear a path towards inspecting script variables from the REPL. see this issue:


and this PR:

and elsewhere.

we just need to commit to a solution and then revise all the existing scripts.


#132

Some questions regarding Parameter Sets (as discussed in norns study 3):
I do have several tables in my script, whose values I’d like to store for later recall. From what I understood in study 3, in order to store a value in a parameter set, I first need to define a parameter (add_number) to which I assign the value. Does this mean that if I want to store a table, I do need to define a parameter for each value in the table? Or can a parameter be a table? If yes, how do the minimum/maximum/default values of the parameter definition work for a table?

I should mention that I am a complete novice at programming, and all that I am currently doing is trying to understand the AWAKE script, and modifying it step by step so that it does more of what I want…and I am really enjoying this feeling of embarking onto a new journey!


Norns: help
#133

that’s one possible way of storing/recalling tables, but depending on the table and its content it may not be the most optimal one. what are you storing in your table?

no.

you might want to look at the playfair script which persists table content in a playfair.data file.

also read about table persistence in lua in general at https://www.lua.org/manual/2.1/subsection3_7_3.html.

i’m quite new to lua aswell.

imo, utility methods could be added to the norns framework to support these kinds of operations.


#134

@wolfgangschaltung FYI @jah is referring to the new version of playfair which is on github, not on release yet:

see the functions at the very bottom


#135

@jah & @tehn: Thanks for your answers, these gave me quite something to chew on.

There are 2 tables (with integer numbers), and 4 integer numbers, the totality of which I want to store and recall as a preset (while the AWAKE sequence is running, without messing with the timing).

I understand that such a preset can be accomplished with nested tables inside the script, but when I send norns to sleep, everything will be gone, so I must find a way to write the contents of all presets I might create at runtime to disk to retrieve them later (e.g. after awakening norns).

Thanks, but I have to admit this is way over my head (I am not only new to Lua, but new programming at all). To name just 2 questions I had after reading it:

  • where do I specify the name for the text file that is generated?
  • how do I load the values stored in the text file? The text just said “In order to restore this value, a lua_dofile suffices.”, which left me rather puzzled.

Thanks much, while I am far from really understanding it, this looks way more explicit and thus comprehensible to me than the section from the Lua manual! I will try to work my way thru this during the next days, for now I will try to create the (volatile) preset management in AWAKE first.

Just 2 questions for now:

  • The code for the new PLAYFAIR ends with
cleanup = function()
  playfair_save()
  end

What does this mean? cleanup is not used anywhere else in the script, so when&how do these lines get executed?

  • What is the purpose of these require commands, and why are they sometimes put into variables?

require 'er'

engine.name = 'Ack'

local ack = require 'jah/ack'
local BeatClock = require 'beatclock'

Thanks much in advance!


#136

by default, numbers in lua are represented as double-precision floating point (64 bits). so max value is approximately 1.8 x 10308.

with lua 5.3, you can also explicitly create variables of integer type using foo = int(555). this also uses 64 bits, giving the range [-9223372036854775808, 9223372036854775807].

for example

local foo = require 'foo'
foo.dosomething()

this is the recommended way to use code modules in lua (aka libraries.) require 'foo' looks for a module source named foo.lua. the assumption in this idiom is that foo.lua returns a table (here containing the dosomething() method.) if foo has previously been loaded by require, it returns a reference to this table; otherwise it executes foo.lua.

local foo = require 'foo' stores a local reference to foo. this is safer than polluting the global namespace for various reasons, and also allows you to name the local reference whatever you want. f = require 'foo'; f.dosomething() is also valid (in case you want to use the name foo for something else, or don’t want to type it, or whatever.)

more on modules in lua: http://lua-users.org/wiki/ModulesTutorial

if your script defines cleanup(), it will be executed by the script loading module when the next script is loaded.

[mod: i’m moving this to Norns:scripting]


Norns: dust
#137

Thanks much for the explanations, also for the link to lua-users.org!


#138

I remember I read about it in the reference manual some days ago:
“Tables, functions, threads, and (full) userdata values are objects: variables do not actually contain these values, only references to them.”, but obviously I did not understand the consequences. Thanks much, I get it now!

Thanks for pointing me to this trick…another example where I forgot what I have read just a couple of days ago in norns study #2. I blame it on the excessive heat here in Germany :wink:


#139

Reading up the thread you are new to all this I see. Reference and value is easy to trip up on early on (& another good reason to stay away from copying things - it quickly gets you into language depths) when I start using a new language it’s something I look up very quickly.

The best advice I can give you from a life time of coding (nigh on 40 years - eek!) is don’t try and do fancy things. Go for straight forwards and obvious. And especially don’t take short cuts. So in this case copying and rotating are two separate things. So do them separately. I know the temptation is to think “oh I can do that in one step”. Resist. Future you will like now you much more :wink: