hamster

Audio filters in general

Recommended Posts

How do most people implement Audio filters in an FPGA?

 

Is it by some sort of multi-pole digital FIR model?

 

Using FFT?

 

or is it by using a first-order approximation of the analogue equivalent? (e.g. accumulators to model capacitors, and having 'charge flow' based on voltage difference between nodes in the filter). 

Because we it would be modelling passives components at 1000x the desired filter frequencies it could be pretty cheap to implement, It would also be much easier to alter the filter on the fly

 

When I was young I remember reading about simulating a 741 op-amp on a Commodore 64 (http://archive.org/stream/byte-magazine-1986-07/1986_07_BYTE_11-07_Engineers_Toolbox#page/n167/mode/2up, page 174) - that article and the one before it would give enough info to model it.

 

 

Share this post


Link to post
Share on other sites

I think it's rare to use FFT for filtering as it's very resource intensive. Most filters are implemented as delay lines with multiple taps. Each tap is a delayed version of the input sample, eg if Z0 is the input sample then z1 is the sample delayed by one clock cycle, z13 after 13 clock cycles. The filtering is implemented by feeding the output with a mix of the the input and several taps multiplied by a coefficient, for example, output = z0*c0 + z1*c1 + z2*c2 + z3*c3 where c0, c1, c2, c3 are fixed coefficients. By selecting the right coefficients you modify the filter response, see picture below, they're using b0, b1, b2 instead of c0, c1, c2 but the principle is the same:

 

750px-FIR_Filter.svg.png

EDIT:

 

so for example here's a filter in an FPGA written in Alex pseudo code:

constant c0 : standard_logic_vector(15 downto 0) := blah;constant c1 : standard_logic_vector(15 downto 0) := blah;constant c2 : standard_logic_vector(15 downto 0) := blah;constant c3 : standard_logic_vector(15 downto 0) := blah;... etcsignal z0: standard_logic_vector(15 downto 0);signal z1: standard_logic_vector(15 downto 0);signal z2: standard_logic_vector(15 downto 0);signal z3: standard_logic_vector(15 downto 0);... etcprocess(clock)begin  z3 <= z2;  z2 <= z1;  z1 <= z0;  z0 <= input;  output <= z0*c0 + z1*c1 + z2*c2 + z3*c3 ...etc;end process;

The more taps you have the better your filter is, more accurate fitering, sharper response, etc but more costly in terms of calculations.

Share this post


Link to post
Share on other sites
Humm... what is good about those sort of filters is that they can be tuned to frequencies that are within the ballpark of the sample frequency.

 

I'm thinking of trying to implement an audio filter by a numerical approximation of current flow within an analogue RC filter, driven by an 0 Ohm source, driving an output of infinite impedance.

 

Vout(n) = Vout(n-1)+(Vin(n-1) - Vout(n-1))* K

 

Here is what it looks like when modelled in Excel:

 

K = 0.05, 600 samples per cycle

 

Filter1.jpg

 

K = 0.05, 120 samples per cycle

 

Filter2.jpg

K = 0.05, 60 samples per cycle

 

Filter3.jpg

 

Looking at those graphs it looks to be a pretty close approximation filter with a 3db frequency of about 60 samples / cycle.

 

Because it only uses one 'constant' to set the critical frequency it can be tuned in real time. As you would expect, two of these can be cascaded to make a 6db/octave filter. I'm wondering it it might have application for the SID output filters...

 

Share this post


Link to post
Share on other sites

Remember that for SID you need high pass and low pass then once you have them, you can then use them to get band pass and notch. This is why the SVF filter seemed ideal because it gives you all these outputs simultaneously.

Share this post


Link to post
Share on other sites

I don't want to hijack a productive discussion, but I was talking to my buddy about this and he said "use the onboard DSP blocks".  

 

The 3E doesn't have them, at least I am pretty sure it doesn't, so that begs the question: Are there any plans upgrade the Papilio to a Spartan-6 or something that has DSP onboard? The 3E is "not recommended" for new designs, and one of the things that's nice about a devboard is to use it as a proving ground for a design, then move it to a prototyping phase.

Share this post


Link to post
Share on other sites

3E has 18x18 signed multipliers. That's all you need to implement a filter.

 

I implemented SID filters using any king of signed multiplier. You might want to take a look at that.

Share this post


Link to post
Share on other sites

I don't want to hijack a productive discussion, but I was talking to my buddy about this and he said "use the onboard DSP blocks".  

 

The 3E doesn't have them, at least I am pretty sure it doesn't, so that begs the question: Are there any plans upgrade the Papilio to a Spartan-6 or something that has DSP onboard? The 3E is "not recommended" for new designs, and one of the things that's nice about a devboard is to use it as a proving ground for a design, then move it to a prototyping phase.

 

The Papilio Pro, the board that the RetroCade is based on, uses a Spartan 6 LX9.

 

Jack.

Share this post


Link to post
Share on other sites

 

Humm... what is good about those sort of filters is that they can be tuned to frequencies that are within the ballpark of the sample frequency.
 
I'm thinking of trying to implement an audio filter by a numerical approximation of current flow within an analogue RC filter, driven by an 0 Ohm source, driving an output of infinite impedance.
 
Vout(n) = Vout(n-1)+(Vin(n-1) - Vout(n-1))* K
 
Because it only uses one 'constant' to set the critical frequency it can be tuned in real time. As you would expect, two of these can be cascaded to make a 6db/octave filter. I'm wondering it it might have application for the SID output filters.

 

What you have described here is a first order IIR digital filter.

 

FIR filters are more common in digital applications because it is trivial to achieve linear phase response and they are guaranteed to always be stable.  IIR filters on the other hand require careful design to ensure they are stable, they are usually used for cases where steep cutoffs are needed or where very short filters are necessary.

 

The linear phase response gives the filter a very nice property, it has constant group delay, that is all frequencies that pass through the filter are delayed by an equal amount.

 

As for frequency ranges, digital filters operate on digital frequency, this is a number between -0.5 and 0.5 (for the part of the signal that we care about) and describes the fraction of the sample frequency, the same set of filter coefficients will always have the same response in digital frequency, however the response in terms of Hz will depend on the sampling frequency used.  The reason that -0.5 to 0.5 are the bounds of the signal frequency is that when the signal is sampled it must be band limited so that it does not include any frequencies above half the sample rate, otherwise aliasing will occur and information will be lost.

 

There are several ways to design an FIR filter, one method is to take the ideal impulse response (A sinc function for a low pass filter) and then apply a window function to truncate it to finite length, and in the case of windows other than the rectangular one, minimise the effects of truncating the impulse response.

 

Other methods include frequency sampling, where one produces a sampled version of the desired frequency response and applies the inverse DFT to it to get the impulse response, the Parks-McClellan algorithm which produces an optimal 

Chebyshev response according to parameters such as passband edge, stopband edge, passband attenuation, stopband attenuation, and pass and stop band ripple.

Share this post


Link to post
Share on other sites

Some comments on VHDL code:

 

a) Don't instantiate the multiplier like that. Add generic code, like this:

