Oh neat. It seems like the table for a connected arc persists across disconnects but when a device is removed, the key a.device is nil.

1 Like

I found a bug in how engines are loaded while debugging the Gemini script.

Given a script with an engine is loaded successfully, loading a different script that has an error on the Lua side (in this case an initialization ordering error) causes the previous engine to be loaded.

This is reproducible by loading the FM7 library, followed by loading this commit from the Gemini library, which will throw an exception when an Arc is connected. The third step is to apply my diff to fix the initial exception via Maiden, save and load the script. This will cause SuperCollider to throw an exception expressing a missing engine from Gemini. This is followed by something (perhaps this is the root cause?) loading the last successful engine into this script, exposing all parameters and polls then initializing the “failed” script a second time. Rebooting the device “fixed” this bug. Is it worth an issue on github to hunt this down?

Yes. It sounds like something in the script.lua domain

1 Like

Good evening!

I lost about 30 minutes feeling like a crazy person because I was editing a library that is included in the top level script, then loading the script to the device through Maiden. This did not include the changes I made to the library, even though I was looking right at the code. IIRC there is some kind of caching thing when using the Lua require 'foo' syntax. Is that correct?

Is there a way to develop libraries on the device that does not require rebooting the device to see changes?

Yeah, require caches the file. You can use include('foo') instead. I asked the exact same thing very recently so seems to be a common question

1 Like

require's caching behavior is a lua thing, not a norns thing. we use it in core libs, but not for any great reason

we added include to norns, which doesn’t cache and searches muliple locations starting with the path to the current script.
https://monome.org/docs/norns/script-reference/#using-libraries

2 Likes

Encoder 2 in a FM7 broke at a seemingly random moment. I have the callback firing only when a key toggle is true, which seems to continue to work (when the toggle is false I see no exceptions) but when the toggle is true I get the following exception

stack traceback:

/home/we/norns/lua/core/encoders.lua:57: in function 'core/encoders.process'

lua: /home/we/norns/lua/core/encoders.lua:57: attempt to call a number value (field 'callback')

I noticed this about 3 minutes into a live session I began recording with the internal TAPE feature. Prior to recording, the encoder worked as expected. The encoder changes a parameter (the c:m ratio in this case) so I checked the system parameter screen and I could continue to change the value from there. I haven’t tried to reproduce this yet. Reloading the script “fixed” this state.

update: I can reproduce this. Each time the record action of the TAPE feature is called, my encoder function breaks in the same way. Details on Github.

1 Like

How does norns do GPIO? I’ve looked through gpio.c, and it looks like the knobs show up as an input device. How do they end up in /dev/input?

there’s an overlay that defines the GPIOs

OK, thanks. Looks like I’m in a bit over my head! :sweat_smile:

yeah, it’s not a two-way binding. when you load a script, the script loading module checks if engine.name is defined. if it is, it loads that engine. when the engine is done loading, low-level lua callback is fired and the script’s init is run.

when i made the low-level stuff i thought a single lua script might want to switch engines. but when the script management stuff was built up, the assumption was made that this will not happen. it is one of many undocumented assumptions that you will run into if you start to deeply interrogate the “middle” layers of the lua system. (and to be clear, i’m glad you are doing this!)

but sure, engine.load(name) method should update engine.name field, if it disagrees with the argument.

the .free method for engine resources should free all scsynth resources and stop all sound. if it doesnt, it’s an error. i just quickly tested this with TestSine and seems fine to me.

my test:

new test engine for switching:

Engine_TestNoise.sc

// CroneEngine_TestNoise
// variante of TestSine, for switching

// Inherit methods from CroneEngine
Engine_TestNoise : CroneEngine {
	var <synth;

	*new { arg context, doneCallback;
		^super.new(context, doneCallback);
	}

	alloc {
		synth = {
			arg out, hz=220, amp=0.5, amplag=0.02, hzlag=0.01;
			var amp_, hz_;
			amp_ = Lag.ar(K2A.ar(amp), amplag);
			hz_ = Lag.ar(K2A.ar(hz), hzlag);
			Out.ar(out, (LPF.ar(WhiteNoise.ar, hz_) * amp_).dup);
		}.play(args: [\out, context.out_b], target: context.xg);
		this.addCommand("hz", "f", { arg msg;
			synth.set(\hz, msg[1]);
		});

		this.addCommand("amp", "f", { arg msg;
			synth.set(\amp, msg[1]);
		});
	}
	free {
		synth.free;
	}
}

test script:
switch_engines.lua

function init()
end

function key(n,z)
   if z == 1 then
      if n == 2 then
	 engine.load('TestSine')
      end
      if n == 3 then
	 engine.load('TestNoise')
      end
   end
end

