Is there any way to find out if a SmoothValue object has timed out yet? I tried comparing GetSmoothValue()'s output with that of GetTargetValue(), but it doesn't seem to work correctly. On another note, what is the default time for SmoothValue()?
Reid
SmoothValue Timing
-
- Posts: 625
- Joined: Mon Nov 15, 2021 9:23 pm
SmoothValue Timing
Cyberwerks Heavy Industries -- viewforum.php?f=76
Re: SmoothValue Timing
Interesting. It would be nice to know exactly how SmoothValue works.
I just did a quick test comparing a SmoothValue object's output with the value of a knob that it's tracking and after the smoothing period the difference is zero. So my guess is that SmoothValue might be doing something like slew limitng rather than anything that produces an exponential/asymptotic decay.
I just did a quick test comparing a SmoothValue object's output with the value of a knob that it's tracking and after the smoothing period the difference is zero. So my guess is that SmoothValue might be doing something like slew limitng rather than anything that produces an exponential/asymptotic decay.
Re: SmoothValue Timing
I attached a square wave LFO to R_Ware's Control and got it to wiggle the Out Level slider on Simple Amplifier (with 5V DC input).
This is the plot I recorded and opened inside Audacity:
This initially suggested an asymptotic curve to me, similar to an RC charge/discharge curve. However, when I zoomed in, it became apparent that the curve consisted of a series of straight-line interpolations. This was due to the Control module unconditionally sending out a new target value on a regular basis, so the slider position was generating a new Notify call, even if it hadn't changed. The default rate is 94 Hz. I reduced the Resolution control (knob Notify() call rate) down to 1Hz. This led to a single straight-line transition from one value to another:
Audacity reported the (default) transition time as being 25ms in length:
Note that this is a time value, not a rate value; the slope will be steeper for bigger jumps in value, and shallower for smaller jumps.
The main take-away for me is that if you perform a call to SmoothValue.SetValue(double value) before the previous smoothing period has elapsed, a new slope will be calculated from the current smoothed value to the new target value. Do that more frequently than the smooth time, and you will approach the target value asymptotically.
This is the plot I recorded and opened inside Audacity:
This initially suggested an asymptotic curve to me, similar to an RC charge/discharge curve. However, when I zoomed in, it became apparent that the curve consisted of a series of straight-line interpolations. This was due to the Control module unconditionally sending out a new target value on a regular basis, so the slider position was generating a new Notify call, even if it hadn't changed. The default rate is 94 Hz. I reduced the Resolution control (knob Notify() call rate) down to 1Hz. This led to a single straight-line transition from one value to another:
Audacity reported the (default) transition time as being 25ms in length:
Note that this is a time value, not a rate value; the slope will be steeper for bigger jumps in value, and shallower for smaller jumps.
The main take-away for me is that if you perform a call to SmoothValue.SetValue(double value) before the previous smoothing period has elapsed, a new slope will be calculated from the current smoothed value to the new target value. Do that more frequently than the smooth time, and you will approach the target value asymptotically.
______________________
Dome Music Technologies
Dome Music Technologies
Re: SmoothValue Timing
It turns out that you can also set a rate value instead of a time value:
public void SetFixedTime(double glideMilliseconds, double deltaValue)
Sets a fixed amount to glide to in a fixed amount of time. For example: SetFixedTime(1000, 1.0) will take 1 second to glide a distance of 1.0. Meaning a change from 0 to 5.0 would take 5 seconds.
Parameters:
glideMilliseconds - time in milliseconds.
deltaValue - the amount to change in glideMilliseconds.
public void SetFixedTime(double glideMilliseconds, double deltaValue)
Sets a fixed amount to glide to in a fixed amount of time. For example: SetFixedTime(1000, 1.0) will take 1 second to glide a distance of 1.0. Meaning a change from 0 to 5.0 would take 5 seconds.
Parameters:
glideMilliseconds - time in milliseconds.
deltaValue - the amount to change in glideMilliseconds.
______________________
Dome Music Technologies
Dome Music Technologies
Re: SmoothValue Timing
Excellent work Grant. I was going to do more testing but you've saved me the time.
So it looks like it is calculating a delta when SetValue() is called and then just adding it every call to GetSmoothValue() until the target is hit. Presumably there's also some test to handle rounding error as the final value seems to be exactly right in my test.
However Reid reported that the end state didn't work correctly i.e. GetSmoothValue() != GetTargetValue() so I wonder what's going on there?
By the way I use y[ z ] = a * x[ z ] + ( 1 - a ) * y[ z - 1 ] sometimes to do smoothing and it produces the RC charge/discharge curve you showed. It's effectively a very simple one-pole IIR filter. But the asymptotic curve is often undesirable so the linear response makes more sense in this application as well as being cheaper providing the delta is only calculated at a low control rate.
So it looks like it is calculating a delta when SetValue() is called and then just adding it every call to GetSmoothValue() until the target is hit. Presumably there's also some test to handle rounding error as the final value seems to be exactly right in my test.
However Reid reported that the end state didn't work correctly i.e. GetSmoothValue() != GetTargetValue() so I wonder what's going on there?
By the way I use y[ z ] = a * x[ z ] + ( 1 - a ) * y[ z - 1 ] sometimes to do smoothing and it produces the RC charge/discharge curve you showed. It's effectively a very simple one-pole IIR filter. But the asymptotic curve is often undesirable so the linear response makes more sense in this application as well as being cheaper providing the delta is only calculated at a low control rate.
-
- Posts: 625
- Joined: Mon Nov 15, 2021 9:23 pm
Re: SmoothValue Timing
Thanks mucho, guys. That really helps. I'm not sure why the difference isn't working in my case. Basically, I have a separate class and file that does the dirt in my main class, but doesn't have controls; those are in the main class and file. Sort of a Letter/Envelope pattern. So I'm doing a couple of kludges to make sure the final value for a slider in the outside class gets reported to the inner class eventually. If anyone has a better way of doing that, I'd love to hear it.
I'm not sure the test really doesn't work, because there's something else going on that I can't account for yet: on repeated reads one of my SmoothValue objects assumes successively larger values, actually moving away from the set value. This feels like one of those situations where you've made some (unknown) code change a while back that breaks a number of things, but sotto voce, so they didn't show up right away. Just love those. Anyway, thanks for the assist. You guys are the best.
Reid
I'm not sure the test really doesn't work, because there's something else going on that I can't account for yet: on repeated reads one of my SmoothValue objects assumes successively larger values, actually moving away from the set value. This feels like one of those situations where you've made some (unknown) code change a while back that breaks a number of things, but sotto voce, so they didn't show up right away. Just love those. Anyway, thanks for the assist. You guys are the best.
Reid
Cyberwerks Heavy Industries -- viewforum.php?f=76
Re: SmoothValue Timing
Well, I'm always wary of using the equivalence operator with any kind of floating point variables. My day job is in embedded software and I'm used to working to MISRA standards, so I automatically get a shudder when I see a test for (doubleA == doubleB). I suspect that if you blindly accumulate 48 * 25 (= 1200) deltas, there is the possibility that you could end up one LSB out.ColinP wrote: ↑Sun Aug 20, 2023 4:56 pm So it looks like it is calculating a delta when SetValue() is called and then just adding it every call to GetSmoothValue() until the target is hit. Presumably there's also some test to handle rounding error as the final value seems to be exactly right in my test.
However Reid reported that the end state didn't work correctly i.e. GetSmoothValue() != GetTargetValue() so I wonder what's going on there?
In fact, I'm so paranoid about comparing doubles, I always use nested if statements rather than a switch statement when decoding a VoltageSwitch position! (It's stupid - I know that if you assign an integer to a double, it will be accurately held as an extact integer up to 52 bits. However, old habits die hard.)
______________________
Dome Music Technologies
Dome Music Technologies
Re: SmoothValue Timing
I can only offer a couple of guidelines:UrbanCyborg wrote: ↑Sun Aug 20, 2023 6:35 pm I'm not sure the test really doesn't work, because there's something else going on that I can't account for yet: on repeated reads one of my SmoothValue objects assumes successively larger values, actually moving away from the set value.
1. Ensure that you're constantly performing GetSmoothValue() every iteration of ProcessSample, even if you throw the value away. This is necessary to ensure that the delta values are added to the running smoothed value at the correct rate.
2. Ensure that you only perform SetValue() less frequently than the glide timeout period (25ms / 40 Hz by default). Otherwise, you'll get that asymptotic effect shown above, effectively NEVER reaching the target value until the heat death of the universe.
______________________
Dome Music Technologies
Dome Music Technologies
Re: SmoothValue Timing
I suspect you've got some code unexpectedly calling SetValue() on the SmoothValue object there as it's hard to see how you could get that behaviour otherwise.UrbanCyborg wrote: ↑Sun Aug 20, 2023 6:35 pm I'm not sure the test really doesn't work, because there's something else going on that I can't account for yet: on repeated reads one of my SmoothValue objects assumes successively larger values, actually moving away from the set value.
Nothing wrong with being paranoid sometimes. I often write similar JUST IN CASE code. And given the recent discussion on thread-safety such practice may help hold together code that otherwise might wobble when something weird happens once in a blue moon.utdgrant wrote: ↑Sun Aug 20, 2023 6:59 pm In fact, I'm so paranoid about comparing doubles, I always use nested if statements rather than a switch statement when decoding a VoltageSwitch position! (It's stupid - I know that if you assign an integer to a double, it will be accurately held as an extact integer up to 52 bits. However, old habits die hard.)
Another thing worth mentioning for less experienced programmers than Grant and Reid is that when you print out values when debugging, sometimes the priniting functions round the values to so many decimal places so it's possible that a floating point value is very slightly off but the numbers you see don't reflect this.
-
- Posts: 625
- Joined: Mon Nov 15, 2021 9:23 pm
Re: SmoothValue Timing
That's the odd thing; there's only one call to SetValue() on that object, made from the Notification() call for the control the smoother is attached to. There are a number of calls to GetSmoothValue() for the object in various places, but that shouldn't modify the smoother's value. I know! Must be quantum noise!ColinP: I suspect you've got some code unexpectedly calling SetValue() on the SmoothValue object there as it's hard to see how you could get that behaviour otherwise.
Reid
Cyberwerks Heavy Industries -- viewforum.php?f=76