Middle Layer Adventure - Revolver

So begins the Middle Layer adventure. As discussed in the Middle Layer main thread, I just wanted to get started and see how far I could get with what’s been offered up for examples. Thought I would try to build the round robin style polyphonic sample player that I made a video about in this thread as a stretch goal. But this time, using the middle layer. Thought Revolver made a nice code name.

I like to begin with the end in mind, and so started by trying to add the controls I want to see available on the unit. I was able to add a V/oct control, and a trigger control. Basically by just copying and pasting bits and pieces of code out of the sine oscillator and S&H built in units into the onLoadGraph and onLoadView functions.

It doesn’t do anything right now. Nothing is connected - i assume it’s basically a signal blocker or a null oscillator, however you’d care to describe it. :slight_smile: But I do have the basic view controls that I want. And I only had 2 system crashes trying to get the code right (crash.log is pretty helpful for some clues).

For the next move, I wanted to add some things to the header menu. A way to select, slice, and edit a sample. The idea I had is that you’d select a single sample and it would be used in all four player objects.

It looks like I probably want to be grabbing some code from BasePlayer.lua. But it’s pretty lengthy and I’m not sure what parts I need yet. Also decided this would be a good point to stop and just do a checkpoint. Not sure the best way to share the code. I created a public github repo.

ER-301 Revolver Repo
(We are on commit 756f3b5 as of this post).

First questions. These are probably mostly for @odevices but anyone who is interested in the Middle Layer is invited to this discussion!

  • Is this a reasonable approach to get started?
  • Are the pieces of code I need for the next step likely to be found in BasePlayer.lua?
  • Any other random tips you’d like to throw at me?
  • github repo a reasonable way to share?

This is great @Joe really happy to see you have started this already!

Github perfect for developments like this I think :wink:

1 Like

Thanks for doing this @Joe! I’ll be following this thread with great interest.

1 Like

Excellent, thanks for doing this publicly @Joe. Great learning resource.

1 Like

Thanks for doing this @Joe. I like the way you are showing your thinking as you approach the middle layer for the first time, rather than just documenting a completed project retrospectively.

1 Like

I sort of have to do it this way. I don’t know what I’m doing enough to complete it without some (probably a lot of) coaching from the one guy on the planet who does. I have a fair amount of other development experience, so I know enough to be able to start hacking at this and will try to move it forward on my own. But I wouldn’t assume my approach at any given point is correct, the best way, or even functional.

Hopefully we’ll all learn a lot. I’m just volunteering to be the public guinea pig. :slight_smile:


You’re the Major Tom of the middle layer! :sparkles:

1 Like

Ha! I hope I don’t meet the same fate as Major Tom. :slight_smile:


I’m sure you won’t, was mainly thinking of

”I’m stepping through the door
And I’m floating in the most peculiar way
And the stars look very different today”


(Sorry for the ot, was just inspired by Joe’s daring!)


I am cross-quoting from the other thread here. Been looking at this a bit and I see how you are using this, for example, in the NativeSpeedPlayerUnit and PlayerUnit code. Their LUA files are extremely short and very similar (the difference of args.enableVariableSpeed and the unit names).

So I assume if I did this in my code, I would inherit all of the header menu items, as well as the view controls? Ultimately I think this will contain 4 sample players so I don’t really want 4x duplicates of all of them exposed. If I explicitly include the methods for onLoadViews and onLoadMenus will that overload what it is inherited from the BasePlayer so that I can be specific about what I want?

1 Like

Thinking that calling on the builtins is not going to be the way to go here. A little experimentation proved unsuccessful. However, I did manage to add the sample selection to the header dialogue, and am able to select a sample from the card and get back to the unit view without crashing. This by copiously stealing some code from the Sample Scanner unit and making slight modifications. For now I have just commented out the lines where it actually does anything with the sample, like assign it. The unit still does nothing. :slight_smile:

Detach does not cause a crash. Have not tested slice or edit.

As of this post, commit f942f39.

1 Like

