Hi, I was wondering how straightforward it would be to use incoming OSC messages to set parameter values within scripts? I’m interested in using data from software such as Posenet, which spits out an array of skeletal joint coordinates. So if these values were scaled and ordered as OSC in say Max, then mapped to a Norns script.

this is Posenet;

https://www.tensorflow.org/lite/models/pose_estimation/overview

thanks for the help!

1 Like

You could add an OSC handler to the script you wanted to control, and modify the params within the handler function.

There’s a tutorial on OSC & Norns scripting in this:

1 Like

With param settings, what would be the best way to change the controlspec for param A based on the selection made in param B?

Updating the local declaration of param A’s controlspec (which was used to initialise param A), in the action function of param B doesn’t do anything. Is it possible to set param A’s controlspec directly in the param set (i.e. overwrite it) from within param B’s action function?

can you give an example of what you’re trying to do (so i can see the why) and perhaps i can suggest an approach?

the param system is not designed to be dynamic like this. what i’d generally suggest is having multiple params with different control specs, and then a selector option param. you could conditionally hide/show some params.

but also, i might suggest just writing custom lua code and not using the param system. but again, i’d need to hear more about what you’re trying to do.

fundamentally the param system is meant to simplify your code. if the way you use params ends up complicating the code, it’s probably better to try a different way?

Yes, I’ve tweaked the Beets script to work with multi-bar loops. At the moment it accepts single bar loops and there are param for beat_start and beat_end in the range 0 - 7 because there are 8 per beats per bar.

I have added another param, bars_per_loop
If bars_per_loop is set to 2, the range of beat_start and beat_end should update to be 0 - 15.

1 Like

yes, like this:

ControlSpec = require 'controlspec'

-- param 'A' just prints its mapped value
params:add { type='control', id='A', name='A',
	     controlspec = ControlSpec.new(0, 100, 'lin', 0, 1, ''),
	     action = function(value) print('A: '..value) end}

----------------------
----- warning: bad code smell here
-- memoize raw 'A' param structure to avoid excessive lookups
local pA = params:lookup_param('A')
-- hack the maxval of the existing controlspec.
function change_a_max(newmax)
   print('setting new max value: ' .. newmax)
   pA.controlspec.maxval = newmax
end

-- param 'B' action changes control spec of param A
params:add { type='control', id='B', name='B', controlspec = ControlSpec.new(0, 100),
	     action = function(value) change_a_max(value) end}
---------------------

params:set('A', 75) -- prints '75'
params:set('B', 15)

params:set('A', 75) -- prints '15' (clamped)

but like @tehn, i don’t recommend it. i imagine you will run into edge cases when loading param sets from disk and stuff; it’s complex, &c.

i would consider whether you really need the “bars” abstraction (what about just having a sequence with a count of beats?), and also consider just constraining the values of beat_start and beat_end where they are used (e.g. within in the action functions.)

(as an aside, i’d recommend using 1-based indexing in lua programs, since it is the native paradigm and mixing them in the same codebase leads to confusion. )

Thanks, that may come in useful. I would say this is a fairly common requirement in web development, which seems like quite a good analogy, UI-wise. That is, the ability to update the same data via a series of different UI components. The one-way data flow you get with modern js frameworks, calling actions through dispatchers, is a good solution for that. Perhaps unnecessarily complex here.

I think the dispatchers abstraction doesn’t really add much value for norns scripts, but the “have a single source of truth for data, and subscribe to its changes” advice has stuck around for decades for good reason. params, despite some small limitations, is a great solution for that in my norns coding experience (saving/loading and MIDI-mappability are awesome things to get for free, too)

2 Likes

That abstraction uses a data store, which is a single source of truth, so I don’t see a contradiction there. The topic I find interesting in this case is having the ability to update the data store from different UI components, and how that might be done without a ‘code smell’ as put in the example.
It’s encouraging that having coded for Norns you haven’t come across much of a need for that. This example was the first thing I have looked at in Norns coding and Lua. The difficulty may have been principally because I was adapting an existing script and didn’t want to reinvent it too much.
All the same I think the case of dependencies between settings is an interesting topic and worth discussing. It’s not an indictment of the current params system and doesn’t cancel out any of its virtues.

the ‘bad code smell’ here is breaking into the “private” data structure underlying a parameter that has already been :added to the default paramset, making it part of the persistent storage for the script, target of midi-learn, etc.

well, that is fine, but there is no concept of parameter dependencies in a paramset, so there is no particular guarantee that e.g. B will be loaded before A. (in fact, in the example it will be the other way around, so surprising things could happen.)

