Is there not a better DC Blocker algorithm, because this one is broken!

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

The following DC Blocker code snippet can be found on several websites;

Code: Select all

out = in - previousIn + 0.995 * previousOut;
previousIn = in;
previousOut = out;
Now it may be me doing something wrong, but for a saw waveform, where samples starts at -1.00 and gradually goes to 1.00, speed off course dependent on frequency (phase delta), instead of keeping the signal centered, it introduces a DC bias so signal will shift to about -1.18 to 0.82 at 220Hz. At lower frequencies, the bias is greater, at higher frequencies the bias is lower.

For a pure square wave, going from -1.00 and half way through going abruptly to 1.00, at the point where is shifts abrupt from 1- to 1, the above DC formula variables contains the following;

Code: Select all

in = 1.00
previousIn = -1.00
previousOut = -.58
Which means out = 1.00 - -1.00 + 0.995 * -.58 = 1.4229

After a few iterations of the frequency, the signal settles into a range of -1.26 to 1.26. So the above DC Blocker code actually amplifies the signal.

I should mention my block of samples, buffer size is 512, meaning every 512 samples, I run those samples through the above DC Blocker algorithm.

Is there something I missed, doing wrong or is this just a quirk of the high pass filter / DC Blocker algorithm?

And if that is the way it works, does anyone know of a non-destructive DC Blocker algorithm. One that not only does not filter out low frequencies, and but one that also does neither introduce a bias, nor amplifies the signal?
"Scuba divers work best under pressure!"

https://www.youtube.com/user/DKDiveDude (Original music and hardware videos)

Post

DKDiveDude wrote: Sat Dec 30, 2023 4:36 pm The following DC Blocker code snippet can be found on several websites;

Code: Select all

out = in - previousIn + 0.995 * previousOut;
previousIn = in;
previousOut = out;
Now it may be me doing something wrong, but for a saw waveform, where samples starts at -1.00 and gradually goes to 1.00, speed off course dependent on frequency (phase delta), instead of keeping the signal centered, it introduces a DC bias so signal will shift to about -1.18 to 0.82 at 220Hz. At lower frequencies, the bias is greater, at higher frequencies the bias is lower.
So.. the only "problem" with this DC blocker (=one-pole high-pass) is the hard-coded coefficient which makes it sample-rate dependent. What you are observing is not an issue with what it's doing, but rather a (common) misconception of what DC means.

DC does NOT mean that the waveform is centered in such a way that positive and negative peaks are at the same level. Rather DC means that the long-time average of the signal differs from zero and this is what the filter gives you. Because the phase-response of this type of DC blocker is not linear, it introduces a different delay at different frequencies, with more phase non-linearity at lower frequencies (closer to cutoff).
And if that is the way it works, does anyone know of a non-destructive DC Blocker algorithm. One that not only does not filter out low frequencies, and but one that also does neither introduce a bias, nor amplifies the signal?
To remove any DC offsets without distorting the phase, you need a linear-phase high-pass filter.. but unfortunately that implies a rather long FIR filter (stable causal IIR filters cannot have linear-phase), because it needs to be at least a couple of times longer than the wavelength of the lowest signal frequencies you want to preserve and even if we don't care about computational cost linear-phase filters always introduce half their length worth of latency (= delay).

The important thing to understand though is that peak levels carry no useful information (well, beyond how much headroom you need to avoid clipping). We don't hear peak levels and in terms of power (which is a better estimate of loudness, even though human hearing is quite complicated) the signal isn't amplified by shifting the phases of different frequencies even if the peak levels change. In fact, the peak levels can shift slightly even with linear-phase filtering.

ps. For a similar reason the "true peaks" reconstructed by a DAC when converting to analog can actually also be higher than your sampled values, because the brickwall reconstruction filter will also cause some ringing.. and in the worst case then clip the signal in analog domain... but like the main point is that peak levels moving around (and sadly usually increasing slightly) is kinda just the cost of doing DSP. Just leave some headroom and don't stress about it.

Post

It amplifies peak values because phases have shifted. You are just trying to DC filter a sawtooth wave and maintain some headroom, clipping works very good here.

Post

mystran wrote: Sat Dec 30, 2023 7:21 pm So.. the only "problem" with this DC blocker (=one-pole high-pass) is the hard-coded coefficient which makes it sample-rate dependent. What you are observing is not an issue with what it's doing, but rather a (common) misconception of what DC means.

DC does NOT mean that the waveform is centered in such a way that positive and negative peaks are at the same level. Rather DC means that the long-time average of the signal differs from zero and this is what the filter gives you. Because the phase-response of this type of DC blocker is not linear, it introduces a different delay at different frequencies, with more phase non-linearity at lower frequencies (closer to cutoff).
Thanks for the reply. Interesting that on some of the websites featuring the DC Blocker code I found, it mentions specifically that the code is used to center waveforms introduces to a DC offset, such as from a cheap microphone. I.e. http://peabody.sapp.org/class/dmp2/lab/dcblock/

