Yes thanks, I see, no problem I just added the two lines I added in the previous script and it works great! Thanks a lot for your help!
I have clicks and pops when I loop a grain zone, I tried all possible solutions, crossfading the loop and adding a fade in and fade out to the sample but I continue to have those clicks and pops.
Any idea how I can avoid them? My instrument is completely useless with those clicks and pops!
This is the modified script that Iām using, as you can see when I change the sample start and end points the loop start and end point change accordingly.
But The problem is NOT the script, I tried also building a simple instrument with a grain zone, setting the same values for the sample start/end points and the loop start/end points and I have the same problem. This problem is not happening in padshop.
local zones = this.parent:findZones()
zoneNames = {}
for i = 1, #zones do
zoneNames[i] = zones[i].name
end
function zoneSelectChanged()
getSampleLength()
scope = "@0:"..zones[ZoneSelect].name.."/"
end
defineParameter("ZoneSelect", nil, 1, zoneNames, zoneSelectChanged)
defineParameter("scope", nil, "")
defineParameter("SampleStart", nil, 0, 0, 0x7fffffff, 1)
defineParameter("SampleEnd", nil, 0, 0, 0x7fffffff, 1)
defineParameter("Filename", nil, "", function() onFilenameChanged() end)
defineParameter("PitchDetectionProgress", nil, 0, 0, 100)
defineParameter("PitchDetection", nil, false)
function sampleStartChanged()
local zone = zones[ZoneSelect]
zone:setParameter("SampleOsc.SampleStart", SampleStart)
zone:setParameter("SampleOsc.SustainLoopStartA", SampleStart)
if SampleEnd < SampleStart then
SampleEnd = SampleStart
zone:setParameter("SampleOsc.SampleEnd", SampleEnd)
end
end
function sampleEndChanged()
local zone = zones[ZoneSelect]
zone:setParameter("SampleOsc.SampleEnd", SampleEnd)
zone:setParameter("SampleOsc.SustainLoopEndA", SampleEnd)
if SampleStart > SampleEnd then
SampleStart = SampleEnd
zone:setParameter("SampleOsc.SampleStart", SampleStart)
end
end
function setRootkey(sample)
local zone = zones[ZoneSelect]
local pitch, voiced = sample:getPitch(0, -1)
if voiced and 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 = zones[ZoneSelect]
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)
-- sample start and end parameters
defineParameter("SampleStart", nil, 0, 0, sample.length, 1, sampleStartChanged)
defineParameter("SampleEnd", nil, 0, 0, sample.length, 1, sampleEndChanged)
SampleStart = 0
SampleEnd = sample.length
-- 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 and PitchDetection then
sample:analyzePitch(setRootkey)
while sample:getPitchAnalysisProgress() < 1 do
PitchDetectionProgress = sample:getPitchAnalysisProgress() * 100
wait(250)
end
PitchDetectionProgress = 0
end
end
end
function getSampleLength()
local zone = zones[ZoneSelect]
local fn = zone:getParameter("SampleOsc.Filename")
local sample = AudioFile.open(fn)
if sample.valid then
defineParameter("SampleStart", nil, 0, 0, sample.length, 1, sampleStartChanged)
defineParameter("SampleEnd", nil, 0, 0, sample.length, 1, sampleEndChanged)
SampleStart = zone:getParameter("SampleOsc.SampleStart")
SampleEnd = zone:getParameter("SampleOsc.SampleEnd")
end
end
function onLoad()
zoneSelectChanged()
end
function onNote(e)
playNote(e.note, e.velocity, -1, zones[ZoneSelect])
end
Itās probably because the loop points are not at zero crossing. If the fades and crossfades donāt help this could be a problem.
Iām still experimenting but it looks like disabling āSnapā and enabling āSnap to zero crossingā improve a lot the clicks and pops problem
Hi, Iām trying to modify this script so that when I move the start and end points of the sample they snap to a 1/32 grid instead of moving freely, any suggestions on how I can do this?
I would try to calculate how many samples is 1/32. Assuming the sampling rate is 48 kHz and tempo is 120bpm it should be 3000. Then round the sample start/end to multiples of whatever the 1/32 is (3000 in this case).
āThanks for the reply! Iāve tried several methods, but none seem to work quite right. The best approach I have so far involves using the plus/minus buttons to snap the start and end points to the grid. This code works perfectly for the StartStepPlus
and StartStepMinus
buttonsāthe start point snaps precisely to the grid, even when I change the tempo. However, as soon as I use the EndStepMinus
and EndStepPlus
buttons, everything gets messed up. The EndStepPlus
button also doesnāt work at all.
Does anyone know why itās working perfectly for the Start buttons but not for the End buttons?ā
local layer = this.parent
local zones = this.parent:findZones()
zoneNames = {}
for i = 1, #zones do
zoneNames[i] = zones[i].name
end
function zoneSelectChanged()
getSampleLength()
scope = "@0:Layer 01/@0:"..zones[ZoneSelect].name.."/"
end
defineParameter("ZoneSelect", nil, 1, zoneNames, zoneSelectChanged)
defineParameter("scope", nil, "")
defineParameter("SampleStart", nil, 0, 0, 0x7fffffff, 1)
defineParameter("SampleEnd", nil, 0, 0, 0x7fffffff, 1)
defineParameter("Filename", nil, "", function() onFilenameChanged() end)
-- New parameters for plus/minus switches
defineParameter("StartStepPlus", "Start +1/16", false, nil, function() stepStart(1) end)
defineParameter("StartStepMinus", "Start -1/16", false, nil, function() stepStart(-1) end)
defineParameter("EndStepPlus", "End +1/16", false, nil, function() stepEnd(1) end)
defineParameter("EndStepMinus", "End -1/16", false, nil, function() stepEnd(-1) end)
-- Calculate the size of one 1/16th step
function getGridStep(totalLength)
return math.max(1, math.floor(totalLength / 16))
end
-- Function to step the start position
function stepStart(direction)
local zone = zones[ZoneSelect]
local totalLength = zone:getParameter("SampleOsc.SampleEnd")
local gridStep = getGridStep(totalLength)
-- Calculate new position
local newStart = SampleStart + (gridStep * direction)
-- Ensure we stay within bounds
newStart = math.max(0, newStart)
newStart = math.min(newStart, SampleEnd)
-- Update parameters
SampleStart = newStart
zone:setParameter("SampleOsc.SampleStart", SampleStart)
zone:setParameter("SampleOsc.SustainLoopStartA", SampleStart)
end
-- Function to step the end position
function stepEnd(direction)
local zone = zones[ZoneSelect]
local totalLength = zone:getParameter("SampleOsc.SampleEnd")
local gridStep = getGridStep(totalLength)
-- Calculate new position for SampleEnd
local newEnd = SampleEnd + (gridStep * direction)
-- Ensure we stay within bounds
newEnd = math.max(SampleStart, newEnd) -- End point shouldn't be before start
newEnd = math.min(newEnd, totalLength) -- End point shouldn't exceed sample length
-- Update parameters
SampleEnd = newEnd
zone:setParameter("SampleOsc.SampleEnd", SampleEnd)
zone:setParameter("SampleOsc.SustainLoopEndA", SampleEnd)
end
function onFilenameChanged()
local zone = zones[ZoneSelect]
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)
-- Define sample start and end parameters
defineParameter("SampleStart", nil, 0, 0, sample.length, 1)
defineParameter("SampleEnd", nil, sample.length, 0, sample.length, 1)
SampleStart = 0
SampleEnd = sample.length
-- Set the sustain loop points
zone:setParameter("SampleOsc.SustainLoopStartA", SampleStart)
zone:setParameter("SampleOsc.SustainLoopEndA", SampleEnd)
end
end
function getSampleLength()
local zone = zones[ZoneSelect]
local fn = zone:getParameter("SampleOsc.Filename")
local sample = AudioFile.open(fn)
if sample.valid then
defineParameter("SampleStart", nil, 0, 0, sample.length, 1)
defineParameter("SampleEnd", nil, sample.length, 0, sample.length, 1)
SampleStart = zone:getParameter("SampleOsc.SampleStart")
SampleEnd = zone:getParameter("SampleOsc.SampleEnd")
end
end
function onLoad()
zoneSelectChanged()
end
function onNote(e)
playNote(e.note, e.velocity, -1, zones[ZoneSelect])
end