Error using Envelope Script

Hello,
I’m working on an Instrument and I like to define the Envelope Parameter in the same way as it is in the Anima Instrument. I have an existing Script for the Mod Matrix. So I copied the area with the Env from Anima to my Script and link the Parameter from the Script to the Gui. That works, the Values will be set. But when I change the Value using the Sliders, there is an Script Error for the Parameter change callback and the Settings will not work.
I don’t know where the Problem is becaus I don’t know much about scripting. Maybe both Scripts will not work together or I don’t copy all. Maybe there is an easier way.
I also like to use the Env in all Zones, that works fine without the Script.
Is it also possible to set the Parameter for Attack, Decay and Release to “ms” and not to “%” ?
I know this are a lot of questions, maybe someone can help.
Many thanks.

The Functions for the Env are on the Top.

-- keep track of last applied parameter values
last = {}


-- parameter change callbacks for filter envelope offsets

function onFilterEnvAttackChanged()
    last.FilterEnvAttack = FilterEnvAttack
    this.parent:getZone():setParameter("DCFAttOffset", FilterEnvAttack)
end

function onFilterEnvSustainChanged()
    last.FilterEnvSustain = FilterEnvSustain
    this.parent:getZone():setParameter("DCFSusOffset", FilterEnvSustain)
end

-- define parameters for filter envelope offsets

defineParameter("FilterEnvAttack", "Filter Envelope Attack", 0, 0, 100, 0.1, onFilterEnvAttackChanged)
defineParameter("FilterEnvSustain", "Filter Envelope Sustain", 0, 0, 100, 0.1, onFilterEnvSustainChanged)

-- parameter change callbacks for the amplifier parameters

function onAmpVelChanged()
    last.AmpVelocity = AmpVelocity
    this.parent:getZone():setParameter("Amp Env.VelocityToLevel", AmpVelocity)
end

-- define parameters for the amplifier section

defineParameter("AmpVelocity", "Amp Velocity", 100, 0, 100, onAmpVelChanged)

-- parameter change callbacks for amplifier envelope offsets

function onAmpEnvAttackChanged()
    last.AmpEnvAttack = AmpEnvAttack
    this.parent:getZone():setParameter("DCAAttOffset", AmpEnvAttack)
end

function onAmpEnvSustainChanged()
    last.AmpEnvSustain = AmpEnvSustain
    this.parent:getZone():setParameter("DCASusOffset", AmpEnvSustain)
end

-- define parameters for amplifier envelope offsets

defineParameter("AmpEnvAttack", "Amp Envelope Attack", 0, 0, 100, 0.1, onAmpEnvAttackChanged)
defineParameter("AmpEnvSustain", "Amp Envelope Sustain", 100, 0, 100, 0.1, onAmpEnvSustainChanged)

-- parameter change callbacks for the user envelope parameters

function onUserEnvVelChanged()
    last.UserEnvVelocity = UserEnvVelocity
    this.parent:getZone():setParameter("User Env.VelocityToLevel", UserEnvVelocity)
end

function onUserEnvAttackChanged()
    last.UserEnvAttack = UserEnvAttack
    this.parent:getZone():setParameter("UserAttOffset", UserEnvAttack)
end

function onUserEnvLevel0Changed()
    last.UserEnvLevel0 = UserEnvLevel0
    this.parent:getZone():setParameter("UserL0Offset", UserEnvLevel0 * 0.5)
end

function onUserEnvLevel1Changed()
    last.UserEnvLevel1 = UserEnvLevel1
    this.parent:getZone():setParameter("UserL1Offset", UserEnvLevel1 * 0.5)
end

function onUserEnvSustainChanged()
    last.UserEnvSustain = UserEnvSustain
    this.parent:getZone():setParameter("UserSusOffset", UserEnvSustain * 0.5)
end

function onUserEnvLevelRChanged()
    last.UserEnvLevelR = UserEnvLevelR
    this.parent:getZone():setParameter("UserLROffset", UserEnvLevelR * 0.5)