Anyways I am programming a synthesizer. Some DSP processing such as i.e. waveshaping introduces a DC offset, which I do need to correct as much as possible, before signal is entering later stages that may need to do further processing that expects signal to be centered, and within range. Besides if the DC offset is not corrected, it accumulates when pressing multiple notes, as gathered in main pluginprocessor processBlock (JUCE), and then worsening even more if further processing such as delay or chorus is added.

For now I have made a correction over time algo that works ok, but I found this interesting article "Linear-phase DC Removal Filter" here https://www.dsprelated.com/showarticle/58.php, I am just not sure how to convert the Matlab code to plain C.
"Scuba divers work best under pressure!"

https://www.youtube.com/user/DKDiveDude (Original music and hardware videos)

Post

DKDiveDude wrote: Sun Dec 31, 2023 3:20 pm Thanks for the reply. Interesting that on some of the websites featuring the DC Blocker code I found, it mentions specifically that the code is used to center waveforms introduces to a DC offset, such as from a cheap microphone. I.e. http://peabody.sapp.org/class/dmp2/lab/dcblock/
Well.. it does center the waveform in the average sense, just not in terms of peaks. Filters act in terms of frequencies and a "DC blocker" is simply a high-pass filter at some suitably low frequency.
Besides if the DC offset is not corrected, it accumulates when pressing multiple notes, as gathered in main pluginprocessor processBlock (JUCE), and then worsening even more if further processing such as delay or chorus is added.
Correct.. but in terms of DC accumulation peaks don't matter, just the long-term average, which is what our DC blocker is getting rid of just fine. It's quite common for people new to signal processing to be overly obsessed about peaks, but truly the solution is to simply leave some headroom. You don't need so much dynamic range that it would make or break anything. There is absolutely no need to always output at the maximum possible amplitude, that actually just makes one's work when mixing harder (ie. it's usually much easier to mix when you don't need to pull every fader down some 20dB just to get the signals to sensible levels).

The only case where (reasonable) peaks might matter is final mastering and at that point you use some combination of clipping and peak limiting on the final mix.. because in general it actually turns out that mixing "uncorrelated" (which is usually a reasonable assumption for mixing audio signals unless there is a reason to expect high amount of correlation) signals the peak levels don't even increase as the sum of individual peak levels, but rather on average only roughly at the square root and then clipping or peak-limiting can deal with the occasional outliers with less overall damage to the signal than trying to religiously control them at every point.

Now, I said "reasonable" peaks above.. and obviously you don't wanna cause some +20dB spikes above your average levels... but a bit of peak variation is fine, just leave some headroom.

Post

DKDiveDude wrote: Sun Dec 31, 2023 3:20 pm For now I have made a correction over time algo that works ok, but I found this interesting article "Linear-phase DC Removal Filter" here https://www.dsprelated.com/showarticle/58.php, I am just not sure how to convert the Matlab code to plain C.
This is also just another form of an IIR highpass. Phase shift included.

---------

I've seen a DC blocker with the below alogo in the past. Basically it's the same a FIR with a brick of 2000 coeeficents all set to 1. The trick here is that the first sample is added to the sum while the last one is substracted. It's the average DC offset of the block. Blocklength is the wavelength of the cutoff frequency you want to remove. In this case 2000. This filter has a 'delay' of 1000 samples as the phase shift. It's very CPU efficienct as it does not need to sum all 2000 samples.


FIRsum += samplebuffer[offset+1] - samplebuffer[offset+2000]; //average DC offset of the block
out = in -FIRsum/2000;
samplebuffer[offset]=in;
offset++;
We do not have a support forum on kvr. Please refer to our offical location: https://www.tone2.com/faq.html

Post

Tone2 Synthesizers wrote: Sun Dec 31, 2023 6:12 pm I've seen a DC blocker with the below alogo in the past. Basically it's the same a FIR with a brick of 2000 coeeficents all set to 1. The trick here is that the first sample is added to the sum while the last one is substracted. It's the average DC offset of the block. Blocklength is the wavelength of the cutoff frequency you want to remove. In this case 2000. This filter has a 'delay' of 1000 samples as the phase shift. It's very CPU efficienct as it does not need to sum all 2000 samples.


FIRsum += samplebuffer[offset+1] - samplebuffer[offset+2000]; //average DC offset of the block
out = in -FIRsum/2000;
samplebuffer[offset]=in;
offset++;
Thanks for chiming in. I would like to try the above, but are a bit confused about the offset index variable. Seems it will cause the samplebuffer index to overflow.

I use a buffer sample loop like below;

Code: Select all

// numSamples is set via the DAW, by default for my synth it's 1024
for (int currentSample = 0; currentSample < numSamples; currentSample++)
{
	// Only showing left channel
	float sample = tgBuffer.getSample (0, currentSample);
	
	// Here would be some sort of DC Blocker adjustment
	
	
}
"Scuba divers work best under pressure!"

https://www.youtube.com/user/DKDiveDude (Original music and hardware videos)

Post

