Is it possible on the macro page to set up in your GUI a sort of “drag and drop” section where you can import your own samples into your instrument? Something like is possible with scripting in Kontakt 6?
Thanks in advance for any help!
Is it possible on the macro page to set up in your GUI a sort of “drag and drop” section where you can import your own samples into your instrument? Something like is possible with scripting in Kontakt 6?
Thanks in advance for any help!
Yes, it is. You need to create a Drop element on your macro page.
A little scripting might be necessary though. While you can connect the Drop to the SampleOsc.Filename parameter directly it will change just that… the file name. But it will not set the other parameters like sample end, loop points… Those will still have the same values as the previous sample.
But you can set those to correct values using script. In that case I would create a script parameter for the sample path and connect the Drop to it. Then use the script to change the zone parameters (filename, sample end…)
Hello, thanks for the replay!
is there a script example? I have searched this forum but it looks like nobody posted about this.
Does it look like both links you posted are not working?
Thanks!
Yes, the webpage is down for some reason right now.
You could try something like this:
defineParameter("Filename", nil, "", function() onFilenameChanged() end)
function onFilenameChanged()
local zone = this.parent:getZone()
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
end
end
You may need to adjust the zone variable to make sure it finds the sample zone you want.
Drop Sample.vstpreset (8.8 KB)
Thank you it works really well!
is not possible to add a line to have Halion automatically detect the pitch and set the correct root key of the sample? Just checked on the developer’s website but it looks like every page is not working!
It is. You can check if sample metadata contains rootkey and use that or try the pitch detection.
However if the user drops a drum sample or something that doesn’t have a clear pitch the detected pitch can be way off. This could be quite annoying. So setting the rootkey automatically without giving the user option to decide may not be the best option.
I was trying to add a line to have the pitch detected when I drop a sample, is this something close?
The developer website is still downn!
zone:setParameter(‘SampleOsc.Rootkey’, sample.pitch)
I cannot test it right now but I would try:
zone:setParameter(‘SampleOsc.Rootkey’, sample.rootKey or 60)
I just tried but it seems not working for me root is always set up as C3, I tried also to remove the “or 60 " part” but is always set up as C3 I tried with very stable pitch sample like piano
This will work only if the sample metada contains the rootkey information. It doesn’t do any pitch detection.
Ok, my sample name is like this but is not working:
Key 23_F3.wav
also tried this but not working too:
Key 23_F 3.wav
and how is working the script to auto-detect the pitch?
thanks a lot for any help
You can try this one:
defineParameter("Filename", nil, "", function() onFilenameChanged() end)
defineParameter("PitchDetectionProgress", nil, 0, 0, 100)
function setRootkey(sample)
local zone = this.parent:getZone()
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 onFilenameChanged()
local zone = this.parent:getZone()
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
zone:setParameter("SampleOsc.Rootkey", sample.rootKey or 60)
zone:setParameter("SampleOsc.Tune", sample.detune or 0)
if not sample.rootKey then
sample:analyzePitch(setRootkey)
while sample:getPitchAnalysisProgress() < 1 do
PitchDetectionProgress = sample:getPitchAnalysisProgress() * 100
wait(250)
end
PitchDetectionProgress = 0
else
print("Rootkey from sample file: ", sample.rootKey)
end
end
end
It should try to read metadata first as this is quicker. If the rootkey information isn’t found then it does pitch detection.
Drop Sample 2.vstpreset (8.8 KB)
Thanks but it still not reading the metadata present in the file I upload.
My file is written like this:
Guitar 14_A3.wav
Why is not detecting the correct root key?
Di I have to write it in a specific way?
Well, it doesn’t matter what the file name is. It tries to read the rootkey from sample metadata if there is any. It doesn’t search for the pattern in the file name.
If there is no information in the sample metadata then it should try to analyse the sample itself (the audio, not the sample name).
Does it do anything if you drop a piano sample? Or something that has a clear and stable pitch?
I was thinking It was possible to do with a script something like this you can do in the Zone Editor (pls see screenshot attached).
That could be very helpful if it can read the pitch from the name file, also because based on my tests it detects the pitch correctly only if the sample has an extremely stable pitch… otherwise It almost always put C3
I see. You can try this one. Not guaranteed to work 100% though.
defineParameter("Filename", nil, "", function() onFilenameChanged() end)
defineParameter("PitchDetectionProgress", nil, 0, 0, 100)
function setRootkey(sample)
local zone = this.parent:getZone()
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()
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 = sample.rootKey or 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
Thanks this works great but It looks like is working only for NON-looped samples.
If I try to load a looped sample it looks like is doing nothing, no written message in the script output message window. All looped samples I tried to load are mapped to C3, any idea why?
Don’t know. Loops and rootkey shouldn’t conflict in any way.
Can you upload some of the samples that cause problems? Maybe somewhere dropbox or similar and then post a link.
I made several tests with other samples and it looks like the problem is not if the sample is looped or not.
I tried to remove from line 55 “sample.rootKey or” so that the line now is like this:
local rootKey = keyTextFromSampleName(sample.fileName)
and it seems to work properly now… It looks like is making confusion with metadata in the sample?
You are probably right. I changed the script to help you troubleshoot. It should print the rootkey from sample attributes and also from sample file name. I guess the samples that were causing problems had the rootkey set in sample attributes. At the moment the script checks in this order:
sample attributes >> sample file name >> default (C3)
But this can be changed to look at the sample file name first and ignore the sample attributes.
As you already did:
Here is the updated script (still checking attributes first - you may want to change that)
I also removed the pitch detection.
defineParameter("Filename", nil, "", function() onFilenameChanged() 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()
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
print(string.match(sample.fileName, "([^\\/]+)%.%a+$"))
print("Rootkey from sample attributes: ", sample.rootKey)
print("Rootkey from sample file name: ", keyTextFromSampleName(sample.fileName))
print() -- empty line
local rootKey = sample.rootKey or keyTextFromSampleName(sample.fileName)
zone:setParameter("SampleOsc.Rootkey", rootKey or 60)
zone:setParameter("SampleOsc.Tune", sample.detune or 0)
end
end