Skip to content

change proposals for universal engine support

Anton Hörnquist edited this page Sep 7, 2019 · 19 revisions

How crone things work right now, basically:

C++ based crone process

  • handles bussing of hardware inputs, outputs and monitor signals and signals to and from loaded SuperCollider-based engine
  • implements tape record to disk
  • implements tape playback from disk
  • implements the always-available (yet enabled/disabled upon demand) softcut engine
  • provides softcut voice position via polls:
  • provides coarse ADC and DAC levels for VU, via polls (https://github.com/monome/norns/blob/master/crone/src/MixerClient.cpp#L65

SC based crone process...

  • maintains engine metadata (names of all engines), sent to matron upon request via OSC
  • maintains engine command metadata, sent to matron upon request after an engine has been loaded via OSC
  • maintains engine poll metadata, sent to matron upon request after an engine has been loaded via OSC
  • maintains crone none-engine poll metadata (amp, pitch)
  • handles loading & freeing engines
  • starts and stops registered engine and non-engine (amp, pitch) polls
  • sets up amp envelope used in engines
  • sets up optional pitch envelope possible to use in engines
  • forwards a number of messages originally handled in SC to the C++ based crone.
  • answers /crone/ready when it receives a /ready osc message from matron
  • dictates dsp processing in scsynth server process (well, custom engine subclasses do, but anyway)
  • connects C++ crone and SC crone: https://github.com/monome/norns/blob/master/sc/core/Crone.sc#L82-L88
  • clears some environment variables for jack defaults: https://github.com/monome/norns/blob/master/sc/core/Crone.sc#L42

Architectural change proposals

Suggestions by @antonhornquist when adding support for multiple/arbitrary engine backends is added and clean up things a bit

C++ based crone process

still...

  • handles bussing of hardware inputs, outputs and monitor signals and signals to and from loaded SuperCollider-based engine
  • implements tape record to disk
  • implements tape playback from disk
  • implements the always-available (yet enabled/disabled upon demand) softcut engine
  • implements VU and softcut phase polls

but also...

  • implements higher-resolution amp env polls
  • implements pitch polls

.lua engine metadata files are introduced to

  • maintain engine metadata (names of all engines)
  • maintain engine command metadata
  • maintain engine poll metadata
  • maintain engine backend and configuration settings required to spawn engines
  • possibly: maintain lua utility functions automatically loaded on engine load

complete engine metadata will be read from disk and not sent to matron upon request via OSC

pros: full engine schema always available

con: limits dynamic metadata possible to leverage using OSC

Engines of different kinds / with different backends

  • replace sclang based engines in the current solution.
  • adhere to an osc protocol to handle commands and start/stop of polls, as required
  • answers /engine/ready when it receives a /ready osc message from matron

Matron - or some linux service-kind-of-thing (systemd?)

  • handles loading & freeing engines by providing ways of complete engine setup and teardown on engine load / free based on a number of supported backends
  • connects engine jack clients

Matron

  • forwards the messages for C++ crone backend directly to the C++ process instead of via SC.
  • connects core always-running jack clients(?)

Engine backend ideas

Lua engine metadata requirements

Lua engine metadata is required to include a kind field which determines what engine backend to use. It is also required to contain backend specific configuration information (such as "path to main source file or executable" and any environment specific stuff) in a config field.

In addition to this:

if commands are used the commands field contains a table of commands in the form of: { name="[command_name]", format="[tag]" }, ie. { name="gate", format="ii" }. TODO: here metadata could potentially be extended with display_name, docstring, controlspec, i dunno.

if polls are used the polls field contains a table of polls in the form of: { name="[poll_name]" (, type="[value|data]", periodic=true|false) }, ie. { name="phase_1" }. Defaults for optional fields are type = "value" and periodic=true. TODO: here metadata could potentially be extended with display_name, docstring... i dunno.

SuperCollider engines

kind = "sc"

config required to include source field which should consist of a string designating a path to the main scd file. The path is assumed to be relative to the engine metadata file so it does not matter where the engine folder is put in the file system.

config optionally to include a include_path field which should consist of a string designating a path to a folder containing SC class files or ugens (as above, the path is assumed to be relative to the engine metadata file).

config optionally to include a runtime_directory field which should consist of a string designating a path to a folder which will be set as sclang's runtime directory (as above, the path is assumed to be relative to the engine metadata file). (TODO: not sure on this, whether it's needed, since thisProcess.nowExecutingPath might serve the same purpose...)

