the upcoming plugin framework

215 views
Skip to first unread message

Brian@O|D

unread,
Nov 6, 2016, 9:14:07 AM11/6/16
to odev...@googlegroups.com
Before I say anything else, please let me empasize that I will NOT let the long-term viability of the ER-301 rest simply on its plugin framework.  It is just a bonus and it is my intention that the ER-301 should contain enough O|D developed value to justify its price without a single 3rd-party or user plugin ever being developed.

The earliest an official plugin framework will be available is early(-ish) next year because there are one or two deep architectural decisions that need to be finalized still.  For example, how to deal with loops in the processing network (which is currently constrained to be a directed acyclic graph, DAG).  Rather than make the whole code tree open-source, I plan on creating a plugin framework at two levels: one at the C++ level and another at the Lua level.  Just like VST and similar, a plugin developer should be able to add functionality without having to know about how I've implemented low-level OS layer and so on.  The current architecture has been designed so that the C++ layer exposes basic DSP building blocks (add 2 or more signals, buffer playhead, interpolated delay line, and so on) and GUI widgets (ListBox, SampleDisplay, Label, Fader, etc.) while the Lua layer is where you connect these C++ blocks together into a processing network to create a unit and then describe its UI in terms of the available widgets. 

For example, here is the Lua source code for the Variable Delay Unit (an interpolating delay line):

local Class = require "Class"
local Unit = require "Unit"
local Fader = require "UnitControls.Fader"
local GainBias = require "UnitControls.GainBias"
local ply = app.SECTION_PLY

local VariableDelayUnit = Class{}
VariableDelayUnit:include(Unit)

function VariableDelayUnit:init(name,depth)
 
Unit.init(self,name or "Variable Delay","VD",depth)
end

function VariableDelayUnit:loadGraph(pUnit,channelCount)
 
Unit.loadGraph(self, pUnit, channelCount)
 
if channelCount==2 then
   
self:loadStereoGraph(pUnit)
 
else
   
self:loadMonoGraph(pUnit)
 
end
end

function VariableDelayUnit:loadMonoGraph(pUnit)
 
-- create objects
 
local delay = self:createObject("VariableDelay","delay",1.0)
 
local secs = self:createObject("GainBias","secs")
 
local secsRange = self:createObject("MinMax","secsRange")
 
local xfade = self:createObject("CrossFade","xfade")
 
local fader = self:createObject("GainBias","fader")
 
local faderRange = self:createObject("MinMax","faderRange")
 
local feedback = self:createObject("ConstantOffset","feedback")
  feedback
:setClampInDecibels(-35.9)

 
-- connect objects
  connect
(delay,"Out",xfade,"A")
  connect
(secs,"Out",delay,"Delay")
  connect
(secs,"Out",secsRange,"In")
  connect
(fader,"Out",xfade,"Fade")
  connect
(fader,"Out",faderRange,"In")
  connect
(feedback,"Out",delay,"Feedback")
  connect
(pUnit,"In1",xfade,"B")
  connect
(pUnit,"In1",delay,"In")
  connect
(xfade,"Out",pUnit,"Out1")

 
self:addBranch("Delay",secs,"In")
 
self:addBranch("Wet/Dry",fader,"In")
end

function VariableDelayUnit:loadStereoGraph(pUnit)
 
-- create objects
 
local delayL = self:createObject("VariableDelay","delayL",1.0)
 
local secsL = self:createObject("GainBias","secsL")
 
local secsLRange = self:createObject("MinMax","secsLRange")

 
local delayR = self:createObject("VariableDelay","delayR",1.0)
 
local secsR = self:createObject("GainBias","secsR")
 
local secsRRange = self:createObject("MinMax","secsRRange")

 
local xfade = self:createObject("StereoCrossFade","xfade")
 
local fader = self:createObject("GainBias","fader")
 
local faderRange = self:createObject("MinMax","faderRange")
 
local feedback = self:createObject("ConstantOffset","feedback")
  feedback
:setClampInDecibels(-35.9)

 
-- connect objects
  connect
(delayL,"Out",xfade,"Left A")
  connect
(secsL,"Out",delayL,"Delay")
  connect
(secsL,"Out",secsLRange,"In")
  connect
(delayR,"Out",xfade,"Right A")
  connect
(secsR,"Out",delayR,"Delay")
  connect
(secsR,"Out",secsRRange,"In")
  connect
(fader,"Out",xfade,"Fade")
  connect
(fader,"Out",faderRange,"In")
  connect
(feedback,"Out",delayL,"Feedback")
  connect
(feedback,"Out",delayR,"Feedback")

  connect
(pUnit,"In1",xfade,"Left B")
  connect
(pUnit,"In1",delayL,"In")

  connect
(pUnit,"In2",xfade,"Right B")
  connect
(pUnit,"In2",delayR,"In")

  connect
(xfade,"Left Out",pUnit,"Out1")
  connect
(xfade,"Right Out",pUnit,"Out2")

  -- specify branches
 
self:addBranch("Left Delay",secsL,"In")
 
self:addBranch("Right Delay",secsR,"In")
 
self:addBranch("Wet/Dry",fader,"In")
end

function VariableDelayUnit:loadInterface(pDisplay,hSection)
 
Unit.loadInterface(self,pDisplay,hSection)
 
self:defineViews()
end

function VariableDelayUnit:defineViews()
 
local control
 
local objects = self.objects
 
local view = self:createView("controls")

 
if self.channelCount==2 then
    control
= GainBias("Left Delay", "left", objects.secsL, objects.secsLRange)
    control
:setBiasMap(app.unitSecs,"unit")
   
self:addControl(view,control)

    control
= GainBias("Right Delay", "right", objects.secsR, objects.secsRRange)
    control
:setBiasMap(app.unitSecs,"unit")
   
self:addControl(view,control)
 
else
    control
= GainBias("Delay", "delay", objects.secs, objects.secsRange)
    control
:setBiasMap(app.unitSecs,"unit")
   
self:addControl(view,control)
 
end

  control
= Fader("Feedback", "fdbk", objects.feedback,"Offset")
  control
:setMap(app.unitDecibels,"feedback")
  control
:setTextBelow(-35.9,"-inf dB")
 
self:addControl(view,control)

  control
= GainBias("Wet/Dry", "wet", objects.fader,objects.faderRange)
  control
:setBiasMap(app.unitNone,"unit")
 
self:addControl(view,control)
end


return VariableDelayUnit


Nik L

unread,
Jan 5, 2017, 10:32:18 AM1/5/17
to odevices
This looks great!

Do you have any idea yet about how C++ compilation will work? I'm assuming it will be using dynamic linking rather than having to perform a firmware update (as we won't have access to all the source code). I've never compiled anything for Arm before, what sort of toolchain will we need?

Brian@O|D

unread,
Jan 9, 2017, 2:05:27 AM1/9/17
to odevices
I expect the toolchain for the C++ part of the plugin framework will just be gcc-arm-none-eabi.  I still haven't worked out the linking details yet but it will be either dynamic linking or jump tables.

ngwese

unread,
Feb 12, 2017, 3:57:50 PM2/12/17
to odevices
I realize plugin support is lower on the priority list and that is understandable - I do look forward to it coming eventually. The functionality you appear to have under the hood already looks quite nice.
Reply all
Reply to author
Forward
0 new messages