guess i’m just saying that i would be careful about making the UI component configurations part of the same data store as their targets, at present. this doesn’t mean you can’t leverage control and/or controlspec outside of paramset.

maybe the easiest solution is to just formally guarantee the present behavior: the order in which parameters are :added to a paramset, is the order in which they will update when the paramset is banged. it’s the most basic kind of dependency management but maybe it’s enough.

in any case i’d be more comfortable recommending this if we added ParamSet:set_range(index) to the API in addition to get_range. then we would at least have a shot at managing the edge cases.

if there is desire for something like Flux pattern:
store -> events/queries -> views -> actions -> dispatcher -> callbacks -> store…

then i agree that it seems like asking for more layers on top of paramset and parameter actions.

The great benefit of a flux type approach is the data reactivity. So if you update the data model, from wherever, the UI renders the required updates without imperative programming. That solves these kinds of edge cases because as long as values are initialised with reasonable defaults, then after loading or user input, updates are handled appropriately.
To be clear I’m not espousing a full-blown approach like that in this context because of the overhead. Just as importantly I think it should be easy from a community perspective for people to get into writing scripts. I’m sure you have thought long and hard about these various options and getting the right compromise.

Using event listeners might be a simple way of accessing some benefits of the reactive approach. (Listening to model changes, not to specific user inputs).

But I’m just thinking out loud here, I will stop commenting on this until I know the system a bit better.

having trouble with incrementing param values

I have the following - but… my param menu item increments 50 ms for each turn of the encoder.

  local delay_time_spec = controlspec.new(0.1, 5000, "lin", 1, 300, "ms") 
  delay_time_spec.default = 1000
  params:add {
    type="control",id="delay_time_spec",name="DelayTime",controlspec=delay_time_spec, formatter=Formatters.round(1),
    action=function(value) engine.set("Delay.DelayTime", value) end
  }

The step for the controlspec seems to be ignored?

I tried also adding a formatter - but I’m not really certain how that should be used.

What I am doing wrong?

step is the output quantum

i gather that at some point you are calling something like

params:delta("DelayTime", 1)

the delta method i hard-coded to incremement in steps of 1/100 of the range (maxval - minval), times the increment.

but the increment passed to delta doesn’t have to be an integer, so you can work around this in scripts by scaling the input to delta; unfortunately that’s of no use in the params menu.

i mentioned this a bit here, it would be nice to have an input quantum:

opend GH issue.

2 Likes

Aha! Thanks for the detailed response!

This is also crow related (although I guess a similar situation can occur when using a MIDI to CV adapter as well), but I think this is the best topic for this question.

I was wondering for those who have written something that outputs pitch/voltages for modular/eurorack how you handle the different types of CV inputs on modules?

I’m mainly wondering about the difference between modules that only take a positive voltage as pitch input vs modules that support both positive and negative voltage.

I was thinking of putting together a super basic norns + lua tutorial yesterday, which led me to another, probably more interesting and useful idea…a game of exquisite corpse in the scripting realm :slight_smile:

The basic idea is that I (or someone) would create a basic tutorial to get started (could be video or written), and then someone picks up where I left off and keeps building the script, turning their work into a second tutorial, and so on and so forth. Whatever I explore and develop in my initial tutorial is by no means set in stone; future participants have the freedom to take the script in a completely different direction.

What I find potentially interesting about the idea is that anyone can participate, and each successive pass doesn’t necessarily have to push the script into further complexity (although it could do that, too). So long as each one sheds light on something new, that’s cool.

Does that sound fun to anyone else? What would we end up with?!

9 Likes

that sounds very fun to me

could be a live streaming thing

2 Likes

Totally! Live streaming scares the shit out of me, personally, but that shouldn’t stop anyone from going that route to share their work/tutorial :slight_smile:

EDIT — I’ll try to record a first installment today or tomorrow, and then I’ll create a (wiki?) thread with a sign-up sheet.

4 Likes

What’s your git workflow?

I’ve been connected to smb, then clone my repo in /dust/code/mything. This seems to be the best way? Though I don’t really like having my git stuff in there that I wouldn’t have bundled with the actual release. So maybe I should have this be /dust/code/mythingdev and then once mything is in released and in maiden don’t touch that at all?

soooo

lets say i’m weirdly curious about how to visualize the two orbiting “tape” leds from the ob-4 for an interface

how might i script something that simple in lua?

2 Likes