end

-- define parameters for the user envelope

defineParameter("UserEnvVelocity", "User Envelope Velocity", 0, 0, 100, 0.1, onUserEnvVelChanged)
defineParameter("UserEnvAttack", "User Envelope Attack", 0, 0, 100, 0.1, onUserEnvAttackChanged)
defineParameter("UserEnvLevel0", "User Envelope Start Level", 0, -100, 100, 0.1, onUserEnvLevel0Changed)
defineParameter("UserEnvLevel1", "User Envelope Attack Level", 0, -100, 100, 0.1, onUserEnvLevel1Changed)
defineParameter("UserEnvSustain", "User Envelope Sustain Level", 0, -100, 100, 0.1, onUserEnvSustainChanged)
defineParameter("UserEnvLevelR", "User Envelope Release Level", 0, -100, 100, 0.1, onUserEnvLevelRChanged)







-- functions for assigning the modulation sources
function setSource(row, source, polarity, smoothing, sourceIndex, sourceExtra1, sourceExtra2)
  if source == 1 then
    row:setSource1(sourceIndex, sourceExtra1, sourceExtra2)
    row:setParameter("Source1.Polarity", polarity)
    row:setParameter("Source1.Smoothing", smoothing)
  else
    row:setSource2(sourceIndex, sourceExtra1, sourceExtra2)
    row:setParameter("Source2.Polarity", polarity)
    row:setParameter("Source2.Smoothing", smoothing)
  end
end

function assignStandardSource(row, source, polarity, smoothing, sourceIndex)
  setSource(row, source, polarity, smoothing, sourceIndex)
end

function assignLFO3(row, source, polarity, smoothing)
  local LFO1 = this.parent:getMidiModule("LFO 3") -- needs to match midi module name
  if LFO3 then
    setSource(row, source, polarity, smoothing, ModulationSource.modulationModule, LFO3)
  else
    setSource(row, source, polarity, smoothing, ModulationSource.unassigned)
  end
end

function assignLFO4(row, source, polarity, smoothing)
  local LFO4 = this.parent:getMidiModule("LFO 4")
  if LFO4 then
    setSource(row, source, polarity, smoothing, ModulationSource.modulationModule, LFO4)
  else
    setSource(row, source, polarity, smoothing, ModulationSource.unassigned)
  end
end


function assignQC(row, source, polarity, smoothing, qcIndex)
  setSource(row, source, polarity, smoothing, ModulationSource.quickControl, this.parent, qcIndex)
end

function assignMidiCtrl(row, source, polarity, smoothing, sourceIndex)
  setSource(row, source, polarity, smoothing, ModulationSource.midiControl, sourceIndex)
end

