Page 1 of 2

Declicking

Posted: Fri Sep 30, 2022 4:28 pm
by UrbanCyborg
If anyone who's done it would care to share, how did you go about dealing with the square-wave noise introduced by switching a signal on or off? Filtering? Wave modification? What? I've tried several routes, and am not having much luck coming up with anything that doesn't introduce more noise than it takes out.

Reid

Re: Declicking

Posted: Fri Sep 30, 2022 5:42 pm
by ColinP
Hi Reid,

After quite a lot of experimentation I settled on doing a fade in/out that lasts about 4 ms using a polynomial function. If you do it with a linear function you might need roughly twice that.

But it all depends on how much noise you can tolerate. Mathematically you need an infinitely long fade to remove all distortion.

Re: Declicking

Posted: Sat Oct 01, 2022 8:28 am
by UrbanCyborg
Well, that's reassuring, at least. My current attempt uses a linear ramp. I'm having trouble getting the ramp to start where it needs to, though. Button presses are handled in Notify(), where I set flags, but the actual handling is in ProcessSample(). Getting it wrong by even a sample means creating more noise, not less, and I'm not exactly sanguine about the chances of accurate timing between threads. I'm currently using a 1 ms ramp; I'll try longer. Thanks, Colin.

Reid

Re: Declicking

Posted: Sat Oct 01, 2022 1:40 pm
by ColinP
Hmm, I don't understand your point as presumably the ProcessSample() code won't do anything until the flag is set in Notify(). Yes there will be a jitter delay up to the Notify() timer duration but as long as ProcessSample() waits for the flag there shouldn't be any noise.

The problem with using a linear ramp is the discontinuity on start and end as there is instantaneous change rather than it being band-limited. Hence the need to have a much slower fade to attempt to mask this.

The following demonstrates what I mean...
declick.png
declick.png (43.98 KiB) Viewed 1630 times
In the interval [0,1] the linear function has nasty "kinks" at 0 and 1 while the cubic function gently accelerates and decelerates.

This particular polynomial function is well known in games and graphics programming and can be implemented very efficiently but I'll leave the details as a homework project as I don't want to give away too many tricks of the trade. ;)

Re: Declicking

Posted: Sat Oct 01, 2022 6:55 pm
by UrbanCyborg
Yeah, I recognize the curve. Over the range from 0 to 1 it's sigmoid in character. I imagine other types of sigmoid would work here as well, although this one might be a bit cheaper to compute. Of course, I'm holding the ramp in a buffer, so it only gets computed once (and its reverse saved, as well, for spots where the signal stops, instead of starts). I was hoping to get by on the cheap, but you're right, the second-order C1 continuity is a definite plus.

Reid

Re: Declicking

Posted: Sat Oct 01, 2022 7:06 pm
by ColinP
These days LUTs are rarely a good idea because hitting the cache generally costs more than actually computing the function live, unless it's a really complex function and this polynomial only costs three multiplications and a subtraction if your algebra is up to scratch.

Re: Declicking

Posted: Sun Oct 02, 2022 10:59 am
by ColinP
I should mention that the approximate 4 ms I mentioned is the minimum fade time I found usable. It still produces a thud or a pop in many circumstances. To attenuate the noise more you might need a fade that lasts ten times longer than that. It depends on the application.

The thing is a tradeoff though as once you get above a certain amount of time it's no longer reallly acting as a switch as it begins to sound like a noticable fade.

I've been thinking a little more about what's happening in the frequency domain and one could maybe see the de-clicking as amplitude modulation that briefly generates sidebands to the content spectrum.

In effect what we end up with is something like a brief burst of ring modulation.

So the content we are trying to switch on and off is the carrier and the modulator is 0.5 / fadePeriod (when the modulator is strongly band-limited).

So with a fade period of 1 ms the modulatior is kind of like a 500 Hz sine wave so we'll get sidebands at +/- 500 Hz which will sound pretty unpleasant.

But if we increase the fade period to say 50 ms then the sidebands will be at +/- 10Hz and so the spectrum will be far less mangled.

As the fade period increases the modulation frequency tends to zero and the sidebands converge on the carrier so effectlively disappear.

I'm simplfying things here a bit but I think it makes some sense.

Re: Declicking

Posted: Sun Oct 02, 2022 1:19 pm
by UrbanCyborg
That agrees with what I see on a spectrum analyzer. Switching on an un-declicked 125 Hz signal results in extra frequencies at around 8 KHz, declicking that I've tried via several different methods tends to spread that extra signal out quite a lot, as well as smearing the original signal in an area around 125 Hz.

Reid

Re: Declicking

Posted: Wed Oct 19, 2022 8:02 pm
by UrbanCyborg
Incidentally, you can also get that cubic in two multiplications and four additions. Which is more efficient, I dunno. Once the processors started doing on-the-fly optimizations, sometime around the x686, as I recall, any calculations you could do for clock cycles and latency went out the window, because you had no idea of what the CPU was going to do to your finely-crafted assembly code. I used to be good at that, had Agner Fog pretty well memorized, along with large swathes of the "secret" addendum to the CPU manuals ;) ; now, I don't even bother.

Reid

Re: Declicking

Posted: Sat Oct 22, 2022 7:45 pm
by UrbanCyborg
Here's the version I mentioned:

Code: Select all

    // Computes 3x**2 - 2x**3
    // Takes input in the range [0,1), outputs a cubic sigmoid in the same range
    // Smooth transitions at the endpoints
    private double Sigmoid(double input) {
        double squared = input   * input;
        double cubed   = squared * input;
        return squared + squared + squared - (cubed + cubed);
    }   // Sigmoid()
Reid