-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Data race and precision issues in jog control object interaction from controller scripts #11232
Comments
Great, that you have found it. Maybe this is the cause of scratching instability? For my understanding this comment along with the set to 0 is wrong. Maybe it was a workaround for a broken controller script? mixxx/src/engine/controls/ratecontrol.cpp Line 370 in eff27ab
The "jog" co should always contain the current rate (velocity) of the jog wheel, not the number of degrees it has tuned since the last callback. When the controller is updated twice, the value must not be changed, when the velocity has not changed. The bug happens now, when we have two consecutive Audio Callback without a controller callback, because the drops to zero, which is wrong. The other issue it the jitter in case of changing jogwheel rates here it may be the case that an old value is read just before new value is written, which causes a dip in the rate ramp. I think the smoothing filter aim to deal with it but also lints this bug. With 64 bit OSs the control objects we have no read and write API. So I think we should make sure that only the controller thread is reading. |
Scratching using Control scripts that directly modify the
No that is indeed correct! The This value is then processed through a low-pass filter to obtain a velocity. The currently used low-pass filter is not idea though since it uses a fixed 25 buffer smoothing period (which is 33.3 ms at a buffer size of 64 and a sample rate of 48 kHz, and over half a second at a buffer size of 512 samples). To avoid the issue with writes getting lost and with a write happening between the read and reset the control object should use atomic operations. An atomic fetch-and-add operation should be used to increment the jog control object's value so duplicate writes don't get lost, and an atomic swap/exchange can be used in the rate control function to get the control object's value and to simultaneously reset it to 0. |
You describe the behavior how it is described. But this cannot work as you correctly pointed out and it is also not implemented most of the controller scripts. Like here for instance:
A Quick search only reveals: mixxx/res/controllers/Denon-MC4000-scripts.js Line 127 in 52bcaf1
I consider them as broken, because it results in a different Jog sensitivity depending on the audio buffer size. Some insights: We consider to offer that topic as a GSoC Projec to fix the issue at the root: |
Like I said, those are all incorrect. These two you linked: mixxx/res/controllers/Denon-MC4000-scripts.js Line 127 in 52bcaf1
Are also broken. The control object's value may change between the two calls, which means that this version may move the playhead too much rather than too little. The fix as I outlined earlier would be to:
|
'jog" takes the velocity, not the distance passed like the last callback. That's why the solution of reading back the jog value in the controller mapping is wrong. With the solution you have outlined, the jog sensivity depend on the audio buffer size which is not desired. With the current velocity solution and long audio callbacks we may miss a jog update or read the same value twice. This is the jitter issue we may compensate. |
Sending distance instead of velocity to the control object may result in more consistent timings and be the preferable approach since the filtering is only done on the audio thread, but using instantaneous velocity like it's doing now is not necessarily wrong. The data needs to be treated differently and the final result is slightly different, but the approaches are mostly equivalent. Either approach just moves the derivative step to a different place in the equation. |
Bug Description
Mentioned in #11199. There are some potential issues with the way the
jog
control object is interacted with from controller scripts. This value is an accumulator that is read and then set to zero inRateControl::getJogFactor()
. There are two issues with the way it's used now:jog
values from older ticks will have been overwritten by the later ones and are thus discarded. This can be fixed by storing the value in the control object as an atomic double, and using a fetch-and-add to atomically add to it from the control script.getJogFactor()
function the control object is read, checked, and then set to 0 if the old value was nonzero. The control script could have changed the value in between the read and set, which causes that value to get thrown out. This can be solved by using an atomic exchange operation to get the value and reset the control object to 0 in one operation.Additionally, but this has nothing to do with race conditions, the smoothing on the jog control should probably be modified. The default is a linear-phase box filter with a latency of 12.5 buffers. This causes nudging to feel unresponsive, especially at larger buffer sizes. A simple but effective replacement would be a simple first order low-pass filter/exponential moving average that takes the buffer length into account..
Version
all current versions
OS
No response
The text was updated successfully, but these errors were encountered: