Any ideas why params always show up as a float when rendered on norns itself? For example, when I spec:

LUA:
params:add_control("compress", "compress", controlspec.new(0, 100, 'lin', 1, 10, "%"))

SC:
this.addCommand("compress", "i", { arg msg; synth.set(\compress, msg[1]); });

it shows up in norns as
compress 10.0 %

1 Like

numbers in lua are always double-precision float, unless specific contortions are performed to make them integers.

currently on norns lua side, we don’t really use integers at all (AFAIK). certainly the parameter “control” class doesn’t.

(the only case i can think of where this would really, really matter is if the param value was acting as a bitfield. which would entail its own parameter class i’d think.)

at the C level (between lua and OSC) the value in your example will be cast to int before its sent, b/c of the command format specification.

i suppose it would make sense to tweak the parameter display routine (in lua) to drop the decimal when step size is an integer.

2 Likes

There is also a ‘number’ param type that works well for integers :blush:

2 Likes

Are Lua drawing libraries compatible with Norns? Or, can we import SVG? Thinking of creating more complex interfaces.

norns uses cairo

see https://github.com/monome/norns/blob/master/lua/screen.lua

1 Like

I did some preliminary research on svg here - Norns: Development

I’ve been experimenting with reading images to pixel arrays to make “sprites”

There’s also a display-png function added in 2.0

3 Likes

Use a number or a controlspec combined with a formatter (Formatters.round(1) should do)

1 Like

did you ever get anywhere with this?

Nope - I could not make the web socket stuff work

Have been doing norns.script.load(...) from REPL

2 Likes

Sorry I never answered this, but just look at he websock wrapped code, it is a few lines using nanomsg

There’s no protocol to speak of since the two processes (matron, sclang) have each their own repl already. ASCII utf8

There are a couple special commands for maiden, not needed to get started

hoping to get some help for @capsella with my less concepts script on 1.0 (I’m assuming the fix would also help with 2.0).

apparently, my external midi clocking code isn’t working as intended.

following study 4, I’ve implemented the following at the top of the script:

local beatclock = require ‘beatclock’
local clk = beatclock.new()
clk_midi = midi.connect()
clk_midi.event = clk.process_midi

then this in my init():

m = midi.connect()
clk.on_step = function() iterate() end <~~~~ iterate triggers all the action in the script
clk.on_select_internal = function() clk:start() end
clk.on_select_external = function() print(“external”) end
clk:add_clock_params()
clk:stop() <~~~~ I don’t want the clock to start when loaded + it’s the only difference

I then have some hardware interaction, key 1 to start and stop internal clock:

if n == 1 and z == 1 then
automatic = automatic + 1
if automatic % 2 == 1 then
clk:start()
elseif automatic % 2 == 0 then
clk:stop()
end
end

per the study, clk.on_step = function() iterate() end should trigger regardless of clock source (internal or external), but @capsella is reporting that the script only responds to internal clocking. any ideas?

edit, OH WAIT: is clk:start() a global command that affects both internal and external clocking? e.g.: clk:stop() means ALL clock signals will not trigger clk.on_step?? I was naively assuming that those commands were only applicable to internal clocking – can anyone confirm?

Yeah, looking at the code for BeatClock it seems like it is stopping and starting both internal and external.

It’s trying to hide from you the fact that stop and start use completely different methods under the hood. But they both clk:tick() periodically when you run clk:start()

It seems like it would be pretty doable to make a version of beatclock that breaks those out with a clk:start_internal() and clk:start_external(), you’d mostly have to shuffle some existing code around in that class.

1 Like

vindication, thank you so much @mimetaur, knowing this is hugely helpful – I’ll do some fiddling and testing to change how I’m handling those events in my script :slight_smile:

2 Likes

@dan_derks there are some new midi things in 2.0 (helpers for clock/start/stop/continue) which will probably with help your situation.

check out these midi-demo scripts
https://github.com/okyeron/midi-demo which I did as examples for working with external clock from my TR-09

3 Likes

@ppqq prompted a little investigation into meta-parameters (and i think i recall seeing @carvingcode exploring this earlier?)

basic goal: have a parameter which is the assignment for another parameter.

ie: a parameter which maps the destination of an encoder turn.

here’s some existing code from earthsea, sound params (without set_action for each)

params:add_control("shape", "shape", controlspec.new(0,1,"lin",0,0,""))
params:add_control("timbre", "timbre", controlspec.new(0,1,"lin",0,0.5,""))
params:add_control("noise", "noise", controlspec.new(0,1,"lin",0,0,""))
params:add_control("cut", "cut", controlspec.new(0,32,"lin",0,8,""))

add a meta param:

params:add_option("enc2","enc 2 mapping", {"shape","timbre","noise","cut"})

then let’s implement the mapping:

function enc(n,delta)
  if n==2 then
    params:delta(params:string("enc2"),delta*4)
  end
end

calling params:string returns the string name from the OPTION, which sets the destination of the delta change. i multiply by 4 to increase sensitivity.

so now encoder turns can be redirected very easily in PLAY mode.

and you could of course have a key action rotate/change the OPTION as well

12 Likes

Interesting possibilities, @tehn. A couple of these, placed on a UI Page and we’re close to having capabilities similar to boxes like Octatrack which can modulate multiple params from a single knob. Used with Passersby or MollyThePoly… nice!

3 Likes

I spent a few hours trying to understand how the Arc works by stepping through the code from the angl script. I understood everything except for what I see as a crucial piece of functionality.

How do the values in the table named positions get set? The code says that it is assigned to the value of pos which is an argument to a callback function for a poll named phase_1. Looking at the console, I can see 7 polls named phase_n. What do these polls do and how do they get their input values?

The only suspicion I have is the new softcut engine is being used and it exports a bunch of phase values between 0 and 1 for the buffer read location.

line 82 sets up the polls on init
local phase_poll = poll.set('phase_' .. v, function(pos) positions[v] = pos end)

then I look up study 5 about polls:
https://monome.org/docs/norns/study-5/

Aha - In the Glut engine, the phase and levels polls are created.

Engine_Glut.sc line 212

			this.addPoll(("phase_" ++ (i+1)).asSymbol, {
				var val = phases[i].getSynchronous;
				val
			});

			this.addPoll(("level_" ++ (i+1)).asSymbol, {
				var val = levels[i].getSynchronous;
				val
			});
		});

but… if VOICES is 4 why are there 7 phase polls created? Or is 7 the default in the engine, and angl is just setting 4 of those?

Seems like the polls get updated by the callback function every time iteration?

EDIT - Aha the engine defines 7 voices classvar nvoices = 7;

3 Likes

Thanks. This makes sense. I had a long discussion with a friend about how this implementation of an oscillating circle is based on the length of the sample. I forgot about the SC layer exporting polls from engines.

1 Like

So are you running the REPL at the command line on the Norns, and typing “norns.script.load()…” to reload? Right now I’m doing this mix of VS code, rsync, and hitting the play button in Maiden that gets pretty old.

I’d love to automate reloading the script on the Norns side and keep the console/REPL up for errors

1 Like