result: each engine correctly frees its single running Synth when the other engine is loaded. (the mechanism that calls .free on the last engine is on the supercollider side, in CroneEngine iirc.

(if you have softcut running with engine input, or reverb you may of course still hear sound from the old engine that has been captured in a buffer or something?)

(i don’t know about Why. it’s a very old “hello world” that shouldn’t even be availble in any repo and probably has outdated syntax. maybe it was just lying around on your norns’s filesystem.)

that’s an internal method for the PolySub engine, which is polyphonic.


aside: i’m wary of making too many standards for engines. standards are assumptions that change how you can make music. e.g. for some applications, you want “polyphony” to be implicit voice assignment with a single command. for others, you want it to mean a big array of oscillators that are manipulated independently.

i think we should try to preserve some of the freedom of SuperCollider and of digital instrument design in general when thinking about what engines are or should do. engine commands can take any number of arguments including strings, by design. youlu should be able to implement whatever you want in an engine, including toy languages, modular systems, or even things that execute arbitrary sclang code chunks.

not trying to start a big debate on this, BTW. obviously there are benefits to having “classes” of engines with common interface. just want to point out the other side of it.

there is more detailed discussion on this issue, mentioned above - i even did an example of how we could have an abstract class for MidiPolyEngine or whatever to enforce common API.

i’ve also just transferred the issue to the norns repo.

6 Likes

I totally agree with keeping this freedom in engine APIs as I’ve been doing some fairly experimental generative work on Norns where the conventions we’d see in synths and FX don’t apply.

As script and engine writers, we could create sets of conventions, that we “tag” engines with like poly-synth, mono-synth etc, that we treat sort of like a Java interface or whatever, so a script like Orca could know that every engine tagged orca uses .noteOn() and .noteOff() — but I wouldn’t want the Norns dev environment make every engine hold to a format like that.

2 Likes

Another approach I was thinking about is some lua “middleware” that introspects engines for various common commands like hz and noteOn and then dispatches commands to the engine accordingly.

So as a script author you’d just write synth.playNote(midiNoteNum) and the details would get worked out in the middleware. We’d rely on duck typing of checking for available commands rather than testing on engine names, which gets fragile anyway.

1 Like

that makes sense.

for technical reasons i would change the focus a little bit though, and actually do the introspection on the SuperCollider side:

  • SC classes can be defined all over the file system, Crone knows about all of them
  • SC has powerful class introspection methods built in (e.g. can just loop over CroneEngine.allSubClasses)
  • (hm actually there is a bit of a problem in that engine commands are added dynamically at allocation time. but SC has a better chance of coping with that, then lua does.)

problem: communication glue between SC and lua is actually kind of brittle since it’s all hardcoded in C.

potential answer: Crone should perform introspection and just write metadata to disk as a lua file on SC startup.


in fact, it has been suggested in Slack conversations (and i agree) that a potentially complex gordian-knot of metaprogramming tasks can be cut just by accompanying each engine class with an (optional?) handmade lua file containing metadata for that engine.

3 Likes

Should I throw a PR up for this?

So I think there’s a bug somewhere but I’ve not been able to reproduce it.

At some point in testing @neauoire’s engine switching script I ended up with Why continuing to play after switching. Then in additional testing, maiden output for engine loading was being printed twice in REPL. A full reboot got me back to normal again.

I’ll need to do some more testing and see if this pops up again.

ok well, i can indeed get TestSine to be “stuck” in the above example, eventually, by mashing both buttons as fast as possible.

this isn’t super surprising: even though engine.load has always been available in or outside of scripts, it has never been used in this fashion and the focus has been on using 1 script -> 1 engine assumption. @tehn may even be annoyed that i’m encouraging this other usage at all.

bear in mind that these things are asynchronous. for this to be robust i think you would (at least) not want to allow an engine load request to go out before the callback from the previous request

Should I throw a PR up for this?

sure, but i imagine that there might be a few changes needed to make things robust if this is going to be a common usage. this might mean changes to the interactions between Crone and the CroneEngine superclass when loading / freeing engines, which are non-trivial and involve some weird multithreading:

[ https://github.com/monome/norns/blob/master/sc/core/Crone.sc#L104 ]
[ https://github.com/monome/norns/blob/master/sc/core/CroneEngine.sc#L20 ]
[ https://github.com/monome/norns/blob/master/sc/core/CroneEngine.sc#L42 ]

(btw, we are way into “Development” territory so i am likely gonna move these last few posts)

2 Likes

i am totally into facilitating engine switching.

i’m not going to have time to rework the engine/script system immediately, but welcome anyone to dive deep into the core menu/script/engine functionality.

part of the early decision with engines had to do with simplifying. it’s hard to know what a system wants/needs until people use it. so now is the time to start making changes!

6 Likes

I just flashed the norns and started from a fresh image, I was wondering why it comes with so many libraries(some are broken btw). I remember when I first booted, I was kind of confused with all these various things that were present on the device. Would it be good idea to ship the disk image with just nothing on it. Or maybe just the awake library.

I would remove everything else.

2 Likes

@neauoire i’ll get a new image up on the next update (soon)


we’ve been discussing a much more ambitious change to the engines system, namely supporting things that are not sc (pd, custom dsp, etc). i made some quick and likely confusing notes.

needless to say there is some substantial complication in facilitating swapping of engines, if they are extended to mean other dsp applications. we may need new terminology— ie, swapping engines within an environment like sc is no big deal (if the scaffolding is created for such) but switching sc to pd to custom dsp is a messier endeavor.

also, keep in mind that most of the well-used engines have an accompanying lua file which describes the param list. dynamic param list management is another consideration for all of this.

5 Likes

@neauoire, et al:

so, seems to me that the bug is in fact on supercollider side, the callback sequence is just not smart enough to handle the edge case where you ask it to load two engines “at once.”

fixing this on SC side will entail some work and maybe some basic architectural changes (which are probably overdue since the norns 2.0 rewrite obviated much of the SC-side complexity.) but a robust workaround on lua side is straightforward: wait until engine load is complete before initiating a new one.

this gist modifies the script i posted above, and works for me. k2 and k3 switch between 2 engines, and enc2 toggles in and out of ‘autoswitch’ where the two engines are switched as rapidly as possible. (sounds horrible btw)

1 Like