Flexible load of layers

hello, I am using the layer selector script (made by misohoza) this mutes all the layers except the selected one, the problem is that when you have a lot, about 150 in my case, even if they are muted they continue loading the cpu and as a result there is a very high load.
I have been told that to reduce the load instead of mutating, just load the selected layer.
Someone has some example of this or can give me some clue.

Hi miclo,

There are some examples in the online script documentation. You can use loadPreset or loadPresetAsync to load a layer preset then remove the old layer and append or insert new layer.

You can also use the Sample Selector template that’s used by Hot Brass, Studio Strings or Skylab instruments. Check one of those instruments to see how it’s done. The advantage of using the sample selector is that you don’t need to write the path of each layer preset into your script, it works like a preset browser.

thank you very much, I have already lost count of all the beers that I owe you :slight_smile:, I will investigate

hello I’ve been seeing how to do it with Sample Selector template (as Hot Brass)
I first created a working directory with Export Program as VST3 Preset with Files …
I have saved it in c: / documents / my library
and a vstpreset and a folder called Mother Source have been generated with the samples and macropages, I have added a folder called Layer Presets and added a few preset of my layers.
and I have loaded the vstpreset again to create the relative paths that I think are needed and I have configured the Sample Selector template as you see in the photo, but it doesn’t find anything …
something escapes me?
I tried to find the Hot Brass folder to see what the structure is like but it has been impossible for me to find it.
any advice?

It seems that you have to have this route: documents / steinberg / and already the instrument folder.
Inside the instrument folder you have to create a folder called “sub Presets” and inside one called “Layer Presets”
in this way he recognizes my instrument and the vstpresets of the layers appear to me …
the first load of a layer always works fine but when I want to change to another layer halion I get the error “A serius problem has occurred …”
or gives a script error that says:
Parameter change callback selectedPath = data: cannot resume non-suspended coroutine
Any ideas anyone?

  • I am using the Hot Brass script

I think I know what the problem is …
The rest of the script’s functions conflict with my macro page, if I remove my macro page and leave only the sampler selector template, everything seems fine.
I need to leave the script only with the functions necessary for the “selectedPatch” and “selectedPreset” parameters to work and remove everything else.
could someone tell me how it should be the script?
I’m trying but it always throws errors
I paste the complete script

-- copy relevant zone parameters
defineSlotLocal("copiedParameters")
copiedParameters =
{
	{"Filter.Type", nil},   -- second value will be set to the parameter of the previous zones
	{"Filter.VelocityToCutoff", nil},
	{"DCFAttOffset", nil},
	{"DCFSusOffset", nil},
	{"DCAAttOffset", nil},
	{"DCASusOffset", nil},
	{"Amp Env.VelocityToLevel", nil},
	{"UserAttOffset", nil},
	{"Pitch.Coarse", nil},
	{"Pitch.Fine", nil},
	{"Pitch.Random", nil},
	{"VoiceControl.UseStartRange", nil},
	{"Amp.Pan", nil},
	{"Filter.Cutoff", nil},
	{"Filter.Resonance", nil},
	{"Filter.Distortion", nil},
	{"Filter.EnvAmount", nil},
	{"Filter.Keytrack", nil},
	{"Filter.Bypass", nil},
	{"Filter.ShapeA", nil},
	{"DCFRelOffset", nil},
	{"DCFDecOffset", nil},
	{"DCARelOffset", nil},
	{"DCADecOffset", nil},
	{"Amp.Level", nil},
	{"User Env.VelocityToLevel", nil},
	{"UserDecOffset", nil},
	{"UserSusOffset", nil},
	{"UserRelOffset", nil},
	{"VoiceControl.GlideOn", nil},
	{"VoiceControl.Fingered", nil},
	{"VoiceControl.Glide", nil},
	{"Pitch.PitchBendUp", nil},
	{"Pitch.PitchBendDn", nil},
	{"UserL0Offset", nil},
	{"UserL1Offset", nil},
	{"UserLROffset", nil},
}

defineSlotLocal("copiedModulationMatrixParameters")
copiedModulationMatrixParameters = {}

for i = 1, 19, 1 do
	copiedModulationMatrixParameters[i] =
	{
		{"Source1.Polarity", nil},
		{"Source2.Polarity", nil},
		{"Source1.Smoothing", nil},
		{"Source2.Smoothing", nil},
		{"Destination.Depth", nil},
		{"Destination.Bypass", nil}
	}