-- define modulation sources
defineSlotLocal("modSources")
modSources = {
  { name = "/-", 					assignFunc = assignStandardSource, index = ModulationSource.unassigned, bipolar = 0, smoothing = -1 },
  { name = "/LFO/LFO 1", 			assignFunc = assignStandardSource, index = ModulationSource.lfo1, bipolar = 0, smoothing = -1 },
  { name = "/LFO/LFO 2", 			assignFunc = assignStandardSource, index = ModulationSource.lfo2, bipolar = 0, smoothing = -1 },
  { name = "/ENV/Amp Envelope", 		assignFunc = assignStandardSource, index = ModulationSource.ampEnv, bipolar = 0, smoothing = -1 },
  { name = "/ENV/Filter Envelope", 		assignFunc = assignStandardSource, index = ModulationSource.filterEnv, bipolar = 0, smoothing = -1 },
  { name = "/ENV/Envelope 3", 		assignFunc = assignStandardSource, index = ModulationSource.userEnv, bipolar = 0, smoothing = -1 },
  { name = "/Wheel/Modulation Wheel", 	assignFunc = assignStandardSource, index = ModulationSource.modWheel, bipolar = 0, smoothing = -1 },
  { name = "/Wheel/Pitch Bend", 			assignFunc = assignStandardSource, index = ModulationSource.pitchBend, bipolar = 1, smoothing = -1 },
  { name = "/Wheel/Aftertouch",		assignFunc = assignStandardSource, index = ModulationSource.aftertouch, bipolar = 1, smoothing = -1 },
  { name = "/Bus/Bus 1", 				assignFunc = assignStandardSource, index = ModulationSource.bus1, bipolar = 1, smoothing = -1 },
  { name = "/Quick Control/Quick Control 1",		assignFunc = assignQC, index = 1, bipolar = 1, smoothing = -1 },
  { name = "/Quick Control/Quick Control 2",		assignFunc = assignQC, index = 2, bipolar = 1, smoothing = -1 },
  { name = "/Quick Control/Quick Control 3",		assignFunc = assignQC, index = 3, bipolar = 1, smoothing = -1 },
  { name = "/Quick Control/Quick Control 4",		assignFunc = assignQC, index = 4, bipolar = 1, smoothing = -1 },
  { name = "/Quick Control/Quick Control 5",		assignFunc = assignQC, index = 5, bipolar = 1, smoothing = -1 },
  { name = "/Quick Control/Quick Control 6",		assignFunc = assignQC, index = 6, bipolar = 1, smoothing = -1 },
  { name = "/Quick Control/Quick Control 7",		assignFunc = assignQC, index = 7, bipolar = 1, smoothing = -1 },
  { name = "/Quick Control/Quick Control 8",		assignFunc = assignQC, index = 8, bipolar = 1, smoothing = -1 },
}

-- create table with mod source names
function getModSrcNames()
  modSrcNames = {}
  modSrcNamesText = {}
  for i=1, #modSources do
    modSrcNames[i] = modSources[i].name
    modSrcNamesText[i] = string.match(modSources[i].name, "[^/]+$")
  end
end

getModSrcNames()

-- parameter change callback to set the modulation source
function onModSourceChanged(row, source, modSourceParam)
  if source == 1 then
    _G["ModSourceText"..row] = modSourceParam
  else
    _G["ModModifierText"..row] = modSourceParam
  end
  local modSource = modSources[modSourceParam]
  local zones = this.parent:findZones(true)
  for i, zone in pairs(zones) do
    modSource.assignFunc(zone:getModulationMatrixRow(row), source, modSource.bipolar, modSource.smoothing, modSource.index)
  end
end

defineSlotLocal("mmParameterInfo")
mmParameterInfo =
{
  default = 1,
  automatable = false,
  strings = modSrcNames
}

-- define parameters for modulation matrix rows
for i = 1, 16 do
  mmParameterInfo.name = "ModSource"..i
  mmParameterInfo.longName = "Modulation Source "..i
  mmParameterInfo.onChanged = function() onModSourceChanged(i, 1, _G["ModSource"..i]) end
  defineParameter(mmParameterInfo)  


  mmParameterInfo.name = "ModModifier"..i
  mmParameterInfo.longName = "Modulation Modifier "..i
  mmParameterInfo.onChanged = function() onModSourceChanged(i, 2, _G["ModModifier"..i]) end
  defineParameter(mmParameterInfo)
end

mmParameterInfo.strings = modSrcNamesText
for i = 1, 16 do
  mmParameterInfo.name = "ModSourceText"..i
  defineParameter(mmParameterInfo)  


  mmParameterInfo.name = "ModModifierText"..i
  defineParameter(mmParameterInfo)
end

