Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

FIR Audio Spectrum Analyzer

Started by Siekmanski, September 03, 2014, 10:14:36 PM

Previous topic - Next topic


Yes, the audio in the wav-file is like this:

leftchannel,rightchannel,leftchannel,rightchannel,leftchannel,rightchannel.................... each channel sample = 16 bit signed int
Creative coders use backward thinking techniques as a strategy.


Quote from: guga on October 22, 2014, 12:21:42 PM answered at the same time. :)

And how to convert sample to hertz ?

What do you mean exactly?
Creative coders use backward thinking techniques as a strategy.


QuoteWhat do you mean exactly?
It is like i asked on the previous post

"Do i need to convert each sample to Frequency to remove it ?"
"Yes, just add zeros ( total FIR coeffs / 2 ) add the beginning and the end of your audio data."

Since you explained how to convert each sample to decibel (dBFS ), which is awsome, because it can then remove some noise for the audio data, i´m wondering what is the way to convert each sample data (the word value) to frequency (in hertz).

On this way, once i know the frequency of each sample, i can remove unwanted frequencies. For example, if i found a sample that have 4000 Hz and i want it to remove (or simply change this frequency value to another one).

So, when you said, add zeros (total Fir coeffs/2) and add the begginning and the end of my audio data, you meant to add it to each Word pair ? (that is what forms the sample)


Frequency = (Sum_of_Firs/2) + Word1 (the word representing the left channel of the sample) + Word2 (the word representing the right channel of the sample)

Since a sample can be interpreted as a Word structure like


Sample1.Channel.Left: W$ 0
Sample1.Channel.Right: W$ 0

Sample2.Channel.Left: W$ 0
Sample2.Channel.Right: W$ 0


To compute the frequency of each sample i can do this ?

Frequency of Sample1 = (Sum_OfFirs/2) +  Sample1.Channel.Left + Sample1.Channel.Right
Frequency of Sample2 = (Sum_OfFirs/2) +  Sample2.Channel.Left + Sample2.Channel.Right

If it can be done, then, i presume the resultant value would be something between 0 to 1 (or -1 to 1), right ?

If it is, then, if the generate sum above of "Frequency of Sample1" gives me the result of let´s say 0.1256879. How to convert this value to hertz ? I mean, how to know that this "sample1" has a frequency of let´s say 12 Hz for example ?
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:


You can not get a frequency value out of one sample !
To get the frequency response of the audio you should do a DFT or FFT.

In this FIR analyzer example i just showed that you can do a band-pass filter for let's say 1000 Hz and 2000 Hz.
It removes the frequency content below 1000 Hz and removes the frequency content above 2000 Hz
Now we have only the frequencies between 1000 Hz and 2000 Hz.
You could save this as a wav-file and the only sound you will hear is the frequency range of 1000 Hz to 2000 Hz, all other frequencies are filtered out.

But as in this example i use the filtered frequencies to show them on screen to see which frequencies are played.
I showed 3 ways to present those filtered frequencies per frequency band.

1) Peak = find the highest sample value.
2) RMS = multiply each sample with it self, add all multiplied samples and then divide by the number of samples, then get the sqrt.
3) Average = add all samples and then divide by the number of samples.

Why use zero padding at the beginning and the end of the audio data ?

If you have 3 FIR coefficients, you need 1 zero before the audio-data and 1 zero after the audio-data.
Because you start filtering with the first audio sample, you need the sample numbers "-1,0,1" to do the filter calculations.
The same fore the last audio sample you need an extra sample, so there has to be an extra zero ad the end of the audio-data.

So if you have 8191 FIR coefficients you need 4095 zeros before and 4095 zeros after the audio-data.

Now the whole sequence:

1) Create a FIR window:

   ; example 2th order band-pass
   invoke CalculateFIRcoefficients,2,44100,1000,2000,FIR_BandPass,addr FIRcoefficients ; get the frequencies between 1000 and 2000 Hz 

2) Load your wav-file to memory ( reserve extra memory for the zero padding )
   It looks like this:
   0,L,R,L,R,L,R,L,R,0 ( add zeros for the FIR window size == number of FIR coeffs )

3) Now convert this to 2 seperate 32 bit floating point audio channels.
   It looks like this:
   0,L,L,L,L,L,L,L,L,.....,0 ( Left channel )
   0,R,R,R,R,R,R,R,R,.....,0 ( Rigth channel )

   And reserve also memory for the 2 output channels if you like to save it as a wav-file.

4) Do the FIR calculations for both seperate audio channels:

s = audio samples
f = fir coeffs

example with a 2th order filter (3 FIR coeffs):

ouput(s0) = f0*input(s-1) + f1*input(s0) + f2*input(s1)
ouput(s1) = f0*input(s0) + f1*input(s1) + f2*input(s2)

Note: a 2th order FIR filter has 3 coeffs, a 4th order FIR filter has 5 coeffs etc....

5) Your output has now the fitered audio data.
   To save as Wav-file, convert the two output 32 bit floats audio channels back as interleaved (L,R,L,R,L,R,L,R,...) 16 bit signed ints.

6) Your done.....   :biggrin:

There are 4 types of band filtering:

    invoke      CalculateFIRcoefficients,OrderNumber,Samplerate,1000,NULL,FIR_LowPass,addr FIRcoefficients ; remove the frequencies above 1000 Hz 
    invoke      CalculateFIRcoefficients,OrderNumber,Samplerate,1000,NULL,FIR_HighPass,addr FIRcoefficients ; remove the frequencies below 1000 Hz 
    invoke      CalculateFIRcoefficients,OrderNumber,Samplerate,1000,2000,FIR_BandPass,addr FIRcoefficients ; remove the frequencies below 1000 and above 2000 Hz 
    invoke      CalculateFIRcoefficients,OrderNumber,Samplerate,1000,2000,FIR_BandReject,addr FIRcoefficients ; remove the frequencies between 1000 and 2000 Hz 

