Import your own sample?

the code you posted before this one works great for me I just removed the “sample.rootKey or” part.
basically, if the root note is written in the name is going for that first otherwise it tries to detect the pitch, and if it is not able to do it set the root to C3. I’m going to skip the metadata thing, it creates only confusion with my samples. Thanks again for your help!

1 Like

Is it possible to have the Drop element be active only when a specific zone is selected?

In my instrument I have let’s say 10 sample zones, I select the different zones with a menu in my UI linked to the script “Zone Select Sample” you helped me some time ago:

local layer = this.parent
local zones = layer:findZones()

local zoneNames = {}
for i = 1, #zones do
	zoneNames[i] = zones[i].name
end

function setScope()
	local layerName = layer.name
	local zoneName = zones[activeZone].name
	scope = "@0:"..layerName.."/@0:"..zoneName
end

defineParameter("activeZone", nil, 1, zoneNames, setScope)
defineParameter("scope", nil, "")

setScope()

function onNote(e)
	playNote(e.note, e.velocity, -1, zones[activeZone])
end

I would like that the Drop element to be active only in the last zone I have named 'User Sample" is selected with the “Zone Select Sample” menu and if It’s possible to show the “Pitch Detection “Progress” bar only if the 'User Sample” zone is selected. I know there is a sort of trick in Kontakt scripting to move in and out elements from the UI, for example ;
if the group “'User Sample” is active move those elements inside the UI… else move them out…
Is it possible to do something similar in Halion with LUA?

Thanks a lot for your help!

Create a Disable element on your macro page and place both the Drop and the progress bar inside it. Connect the Disable to your zone selector parameter. Set up the Disable so it’s only enabled for the last zone and disabled for all other zones.

The only thing you need to adjust in the other script is the zone variable to make sure you get the user sample zone. It’s difficult to say without seeing the program tree but it could be something like:

local zone = this.parent:getLayer():getZone(10)

That is if the zone is the 10th zone in the first sublayer.

ok, I’m doing it step by step.
I have 3 Zones: Zone 1 - Zone 2 - User Sample
I have successfully set up the 'Drop Sample" script so that when I drop a sample it is loaded only in Zone 3 “User Sample”.
This is the Drop sample code I’m using:

defineParameter("Filename", nil, "", function() onFilenameChanged() end)
defineParameter("PitchDetectionProgress", nil, 0, 0, 100)

function setRootkey(sample)
    local zone = this.parent:getZone(3)
    local pitch, voiced = sample:getPitch(0, -1)
    local rootkey, detune = math.modf(pitch)
    if detune >= 0.5 then
        rootkey = rootkey + 1
        detune = detune - 1
    end
    detune = math.floor((detune * 100) + 0.5)
    print(pitch, voiced, rootkey, detune)
    if voiced then
        zone:setParameter("SampleOsc.Rootkey", rootkey)
        zone:setParameter("SampleOsc.Tune", detune)
    end
end