After examining BasePlayer.lua for a while, I decided it was quite complicated and that I would take a short instant gratification detour and see if I could make this unit make some sound. So there is a new branch in the repo called “makesound”. I simply added sine oscillator (code copied from the FMOperator example) with a V/Oct and f0 control (The V/Oct control is the original added above in the master branch).


I really tried not to add the f0 control, but rather just do without it and hard code the base frequency f0 of sinosc to a “C” note:


This didn’t work. There was no output until I added the GainBias control and connected the it up to the sine oscillators fundamental:

local f0 = self:createObject(“GainBias”,“f0”)

Why, I wonder… :slight_smile:

Also, nearly every createObject I have added to the project has necessitated that I add a “require” library up at the top, if it wasn’t already there. The SineOscillator did not. Where does it live?

Edit: Regarding eliminating the f0 control also tried this, which causes a system crash.


And then this was just a total SWAG. Also caused a system crash.


Oops. I totally forgot to say anything about the difference between ports (Inlets and Outlets) and Parameters and Options. Ports come in two flavors, Inlets and Outlets. Signal travels from an Outlet port to any number of Inlet ports at the system sampling rate (48kHz or 96kHz). Parameters are values that can be assigned (tied) to other Parameters or controlled by UI controls. Parameters are updated at the system frame rate (375Hz or 750Hz). Options are like Parameters except they take their values from a given list of valid discrete values (like yes/no or left/both/right).

sinosc:hardSet(“Fundamental”,32.7)  -- won't work

So in your case you were trying to set the value of an Inlet (SineOscillator’s Fundamental inlet) as if it was a Parameter. There are a number of ways to go about doing this. The simplest is to use a Constant object like this:

local freq = self:createObject("Constant","freq")

The Constant object will output the value of the Value parameter at audio rate which is what the SineOscillator’s Fundamental inlet needs.

Parameters can be hardSet or softSet. The former sets the value immediately, while the latter will linearly tween towards the target value over time.

(P.S. Documentation is forthcoming but I’m in the middle of some manufacturing tasks at the moment. :bowing_man: )


I am catching errors in the initial execution of the unit modules (when your unit class is defined) but it looks like I have neglected to catch errors during the instantation (when onLoadGraph and so on are called). Was never necessary until now :wink: I’ll fix that.


Does softSet accept a second optional param for the tween time? Or is it fixed? I see it used in some of the delay units, and it looks like it is used a if a preset was made prior to v0.3.04 when the feedback was made modulatable it converts it to the new schema. Not really sure why it was needed there - I guess to cause an abrupt jump from zero when it’s loaded?

Also, where does app.log write to? A file on the card, or somewhere on the display?

I have encountered the error.log in the libs\appfolder too. So I guess the goal would be for all debugging information relevant to the app (should we call these middle layer patches apps? :slight_smile:) would end up in this file (and also not force a reboot)?

The crash.log seems to append to itself, and assume error.log will too. Is it safe to delete these files and they will just get recreated?

Yay! This works great and the f0 control was successfully removed in commit 67ce03d of branch makesound.

1 Like

No. It’s a fixed length tween at the moment. It’s just there to prevent unseemly noises. You wouldn’t use it to do to automation or modulation. Also, it is not necessary in the onLoadGraph function because the unit hasn’t been inserted into the DSP scheduler yet. You would typically only use it to a make change to a parameter while the unit is running and generating signal.

app.log(…) writes to the UART which I monitor in real-time via one of these.

You are of course free to do file I/O on your own using the builtin Lua io library.

1 Like

Yes, it is safe to delete either of these files.

  • ER-301/crash.log: Trace and error message dumped here when there is a system-wide crash.
  • ER-301/libs/your-library/error.log: Trace and error message dumped here when there is an error instantiating a unit in this library.
1 Like

Now that I have another folder in ER-301/libs, and another init.lua inside that folder, I no longer see the FMOperator or Countdown units from ER-301/libs/testlib. I only see my “Revolver” unit when I go to insert a unit. Can there only be a single init.lua in the directory structure?

Just tried it and works for me…


│      Countdown.lua
│      FMOperator.lua
│      init.lua
│      error.log

Are you using the most recent version of testlib?