end

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

-- load handling
-- selectedPath and selectedPreset should not appear in the undo history,
--  so we make them non-persistent, but save and restore them explicitly with
--  onSave and onLoad
defineParameter{name = "selectedPath", default = "", onChanged = function() onSelectPath() end, persistent = false, automatable = false}
defineParameter{name = "selectedPreset", default = "", persistent = false, automatable = false}

function onSave()
	return { selectedPath = selectedPath, selectedPreset = selectedPreset }
end

function onLoad(data)
	if type(data) == "table" then
		-- beware of old tables still filled with other entries
		if data.selectedPath then
			selectedPath = data.selectedPath
		end
		if data.selectedPreset then
			selectedPreset = data.selectedPreset
		end
	end
end

function onUndo()
	-- print("onUndo: updatePreset = ", undoInfo.updatePreset)
	if undoInfo.updatePreset then
		local layer = this.parent:getLayer()
		if layer then
			selectedPreset = layer.name
		end
	end
end

-- pseudo parameter to add a callback into the undo group for replacing the sample layer
defineParameter{name = "undoInfo", default = {}, onChanged = onUndo, automatable = false}

-- all parameters but the modulation sources can be restored
-- before insertion into the parent layer
function restoreZoneParameters(layer)
	local zones = layer:findZones()
	for i, zone in pairs(zones) do
		for j, parameter in pairs(copiedParameters) do
			zone:setParameter(parameter[1], parameter[2])
		end
		for j = 1, 16, 1 do
			local modDestination = modDestinations[_G["ModDestination"..j]]
			zone:getModulationMatrixRow(j):setParameter("Destination.Destination", modDestination.index)
		end
		for j = 1, 3, 1 do
			local modDestination = modDestinations[_G["CtrlLane"..j.."Dest"]]
			zone:getModulationMatrixRow(j + 16):setParameter("Destination.Destination", modDestination.index)
		end
		for j, parametersOfRow in pairs(copiedModulationMatrixParameters) do
			local row = zone:getModulationMatrixRow(j)
			for k, parameter in pairs(parametersOfRow) do
				row:setParameter(parameter[1], parameter[2])
			end
		end
	end
end

-- modulation sources must be restored after insertion into the parent layer
--  because QC and MidiModule references do not exist before
function restoreMatrixSources(layer)
	local zones = layer:findZones()
	for i, zone in pairs(zones) do
		for j = 1, 16, 1 do
			local row = zone:getModulationMatrixRow(j)
			local polarity1 = row:getParameter("Source1.Polarity")
			local polarity2 = row:getParameter("Source2.Polarity")
			local modSource = modSources[_G["ModSource"..j]]
			modSource.assignFunc(row, 1, modSource.bipolar, modSource.smoothing, modSource.index)
			local modModifier = modSources[_G["ModModifier"..j]]
			modModifier.assignFunc(row, 2, modModifier.bipolar, modModifier.smoothing, modModifier.index)
			row:setParameter("Source1.Polarity", polarity1)
			row:setParameter("Source2.Polarity", polarity2)
		end
	end
end

function saveZoneParameters(layer)
	local sourceZone = layer:getZone()
	if not sourceZone then
		return false
	end
	for i, parameter in pairs(copiedParameters) do
		parameter[2] = sourceZone:getParameter(parameter[1])
	end
	for i, parametersOfRow in pairs(copiedModulationMatrixParameters) do
		local row = sourceZone:getModulationMatrixRow(i)
		for j, parameter in pairs(parametersOfRow) do
			parameter[2] = row:getParameter(parameter[1])
		end
	end
	return true
end

function forwardSphere(layer)
	local sphereH = layer:getParameterDefinition("QuickControl.QC9").id
	local sphereV = layer:getParameterDefinition("QuickControl.QC10").id
	this.parent:addQCAssignment(9, layer, sphereH, layer)
	this.parent:addQCAssignment(10, layer, sphereV, layer)
end