function keyTextFromSampleName(st)
    local notes = {c = 0, d = 2, e = 4, f = 5, g = 7, a = 9, b = 11}
    local mn = {}
    st = string.match(st, "([^\\/]+)%.%a+$")
    print(st)
    for k, s, n in string.gmatch(st, "[_%-%s]?([A-Ga-g])(%#?)(%-?%d)[_%-%s%.]?") do
        if k and n then
            local midinn = notes[string.lower(k)] + (s == "#" and 1 or 0) + (n + 2) * 12
            table.insert(mn, midinn)
            print(k, s, n, midinn)
        end
    end
    return mn[#mn]
end

function onFilenameChanged()
    local zone = this.parent:getZone(3)
    local sample = AudioFile.open(Filename)
    if sample.valid then
        zone:setParameter("SampleOsc.Filename", sample.fileName)
        zone:setParameter("SampleOsc.SampleStart", 0)
        zone:setParameter("SampleOsc.SampleEnd", sample.length)
        -- check loops
        local loop = sample.loops[1]
        if loop then
            zone:setParameter("SampleOsc.SustainLoopModeA", 1)
            zone:setParameter("SampleOsc.SustainLoopStartA", loop.loopStart)
            zone:setParameter("SampleOsc.SustainLoopEndA", loop.loopEnd)
        else
            zone:setParameter("SampleOsc.SustainLoopModeA", 0)
            zone:setParameter("SampleOsc.SustainLoopStartA", 0)
            zone:setParameter("SampleOsc.SustainLoopEndA", 0)
        end
        -- rootkey and detune
        local rootKey = keyTextFromSampleName(sample.fileName)
        zone:setParameter("SampleOsc.Rootkey", rootKey or 60)
        zone:setParameter("SampleOsc.Tune", sample.detune or 0)
        if not rootKey then
            sample:analyzePitch(setRootkey)
            while sample:getPitchAnalysisProgress() < 1 do
                PitchDetectionProgress = sample:getPitchAnalysisProgress() * 100
                wait(250)
            end
            PitchDetectionProgress = 0        
        end
    end
end

The only problem now is that the waveform display is working only for Zone 1 and Zone 2, when I select Zone 3 “User Sample” the waveform of the sample is not loaded… any idea why?

It’s for sure a problem related to the “Drop Sample” script I have copied above because as soon as I delete it from the tree the waveform of Zone 3 is displayed correctly…

Not sure… Can you upload a test vstpreset?

Or a screenshot so I can try to recreate a similar preset.

I have recreated it in an empty instrument and it works perfectly so It’s a problem with my instruments. There is a conflict with something else I have to find out what…

Sometimes when I upload a sample without the root in the name and halion start detecting the pitch I’m getting this script error and everything gets stuck until I reset the script… any idea why?
Screen Shot 2022-06-08 at 13.00.56 PM
Line 7 of the scrip is this;

local rootkey, detune = math.modf(pitch)

Can you post the whole script?

If you reload the script and drop the same sample does it work?

The pitch variable should be a number. The error message means it was nil. This can happen if you try to use getPitch before the analysis has finished.

To trouble shoot you can try to print the pitch and voiced variables to see what you get.

this is the full code, I tried to reload the same sample and it worked, but before it failed with that error. it also happened a few days ago with another sample. The problem is that if it happens when you are in Sonic you can’t reset the script and you are stuck…

defineParameter("Filename", nil, "", function() onFilenameChanged() end)
defineParameter("PitchDetectionProgress", nil, 0, 0, 100)

function setRootkey(sample)
    local zone = this.parent:getZone(1)
    local pitch, voiced = sample:getPitch(0, -1)
    local rootkey, detune = math.modf(pitch)
    if detune >= 0.5 then
        rootkey = rootkey + 1
        detune = detune - 1
    end
    detune = math.floor((detune * 100) + 0.5)
    print(pitch, voiced, rootkey, detune)
    if voiced then
        zone:setParameter("SampleOsc.Rootkey", rootkey)
        zone:setParameter("SampleOsc.Tune", detune)
    end
end

function keyTextFromSampleName(st)
    local notes = {c = 0, d = 2, e = 4, f = 5, g = 7, a = 9, b = 11}
    local mn = {}
    st = string.match(st, "([^\\/]+)%.%a+$")
    print(st)
    for k, s, n in string.gmatch(st, "[_%-%s]?([A-Ga-g])(%#?)(%-?%d)[_%-%s%.]?") do
        if k and n then
            local midinn = notes[string.lower(k)] + (s == "#" and 1 or 0) + (n + 2) * 12
            table.insert(mn, midinn)
            print(k, s, n, midinn)
        end
    end
    return mn[#mn]
end

function onFilenameChanged()
    local zone = this.parent:getZone(1)
    local sample = AudioFile.open(Filename)
    if sample.valid then
        zone:setParameter("SampleOsc.Filename", sample.fileName)
        zone:setParameter("SampleOsc.SampleStart", 0)
        zone:setParameter("SampleOsc.SampleEnd", sample.length)
        -- check loops
        local loop = sample.loops[1]
        if loop then
            zone:setParameter("SampleOsc.SustainLoopModeA", 1)
            zone:setParameter("SampleOsc.SustainLoopStartA", loop.loopStart)
            zone:setParameter("SampleOsc.SustainLoopEndA", loop.loopEnd)
        else
            zone:setParameter("SampleOsc.SustainLoopModeA", 0)
            zone:setParameter("SampleOsc.SustainLoopStartA", 0)
            zone:setParameter("SampleOsc.SustainLoopEndA", 0)
        end
        -- rootkey and detune
        local rootKey = keyTextFromSampleName(sample.fileName)
        zone:setParameter("SampleOsc.Rootkey", rootKey or 60)
        zone:setParameter("SampleOsc.Tune", sample.detune or 0)
        if not rootKey then
            sample:analyzePitch(setRootkey)
            while sample:getPitchAnalysisProgress() < 1 do
                PitchDetectionProgress = sample:getPitchAnalysisProgress() * 100
                wait(250)
            end
            PitchDetectionProgress = 0        
        end
    end
end

You can reset the script in Sonic but this is no good. We better try to avoid script errors.

The getPitch should return 2 values: number and boolean. That’s our pitch and voiced variables. If the pitch analysis failed the pitch will be -1 and voiced will be false. In sample editor you should see Pitch: N/A. If the pitch analysis succeeds the pitch should be the actual pitch as midi note number and voiced should be true.

If you call getPitch before the analysis has finished it will return nil. That’s most likely the reason for the script error you posted. But that shouldn’t happen though. Halion should wait until the analysis has finished and only then call the setRootkey function.

To play it safe we could do it like this:

defineParameter("Filename", nil, "", function() onFilenameChanged() end)
defineParameter("PitchDetectionProgress", nil, 0, 0, 100)

function setRootkey(sample)
    local zone = this.parent:getZone(1)
    local pitch, voiced = sample:getPitch(0, -1)
    if voiced then
        local rootkey, detune = math.modf(pitch)
        if detune >= 0.5 then
            rootkey = rootkey + 1
            detune = detune - 1
        end
        detune = math.floor((detune * 100) + 0.5)
        print(pitch, voiced, rootkey, detune)
        zone:setParameter("SampleOsc.Rootkey", rootkey)
        zone:setParameter("SampleOsc.Tune", detune)
    end
end

function keyTextFromSampleName(st)
    local notes = {c = 0, d = 2, e = 4, f = 5, g = 7, a = 9, b = 11}
    local mn = {}
    st = string.match(st, "([^\\/]+)%.%a+$")
    print(st)
    for k, s, n in string.gmatch(st, "[_%-%s]([A-Ga-g])(%#?)(%-?%d)") do
        if k and n then
            local midinn = notes[string.lower(k)] + (s == "#" and 1 or 0) + (n + 2) * 12
            table.insert(mn, midinn)
            print(k, s, n, midinn)
        end
    end
    return mn[#mn]
end

function onFilenameChanged()
    local zone = this.parent:getZone(1)
    local sample = AudioFile.open(Filename)
    if sample.valid then
        zone:setParameter("SampleOsc.Filename", sample.fileName)
        zone:setParameter("SampleOsc.SampleStart", 0)
        zone:setParameter("SampleOsc.SampleEnd", sample.length)
        -- check loops
        local loop = sample.loops[1]
        if loop then
            zone:setParameter("SampleOsc.SustainLoopModeA", 1)
            zone:setParameter("SampleOsc.SustainLoopStartA", loop.loopStart)
            zone:setParameter("SampleOsc.SustainLoopEndA", loop.loopEnd)
        else
            zone:setParameter("SampleOsc.SustainLoopModeA", 0)
            zone:setParameter("SampleOsc.SustainLoopStartA", 0)
            zone:setParameter("SampleOsc.SustainLoopEndA", 0)
        end
        -- rootkey and detune
        local rootKey = keyTextFromSampleName(sample.fileName)
        zone:setParameter("SampleOsc.Rootkey", rootKey or 60)
        zone:setParameter("SampleOsc.Tune", sample.detune or 0)
        if not rootKey then
            sample:analyzePitch(setRootkey)
            while sample:getPitchAnalysisProgress() < 1 do
                PitchDetectionProgress = sample:getPitchAnalysisProgress() * 100
                wait(250)
            end
            PitchDetectionProgress = 0        
        end
    end
end

Instead of error it should do nothing and the sample should stay mapped to C3 (default).

1 Like

Unfortunately, I’m still having script errors. I’m attaching a screenshot of the error reported by Halion in the output message window.
Halion instrument gets stuck after this and I have to reload it.
Thanks in advance for any help!

Screen Shot 2022-07-27 at 10.07.21 AM

This is strange. Try to change line 7 to:
if pitch and voiced then
For troubleshooting you could also try to print the pitch and voiced variables. To see what you get. The error suggests the pitch variable is nil.

Is it any particular sample so you can reproduce it?