-- define modulation destinations
defineSlotLocal("modDestinations")
modDestinations = {
  { name = "/-", 					index = ModulationDestination.unassigned    },
  { name = "/Pitch", 				index = ModulationDestination.pitch    },
  { name = "/Cutoff", 				index = ModulationDestination.cutoff    },
  { name = "/Resonance", 			index = ModulationDestination.resonance    },
  { name = "/Distortion", 			index = ModulationDestination.distortion    },
  { name = "/Level", 				index = ModulationDestination.level    },
  { name = "Volume 1", 			        index = ModulationDestination.volume1   },
  { name = "/Volume 2", 			index = ModulationDestination.volume2   },
  { name = "/Pan", 				index = ModulationDestination.pan   },
  { name = "/Bus/Bus 1",  index = ModulationDestination.bus1},
                                        { name = "/Amp Env/Amp Env Attack",	 	index = ModulationDestination.ampEnvAttack   },
					{ name = "/Amp Env/Amp Env Decay",	 	index = ModulationDestination.ampEnvDecay   },
					{ name = "/Amp Env/Amp Env Sustain",	 	index = ModulationDestination.ampEnvSustain   },
					{ name = "/Amp Env/Amp Env Release",	 	index = ModulationDestination.ampEnvRelease   },
                                  { name = "/Filter Env/Filter Env Attack", 	index = ModulationDestination.filterEnvAttack   },
                                  { name = "/Filter Env/Filter Env Decay", 	index = ModulationDestination.filterEnvDecay   },
                                  { name = "/Filter Env/Filter Env Sustain", 	index = ModulationDestination.filterEnvSustain   },
                                  { name = "/Filter Env/Filter Env Release", 	index = ModulationDestination.filterEnvRelease   },
					{ name = "/Env 3/Env 3 Start Level", 	index = ModulationDestination.userEnvStartLev   },
					{ name = "/Env 3/Env 3 Attack", 		index = ModulationDestination.userEnvAttack   },
					{ name = "/Env 3/Env 3 Attack Level",	index = ModulationDestination.userEnvAttLev   },
					{ name = "/Env 3/Env 3 Decay", 		index = ModulationDestination.userEnvDecay   },
					{ name = "/Env 3/Env 3 Sustain", 		index = ModulationDestination.userEnvSustain   },
					{ name = "/Env 3/Env 3 Release", 		index = ModulationDestination.userEnvRelease   },
					{ name = "/Env 3/Env 3 Release Level",	index = ModulationDestination.userEnvRelLev   },
{ name = "/Osc 1/Osc 1 Pitch", 	index = ModulationDestination.osc1Pitch   },
{ name = "/Osc 1/Osc 1 Level", 	index = ModulationDestination.osc1Level   },
{ name = "/Osc 1/Osc 1 Waveform", 	index = ModulationDestination.osc1Waveform   },
{ name = "/Osc 1/Osc 1 Multi Detune", 	index = ModulationDestination.osc1MultiDetune   },
{ name = "/Osc 1/Osc 1 Multi Pan", 	index = ModulationDestination.osc1MultiPan   },
{ name = "/Osc 1/Osc 1 Multi Voices", 	index = ModulationDestination.osc1MultiVoices   },
{ name = "/Osc 2/Osc 2 Pitch", 	index = ModulationDestination.osc2Pitch   },
{ name = "/Osc 2/Osc 2 Level", 	index = ModulationDestination.osc2Level   },
{ name = "/Osc 2/Osc 2 Waveform", 	index = ModulationDestination.osc2Waveform   },
{ name = "/Osc 2/Osc 2 Multi Detune", 	index = ModulationDestination.osc2MultiDetune   },
{ name = "/Osc 2/Osc 2 Multi Pan", 	index = ModulationDestination.osc2MultiPan   },
{ name = "/Osc 2/Osc 2 Multi Voices", 	index = ModulationDestination.osc2MultiVoices   },
{ name = "/Osc 3/Osc 3 Pitch", 	index = ModulationDestination.osc3Pitch   },
{ name = "/Osc 3/Osc 3 Level", 	index = ModulationDestination.osc3Level   },
{ name = "/Osc 3/Osc 3 Waveform", 	index = ModulationDestination.osc3Waveform   },
{ name = "/Osc 3/Osc 3 Multi Detune", 	index = ModulationDestination.osc3MultiDetune   },
{ name = "/Osc 3/Osc 3 Multi Pan", 	index = ModulationDestination.osc3MultiPan   },
{ name = "/Osc 3/Osc 3 Multi Voices", 	index = ModulationDestination.osc3MultiVoices   },
{ name = "/Sample/Sample Start", 	index = ModulationDestination.sampleStart   },
{ name = "/Sample/Speed Factor", 	index = ModulationDestination.speedFactor   },
{ name = "/Sample/Formant Shift", 	index = ModulationDestination.formantShift   },
{ name = "Noise Level", 	index = ModulationDestination.noiseLevel   },
{ name = "Sub Osc Level", 	index = ModulationDestination.subOscLevel  },
{ name = "Ring Modulator Level", 	index = ModulationDestination.ringModLevel  },
{ name = "/LFO 1/LFO 1 Frequency", 	index = ModulationDestination.lfo1Freq   },
{ name = "/LFO 1/LFO 1 Shape", 	index = ModulationDestination.lfo1Shape   },
{ name = "/LFO 2/LFO 2 Frequency", 	index = ModulationDestination.lfo2Freq   },
{ name = "/LFO 2/LFO 2 Shape", 	index = ModulationDestination.lfo2Shape   },
}