function loadFinished(currentProgressInfo)
	if currentProgressInfo.root then
		startUndoBlock("Load Layer " .. currentProgressInfo.root.name)

		undoInfo = { updatePreset = true }
		this:setParameter("undoInfo", { updatePreset = false }, true)
		
		local saved = false
		local layer = this.parent:getLayer()
		if layer then
			saved = saveZoneParameters(layer)
			if saved then
				restoreZoneParameters(currentProgressInfo.root)
			end
		end

		this.parent:appendLayer(currentProgressInfo.root)

		if layer then
			this.parent:removeLayer(layer)
			if saved then
				restoreMatrixSources(currentProgressInfo.root)
			end
		end
		forwardSphere(currentProgressInfo.root)

		undoInfo = { updatePreset = false }
		this:setParameter("undoInfo", { updatePreset = true }, true)

		endUndoBlock()
	end
end

function onSelectPath()
	loadPresetAsync(selectedPath, loadFinished)
end

-- set parameter of all zones
function setParameterOfZones(parameterName, value)
	local zones = this.parent:getLayer():findZones()
	for i, zone in pairs(zones) do
		zone:setParameter(parameterName, value)
	end
end

-- parameter change callback for pitch random
function onPitchRandomChanged()
	last.PitchRandom = PitchRandom
	setParameterOfZones("Pitch.Random", PitchRandom)
end

defineParameter("PitchRandom", "Pitch Random", 0, 0, 1, onPitchRandomChanged)

-- define the filter types
defineSlotLocal("filterTypes")
filterTypes = {
				{ name = "Tube", 					  index = 2 },
				{ name = "Hard Clip", 				  index = 3 },
				{ name = "Bit Reduction", 			  index = 4 },
				{ name = "Rate Reduction", 			  index = 5 },
				{ name = "Rate Reduction Key Follow", index = 6 },
			  }

-- create table with the names of the filter types
function getFilterTypeNames()
	filterTypeNames = {}
	for i=1, #filterTypes do
		filterTypeNames[i] = filterTypes[i].name
	end
end

getFilterTypeNames()

-- parameter change callbacks for the filter parameters
function onFilterTypeChanged()
	setParameterOfZones("Filter.Type", filterTypes[FilterType].index)
end

function onFilterVelChanged()
	last.FilterVelocity = FilterVelocity
	setParameterOfZones("Filter.VelocityToCutoff", FilterVelocity)
end

-- define parameters for the filter section
defineParameter("FilterType", 	  "Filter Type", 	 1, filterTypeNames, 	onFilterTypeChanged)
defineParameter("FilterVelocity", "Filter Velocity", 0, 0, 200, 			onFilterVelChanged)

-- parameter change callbacks for filter envelope offsets
function onFilterEnvAttackChanged()
	last.FilterEnvAttack = FilterEnvAttack
	setParameterOfZones("DCFAttOffset", FilterEnvAttack)
end

