Skip to content
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

MIDI: Audible artifacts for knobs with center position caused by absoluteLin() #14383

Open
uklotzde opened this issue Feb 20, 2025 · 4 comments
Labels

Comments

@uklotzde
Copy link
Contributor

Bug Description

The following code causes audible artifacts:

filterKnob = function(_channel, _control, value, _status, group) {
    let filterParam = script.absoluteLin(value, 0.0, 1.0);
    engine.setParameter(group, "super1", filterParam);
};

Similar code can be found in various controller scripts for symmetric parameters with the value range 0..1 and the center value 0.5.

When the knob is centered it sends the MIDI value 0x40 (= 64). This value should be mapped to 0.5 to disable the effect/filter. Unfortunately, the simplified linear interpolation in absoluteLin() results in a slightly different value 0.503937008 and the filter is active all the time. Depending on the selected effect this noticeably degrades the sound quality!

The following code works as expected:

filterKnob = function(_channel, _control, value, _status, group) {
    var filterParam = script.absoluteLin(value, 0.0, 1.0);
    if (value === 0x40) {
        // Fix value for center position.
        value = 0.5;
    }
    engine.setParameter(group, "super1", filterParam);
};

Version

No response

OS

No response

@uklotzde uklotzde added the bug label Feb 20, 2025
@uklotzde
Copy link
Contributor Author

uklotzde commented Feb 20, 2025

Using absoluteNonLin() instead might work, but Math.round() looks brittle. For the typical MIDI range the exact calculated center value is (127 - 0) / 2 = 63.5. Rounding errors could flip it into either direction, e.g. either 63 or 64.

Even if it works for MIDI it might not work correctly for other ranges.

For a solid implementation of absoluteNonLin() the input center value also needs to be provided as an argument instead of calculating it from min and max.

@Swiftb0y
Copy link
Member

Swiftb0y commented Feb 20, 2025

Can confirm. The problem is that there is no middle element in the $[0; 127]$ range. A "fix" could be to set the max to $128$, but then the maximum return value will be off. A fix would be to split the values into two ranges, eg $[min,63]$ maps to $[0, 0.5]$ and then $[64, max]$ to $[0.5, 1.0]$. While this would result in a small step (the function becomes discontinuous), that may even be desirable to implement to mimic some slight snapping. This hack is applied here for instance:

inValueScale: function(value) {
// Hack to get exact center of pots to return 0.5
if (value > (this.max / 2)) {
return (value - 1) / (this.max - 1);
} else {
return value / (this.max + 1);
}
},

Edit, obviously substituting in low, high, min, max, etc instead of the above hardcoded values.

@uklotzde
Copy link
Contributor Author

script.absoluteNonLin = function(value, low, mid, high, min, max, center) {
    if (!min) {
        min = 0;
    }
    if (!max) {
        max = 127;
    }
    if (!center) {
        center = (max - min) / 2;
    }
    if (value === center || value === Math.round(center)) {
        return mid;
    } else if (value < center) {
        return low + (value / (center / (mid - low)));
    } else {
        return mid + ((value - center) / (center / (high - mid)));
    }
};

@uklotzde
Copy link
Contributor Author

If someone complains about sound quality with a controller connected consider this bug as a possible cause.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants