Integrating with Max

Now that we’ve seen how to make a simple module with a knob and some audio inputs and outputs, let’s continue with all that is possible in Eurorack-blocks. There is a lot of different controls you can use on your front panel, and this list will grow! Mapping those controls to your gen patch is quick, and you have very little to know. Actually, you already know most of it.

First, we will have a quick look at erbui, a simple language to describe your Eurorack module front panel. We plan to make a GUI editor (coming soon), but for now you need to use this language. And even if you are using the GUI editor, it might be handy to make quickly some touch-ups.

Second, we will see how to map the inputs and outputs of your gen patch to the inputs and outputs on your module front panel. You already know most of it with the previous tutorial. We just need know to talk about CV out and LEDs for example, but the principle is the same. We will talk also about how to nicely map for example a knob that is supposed to map to a frequency.

Finally, we will see how to use samples into your gen patch. It’s quite simple too, and we have another language for that, erbb, which describes what sources and resources you use and where they are.

Quick Look at Erbui

We will go quickly on this part, as we plan to make a GUI editor (coming soon). With the GUI editor, learning erbui becomes quite superfluous.

erbui is a declarative domain specific language used to describe a module user interface.

It is mostly a collection of control elements, with each control representing a physical piece of hardware on the panel. For example you can have a control for a pot and its knob, a control for a button, but also a control for gate inputs and outputs, CV inputs and outputs, audio inputs and outputs, or even LEDs with either one or multiple colors.

Position and style

Every control has at least a position and optionally a style attributes, for example:

control sync_button Button {
   position 2hp, 75.3mm
   style ck, d6r, black
}

The first argument of the position, 2hp expresses the distance between the left side of the front panel to the center of the control, on the horizontal axis. Similarly, 75.3mm, expresses the distance between the top side of the board to the center of the control, on the vertical axis.

Those distances always come with a unit, like hp, mm or cm. A hp is the Doepfer defined Eurorack HP distance, which defines 1HP = 5.08mm.

The arguments of style, ck, d6r, black, define the hardware component to use. Here, ck, d6r, black denotes the C&K D6 series, variant R (round button), in black color.

The style is dependent on the control type, here Button. For each control type, you can visit its control reference, which lists the available styles. For example here is the control reference for Button.

Label

../_images/ui-label.png

An optional label can be added to the control. This will be shown on the front panel, for example:

control freq_pot Pot {
   position 6hp, 34mm
   style rogan, 6ps
   label "FREQ"
}

This will place the label automatically below the pot, while taking into account here the size of the knob attached to the pot.

The default label positioning is dependent on the parent control. For example for the jack connectors, the default placement label placement will be done on the top of the control.

../_images/ui-label-positioning.png

The label positioning can be customized, and even multiple labels can be associated to the same control, for example:

control mute_hp Switch {
   position 6hp, 62mm
   style on_on
   rotation 90°ccw
   label "MUTE" { positioning left }
   label "HP" { positioning right }
}

That’s it. This is mostly what you need to know. If you are adventurous, you may want to read the Erbui grammar!

Mapping Names

As you may have seen, the main concept to map a gen patch input or output to a front panel control is to give them the same name. Depending on the type of the control, there are slightly different boxes to use in your gen patcher. Let’s go through them now:

Audio inputs

AudioIn on your panel is mapped to in <n> in your patcher where <n> is the index of the input, starting from one. The index follow the order of declaration in your Erbui file, so if you have the following file:

control audio_in_left AudioIn {
   position 2hp, 111mm
   label "IN L"
}

control audio_in_right AudioIn {
   position 4.66hp, 111mm
   label "IN R"
}

Then in 1 would map to audio_in_left and in 2 to audio_in_right. Note that the control names are just ignored in the gen patcher, just the order counts. Yet, it is still important to have the same number of audio inputs in both files. If this is not the case, the Erbui transpiler will report an error.

One neat thing you can do with audio inputs, is to cascade them. For example using the previous example, you might want your right audio input to take the left audio input, if the right audio input is not connected. You can do this simply using the normalling keyword:

control audio_in_left AudioIn {
   position 2hp, 111mm
   label "IN L"
}

control audio_in_right AudioIn {
   position 4.66hp, 111mm
   normalling audio_in_left
   label "IN R"
}

Audio outputs

AudioOut on your panel is mapped to out <n> in your patcher where <n> is the index of the output, starting from one. The index follow the order of declaration in your Erbui file, so if you have the following file:

control audio_out_left AudioOut {
   position 7.33hp, 111mm
   label "OUT L"
}

control audio_out_right AudioOut {
   position 10hp, 111mm
   label "OUT R"
}

Then out 1 would map to audio_out_left and out 2 to audio_out_right. Note that the control names are just ignored in the gen patcher, just the order counts. Yet, it is still important to have the same number of audio outputs in both files. If this is not the case, the Erbui transpiler will report an error.

Input controls

CvIn, GateIn, Button, Pot, Switch and Trim on your panel are all mapped to param <name> in your patcher where <name> is the name of the control in your Erbui file. So if you have a param freq box in your gen patch, then you will need a control freq definition in your Erbui file.

The mapping also supports gen param attributes @min and @max.

