Hi, this may be useful to someone somewhere.
In a Waldorf Micro Q synth, one has multiple envelope modes:
- ADSR : classical four stage
- ALDSDSR : 7 stage envelope with an additional attack level, breakpoint and double decay
- One Shot : as an ADSR, but the Sustain has become a breakpoint
- 2 looping envelopes
Normally I use the envelope controls to set the values of the envelopes directly and then deal with the ‘special’ stages in an bit of an awkward way, but this time I choose not to: the envelope control is merely used to visualize the varying envelope shapes, not to control it.
That gave me the opportunity to differentiate between the usable controls themselves and their visualizing counterparts.
What are the benefits?
- only showing the controls that matter to the chosen envelope
- rearranging the controls in a sensible way, and providing them with meaningful names
- blue control colors are used to indicate what part of the envelope is looping when holding the keys. No need to memorize which parts of the envelopes are within the looping cycle.
- graphically : what you see is what you get
Here’s how the initial design looks like:
- Slim fader controls are used for time stages, the large ones for level stages.
- Upper right is the graph (visuals only, doesn’t do anything)
- Lower right is the option list to select the desired envelope.
In the case of the micro Q, the following parameter numbers appear in ENV1 (= filter):
- stages 1-7: 199..205. They all have a “showStage” function assigned to them.
- graphics: 4199..4205 except L4 that has 9999 and message type “None” as it is not in use
- filter env mode = 1196 (controlling the real param 196 but that is another story). This has a “setEG” function assigned.
- do remark the graphic elements are exactly 4000 apart from their original controls.
The other 3 envelopes have the same distances between parameters but are 12, 24 and 36 higher in their numbering.
The code for function “showStage” :
function showStage(valueObject,value) ---show EG sustain level into DX7 style EG graph
local message = valueObject:getMessage ()
local paramNum = message:getParameterNumber() -- 202 = Fil, 214 = Amp, 226 = Env3, 238 = Env4
local envNum = math.floor((paramNum-199)/12)+1 -- 1 = Fil.. 4 = Env4
local stageNum = (paramNum+1-199)%12 -- 1 = attack...7 = release
local envMode = parameterMap.get(deviceId, PT_VIRTUAL,1196+(envNum-1)*12) -- 0 = ADSR.. 4 = Loop All
if stageNum%2 == 0 then parameterMap.set (deviceId, PT_VIRTUAL, paramNum+4000, value) -- level controls
else parameterMap.set (deviceId, PT_VIRTUAL, paramNum+4000, 127-value) -- time controls
end
if envMode == 0 and stageNum == 4 then -- ADSR copy sustain to L2
parameterMap.set (deviceId, PT_VIRTUAL, paramNum+4000+2, value)
elseif envMode == 2 and stageNum == 7 then -- one-shot copy release to R3
parameterMap.set (deviceId, PT_VIRTUAL, paramNum+4000-2, 127-value)
parameterMap.set (deviceId, PT_VIRTUAL, paramNum+4000, 127) -- fix R4 to 127
end
end
Two remarks here:
- as the 8 stage graph in the E1 is showing rates rather than times, the values shown equal 127-value of the parameter value stored.
- for good representation:
- L3 must be set to L2 for an ADSR,
- R3 must show the release for a One-Shot, and R4 should not be used
The code for function “setEG” :
function setEG(valueObject, value) ---- hides the non used controls of envelope
local message = valueObject:getMessage ()
local paramNum = message:getParameterNumber()
local egNum = 1
if paramNum == 1208 then egNum = 2 elseif paramNum == 1220 then egNum = 3 elseif paramNum == 1232 then egNum = 4 end -- 1 = Filter, 2 = Amp, 3 = Env3, 4 = Env
-- Data typical per envelope ---------------------------------
local egCol ={ORANGE,WHITE,182*65536+250*256+194,35*65536+255*256+146} -- colors for each envelope
local defCol = egCol[egNum]
-- egStages = {"r1","l1","r2","l2","r3","l3","r4"} -- the 7 stages as used in the graph
local egCtls = {{338,339,340,341,344,345,346},{351,352,353,354,357,358,359},{364,365,366,367,370,371,372},{375,376,377,378,381,382,383}} -- ctl numbers R1,L1,R2..R4
local stagePar = {{4199,4200,4201,4202,4203,4204,4205},{4211,4212,4213,4214,4215,4216,4217},{4223,4224,4225,4226,4227,4228,4229},{4235,4236,4237,4238,4239,4240,4241}} -- virtual par of each stage in the EG graph
local egColor = {{defCol,defCol,defCol,BLUE,defCol,defCol,defCol},{defCol,defCol,defCol,defCol,defCol,BLUE,defCol},
{defCol,defCol,defCol,defCol,defCol,defCol,defCol}, {defCol,defCol,BLUE,BLUE,BLUE,BLUE,defCol},{BLUE,BLUE,BLUE,BLUE,BLUE,BLUE,BLUE}} -- ctrl colors
local offsetAndPage = {{0,4},{12,4},{24,4},{24,9}} -- indicates start slot offset and page of each EG
-- Date typical per Envelope Mode -----------------------------
local egSlots = {{1,0,2,3,0,0,4},{1,2,3,4,8,9,10},{1,0,2,3,0,0,4},{1,2,3,4,8,9,10}} -- relative slot position or hide
local egNames = {{"Attack",127,"Decay","Sustain",127,"Sustain","Release"}, -- ADSR
{"Attack","Attack Level","Decay","Breakpoint","Decay2","Sustain","Release"}, -- ALDSDSR
{"Attack",127,"Decay","Breakpoint",63,0,"Release"}, -- One-Shot
{"Attack","Attack Level","Decay","Breakpoint","Decay2","Breakpoint2","Release"}} -- Loop S1S2 + Loop All
local egType = math.min(value+1,4)
local position = 0
local control = controls.get(egCtls[1][1])
for i = 1,7 do
control = controls.get(egCtls[egNum][i])
if egSlots[egType][i] then position = egSlots[egType][i] else position = 0 end
if position == 0 then
control:setVisible (false)
if type(egNames[egType][i]) == "number" then -- set a fixed value
parameterMap.set(deviceId, PT_VIRTUAL,stagePar[egNum][i],egNames[value+1][i])
end
else -- visible control
control:setSlot(egSlots[egType][i]+offsetAndPage[egNum][1], offsetAndPage[egNum][2])
control:setName (egNames[egType][i])
control:setColor (egColor[value+1][i])
control:setVisible (true)
end
end
if egType == 1 then -- ADSR
parameterMap.set (deviceId, PT_VIRTUAL, 4204+(egNum-1)*12, parameterMap.get (deviceId, PT_VIRTUAL,202+(egNum-1)*12)) -- sync sustain
parameterMap.set (deviceId, PT_VIRTUAL, 4205+(egNum-1)*12, 127 - parameterMap.get (deviceId, PT_VIRTUAL,205+(egNum-1)*12)) -- set release
elseif egType == 3 then -- one-shot
parameterMap.set (deviceId, PT_VIRTUAL, 4203+(egNum-1)*12, 127 - parameterMap.get (deviceId, PT_VIRTUAL,205+(egNum-1)*12)) -- - set decay 2 to release
parameterMap.set (deviceId, PT_VIRTUAL, 4205+(egNum-1)*12, 127) -- - set release in graph to 0
else
parameterMap.set (deviceId, PT_VIRTUAL, 4203+(egNum-1)*12, 127 - parameterMap.get (deviceId, PT_VIRTUAL,203+(egNum-1)*12)) -- set decay 2
parameterMap.set (deviceId, PT_VIRTUAL, 4204+(egNum-1)*12, parameterMap.get (deviceId, PT_VIRTUAL,204+(egNum-1)*12)) -- set sustain 2
parameterMap.set (deviceId, PT_VIRTUAL, 4205+(egNum-1)*12, 127 - parameterMap.get (deviceId, PT_VIRTUAL,205+(egNum-1)*12)) -- set release
end
end
some remarks here:
- egCtls is a table containing the control numbers of the controls. they will differ in another preset.
- stagePar is a table containing the parameter numbers of the graphic elements. Allthough I could easily have calculated them instead of listing, I prefer listing them so it becomes easier to reuse the code in a different synth context.
- egColor table contains for each egType the info whether the original EG color is to be shown, or whether the stage is to be colored in BLUE (looping part)
- egSlots table dictates per egType in what (relative) slot the stage is to be shown or, if 0, is to be hidden
- egNames table dictates per egType whant name the stage should get, or if a number, with what constant that stage (that will be hidden) will be shown
- at the end, some values are copied to other stages for a correct visualisation:
- sustain2 must be set to sustain for an ADSR,
- decay2 must be set to release for a one-shot, and the release stage itself in the graph is not to be shown.
- In all other cases decay2, sustain2 and release must be set against the controlled parameters.
If you want to test it out yourself, check out this preset using page ‘Env 1-3’
