Script time out when mapping samples

I’m mapping 1440 samples via a script I wrote for that (using loops). But at 501 iteration I get an alert “Script time out. Reason could be an infinite loop”. However if I reduce the number of samples to be mapped down to 480 (one third), the script works perfectly. How can I force the script to proceed anyway for the higher number of samples? Is there indeed a limitation on iteration number or maybe it is a bug in my script?

There is a time limit. But you can increase the script execution time out.

https://developer.steinberg.help/display/HSD/setScriptExecTimeOut

Are you using print()? This can slow down the script a lot.

There were a couple of print lines, but commenting them out didn’t help. Also setting ScriptExecTimeOut even to 200000 ms didn’t help either.

I was advised to use wait(0) somewhere in any loop, however I’m getting an error alert “This function cannot be called in global code” when adding wait(). Where can be a mistake? In dev manual I see that wait() is both “Available in: Controller, Processor”.

This is true. It means it cannot be used in main chunk. You should be able to use it in onLoad, onInit… I guess.

If this doesn’t work create a pseudo on/off parameter. Put everything you need to do in its callback and trigger the callback by manually changing the parameter after the script loads.

I am a bit surprised that changing the script execution time didn’t work. How much time you need for the script to finish? The default for controller thread is 5 seconds.

Just tried it and the wait(0) trick seems to work. But I had to use a parameter callback. Halion froze when I did this directly in onLoad(). Following script took almost 15s to finish.

a = {}
t = 0

print(getContext(), " Script Execution Time Out: ", getScriptExecTimeOut() / 1000, " s")

function onStart()
	t = getTime()
	for i = 1, 25000 do
		local x = i * 2
		table.insert(a, 1, x)
		if i % 1000 == 0 then
			wait(0)
			print(getTime() - t, "***", i)
		end
	end
	print("Total time: ", (getTime() - t) / 1000, " s")
end

defineParameter("Start", nil, false, onStart)

function onLoad()
	this:setParameter("Start", true)
end

Thanks a lot for such thorough answer! I have a connected question (don’t remember if I asked before) – can Halion’s Lua read a list of all files in a catalog? So I could fill a string array with file names and use it afterwards for mapping? Because for the present I have to create such string array manually.

Coroutine, io and os lua libraries are not supported in Halion.

But it depends on what you are trying to achieve. There is a Path Browser template that can be useful to let users import their own samples. But it’s only one at a time I think.

You can also use lua require function. Put all the path strings in a separate lua file and require it from your main script. But you still need to create that lua file yourself.

If it’s just about mapping the samples you could also consider mapping the samples manually and saving layer presets. Then you can load the layer presets from the script. Some Halion factory libraries make use of this (Hot Brass, Studio Strings, Skylab)

I have another couple of question concerning mapping:

  1. Suppose I’ve accomplished mapping with a script and now would like to replace/swap certain samples. Can it be done somewhere in Halion by just changing the name of a sample in its full path. For example I’d like to change an index in a sample’s name from, say, “sample_name_1” to “sample_name_2”. Can I achieve it without dragging a new sample in that zone, but by simply typing-in somewhere “_2” instead of “_1”?

  2. This is a piece of script I’m using to append a new zone when auto-mapping

      local zone = Zone()
      layers[1]:appendZone(zone)
      zone:setName(SampleNamesArray[zones_index])
      zone:setParameter("ZoneType",1)
      zone:setParameter("SampleOsc.Filename","C:/My_Samples/” .. loop_index .. “.wav”)

Is it possible to check before creating a new zone whether the actual audio-file in "C:/My_Samples/” … loop_index … “.wav” exists at all? And if doesn’t just skip the cycle.

  1. I don’t think it’s possible in the zone or sample editor but you can try the parameter list and the Filename parameter. It is a string parameter so in theory you should be able to do this.

  2. Check the AudioFile class.
    AudioFile - HALion Script - Steinberg Developer Help
    The .valid field will tell you if the file exists and can be opened.

You may also need to check the sample length to set the sample end parameter and possibly check for loop markers. When you load the sample from the script by simply changing the file name parameter I don’t think that Halion does that automatically.

Ok, to check file availability I added a couple of lines to my script above:

af = AudioFile.open("SampleOsc.Filename","C:/My_Samples/" .. loop_index .. ".wav")

if af.valid then

local zone = Zone()
layers[1]:appendZone(zone)
zone:setName(SampleNamesArray[zones_index])
zone:setParameter("ZoneType",1)
zone:setParameter("SampleOsc.Filename","C:/My_Samples/" .. loop_index .. ".wav")

else 

print ("not found " .. "SampleOsc.Filename","C:/My_Samples/" .. loop_index .. ".wav")

end 

And it works perfectly (warning about missing samples properly) until at some point an error alerts pops up:

Error: No matching overload found, candidates:
void (Zone&,lua_State*,int)

I don’t have a clue what is wrong with the script. Maybe again a timeout is reached?

Well, I’m not sure either. Which line is it that gives you the error?

The first line in your script is a bit strange. You should give it just one argument-path of the audio file.

I am trying a variation of your script.

The way I understand the error message is that some of the functions in your script was called with wrong arguments. I get similar errors if I use AudioFile.open with 2 strings as arguments.

path = "vstsound://F29C895D6D8E4D6C9BCBBA5198412192/.samples/Ambient Pad 01/Ambient Pad 01 - C3.tg3c"

for i = 1, 1500 do

    local af = AudioFile.open(path)

    if af.valid then
        local zone = Zone()
        this.parent:appendZone(zone)
        local name = af.fileName:match('^.+[\\/](.+)%..+$')
        zone:setName(name)
        zone:setParameter("ZoneType",1)
        zone:setParameter("SampleOsc.Filename", af.fileName)
        zone:setParameter("SampleOsc.SampleEnd", af.length)
    else 
        print ("not found " .. path)
    end
end

Thanks for checking! So is it happening because of spaces in the file’s name “Ambient Pad 01/Ambient Pad 01 - C3.tg3c”?

Spaces in file names are fine. I meant this:

af = AudioFile.open("SampleOsc.Filename","C:/My_Samples/" .. loop_index .. ".wav")

You called the function with 2 arguments while it expects 1.

Function overloading is a concept used in some programming languages. You define multiple functions with the same name but with different arguments. If you call this function with arguments it doesn’t expect you would get a no matching overload found error.

Do you get the error straight away when you run the script or does it work fine and halfway through the loop it stops and gives you the error?

Only after about 3k of properly working iterations I got this error alert. Not straight away and that bugs me a lot.

Is there anything unusual in the filename when it happens? Does it contain quotes or apostrophe or maybe some other special character?