Creative coders use backward thinking techniques as a strategy.


Quote from: Siekmanski on October 12, 2014, 09:40:31 PM

your fir coeffs: (-0.069, 0.138, -0.069) ; order 2 has 3 taps. ( taps == num. orders + 1)

your audio data: (0.5, 1.0, 0.0, 0.4) ;left channel or right channel.

alloc mem for filtered audio: == audio size
audio mem = audio size + order size = 4 + 2 = 6 ;(all zeros)
audio offset = order / 2 = 1

copy audio channel data to allocated mem, starting at audio offset.

audio mem: (0.0, 0.5, 1.0, 0.0, 0.4, 0.0)

Now everything is set up to do the filtering.

filtering pseudo code:

f = fir coeffs
a = audio mem
b = filtered audio mem

b[0] = a[0]*f[0] + a[1]*f[1] + a[2]*f[2]
b[1] = a[1]*f[0] + a[2]*f[1] + a[3]*f[2]
b[2] = a[2]*f[0] + a[3]*f[1] + a[4]*f[2]
b[3] = a[3]*f[0] + a[4]*f[1] + a[5]*f[2]

For simplicity this example uses order 2.
For a better "Frequency Response" you have to use higher order settings.


Hi Marinus. I continued to analyze this app, but i´m still struggling to understand how to apply the Fir coefficients.  I succeeded to create a save routine as you said years ago, and can export a wave audio correctly. But, i didn´t understood how the  FIR filters are applied.

One small thing. On your app, here:
CalculateFIRcoefficients proc uses esi edi order:DWORD,samplerate:DWORD,cutofffrequency1:DWORD,cutofffrequency2:DWORD,filtertype:DWORD,coefficients:DWORD
; Introduction to Digital Filters:

    invoke      RtlZeroMemory,coefficients,FIR_MaxTaps

The call to RTLZeromemory must be a multiple of 4 since each FIR_MaxTaps is a dword. So, it must be:
    invoke      RtlZeroMemory,coefficients, (FIR_MaxTaps*4) let´s back to understand this FIR algorithm. The routine you said to create another buffer in memory etc, seems a little bit different then what is explained here: and also here

That´s why i´m getting a bit confused in what to do. Let´s say i have a situation a array of coefficients of let´s say only 6 values, and a Data chunk of 13 values to be applied.

Presuming a order 2 (If i understood it correctly), we must multiply the data at the 1st offset with the one at the same offset in the Fir Table and the others values we multiply by the preceding ones (X-1) pos for Order2, right ?


Original data
X0   X1   X2   X3   X4   X5   X6   X7   X8   X9   X10   X11   X12
17   12   1   7   19   25   22   77   13   11   150   2   111

Coefficient Data (Say we have only 6 coefficientes)
A0   A1   A2   A3   A4   A5
22   145   26   88   99   101

Y0 = A0*X0 + A1*(X0-1)+A2*(X0-2)+A3*(X0-3) ....
Y0 = 22*17 + 26*0 . Since the previous position contains nothing, we set it as is 0, right ?
Y0 = 22*17

On the next pos at X1 we have:

Y1 = A0*X1 + A1*(x1-1) + A2*(x1-2)....
Y1 = A0*X1 + A1*(X0) + A2*(0)....
Y1 = 22*12 + 145*17 + 26*0

On the pos  X2 we have:

Y2 = A0*X2+A1*(X2-1)*A2*(X2-2)+A3*(X2-4)+....
Y2 = A0*X2+A1*(X1)*A2*(X0)+A3*(0)+....
Y2 = 22*1 + 145*12 + 26*17+88*0 + .....

but, on Pos X7 we have more samples then coefficients
Y7 = A0*X7+A1*(X7-1)*A2*(X7-2)+A3*(X7-4)+....
Y7 = A0*X7+A1*(X6)*A2*(X5)+A3*(X4)+A4*X3+A5*X2+A6*X1+A7*X0 + 0+0+0
Y7 = 22*77 + 145*22 + 26*25+88*19+99*7+101*1 + ??*12 + ????*22

See ? We have no more coefficients to multiply, since the total size of our data is bigger then the amount of coefficients.

How this works, exactly ?

Can you show an example using the same values ? I mean a Table of coefficients of 6 values and a Table of samples of let´s say 14. And also shows the same example how it works on Order2, 3or 10 ?

One last thing. On your example you set the Order values as a multiple of 2 minus 2, but the OrdersBand3                 equ 2022 seems to be incorrect.

; Change the order numbers to narrow or widen the frequency band width ( the roll off factor )
[ORDERSBAND1 8190]  ; (16*2^9)-2
[ORDERSBAND2 4094]  ; (16*2^8)-2
;[ORDERSBAND3 2022]  ; ((16*2^7)-2-24), but, it should be: (16*2^7)-2
[ORDERSBAND3 2046]  ; ((16*2^7)-2-24), but, it should be: (16*2^7)-2. This seems to be the correct value
[ORDERSBAND4 1022]  ; (16*2^6)-2
[ORDERSBAND5 510]   ; (16*2^5)-2
[ORDERSBAND6 254]   ; (16*2^4)-2
[ORDERSBAND7 126]   ; (16*2^3)-2
[ORDERSBAND8 62]    ; (16*2^2)-2
[ORDERSBAND9 30]    ; (16*2^1)-2
[ORDERSBAND10 14]   ; (16*2^0)-2

And also...why you decreased the equates values by 2 ?

Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites: