norns: scripting

I’m looking at the norns scripting tutorials, but I’m getting engine missing errors.
Is there a way I can load in the PolySub or TestSine engines from the Maiden package manager?

those are part of the we repo. so update that one from the base collection.

That makes sense, and that’s where I thought. I just realized that I didn’t reboot norns after I re-installed it! Thank you!

Here’s some questions I’ve come up with tinkering with softcut:

Does that buffer start at 1 or 0 ? Starting at 1 would be more inline with Lua conventions, but buffer position is specified in seconds, so starting at 0sec makes some sense too?

Is there a way to slew the panning without re-implementing a slew function in my script? (Softcut feature request?)

Is there any reason that buffers get “stuck” on script (re)start from maiden?

(Update: two more softcut questions)

Is there a way to get the length of a loaded file?

Is there a way to get the current output level of a voice? i.e. envelope follower mentioned in another thread…

And more general Norns than Softcut, For a static background, is it better to draw a bunch of polys on every frame, or draw them once and write them to a PNG & then load the PNG every frame?

zero. lua convention is that arrays use 1-base indexing, and we follow this in norns lua code whenever a variable or argument is an index. when it is a quanitity, like time, this doesn’t apply.

you will notice a lot of softcut scripts that use 1s as a starting place, which maybe muddies the water. there is not a good reason for starting at 1s, except a general awareness that having some pre- and post-roll is a good idea. (but for the record, crossfades in softcut are applied at the end of the loop; so leaving some post-roll is important but pre-roll is not. [oh, ha: unless the rate goes negative, d’oh.])

Is there any reason that buffers get “stuck” on script (re)start from maiden?

not sure what you mean

Is there a way to slew the panning without re-implementing a slew function in my script? (Softcut feature request?)

sofcut.pan_slew_time(voice, time)

Is there a way to get the length of a loaded file?

not from softcut. you can query the duration of a soundfile before loading it with audio.file_info() (returns a table with channels, frames, samplerate)

Is there a way to get the current output level of a voice? i.e. envelope follower mentioned in another thread…

no. there are no envelope followers on per-voice output. (what thread?)

And more general Norns than Softcut, For a static background, is it better to draw a bunch of polys on every frame, or draw them once and write them to a PNG & then load the PNG every frame?

don’t load a PNG every frame; it incurs disk IO and creates/destroys a new cairo surface.

[mod: moving to norns: scripting]

4 Likes

Thanks for the informative answers.

Just a thought, I’m not sure about moving this to the Norns: Scripting thread, the drone in three worlds thread does prompt to ask questions …

zero. [snip] when it is a quanitity , like time, this doesn’t apply.

Good to know my initial suspicion was correct that buffers start from zero, but all the examples starting at 1 was misleading me, considering the documentation is not clear either.

Is there any reason that buffers get “stuck” on script (re)start from maiden?

not sure what you mean

Intermittently on restarting (hitting play in maiden, while the script is already runnning) a softcut script (grainular style, 7 voices) I get a tone, until the voic? is reclaimed. FYI This could be a non-norns driver issue (fates)

don’t load a PNG every frame; it incurs disk IO and creates/destroys a new cairo surface.

Is there a scriptable screen buffer? Maybe worth a feature request? Then again, Norns is not exactly designed to be a games console.

Once upon a time I started on expanding the load png stuff so they could load and show as separate functions - which would help.

I have also done some very basic “sprites” reading bitmaps into arrays.

One of these days I’ll have time to get back to that.

1 Like

thanks! can you link the script? could be an issue with how softcut params are reset between script loads.

re: scriptable screen buffers yes, i do actually think it would be a nice thing to have. but doing this correctly would involve the level below lua… it’s not a high priority but could be added if/when we get to refactoring screen.c to better support emulation.

:point_up: This has been super helpful in my scripts, but now I’m struggling to figure out how I’d constrain the range when the min value isn’t 1 (for example, if I wanted to increment/decrement a value within a range like 4-10). Any ideas? My stackoverflow searches are hurting the old brain.

i guess i wouldn’t overthink it

function wrap (x, min, max)
   local y = x
   while y > max do y = y - max end
   while y < min do y = y + min end
   return y
end

otherwise i guess you could do something like (x - min) % (max-min) + min

but preferring % operator over conditional isn’t really an optimization, at least in lua where i’m pretty sure it is literally implemented as a%b === a - math.floor(a/b)*b

4 Likes

4…10 is really 0…6 with an offset of 4. So you could change to % 6 and then add 4 instead of 1 at the end. I’m not at my computer but something like that should work.

1 Like

Yeah, I thought so too at first, but that was just incrementing my value by 4 instead of adding an offset :thinking: (x - min) % (max-min) + min seems to be doing the trick, though!

1 Like

I tried to search for this, but I couldn’t find anything: is there a way to use supercollider’s pattern language (from sclang) in norns. E.g., make a script that plays a pbind when a button is pressed…

Edit: maybe it’s as easy as adding a this.addCommand in the engine that calls a pbind

2 Likes

I’ve been thinking about that too. That should work, but it seems like if you want to do anything more than play/stop you’ll get a crazy web of polls and this.addCommands pretty fast.

For instance, I was experimenting with polling control rate ugens in SuperCollider to use elsewhere in my script as LFOs and modulators, and it was cool but it was beginning to feel like twice the work once I wrote a bunch of Lua code to wrap it up and add params.

Another day, another lua question.

I’m wondering if folks have tips for modifying the range of a for loop dynamically, or if such a thing is even possible. :thinking:

For context, I originally had a function - newPattern() - that created a new 16-step note pattern in my recent script, Patchwork. It’s called from init, but it can also be called via a special command at any point by the user. Works great as is:

function newPattern()
   for i=1,16 do
    table.insert(pattern[1], i, math.random(8))
   end
end

I’ve since been experimenting with new variables that let you change the start and end points of the sequence with the encoders (which also works great)…

local seqStart = 1
local seqEnd = 16

…which naturally led me to updating my newPattern() function accordingly:

function newPattern()
   for i=seqStart,seqEnd do
    table.insert(pattern[1], i, math.random(8))
   end
end

The weird thing - and this may very well be due to my limited knowledge of for loops - is that this modified function works great, but when I change the seqStart and seqEnd values and then call newPattern() again, the loop iterates from seqStart (cool) to 16 when I’d expect it to stop at whatever seqEnd is set as. Basically, I just want new values inserted within the new range set by seqStart and seqEnd.

Does this make any sense? It’s been driving me a little nuts because half of it works as expected. I suspect there’s some fundamental thing I’m missing…

numbers in lua are always passed by value, not by reference (i.e. it’s passing 16 to ‘for’, not your variable)

i think the best way to do this would be giving a function to ‘for’ that returns your variable

(untested)

1 Like

this sounds like a scope issue (short of seeing the whole script, if you want to post more)

i’d suggest:

function newpattern(start, end)
for i=start,end do
    table.insert(pattern[1], i, math.random(8))
   end
end

and then use it newpattern(seqStart, seqEnd) or similar.

you could parameterize the pattern number as well. basically using less global vars might make things cleaner

3 Likes

the first statement is true, but also kinda red herring here. the "numeric for" is a subset of "generic for" - under the hood it generates a factory function with an invariant state (the endpoints a and b), a control variable (i), and a closure (i = i + 1; return i < b). so:

i’m not sure i understand the original problem without more context. but let’s say in general you want to mutate the constraints of the loop from within the loop (my gut reaction is that this is not a good pattern, but OK.)

two ways i think think of

  1. “generic for,” that is, an explicit iterator function that mutates
  2. while loop
3 Likes

Love it - thank you all for the super helpful explanations. A good incentive to dust off my pdf of Programming in Lua and sharpen my skills.


EDIT: The solution was simple, in the end. Instead of using table.insert, I modified my function and now all is working great. The answer was in my own script the whole time :man_facepalming:

function newPattern_A(a,b)
  for i = a,b do
    pattern[1][i] = math.random(scaleLength)
  end
end

Hi,

Does anyone know if there is a way to save a pattern_time as a textfile with tab.save? I’ve recorded some button presses on my grid to a pattern_time and I want to save it and call it up with tab.load, sort of as a saved pattern/preset pattern.
When I use tab.save it makes a nice textfile but of course with a lot of “extra”. And when I load it in again, it is of course just some text, and no longer the function that I can send start() to for example.
Is there a way to store a pattern recorded with pattern_time, and recall it again later?
Does this make sense?
Thanks
Anders