dsp::Filter reset question

I just noticed that when I reset a cascade of filters, I don’t always get the response I expect.

I am filtering a block of audio, and since the first sample is not 0, I reset the filters to be at that value, just using reset(value).

I then process the block of audio and take a look at the result.

If I am using a single filter, or a cascade of TPTstatefilters, all seems right (the first value of the filtered output is the same as the input, ie the value used in reset()).

However, if I have a short cascade of filters (2 elliptical filters), and I reset both of them to the input value, then filter the block, the first sample is altered.

I’m not providing code because I think this is a concept question, not a code related issue, but just to be clear on what filters I am using:

::::::::::

I can create any number of dsp::StateVariableTPTFilter() filters,

set them to low pass, then reset to 1, and filter an input block starting with a value of 1.

And the first output sample will be 1

:::::::::

If I create an array (cascade) of filters using this method:

dsp::FilterDesign::designIIRLowpassHighOrderEllipticMethod

and then reset to 1, and filter an input block starting with a value of 1.

My first output sample is 1.13

I am aware that higher order IIR filters are unstable, and of course they can phase distort, but this is a DC signal being fed to a low pass filter. If I reset to 0 and then feed it 0s, it behaves as expected.

Anyway, I can’t figure out if I am misunderstanding something here? Let me know :slight_smile:

I should note … my current workaround is to just shift the input signal so that it starts at 0.

It seems that:

higher order Low Pass filters, reset to 0, will of course process a 0 and output 0.

Shouldn’t it be doing the same for other values though?

I think what you’re seeing is about the result I would expect. At least it isn’t unexpected really.

Glossing over some details: A filters output depends not only on its current input but also at least one previous input. When you construct a new filter object to start processing some samples, what is that previous input? Most filter implementations say “0” by default. Because, well what else would it be? Reset() lets you set that previous value to something else. But then of course, what was the value before that? Well, zero again of course.

The point is, whether you do filter.reset(1); filter.process(1); or justfilter.process(1);, the filter is going to see a value of 0 followed by a 1. The reset call just makes that event occur one sample sooner.

What a filter does with a 0 followed by a 1 depends on what the filter is, and its first output value won’t necessarily be easy to predict. Sure if you got like 4327.313 or something, that’d be weird. But a filter is a dynamic system after all, and you’re asking it it to shoot up from 0 to full scale. Getting a bit of overshoot doesn’t seem unexpected to me.

And yeah, a filter reset to 0 (or not reset) and processing 0 outputting 0 is also expected of course. But expecting the same steady state at any other value is not reasonable. For a lowpass filter you can probably expect it to eventually settle on or near the input value. But it has to get there somehow, and if the way there is an instant change you can expect some ripple.

I was thinking about that, but shouldn’t reset() reset both? I mean, it’s rather useless otherwise, and the filter state is reliant on both values ….

Also, it works when it’s just one filter … and I would expect that to also be borked if reset wasn’t resetting the state in a deeper way (meaning, both values)

It looks like the IIR filter is a Transposed Direct Form II, and the reset to a non-zero value just sets all states to that value. That’s a different beast than with non-transposed forms where you can just set all states to the value and then it is like both the input and the output have been that value forever. However, even that only works as expected if you have a lowpass filter with 0dB gain at DC.

I think more generally, a reset() function with a nonzero reset value should act like there has been that DC value at the input forever in the past. But I doubt that is the case the way it’s implemented now, maybe only accidentally so in some special cases, also very dependent on filter design. The problem gets much worse when there’s a cascade of multiple filters involved.

The easy fix would be to, instead of using the reset() function, to feed enough constant samples to the filter for the states to settle. But that depends on time constant and can become very costly. Also it’s inelegant.

The proper fix would be to do some math and figure out a closed form for the asymptotic state to implement a correct reset function. There’s a good chance that this has been figured out and written down by someone before you. But I’m not sure what the best google keyword for that would be.

