eurorack-blocks icon indicating copy to clipboard operation
eurorack-blocks copied to clipboard

Max integration

Open ohmtech-rdi opened this issue 3 years ago • 0 comments
trafficstars

Our users expressed the need to have different language integrations, and Max is the most used of them.

Overview

Max, also known as Max/MSP/Jitter, is a visual programming language for music and multimedia developed and maintained by the software company Cycling '74.

Epic

Avery has an idea of a new Eurorack module. They want to develop it in Max gen~, because they are familiar with it, and they can leverage quickly the big standard library of dsp blocks available at their mouse clicks.

They started to draw the layout of the module on a piece of paper and a quick schematic of the audio flow. They use the GUI editor to make a first sketch of their idea and visually create the dsp code.

They iterate over the sketch by modifying the GUI to add or remove a control, and then update the Max patch accordingly, or the other way around.

State of Art

An implementation exists already for the Daisy platform, in the form of oopsy.

However this implementation does not fit our needs:

  • It relies on the fact that the hardware is available before the software is produced, which is not our case (the hardware is produced very late in our workflow),
  • It focuses solely on the Daisy platform, while we need to provide support for both the Daisy platform and our simulator running in VCV Rack.

However, oopsy is a working implementation of gen~ targetted at the Daisy platform, and the learnings from it are important for our project, and have been collected in this paper.

Licensing

While our project has its own very permissive open-source license, the code exported by gen~ has a different license, and this needs to be very clear for the end-user, since the Eurorack-blocks abstracts so many things away, a user might not see they are violating a license, especially if they intend to sell their Eurorack-blocks module, which we will facilitate in the future. For enthousiast hobbyists, that shouldn't be a problem though.

At least we should:

  • Make a clear statement about it on the front page of our repository,
  • Have a way to make sure that companies above $200k of revenue know that they need to pay a fee, and that in any case, all users with a commercial usage will contact [email protected].

Workflow

Starting up

One maybe conceptual issue is that Max users probably expect their patch to be the "center" of what they do. In that sense, they would expect some template patches as starting points for their module. The only problem with this approach is that we need the erbb and erbui file to get started, and the erbb references the Max patch (rather than the other way around). Changing the way this work would probably increase the amount of work needed, as everything would need to be done the "reversed way", and this could have severe impact on the ecosystem of tools we are trying to bring (such as the GUI editor).

So for now, we will consider the erbb meta-build-system file to be the "center". Only when we will know in practice which impact would be to have the Max patch at the "center" of everything, and a proper long-term strategy for it, then we would revise the current strategy.

Users will first call erbb max init which will create a skeleton of a max patch to start with, with:

  • An embedded gen~ patch,
  • The 1 audio input and output already mapped out in this gen~ patch,
    • Configured as a bypass by default,
  • Saving the Max patch shall trigger a gen~ code generation, using a savebang (see error early), and the generation (configure + build) of the simulator
  • Unlike oopsy, the patch won't be able to either build or install the firmware. This will be done using the erbb command.

Package

The Max package will contain:

  • The gen code + build patch to import as a bpatcher
  • The associated javascript code
  • Some gendsp functions which mimics what we have in the C++ API (button pressed, held, released, LED animation system, etc.)
  • Some help files

Using a Max package has the advantage to be a search path for the javascript code we have, so that the ERB project stays clean and upgrading the repository updates as well all the user patches accordingly.

Name mapping

Thanks to its recursive structure, we should only consider a flat name space, so that name mapping should be direct.

Compound Types

Since param names should contains only characters that matches C++ identifiers, we can't use dot notation to refer to a property in the compound, so we will use a _ instead.

We can't use an attribute for the history, as this would give the same value.

Not sure if there are really other nice solutions. It seems possible to use buffer or data for those types, or the value can be a vec, but that doesn't seem to be convenient at all. We need to assess this. A vector could be used as well for some reason, and that doesn't interfere with our "underscore notation".

Computed properties

Some controls have additional computed properties. For example a button value is the raw signal value, including electrical bouncing. But we have algos to filter that, and convenient interface like pressed () or held () on the C++ side.

~~Made this accessible to Max with the same technic than the one above: replace dot notation with "underscore notation".~~

Make a gendsp function for that.

LED animation system

Some controls like LED can animate the value they own. For example a LED has some functions like blink, pulse etc. with some arguments about timings.

~~Do history led_blink (using again "underscore notation"). Not sure how to pass arguments though.~~

Make a gendsp function for that.

Audio samples

Internal data is still accessible in generated code, with a nice setter to do the work. We just need to implement genlib_data_setbuffer

Daisy Port

The gen_dsp library doesn't compile without modifications. We have our own copy of gen_dsp and use it instead of the provided one.

Oopsy removed a lot of thing, and it seems that ARM C7 DSP functions are not used, and it's unclear why.

Memory management

Ideally the generated code should be portable for both the simulator and the embedded daisy platform.

However "allocating" big portions of memory, such as delay lines, are going to be handled differently on both platforms:

  • On the desktop dynamic allocation can be used,
  • On daisy the portion of memory needs to have a special section attribute to be placed in the SDRAM section.

Oopsy implemented an heuristic which is similar to the one we implemented on the allocate-ram-auto branch. We used this allocation strategy for FAUST, and we will reuse it for Max.

Also Oopsy is putting the module state in the (most likely) SRAM. Since they already parse the C++ code, it's unclear why they didn't go a bit further and allow to have the State on the stack to put it in DTCMRAM. After all this memory is twice as fast as the AXI SRAM, and all filters history are stored in it.

Tasks

  • [x] Make a PoC manually
    • [x] Audio inputs/outputs
    • [x] Input parameters
    • [x] Output parameters
    • [x] Parameter scaling
    • [x] Name mapping
    • [x] Compound types
    • [x] Assess if compound types can use vectors somehow: No
    • [ ] Computed properties (gendsp in code in Max package)
    • [ ] Animations (gendsp in code in Max package)
    • [x] Sound files
    • [x] Daisy port
    • [x] Build rule when gen generated code changed
    • [x] Skeleton project generation
    • [x] Memory management
    • [x] Build simulator from Max
  • [x] Test
  • [ ] Figure out why Oopsy is not using DTCMRAM for State
  • [ ] Figure out why Oopsy is not using the C7 ARM math
  • [ ] Figure out why Oopsy is using out for output parameters rather than the more practical history (as seen in their paper)
  • [x] Make Erbb Max generator
  • [x] Make Erbui Max generator
  • [x] Add sample code Reverb
  • [ ] Add sample code Drum
  • [x] Update documentation
  • [x] Integrate license

ohmtech-rdi avatar Jan 30 '22 20:01 ohmtech-rdi