function onFilterEnvSustainChanged()
	last.FilterEnvSustain = FilterEnvSustain
	setParameterOfZones("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
	setParameterOfZones("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
	setParameterOfZones("DCAAttOffset", AmpEnvAttack)
end

function onAmpEnvSustainChanged()
	last.AmpEnvSustain = AmpEnvSustain
	setParameterOfZones("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
	setParameterOfZones("User Env.VelocityToLevel", UserEnvVelocity)
end

function onUserEnvAttackChanged()
	last.UserEnvAttack = UserEnvAttack
	setParameterOfZones("UserAttOffset", UserEnvAttack)
end

function onUserEnvLevel0Changed()
    last.UserEnvLevel0 = UserEnvLevel0
    setParameterOfZones("UserL0Offset", UserEnvLevel0 * 0.5)
end

function onUserEnvLevel1Changed()
    last.UserEnvLevel1 = UserEnvLevel1
    setParameterOfZones("UserL1Offset", UserEnvLevel1 * 0.5)
end

function onUserEnvSustainChanged()
    last.UserEnvSustain = UserEnvSustain
    setParameterOfZones("UserSusOffset", UserEnvSustain * 0.5)
end

function onUserEnvLevelRChanged()
    last.UserEnvLevelR = UserEnvLevelR
    setParameterOfZones("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)

-- function for setting the LFO sync mode
function setLfoSyncMode(lfo, LfoSync, LfoSyncMode)
	if LfoSync then
		if LfoSyncMode == 1 then
			lfo:setParameter("Trigger", 0)
			lfo:setParameter("Sync", 2)
		elseif LfoSyncMode == 2 then
			lfo:setParameter("Trigger", 1)
			lfo:setParameter("Sync", 1)
		else
			lfo:setParameter("Trigger", 2)
			lfo:setParameter("Sync", 1)
		end
	else
		lfo:setParameter("Trigger", 0)
		lfo:setParameter("Sync", 0)
	end
end

-- parameter change callbacks for LFO A and B
function onLfoASyncModeChanged()
	local lfo = this.parent:getChild("LFO A")

	if lfo then
		setLfoSyncMode(lfo, LfoASync, LfoASyncMode)
	end
end

function onLfoBSyncModeChanged()
	local lfo = this.parent:getChild("LFO B")

	if lfo then
		setLfoSyncMode(lfo, LfoBSync, LfoBSyncMode)
	end
end

-- names of the LFO sync modes
defineSlotLocal("syncModeNames")
syncModeNames = { "Beat", "First", "Each" }

-- define parameters for the LFO section
defineParameter("LfoASync", 	"LFO A Sync", 		false, 					onLfoASyncModeChanged)
defineParameter("LfoASyncMode", "LFO A Sync Mode", 	1, 		syncModeNames, 	onLfoASyncModeChanged)

defineParameter("LfoBSync", 	"LFO B Sync", 		false, 					onLfoBSyncModeChanged)
defineParameter("LfoBSyncMode", "LFO B Sync Mode", 	1, 		syncModeNames, 	onLfoBSyncModeChanged)


-- 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 assignLfoA(row, source, polarity, smoothing)
	local lfoA = this.parent:getMidiModule("LFO A")
	if lfoA then
		setSource(row, source, polarity, smoothing, ModulationSource.modulationModule, lfoA)
	else
		setSource(row, source, polarity, smoothing, ModulationSource.unassigned)
	end
end

function assignLfoB(row, source, polarity, smoothing)
	local lfoB = this.parent:getMidiModule("LFO B")
	if lfoB then
		setSource(row, source, polarity, smoothing, ModulationSource.modulationModule, lfoB)
	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 A", 				assignFunc = assignLfoA, bipolar = 1, smoothing = -1 },
				{ name = "LFO B", 				assignFunc = assignLfoB, bipolar = 1, smoothing = -1 },
				{ name = "Amp Envelope", 		assignFunc = assignStandardSource, index = ModulationSource.ampEnv, bipolar = 0, smoothing = -1 },
				{ name = "Filter Envelope", 	assignFunc = assignStandardSource, index = ModulationSource.filterEnv, bipolar = 0, smoothing = -1 },
				{ name = "Env3", 				assignFunc = assignStandardSource, index = ModulationSource.userEnv, bipolar = 1, smoothing = -1 },
				{ name = "Key Follow", 			assignFunc = assignStandardSource, index = ModulationSource.keyFollow, bipolar = 1, smoothing = -1 },
				{ name = "Note-on Velocity", 	assignFunc = assignStandardSource, index = ModulationSource.noteOnVelocity, bipolar = 0, smoothing = -1 },
				{ name = "Note-on Vel Squared", assignFunc = assignStandardSource, index = ModulationSource.noteOnVelocitySquared, bipolar = 0, smoothing = -1 },
				{ name = "Pitch Bend", 			assignFunc = assignStandardSource, index = ModulationSource.pitchBend, bipolar = 1, smoothing = -1 },
				{ name = "Modulation Wheel", 	assignFunc = assignStandardSource, index = ModulationSource.modWheel, bipolar = 0, smoothing = -1 },
				{ name = "Aftertouch", 			assignFunc = assignStandardSource, index = ModulationSource.aftertouch, bipolar = 0, smoothing = -1 },
				{ name = "Arp Controller 1", 	assignFunc = assignMidiCtrl, index = 110, bipolar = 0, smoothing = 0 },
				{ name = "Arp Controller 2", 	assignFunc = assignMidiCtrl, index = 111, bipolar = 0, smoothing = 0 },
				{ name = "Arp Controller 3", 	assignFunc = assignMidiCtrl, index = 112, bipolar = 0, smoothing = 0 },
				{ name = "Bus 1", 				assignFunc = assignStandardSource, index = ModulationSource.bus1, bipolar = 1, smoothing = -1 },
				{ name = "Bus 2", 				assignFunc = assignStandardSource, index = ModulationSource.bus2, bipolar = 1, smoothing = -1 },
				{ name = "Bus 3", 				assignFunc = assignStandardSource, index = ModulationSource.bus3, bipolar = 1, smoothing = -1 },
				{ name = "Bus 4", 				assignFunc = assignStandardSource, index = ModulationSource.bus4, bipolar = 1, smoothing = -1 },
				{ name = "Bus 5", 				assignFunc = assignStandardSource, index = ModulationSource.bus5, bipolar = 1, smoothing = -1 },
				{ name = "Bus 6", 				assignFunc = assignStandardSource, index = ModulationSource.bus6, bipolar = 1, smoothing = -1 },
				{ name = "Bus 7", 				assignFunc = assignStandardSource, index = ModulationSource.bus7, bipolar = 1, smoothing = -1 },
				{ name = "Bus 8", 				assignFunc = assignStandardSource, index = ModulationSource.bus8, bipolar = 1, smoothing = -1 },
				{ name = "Quick Control 1",		assignFunc = assignQC, index = 1, bipolar = 1, smoothing = -1 },
				{ name = "Quick Control 2",		assignFunc = assignQC, index = 2, bipolar = 1, smoothing = -1 },
				{ name = "Quick Control 3",		assignFunc = assignQC, index = 3, bipolar = 1, smoothing = -1 },
				{ name = "Quick Control 4",		assignFunc = assignQC, index = 4, bipolar = 1, smoothing = -1 },
				{ name = "Quick Control 5",		assignFunc = assignQC, index = 5, bipolar = 1, smoothing = -1 },
				{ name = "Quick Control 6",		assignFunc = assignQC, index = 6, bipolar = 1, smoothing = -1 },
				{ name = "Quick Control 7",		assignFunc = assignQC, index = 7, bipolar = 1, smoothing = -1 },
				{ name = "Quick Control 8",		assignFunc = assignQC, index = 8, bipolar = 1, smoothing = -1 }
			 }


-- create table with mod source names
function getModSrcNames()
	modSrcNames = {}
	for i=1, #modSources do
		modSrcNames[i] = modSources[i].name
	end
end

getModSrcNames()

-- parameter change callback to set the modulation source
function onModSourceChanged(row, source, modSourceParam)
	local modSource = modSources[modSourceParam]
	local zones = this.parent:getLayer():findZones()
	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

-- 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", 				index = ModulationDestination.volume1   },
					{ name = "Pan", 				index = ModulationDestination.pan   },
					{ name = "Amp Env Attack",	 	index = ModulationDestination.ampEnvAttack   },
					{ name = "Amp Env Decay",	 	index = ModulationDestination.ampEnvDecay   },
					{ name = "Amp Env Sustain",	 	index = ModulationDestination.ampEnvSustain   },
					{ name = "Amp Env Release",	 	index = ModulationDestination.ampEnvRelease   },
					{ name = "Filter Env Attack", 	index = ModulationDestination.filterEnvAttack   },
					{ name = "Filter Env Decay", 	index = ModulationDestination.filterEnvDecay   },
					{ name = "Filter Env Sustain", 	index = ModulationDestination.filterEnvSustain   },
					{ name = "Filter Env Release", 	index = ModulationDestination.filterEnvRelease   },
					{ name = "Env3 Start Level", 	index = ModulationDestination.userEnvStartLev   },
					{ name = "Env3 Attack", 		index = ModulationDestination.userEnvAttack   },
					{ name = "Env3 Attack Level",	index = ModulationDestination.userEnvAttLev   },
					{ name = "Env3 Decay", 			index = ModulationDestination.userEnvDecay   },
					{ name = "Env3 Sustain", 		index = ModulationDestination.userEnvSustain   },
					{ name = "Env3 Release", 		index = ModulationDestination.userEnvRelease   },
					{ name = "Env3 Release Level",	index = ModulationDestination.userEnvRelLev   },
					{ name = "Bus 1",				index = ModulationDestination.bus1  },
					{ name = "Bus 2",				index = ModulationDestination.bus2  },
					{ name = "Bus 3",				index = ModulationDestination.bus3  },
					{ name = "Bus 4",				index = ModulationDestination.bus4  },
					{ name = "Bus 5",				index = ModulationDestination.bus5  },
					{ name = "Bus 6",				index = ModulationDestination.bus6  },
					{ name = "Bus 7",				index = ModulationDestination.bus7  },
					{ name = "Bus 8",				index = ModulationDestination.bus8  }
				  }

-- create table with the names of the modulation destinations
function getModDestNames()
	modDestNames = {}
	for i=1, #modDestinations do
		modDestNames[i] = modDestinations[i].name
	end
end

getModDestNames()

-- parameter change callback to set the modulation destination
function onModDestChanged(row, modDestinationParam)
	local modDestination = modDestinations[modDestinationParam]
	local zones = this.parent:getLayer():findZones()
	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

-- define parameters for arpeggiator controller lane destinations
for i = 1, 3 do
	mmParameterInfo.name = "CtrlLane"..i.."Dest"
	mmParameterInfo.longName = "Ctrl Lane "..i.." Destination"
	mmParameterInfo.onChanged = function() onModDestChanged(16 + i, _G["CtrlLane"..i.."Dest"]) end
	defineParameter(mmParameterInfo)
end

--[[
	verify some parameters for being updated by automation/QC
	(QC relative changes don't produce a change notification because the document isn't changed)
	Relying on the document update has idle timing for absolute changes, so might not be fast enough
--]]

function verifyParam(name, callback)
	if last[name] ~= _G[name] then
		callback()
	end
end

function onNote(ev)

	verifyParam("PitchRandom", onPitchRandomChanged)
	verifyParam("FilterVelocity", onFilterVelChanged)
	verifyParam("FilterEnvAttack", onFilterEnvAttackChanged)
	verifyParam("FilterEnvSustain", onFilterEnvSustainChanged)
	verifyParam("AmpVelocity", onAmpVelChanged)
	verifyParam("AmpEnvAttack", onAmpEnvAttackChanged)
	verifyParam("AmpEnvSustain", onAmpEnvSustainChanged)
	verifyParam("UserEnvVelocity", onUserEnvVelChanged)
	verifyParam("UserEnvAttack", onUserEnvAttackChanged)
	verifyParam("UserEnvLevel0", onUserEnvLevel0Changed)
	verifyParam("UserEnvLevel1", onUserEnvLevel1Changed)
	verifyParam("UserEnvSustain", onUserEnvSustainChanged)
	verifyParam("UserEnvLevelR", onUserEnvLevelRChanged)

	postEvent(ev)

end

This should do the loading of layer presets. It doesn’t copy the state of the parameters though.

-- load handling
-- selectedPath and selectedPreset should not appear in the undo history,
--  so we make them non-persistent, but save and restore them explicitly with
--  onSave and onLoad
defineParameter{name = "selectedPath", default = "", onChanged = function() onSelectPath() end, persistent = false, automatable = false}
defineParameter{name = "selectedPreset", default = "", persistent = false, automatable = false}

function onSave()
	return { selectedPath = selectedPath, selectedPreset = selectedPreset }
end

function onLoad(data)
	if type(data) == "table" then
		-- beware of old tables still filled with other entries
		if data.selectedPath then
			selectedPath = data.selectedPath
		end
		if data.selectedPreset then
			selectedPreset = data.selectedPreset
		end
	end
end

function onUndo()
	-- print("onUndo: updatePreset = ", undoInfo.updatePreset)
	if undoInfo.updatePreset then
		local layer = this.parent:getLayer()
		if layer then
			selectedPreset = layer.name
		end
	end
end

-- pseudo parameter to add a callback into the undo group for replacing the sample layer
defineParameter{name = "undoInfo", default = {}, onChanged = onUndo, automatable = false}


function loadFinished(currentProgressInfo)
	if currentProgressInfo.root then
		startUndoBlock("Load Layer " .. currentProgressInfo.root.name)

		undoInfo = { updatePreset = true }
		this:setParameter("undoInfo", { updatePreset = false }, true)
		
		local layer = this.parent:getLayer()
		
		this.parent:appendLayer(currentProgressInfo.root)

		if layer then
			this.parent:removeLayer(layer)			
		end

		undoInfo = { updatePreset = false }
		this:setParameter("undoInfo", { updatePreset = true }, true)

		endUndoBlock()
	end
end

function onSelectPath()
	loadPresetAsync(selectedPath, loadFinished)
end

Sample Selector.zip (13.8 KB)

The same thing happens :frowning:
it works correctly without the macro page like the other one, at the moment I add the macro page I get the same error of
Parameter change callback selectedPath = data: cannot resume non-suspended coroutine
Now I don’t understand anything :frowning:

The layer presets you want to load, do they have any parameters connected to macro page?

I haven’t tested this thoroughly so I’m not sure if this is the case but when you load new preset the controls on macro page might still refer to the layer preset that has been removed. This could cause problems. If you look at the Hot Brass instrument all macro page controls are connected to script parameters which in turn handle the parameters of the loaded layer.

Edit

It looks like it’s working if the layer preset doesn’t have its own macro page.
(Which is what you found out already. )

If it does then it crashes. Sometimes I was able to load 2 or 3 presets but then it eventually crashed with “serious error…” message.

Hello, I hook up to the interesting topic.
loadPresetAsync script on this page loadPresetAsync - HALion Script - Steinberg Developer Help or similar script, could be good a good choice and easy to apply.
But I don’t know if it is only a my problem, when I reload a saved preset with a “replace layer” function, Halion crash. It is very frustrating…
I use Mac OSX.

thanks

If you look at the Hot Brass instrument all macro page controls are connected to script parameters which in turn handle the parameters of the loaded lay

If it seems that everything has to go in the script, if it doesn’t break, I’m going to have to discard this :frowning:
thanks for everything as always

But I don’t know if it is only a my problem, when I reload a saved preset with a “replace layer” function, Halion crash. It is very frustrating…

If layers are a part of an instrument and are connected to a macro page it is not possible to exchange, delete or move them. Those issues cannot be solved by HALion. You have to remove all connections first before you exchange any layer

A question for misohoza
your layer selection script that does exactly.
Do you mutate the layers minus the selected one or are you doing the same thing as the MIDI layer mute parameter?
The parameter I am referring to is that of Photo 1.
it seems that this 2 option would cut the midi shot and the voice would not be reproduced and the performance would improve.

layers = this.parent:findLayers()

function getLayerNames()
  layerNames = {}
  for i, layer in ipairs(layers) do
    layerNames[i] = layer.name
  end
end

getLayerNames()

defineParameter("LayerSelect", nil, 1, layerNames)


function onNote(event)
  playNote(event.note, event.velocity, -1, layers[LayerSelect])
end

No, I’m not using the Layer Midi Mute parameter. Although this is also an option.

Play note function has optional arguments. You can choose which layer or zone should receive the midi message.

Now that Halion 6.4 is out you could try it again to see if it still crashes when loading layers.

Now that Halion 6.4 is out you could try it again to see if it still crashes when loading layers.

I have to discard the option to load layers, because there is a layer that always has to be available (it is the one of the script that you made of importing sampler).

Play note function has optional arguments. You can choose which layer or zone should receive the midi message.

if I can understand this a bit because as I said my knowledge of programming are bad, bad :slight_smile:
Does this function send midi to the selected layer?

function onNote (event)
   playNote (event.note, event.velocity, -1, layers [LayerSelect])
end

Now I have to do another function that sets the layer midi mute parameter to “on” in all layers except the selected one, right? or I just have no idea :slight_smile:

Well, yes if you want to do it with LayerMidiMute parameter.

layers = this.parent:findLayers()

function getLayerNames()
  layerNames = {}
  for i, layer in ipairs(layers) do
    layerNames[i] = layer.name
  end
end

getLayerNames()

function layerSelectChanged()
  for i = 1, #layers do
    local layer = layers[i]
    if i == LayerSelect then
      layer:setParameter("LayerMidiMute", false)
    else
      layer:setParameter("LayerMidiMute", true)
    end
  end
end

defineParameter("LayerSelect", nil, 1, layerNames, layerSelectChanged)

layerSelectChanged()

thank you very much misohoza, you are the boss

ohhhh … there is a problem with the script, when you save a preset and reload it, the macro shows the saved layer, but the one that sounds is always the first layer, not the saved layer

Put the last line inside the onLoad callback.

function onLoad()
  layerSelectChanged()
end

Or delete the last line altogether.

The purpose of that line is to make sure that only the selected layer is unmuted. But it looks like it’s executed before the correct parameter value is loaded.

Perfect :smiley: