[GH-ISSUE #22] 'zippering' noise in a modulated environment, and what we did #18

Closed
opened 2026-05-05 22:01:48 -06:00 by gitea-mirror · 2 comments
Owner

Originally created by @baconpaul on GitHub (Dec 4, 2020).
Original GitHub issue: https://github.com/airwindows/airwindows/issues/22

Hi Chris

This is not so much an issue as an observation. We are wrapping up our Surge 1.8 release candidate and our users love love love the Airwindows integration. Thank you so much for making the plugs open source.

Surge is an environment with lots of modulation and so I wanted to add a comment on how I integrated your effects and that modulation.

It seems almost all your effects assume parameters are constant over the block. Most of surge does not assume this. It basically takes control rate things (like parameters) and detects change and then does some equivalent of param = param + dParam * sample where dParam = (param_new - param_old ) / BLOCK_SIZE. It's not quite that dumb but its close in some places. Without this you get a real opportunity for 'zipper' noise (where you get high frequency aliasing because the params jump in tiny little square waves). Of course that linear interp has its own alias problems.

So anyway to solve this when I integrated Airwindows in surge rather than passing a whole block to process, I divide my block into 8 and interpolate my params across that block. So basically instead of calling setParam; processReplacing( data, 32) I call for( i in 0..8 ) { setParam( p + dP ); processReplacing( data + i * 4, 4 ); }. This has the effect of having the parameter change more smoothly over a window. This comes at the cost of a bit of CPU because the standup/teardown code in processReplacing gets called more often.

It was easy to make surge patches with modulation onto FX which demonstrated this and the above fix worked great so there's nothing to really 'do' here. But it occurred to me that modulating in a DAW could have the same problem. So I just wanted to kinda flag this to you, and also flag that of the ones we chose to integrate, one of the members of our team did a pretty exhaustive look for which params were impacted, which you can find here: https://github.com/surge-synthesizer/surge/issues/3315

Anyway like I said: no action required, just a way to let you know what we did. Please feel free to just close this issue of course. And thanks again for the great effects. They are part of a set of pretty awesome new Surge 1.8 features!

Originally created by @baconpaul on GitHub (Dec 4, 2020). Original GitHub issue: https://github.com/airwindows/airwindows/issues/22 Hi Chris This is not so much an issue as an observation. We are wrapping up our Surge 1.8 release candidate and our users love love love the Airwindows integration. Thank you so much for making the plugs open source. Surge is an environment with lots of modulation and so I wanted to add a comment on how I integrated your effects and that modulation. It seems almost all your effects assume parameters are constant over the block. Most of surge does not assume this. It basically takes control rate things (like parameters) and detects change and then does some equivalent of `param = param + dParam * sample` where `dParam = (param_new - param_old ) / BLOCK_SIZE`. It's not quite that dumb but its close in some places. Without this you get a real opportunity for 'zipper' noise (where you get high frequency aliasing because the params jump in tiny little square waves). Of course that linear interp has its own alias problems. So anyway to solve this when I integrated Airwindows in surge rather than passing a whole block to process, I divide my block into 8 and interpolate my params across that block. So basically instead of calling `setParam; processReplacing( data, 32)` I call `for( i in 0..8 ) { setParam( p + dP ); processReplacing( data + i * 4, 4 ); }`. This has the effect of having the parameter change more smoothly over a window. This comes at the cost of a bit of CPU because the standup/teardown code in processReplacing gets called more often. It was easy to make surge patches with modulation onto FX which demonstrated this and the above fix worked great so there's nothing to really 'do' here. But it occurred to me that modulating in a DAW could have the same problem. So I just wanted to kinda flag this to you, and also flag that of the ones we chose to integrate, one of the members of our team did a pretty exhaustive look for which params were impacted, which you can find here: https://github.com/surge-synthesizer/surge/issues/3315 Anyway like I said: no action required, just a way to let you know what we did. Please feel free to just close this issue of course. And thanks again for the great effects. They are part of a set of pretty awesome new Surge 1.8 features!
Author
Owner

@airwindows commented on GitHub (Dec 4, 2020):

Sounds fine. If you're looking at the code, you can probably work out which things are assuming non-moving parameters, and which ones already have smoothing on them. For instance, PurestGain/PurestFade and some Console plugins, including the upcoming one, already 'chase' the parameters to avoid zipper noise, and some things (like delay buffers in things like reverbs) really can't chase parameters because it's not the discontinuous nature of the control input that makes the 'glitches': changing the size of a buffer is always going to make a noise even if it's just adding small gaps of silence.

The finest granularity is of course going sample-by-sample as in VCV Rack. I'm comfortable leaving many parameters un-smoothed for efficiency reasons, but you're absolutely right that when you modulate that throws a monkey wrench into things. By all means come up with workarounds: you might also want to implement something like 'gainchase' in Console5Channel, which doesn't incur the overhead of repeatedly invoking processReplacing, and smooths on a sample-by-sample basis. A danger area is when you can't 'snap' to the desired setting from a far-distant setting: some plugins will misbehave if they can't start up with the target value, for 'gainchase' it initializes to -90 and then upon reading the control, if the working setting is negative it automatically jumps to the target without smoothing.

<!-- gh-comment-id:738964037 --> @airwindows commented on GitHub (Dec 4, 2020): Sounds fine. If you're looking at the code, you can probably work out which things are assuming non-moving parameters, and which ones already have smoothing on them. For instance, PurestGain/PurestFade and some Console plugins, including the upcoming one, already 'chase' the parameters to avoid zipper noise, and some things (like delay buffers in things like reverbs) really can't chase parameters because it's not the discontinuous nature of the control input that makes the 'glitches': changing the size of a buffer is always going to make a noise even if it's just adding small gaps of silence. The finest granularity is of course going sample-by-sample as in VCV Rack. I'm comfortable leaving many parameters un-smoothed for efficiency reasons, but you're absolutely right that when you modulate that throws a monkey wrench into things. By all means come up with workarounds: you might also want to implement something like 'gainchase' in Console5Channel, which doesn't incur the overhead of repeatedly invoking processReplacing, and smooths on a sample-by-sample basis. A danger area is when you can't 'snap' to the desired setting from a far-distant setting: some plugins will misbehave if they can't start up with the target value, for 'gainchase' it initializes to -90 and then upon reading the control, if the working setting is negative it automatically jumps to the target without smoothing.
Author
Owner

@baconpaul commented on GitHub (Dec 4, 2020):

Yeah that sort of 'first time vs next time' flagging is the price you pay and what we did in a lot of surge builtins.
I've held the line on surge that we want to not change your DSP code - I figure I would only make it worse - which is why i externalized this stepping (but left it at 4 not 1).

Thanks for the inputs on ones where you've smoothed! Lemme close this now you've seen it :)

<!-- gh-comment-id:738982771 --> @baconpaul commented on GitHub (Dec 4, 2020): Yeah that sort of 'first time vs next time' flagging is the price you pay and what we did in a lot of surge builtins. I've held the line on surge that we want to not change your DSP code - I figure I would only make it worse - which is why i externalized this stepping (but left it at 4 not 1). Thanks for the inputs on ones where you've smoothed! Lemme close this now you've seen it :)
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/airwindows#18
No description provided.