Getting Started with the ER-301 Middle Layer

I’m going to leave this as an unlisted YouTube video since I’m still finding my footing with the middle layer. I’m sure I probably messed up some terminology and blunder a little. Forgive me for that; I’m still learning. But I wanted to put something together for the community to help others get started with one of the biggest 0.3 features. I know it was a little overwhelming for me - just figuring out where to even start.

In this video, we go through creating a ring modulator effect unit in the middle layer, soup to nuts. It’s a long one. But, if you follow along you should end up with a working unit, a functional development environment, and hopefully some inspiration and confidence to get started with a middle layer project! :sunglasses:

Here are links to everything I think I referenced in the video that aren’t already in Brian’s main middle layer info post here on the forum.


I wish I had kept up with coding after GW, Q and Visual Basic in the early 90’s!

1 Like

I bet you’d be successful with this. LUA seems like a very friendly language, and you can use the copy/paste/tweak approach I used in this video - Brian has provided a lot of example code. For me, understanding the ER-301’s large (and growing) SDK will be the bigger challenge, I think.

Wish I’d kept up after Basic in the early 80’s!!! I can follow some kind of syntax there, but I’m realising it’s pretty much beyond me, without a significant amount of currently unavailble time spent on getting a handle on coding!

I can appreciate that. Guess I’m used to looking at and writing code, and a new language with a “friendly” syntax might be pretty relative. Well, I assume that eventually a lot of 3rd party units will get shared, so it’s still all upside. :wink:

1 Like

Oh, I’m very much appreciating the sharing, a getting a feel for the quality of the language…I just don’t think I’ll be writing any units in the short term! I’ll definitely be trying any that the community feel to share ‘tho! :grinning:


fascinating Joe, thanks for this! I’m not a coder but this learning curve is very cool

@Joe, this is brilliant - thank you very much!!

In 45 mins I’ve gone from having a reasonable, but still fairly vague, idea about how it all works to seeing exactly what needs to be done to build a bespoke unit, you have definitely saved me loads of time working all this out.

Of course there are many miles to go to get fully acquainted with the available DSP functions, but there’s time yet hey :smiley:

Great video @Joe :heart_eyes_cat:

Just some comments as I was watching:

The procedure for creating DSP objects might seem rather verbose:

local f0 = self:createObject("GainBias","f0")

because you might think that you could just create an object like this:

local f0 = app.GainBias("f0")

and you can create objects like this also. However, they won’t be stored in the Unit’s object table if you do this and hence will not be accessible outside of this method`s scope. The 2nd argument is the name used to store the object in the objects table so that you can access it later (in this case as self.objects.f0) especially in onLoadViews or onLoadMenu or serialize or deserialize methods.

It might be better to collect all of the stereo-ification code into one chunk so that you don’t need to create objects needed only for the stereo case when the unit has only one channel.

After some thought, it seems I might have misnamed the Parameter class. It should probably be call Variable because they can be both inputs and outputs whereas a parameter might sound more like an input. For example, MinMax class has Min, Max and Center parameters (in addition to its In inlet). However, these are values calculated and updated dynamically based on the signal at the MinMax input. So in this case Min, Max and Center are parameters that you would get the values of, not set. The TapTempo object is another example where it is measuring the period or frequency of the incoming pulse train and providing that as readable parameters (in this case, Base Period, Derived Period, Base Frequency, and Derived Frequency}

The purpose of the GainBias object (f0) is to literally to determine exactly what the gain and bias values in the unit control`s sub display are affecting. This object does that actual gain and bias calculation. The Unit.ViewControl.GainBias, instantiated later in onLoadViews, wraps this GainBias (or similar) object along with the branch (what sub-chain should I dive into when the user presses S1?) and the range object (how do I draw the final modulation value in the unit control?).

The reasons for existence of init.lua:

  • Provide the necessary meta-data that the unit insert menu requires (name of the unit, category, keywords) because it would be very wasteful to have to instantiate each and every unit just to get its description.
  • You can also provide aliases in case you want to replace a unit with a new implementation but still have old presets and quicksaves still work but with the newly named unit.
  • Combine multiple units into one library of units for distribution as one entity.

So, the information that you provide in the table being returned at the bottom of the ```init.lua`` code is actually a description of your library which would usually contain more than one unit.

You can create new unit categories at will. The ER-301 will just add the categories to the unit insert menu as it encounters them during the menu construction process.


Thanks for reviewing this and providing all the additional insights, @odevices! These are very helpful! :heart_eyes:

Hmm, I actually tried that and it did not seem to work. Maybe I did something wrong - will try again. You are saying I would just need to do something like this:


local units = {
    {category="Modulation Effects"},
    {title="Ring Modulator",moduleName="Ringmod",keywords="pitch, modulate"},

and it would create a new unit category called “Modulation Effects” on the insert menu?

1 Like

That is correct. I just tried it again just to make sure I wasn’t dreaming but it worked as expected. I guess make sure that you are actually copying over the modified init.lua file and also make sure that you exit out of the unit insert menu and re-open at least once whenever you make a change to your unit library’s init.lua (assuming that you were in the insert menu when you popped your card out to copy over changes). Unit libraries are scanned once whenever you mount your SD card or reboot.


It get’s better each day I visit this forum. What a world of possibilities there is with this avant-garde unit.

I guess not a lot of people are working this way?

1 Like

@Joe, any chance you’d be interested and willing to do an update to your “Getting Started with the ER-301 Middle Layer” video? I bet you’d be able to provide even more wisdom, guidance, and useful tidbits with a few years of ER301 Lua patching under your belt now!


Yeah, I might consider doing an update. What few hints Brian has dropped about what he’s been working on all seem to point to the 301’s code level layers. So it might make sense to wait a little longer and see what changes with this in 0.6.