Lately,
@thecld asked me for some clarification about the code I posted in this thread, in a private conversation. Since my answer was enxtensive and -dare i say- potentially useful to others, I'm posting both it and his leading question here, with his permission.
All the following is a "condensing" of my earlier posts in this thread, and the code it refers to is the one from this post:
https://vi-control.net/community/th...ut-of-phase-solved-via-ksp.82645/post-4396351
Hi Fredeke,
I'm trying to learn / understand how to build a scripted LFO - I've come across your thread about LFOs being out of sync and I'm trying to understand how do you created that formula for phase increment (I believe that that's the part that I should be interested in if I want to avoid glitches when changing the LFO rate?):
Code:
on ui_control ($vibrato_rate)
$vibrato_period := 5 + real_to_int (995.0 * pow ((129.0-int_to_real($vibrato_rate)) / 129.0 ,1.75) )
end on
That number: 1.75 -> from where does it come from? I can understand that 5+995 = original vibrato_period and 129.00 = vibrato_rate.
I've checked out that site that EvilDragon has linked so I know where you get that idea but I'm having hard time understanding it completely to build my own version of it...
Hope you don't the question, would really appreciate any help if you have the time, thanks in advance!
Roman
Hi
You understand the principle well: add the period to a counter, and derive pitch changes from a sine function of that counter (or a triangle function, or whatever you want your LFO's shape to be).
Also, set your counter back -2PI every time it gets greater than 2PI, just to avoid its value getting too large for KSP to handle over time.
But all that is in the
listener callback, not here.
This is the
ui_control callback. It's really a sophistication you don't need: It's not part of the LFO engine, it's just my overcomplicated way of controlling the LFO's speed.
Apparently my LFO engine needs a period rather than a rate - I can't remember why. So if you want to adapt it, I suggest you start with a simple "period" ui control and no ui_control callback.
So, all I'll be explaining from now on is dispensable, and can be implemented later if at all.
I'll explain it anyway, but only because I like the sound of my own voice.
Me, I wanted to provide users with a "rate" knob instead of a "period" knob (to emulate vintage synthesizers).
Remember: Period = 1 / Rate
So if you want to provide a "rate" control instead of a "period" control, you could just do that. Just make sure your "rate" knob can't reach 0, because you can't divide by 0.
Here's why, in my case, the formula became much more complicated:
In my instrument, all controls used 0-to-127 values to mimic MIDI. They didn't necessarily have to, but I had decided that early on in development, and it was too late to change it. So the whole formula you mention is just meant to translate a 0-to-127
rate into a time unit based
period.
At this point, you can probably stop reading. But I'll explain the formula anyway:
Let's read that line from $vibrato_rate (the knob) outwards to $vibrato_period (the value needed in the actual LFO engine which is in the listener callback):
- The knob has values from 0 to 127, so (127-$vibrato_rate) would invert it, and I guess I wanted it inverted. I suppose that constant 127 became 128 because I wanted to avoid a zero result and then it became 129 just because I liked the feel of that response better.
- Then there's that division by 129 which is meant to get the knob's position in form of a real number between 0 and 1, instead of an integer from 0 to 127, just for my sanity's sake.
- Then I though the knob's response was too sensitive to my taste at one end of its course, and not precise enough at the other end, so I applied a squared (= power of 2) response to it. But then it felt overcorrected so I settled on a power of 1.75. (The neat thing is that when you elevate a number comprised between 0 and 1 to any power, the result is still comprised between 0 and 1)
- Then I needed a value in seconds, or milliseconds, or microseconds - I can't remember which - for the period. Anyway, it required multiplying that 0<x<1 number by 1000. I split 1000 into 995 and 5, because I needed a tiny offset (5), because really, a period of 0 ms would make no sense and have no use. Of course, a multiplier of 995 is not 1000 (and I suppose it could just as well have been 1000), but I'm guessing either it made no difference in practice, or maybe I even liked it better that way.
Again, I arrived at some of those numbers by trial and error.
I hope this helps.