Hello, I have made another tool, similar structure to the last one. A new hotkey that interpolates between two step modulation points, the current step and the next to occur, across patterns. I often set modulation to occur across several patterns and I hate using graphical automation. Now I can define my start and end values and tie between them.
As is with the last one, please point out where I went wrong and I will fix it.
--interpolator
local function interpolate(stepCounter, startValue, endValue, curStep)
if startValue < endValue then
return ((endValue) * (curStep / stepCounter)) + startValue
else
return (startValue) - ((curStep / stepCounter) * startValue)
end
end
This will go larger than endValue and crash when writing until a sufficiently large byte value.
you are getting a normalized value from 0 to 1 with curStep / stepCounter which represents your progress from start to end
If you multiply the endValue (letâs say 200) with 1 and add the startValue (for example 100) you get 300, when it shouldâve been between 100 and 200.
Your second case doesnât even use the endValue which means it cannot be right.
Below is the common equation for linear interpolation (or lerp) a + t * (b - a)
t is your time that goes form 0 to 1
a and b are the two end points, the neat thing is that their order doesnât matter, the equation will work whether or not a is smaller than b
Try updating your function knowing this.
Some other points
Your code never accounts for a possible difference in pattern length, if the interpolation would need to go through patterns with different lengths the tool will leave gaps (if the âbridge patternâ is longer) or possibly crash (if it is shorter).
What if there is no end point to be found? Maybe the tool should alert the user or try interpolating to the currently set value on the parameter until the end of the project
While technically not wrong, Iâd suggest trying to keep it simple when writing logic, for example your isLineEmpty function complicates things to a point it might be difficult to see what it does (especially if you come back to this a few weeks later)
local isLineNotEmpty = true
if deviceString ~= "00" then
isLineNotEmpty = false
end
return isLineNotEmpty
Whenever you want to return a boolean (true or false value) based on some condition, you can just return the condition. These two blocks of code do the same thing, if the deviceString contains â00â it will return true otherwise false.
Thank you for the code review again. I really value the hints. As is clear, I donât really know what Iâm doing and Iâm looking around in the dark. That said, I think I was able to implement everything you suggested.
lerp is now the right math and works better
logic for different pattern lengths
if there is no end point it will use the parameters current value and run it until the end
isLineNotEmpty is now cleaner, although that doesnât matter for the end user, but it helps me learn
gif below shows:
correct values
running across different pattern lengths
to the end
previous functionality is the same, will run between two defined points.
Good job! And donât worry, we are all looking around in the dark.
This tool seems rather complete for what it aims to do now, but if you ever wanted to upgrade it, consider making it so that you could execute the command from anywhere in-between two values.
As you are more likely to enter the end value later (and then you have to scroll back to the start value to do the interpolation), it would make sense to do it backwards as well, but then it would be ambiguous which way you wanted to interpolate if there was a similar parameter value in both directions, so I guess the general solution would be to make it so that it needs to be started from an empty line, it looks in both directions, and if it finds two parameter values with matching device on both ends, it does the interpolation in-between them.
How dare you give me a new idea to obsess over until I complete it as soon as possible. Haha. I will see if I can get that working. You have good ideas.
Works no matter where you use the hotkey (unless there is literally no step modulation in the track)
If you use it between two points it will interpolate between them. goes across patterns of different sizes as well
If you use it ON a point, that point will be used as the start point and it will run to the next point down
If you use it on a blank line and there is no next point, it will run the modulation to the end of the track
If you use it on a blank line and there is no previous point, it will run the modulation to the beginning of the track
I think with this you can really fly with interpolating now. The only glitches that I anticipate are:
that it doesnât run to the end if there are deleted/unused patterns in the track. I donât know how to pull the count of used patterns out. Just delete unused patterns if you donât need them or place a modulation point at the end
It doesnât really know what to do with alias patterns. Neither do I. Iâm sure I could make something clever and complicated that loops and it and then carries on where it left off. I donât think its really in the spirit of the idea though. I just wanted to make something that interpolates from A to B, so Just be mindful of using it around aliases I guess.
There is a bit of a general problem with your method. Patterns in renoise live in renoise.song().patterns[] but they are not necessarily in order (or even used) inside your pattern sequence (which is shown in the pattern matrix on the left of renoise).
So your song may be like 0, 1, 2, 8, 9, 5 or any other ordering. By default renoise will try keeping them at least in order (if the sorting is enabled) but you can still have gaps.
You can get the indices this sequence has using renoise.song().sequencer.pattern_sequencethis.
You can define a helper function to get the pattern based on the index inside the sequence instead of the index in the patterns list.
function pattern_from_sequence(index)
local song = renoise.song()
local seq = song.sequencer
if index <= #seq.pattern_sequence then
return song:pattern(seq.pattern_sequence[index])
else
return nil
end
end
You can also get the currently selected index for this by song.selected_sequence_index.
If a pattern track is aliased the tool could treat it as the end of the song. You can see if it is using song:pattern(pattern_index):track(track_index).alias_pattern_index which will be zero if it is not aliased, otherwise the alias index.
If you feel like your main is getting a bit unwieldy, it can help if you split it up into parts that all do just one thing. For example have a function that gets a position (as a starting sequence index and a line number) and track/device target and returns a range of start and end positions where the interpolation should happen, then have another function that receives such a range with track/device and does the actual interpolation.
While technically you could write everything into one long function, doing the splitting can help a lot with debugging as you donât have to keep all the context in your head to understand where you have some edge-case you didnât cover.
Iâm sorry, I canât recreate this, is this with the latest version I posted this morning? You should only need one modulation point now. Could you share a screenshot or picture of the point and patterns youâre trying to use it on? When I use the hotkey on a single modulation point it runs to the end of the project.
Oh I see now. I donât normally use that base device in the effect column. I didnât write the script to consider L, P, or W commands. I will be able to think of something to fix that, I will take a look tonight. Thanks.
Could you try doing the same thing on a modulation for a device, like a filter sweep?
I would never find these issues myself/if I never shared this because I tend to do the same thing over and over.
we are quickly approaching the limit of my abilities for complexity. I will take a look and try to figure out the indexing of patterns without the sequencer order. Last night I started to break my functions out, but it is always a struggle for me. Lack of experience I guess.
everything seems to be working. but I was confused by the moment where the final point aligns automation for itself even if there are no values for automation. this can cause a problem if the final automation point is the beginning of the next pattern.
if the cursor is on a modulation point, that will be the first point, regardless of if there is modulation prior in the project. (first attempt in gif)
if the cursor is on a blank line, the first point will be the first modulation point above it finds and the second point will be the first modulation point below that it finds (second attempt in gif)
if no modulation point is found (either before or after) it will use the value currently set in the device itself as the destination modulation value
So, when you activate the hotkey the first time, it sends 124D to the end of the project because thatâs also what the parameter was set to on the device too. I think after using a bit, the arbitrary rules Iâve set up will be clear and modulating will be very simple.
This is a really good tool. Honestly itâs really good software: just what it offers and no more. You should be proud!
Only thing I would further request is exponential and logarithmic. I thought I saw someone else request it above but I donât see it. Iâm not sure how hard that would be to implement but this deserves to be a native feature, with those additions. Thanks Garbanzo.