signal m1, m2: signed(17 downto 0);signal mr: signed(35 downto 0);process(clk)begin  if rising_edge(clk) then    mr <= m1 * m2;  end if;end process;

I assume you did it so that the synthesizer shares the multiplier resource.

 

Also, use arrays for the step variables, it might make your code more compact and readable if using many stages.

 

Pay special attention to sign extents! this:

stage0_l  <= "00" & stage0;

does not keep signal. I wrote a small method that you might use:

function s_extend(si: in signed; size: in integer) return signed is  variable r: signed(size-1 downto 0);begin  r(si'HIGH downto si'LOW):=si;  sext: for i in size-1 downto si'HIGH+1 loop    r(i):=si(si'HIGH);  end loop;  return r;end function;

Then you can do this:

stage0_l  <= s_extend( stage0, stage0_l'LENGTH );

Alvie

Share this post


Link to post
Share on other sites

Cool, I'll give those hints go. 

 

One of the confusing things about my code is that the multiplier takes signed values for 'a' and 'b'. the multiplier are of mixed representations - the samples are unsigned (so extending with leading '0's is required, range is (0 though 2^16-1) but the signal 'delta' is signed. 

 

I'll think over it tonight and get a cleaner version 2.0 going tomorrow...

Share this post


Link to post
Share on other sites

Cool, I'll give those hints go. 

 

One of the confusing things about my code is that the multiplier takes signed values for 'a' and 'b'. the multiplier are of mixed representations - the samples are unsigned (so extending with leading '0's is required, range is (0 though 2^16-1) but the signal 'delta' is signed. 

 

I'll think over it tonight and get a cleaner version 2.0 going tomorrow...

 

Convert them into signed, by subtracting the 0-level :)

 

For example, for a 8-bit value, silence is represented by x"80" (128). If you subtract this value from the sample, you get a correct value - 0 unsigned becomes -128, and 255 unsigned becomes 127.

 

Actually this means "flipping" the sign value, which ends up not having much impact on the design. 

 

So, in order to extend a 16-bit unsigned into a 18-bit signed audio sample, add two trailing (not leading) zeroes, and flip the sign bit.

Share this post


Link to post
Share on other sites

Hi,

 

I'm no expert on the SID. That said, here

http://sid.kubarth.com/articles/interview_bob_yannes.html

it is stated it is a "resonant" filter, which would make it similar to this one

http://sourceforge.net/p/fluidsynth/code/463/tree/branches/fluidsynth/fluidsynth/src/fluid_dsp_core.c

line 136 (a 2nd order IIR filter).

In terms of the Xilinx whitepaper, it is a biquad stage.

 

Coefficient calculation is here:

http://sourceforge.net/p/fluidsynth/code/463/tree/branches/fluidsynth/fluidsynth/src/fluid_voice.c

 

Implementing the filter itself is rather simple, but calculating the coefficients doesn't seem completely trivial.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now