DKDiveDude wrote: Sun Dec 31, 2023 7:48 pm
Tone2 Synthesizers wrote: Sun Dec 31, 2023 6:12 pm I've seen a DC blocker with the below alogo in the past. Basically it's the same a FIR with a brick of 2000 coeeficents all set to 1. The trick here is that the first sample is added to the sum while the last one is substracted. It's the average DC offset of the block. Blocklength is the wavelength of the cutoff frequency you want to remove. In this case 2000. This filter has a 'delay' of 1000 samples as the phase shift. It's very CPU efficienct as it does not need to sum all 2000 samples.


FIRsum += samplebuffer[offset+1] - samplebuffer[offset+2000]; //average DC offset of the block
out = in -FIRsum/2000;
samplebuffer[offset]=in;
offset++;
Thanks for chiming in. I would like to try the above, but are a bit confused about the offset index variable. Seems it will cause the samplebuffer index to overflow.
Note that this type of algorithm only works correctly in fixed point math, because in floating-point the addition and subtraction don't typically cancel each other perfectly thanks to rounding, so you'll end up accumulating rounding errors over time.

As for the overflow though, you'll want to wrap around with something like:

Code: Select all

if(++offset == bufsize) offset = 0;

Post

I'd like to add, Just in case you didn't figure it out yet. You can replace 0.995 above with something like:

1 - (vf / sample_rate)

Or something like that. Where vf can go from 0 to sample_rate. The higher vf is, the more the blocker eats from bass, but also the faster it kills DC. If I'm not mistaken, this will at least (more or less) give the same cutoff for different sampling rates. Though not necessarily the same exact response because of other reasons.

You could of-course try to math out a more accurate formula for a one pole DC blocker. But I think this is good enough for most cases.
www.solostuff.net
Advice is heavy. So don’t send it like a mountain.

Post

Does the slight phase shift matter? Especially for a synthesiser?

I'd just use a one pole 6dB high pass, set it to 10Hz and call it a day.

One other thing worth mentioning in the case that you're using wavetables...get rid of waveform's DC offset by zeroing out the bin 0 of the FFT (prior to converting back to time domain). If you're trying to get rid of DC after, say, wave folding, then just use the low frequency one pole high pass.

Post

S0lo wrote: Tue Jan 02, 2024 10:24 am I'd like to add, Just in case you didn't figure it out yet. You can replace 0.995 above with something like:

1 - (vf / sample_rate)

Or something like that. Where vf can go from 0 to sample_rate. The higher vf is, the more the blocker eats from bass, but also the faster it kills DC. If I'm not mistaken, this will at least (more or less) give the same cutoff for different sampling rates. Though not necessarily the same exact response because of other reasons.
This sort of formula is kinda fine with cutoff (vf) is much (several orders of magnitude) smaller than sampling rate, which is typically the case with DC blockers. We could use impulse invariant (exp) or BLT (tan) tuning here as well, but once the cutoff is low enough relative to the sampling rate they all approach a linear curve anyway.

Just remember if your coefficient gets too close to 1 (more of an issue with single-precision) then this type of filter might not work so great anymore.

Post

JustinJ wrote: Tue Jan 02, 2024 11:43 am Does the slight phase shift matter? Especially for a synthesiser?
I wouldn't say so. Not for a synth no. For a mastering tool yeah. Or may be for an effect thats meant to be in the middle of the signal chain instead of at the end of it. On the other hand, Doing FIR to achieve linear phase is not practical due to too many taps requirements. I think mystran touched on that above.

But lets say we ignore the rules just for the sake of an adventure. Construct a filter with only two zeros and no poles. Place the two zeros at 0Hz. One of them inside the unit z-plane circle and the other outside of it with a reciprocal magnitude. (R and 1/R). Where R is small, just enough to remove DC. This reciprocal placement should completely cancel the none-linear phase shift around 0Hz. While maintaining good attenuation. Then normalize the filter at nyquist for 0db gain. This is essentially a 2 (or 3 ?) taps FIR high pass. It would look something like this:

// Initialize:
ft = (R+1.0); norm = R / (ft*ft);
in0 = 0.0; in1 = 0.0; in2 = 0.0;

// Tick
in2 = in1; in1 = in0; in0 = input;
output = norm*(in0 - (R+1.0/R)*in1 + in2);

I would naively think that such a thing would work. but it doesn't. The two zeros are too powerful that they'd attenuate the whole spectrum. You need something to lift up the gain after 0Hz-10Hz or so, to be back at original input level. Unfortunately, this can either be a pole or a large number of well placed zeros. The later is what linear phase FIR methods do, which is still kinda half magic to me.

Edit: lin-phase FIR methods would actually place zeros with R=1 at the stop band then reciprocals elsewhere
www.solostuff.net
Advice is heavy. So don’t send it like a mountain.

Post

I'd say the best compromise is a first order butterworth IIR highpass. These filters have a very natural sound and a reasonable phase shift. Use a cutoff between 10 Hz and 25 Hz.
We do not have a support forum on kvr. Please refer to our offical location: https://www.tone2.com/faq.html

Post Reply

Return to “DSP and Plugin Development”