sclang to get started as from the command line with the scd file as its argument [file] argument.

if config includes an include_path field a norns specific norns_conf.yaml language configuration file (akin to sclang_conf.yaml) will be created (overwritten, if it already exists) in a temporary folder somewhere, with the include_path expanded to an absolute path added as the sole path in the list of includePaths. sclang will then get started with the -l /path/to/norns_conf.yaml option. TODO: if omitted, write empty norns_conf.yaml for clarity (to not load arbitrary sclang_conf.yaml weirdness)?

ie:

norns_conf.yaml

includePaths:
    -   [/absolute/path/to/engine/include_path]

if config includes a runtime_directory sclang will then get started with the -d [/absolute/path/to/runtime_directory] option. TODO: if omitted, default this to the folder where main scd script resides?

Example (glut):

local NVOICES = 7

local glut = {
  name = "glut",
  commands = {
    { name="gate", format="ii" },
    { name="speed", format="if" },
    { name="jitter", format="if" },
    { name="size", format="if" },
    { name="density", format="if" },
    { name="pitch", format="if" },
    { name="spread", format="if" },
    { name="volume", format="if" },
    { name="envscale", format="if" }
  },
  kind="sc",
  config = {
    source="relative/path/to/engine_glut.scd"
  }
}

glut.polls = {}

for voicenum=1,NVOICES do
  table.insert(glut.polls, { name="phase_"..voicenum })
  table.insert(glut.polls, { name="level_"..voicenum })
end

return glut

after successfully booting and loading engine, required jack connections for the engine stereo ins and outs needs to be set up (this is not handled in SC anymore).

Pd engines

kind = "pd"

config required to include path field which should consist of a string designating a path to the main pd file. The path is assumed to be relative to the engine metadata file so it does not matter where the engine folder is put in the file system.

config optionally to include a include_path field which should consist of a string designating a path to a folder containing SC class files or ugens (as above, the path is assumed to be relative to the engine metadata file).

return {
  name = "testsine_pd",
  commands = {
    { name="amp", format="f" },
    { name="hz", format="f" },
  },
  kind = "pd",
  config = {
    patch="relative/path/to/testsine.pd"
  }
}

Other stuff

Outstanding issues

  • How to handle lua metadata files with engines of the same name gracefully?

Install step / resources

Adopting some kind of install step when ie. adding engines to include custom ugens or classes was discussed on slack. One way to work around this would be to make sclang extension dir dynamic (as in a field in the lua engine metadata file) and set this as part of the process of selecting / booting a new engine.

Challenges / Implications

  • Time for changing scripts (and thus, engines) may be longer - at least if we’re to restart processes all the time.
  • On the interpreted scd engines approach
    • Interpreted sclang performance vs compiled sclang performance (on desktop IMO this is a non-issue but on the rpi it might be wise to do some tests)
    • Interpreted sclang code is a bit harder to debug than class based sc code (we can give some tips here).
    • Migration of existing class based engines.
    • Engine backwards compatibility may get broken, though there are not that many engines yet.
    • CroneGenEngine which generates engines from a SynthDef, needs to be rewritten. I'm already rewriting this, so it's not a big deal. Migration of the few CroneGenEngines engines will be simple. (outline somewhere how a 3.0 cronegenengine may look)
  • Error reporting, Failures, Monitoring, Logging (this is not my area of expertise, especially not on linux)

Links

universal engine things https://github.com/monome/norns/wiki/universal-engine-approach

lua engine metadata details https://gist.github.com/antonhornquist/20253f0489a91a4f3b88497998ca320b

Clone this wiki locally