-- create table with the names of the modulation destinations
function getModDestNames()
  modDestNames = {}
  modDestNamesText = {}
  for i=1, #modDestinations do
    modDestNames[i] = modDestinations[i].name
    modDestNamesText[i] = string.match(modDestinations[i].name, "[^/]+$")
  end
end

getModDestNames()

-- parameter change callback to set the modulation destination
function onModDestChanged(row, modDestinationParam)
  _G["ModDestinationText"..row] = modDestinationParam
  local modDestination = modDestinations[modDestinationParam]
  local zones = this.parent:findZones(true)
  for i, zone in pairs(zones) do
    zone:getModulationMatrixRow(row):setParameter("Destination.Destination", modDestination.index)
  end
end

mmParameterInfo.strings = modDestNames

-- define parameters for modulation matrix rows
for i = 1, 16 do
  mmParameterInfo.name = "ModDestination"..i
  mmParameterInfo.longName = "Modulation Destination "..i
  mmParameterInfo.onChanged = function() onModDestChanged(i, _G["ModDestination"..i]) end
  defineParameter(mmParameterInfo)
end

mmParameterInfo.strings = modDestNamesText

for i = 1, 16 do
  mmParameterInfo.name = "ModDestinationText"..i
  defineParameter(mmParameterInfo)
end

-- keep track of last applied parameter values
last = {}

You didn’t say what error did you get. But most likely it didn’t find the zone.
Try it like this:

function onFilterEnvAttackChanged()
  local zones = this.parent:findZones(true)
  for i, zone in ipairs(zones) do
    zone:setParameter("DCFAttOffset", FilterEnvAttack)
  end
end

This should work for both single zone and multiple (all) zones.

Hi Misohoza,
thanks again, now it works. Ah yes, I should have write the error. It was something like this:
Parameter change callback FilterEnvAttack = 2.5: Line 9: attempt to index a nil value: this.parent:getZone():setParameter(“DCFAttOffset”, FilterEnvAttack)

Hopefully one final question to this - now this works for all Zones, what do I have to change for only one zone, so I can use it separate in different zones?

Thank you very much again!

This depends where the zone is located in the program tree. Take a look at this:
https://developer.steinberg.help/display/HSD/Working+with+Objects

You can use getLayer() and getZone() to access particular layer and a zone.

local zone = this.parent:getLayer(1):getZone(2)

this.parent is the parent layer of the script module. This can also be the program itself. Depends where you put the script module. The code above should return second zone in first layer.

O.K., thanks for the Information. I’ll try this next time and see how it works.