Edit: or you could use a non-transposed direct form implementation. As long as there’s 0dB gain at DC, this should give you the behavior you want.

I’m not sure what you mean by “both”. But Hugos reply below is right on.

You seem to expect that feeding in DC into a filter can give you the exact same DC value at the output. Some filters can do that, but if you want that result immediately on the first sample of your DC stream, you need to somehow simulate a situation where the filter acts as if it has already been receiving that value for a while. Jumping from 0 to some DC value will do stuff like this:

(Unfiltered signal below, filtered above)

…and again, a reset function normally sets the past and current samples that the filter stores to a value. No filter class I’ve ever seen keeps enough state around that setting all of it to a value would reliably put that event far enough in the “past” to get the results you expect.

It’s not impossible, just as Hugo says, would require the filter to be written differently.

For the sake of curiosity, why is this important to you?

Not so important, just interesting. I have a workaround as I mentioned.

The use case here is that I’m rendering wavetables on the fly.

The tables always loop, but don’t always have 0 at the start/end.

Since I am shifting play freq when rendering, I have to filter out some frequencies, but also don’t want a nonlinearity at the rollover.

So I had been resetting the filters to the initial value and then running them, thinking that the first value wouldn’t be altered, but of course this doesn’t make sense, as noted above.

I had been thinking of reset() as somehow setting the filter to a steady state of course, which wouldn’t even be possible for many filters.

It hadn’t occurred to me until just now that you could get the impulse response by doing reset(1) and then feeding the filter 0s too, very cool.

What you’ll get out of that is not an impulse response! It’s the response you get when setting all filter states to 1 and then feeding it zeros. The impulse response is what you get when feeding a single 1 to the input, and then zeros. There’s possibly some degenerate case of a one-pole filter with specific constraints on the coefficients where that would be - by pure coincidence - the same as the impulse response.

Only Direct Form 1 uses past input and output samples as their state. For DF2 or DF2T, what’s in the state is “something else” which isn’t as easily interpreted. Generally, you don’t need more state in order to perform a correct reset like that, you just need the right formula.

For the wavetable application, probably none of this what you want though. When filtering a single wave, what you really want as a result is what you’d get if you feed it the same wave periodically forever. So what you’d want to do in this case is feed the wavetable multiple times to “swing it in”, and only then compute your result. Of course that’s a bunch of computation at once, so it might depend on your overall architecture whether that’s feasible (= you can compute in advance rather than on the fly, ideally).

Ah, quite right, got it. So if reset(1) is neither the same as getting the filter to a state where is has been 1 for some time, nor is it like feeding the filter a 1. I’m not sure I see the usefulness of this feature anymore.

If there is some past samples / “other” state …. then what is the usefulness of setting the filter “state” to some value? Put another way … if reset(1) has different results based on what the actual current state of the filter is, then what am I actually “resetting”?

Also - very good to know about DF2, thanks.

And, thanks for the thoughts on the wavetable. This is what I am doing currently, except that I am only using a few hundred wrapped samples on either side of the waveform. It seems to be enough though. I also do a quick crossfade, though the values are identical to within a very small margin.

Still, if I start trying a larger selection of waveforms I might find I need a longer ramp in time for the filter, so maybe it is a better idea to go a couple times through the whole thing before / after. By “on the fly” I still mean in a background thread (and then swap it in when done), so no worries on the comp time.

Ah yeah true. What I shoulda said is: Reset functions commonly just sets the state to 0, what you want would require more work than that.

I know many folks will pre-compute an array of wavetables for the whole range (2-3 per octave or so) and then when making a voice, selecting the nearest one for the frequency in question. That what you’re doing too? In that scenario I’d think running a few more cycles of the wave into the filter before taking the output should be no problem. My intution for how many cycles should be enough says >2 but <15 but I could be totally off.

An easy way to check convergence without thinking too much or relying on a fixed number of passes is to check whether the difference (RMS) to the previous pass after each round of filtering is below some threshold.

1 Like