GateIn and Button represent on or off values that are mapped to the minimum and maximum value of the gen parameter, respectively.

In the case of the Switch, the intermediate value is mapped to the average between the minimum and maximum of the gen parameter.

Output controls

CvOut, GateOut, Led, LedBi and LedRgb on your panel are all mapped to history <name> in your patcher where <name> is the name of the control in your Erbui file.

LedBi and LedRgb are a bit special though, because they are not represented by one scalar value, but 2 for the dichromatic LED (one for each component, usually red and green), and 3 for the Rgb LED (one for each primary colors). In this case you need to address the specific property to set in the control, by appending an underscore _ and the name of the property. For example, if you have the following Erbui description:

control state_led LedBi {
   position 7.33hp, 85mm
}

To set the value of the red portion of the led, you would use history state_led_r and similarly for the green portion, history state_led_g.

Important

When using output controls, make sure that the control name in Erbui doesn’t end with a digit! Just give it another name, or append a _ character.

Jack Detection

AudioIn, CvIn and GateIn supports jack detection by specifying the normalling nothing property:

control clock GateIn {
   position 7.33hp, 85mm
   normalling nothing
}

You can then use param <name>_plugged in your patcher where <name> is the name of the control in your Erbui file. The value will be 1 if and only of a jack is plugged into the physical input.

In the example above, you would have a param clock_plugged box in your gen patch.

Important

Make sure to ignore the values of the input control when it is not plugged, as it is noise.

That’s it, this is all you need to know about how to map your front panel controls to your gen patch. Eurorack-blocks takes care of all the rest!

Mapping Values

Now that we know how to map a name, we need to know how to map the value associated to that name. It’s quite easy indeed.

In Erbui, all values are unipolar, meaning they range from 0 to 1. But you can make them bipolar, so that when the knob is at the middle, it is equivalent to 0. This is only handy when using the simulator, as the knob will be centered by default. You specify a bipolar control using its mode. You typically use this for a trim pot that used as an “attenuverter”:

control feedback_trim Trim {
   mode bipolar
   position 7.33hp, 80.8mm
   label "–/+" { positioning top }
}

On the gen patch side, you just need to specify a minimum and maximum value, and Eurorack-blocks will map it accordingly. The bipolar mode has no effect on that. If your pot is on the most-left side of its angular rotation, the value of a parameter will be the @min value of your gen param box, 0 by default if you didn’t specify it. Similarly, if your pot is on the most-right side of its angular rotation, the value of a parameter will be the @max value of your gen param box, 1 by default if you didn’t specify it.

Though it is usually easier to keep the default minimum and maximum values to 0 and 1 respectively, which allows you to scale the param nicely to accomodate for human perception. For example, to scale a frequency pot, you can use the box expr out1 = 10 * pow (22000/10, in1) which will output a value of 10Hz when the pot is on the most-left side, 22kHz when the pot is on the most-right side, while following a natural perceptual progression for the values in between.

Using Samples

The last point to see is how to use samples in your patch, and Eurorack-blocks makes this task very easy! The samples are defined in the build-system file together with your source code — here just your max patch. You just need to define an audio sample, give it a name and the file path relative to your erbb file.

Important

Adding a sample is done in your .erbb file, not the .erbui file!

module Kick {
   sources {
      file "Kick.erbui"
      file "Kick.maxpat"
   }
   
   resources {
      data kick AudioSample { file "kick.wav" }
   }
}

You just need to add a resources scope and add as many — reasonably though! for a reason we will see below — data declaration as you have samples.

Then you can simply add the sample in your gen patch as a data <name> 0 <channels> box, where, you guessed it, <name> is the name of the data in the Erbb file, so for example data kick 0 1 in the example above. That’s it! A value of 0 seems weird for a data length, but that’s because Eurorack-blocks will change automatically for you, to match the size of your sample.

You might now think, but where are those samples stored? For both the simulator and the firmware, this is stored in the program itself. For your module running in the simulator using VCV Rack, this is not a big issue. The files can be quite large but will run happily. If you put large files though, you will see that building the module will take more time. Though, for the final firmware, things are not so ideal. We have 2 choices here.

Either store the program in the so-called FLASH memory, which is 128K, so there is really not much room. But if you have a few wave tables to store, that might make it.

If you are going passed this point, we can store it in another memory, called the QSPI memory, which has a more ample 8MB to work with. So you can’t really store a full song, but if it’s for drum samples and the like, that should totally work. To use this memory instead you need to add a section qspi in your Erbb file:

module Drum {
   section qspi

   sources {
      file "Drum.erbui"
      file "Drum.maxpat"
   }
   
   resources {
      data kick AudioSample { file "kicks/kick_aba.wav" }
      data kick2 AudioSample { file "kicks/kick_ece.wav" }
      data hat1 AudioSample { file "hihats/hat_01.wav" }
      data hat2 AudioSample { file "hihats/hat_02.wav" }
      data ride AudioSample { file "rides/ride.wav" }
   }
}

Uploading the firmware is then a bit different, but this whole process is explained in great details in “Kick: Samples & Big Programs”.

That’s it! This is all you ever wanted to know about Eurorack-blocks and Max integration. Happy patching 🎉