DSP tutorial: Lowpass filtering using an IIR filter

This example records audio and lowpass filters it using an IIR Butterworth filter. You can design your own filter here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
public class LowPassFilterIIR implements Runnable {
    private final static int SAMPLERATE = 44100;
    private final static int BUFFERSIZE = SAMPLERATE * 2;
    private final static int IIR_NZEROS = 5;
    private final static int IIR_NPOLES = 5;
    private final static double IIR_GAIN = 1.894427191e+01;

    private double xv[], yv[];
    private TargetDataLine tdl;

    LowPassFilterIIR(TargetDataLine tdl) {
        this.tdl = tdl;
    }

    // converts float array to byte array
    private byte[] getBytesFromDoubles(final double[] audioData, final int storedSamples) {
        byte[] audioDataBytes = new byte[storedSamples * 2];

        for (int i = 0; i < storedSamples; i++) {
            // saturation
            audioData[i] = Math.min(1.0, Math.max(-1.0, audioData[i]));

            // scaling and conversion to integer
            int sample = (int) Math.round((audioData[i] + 1.0) * 32767.5) - 32768;

            byte high = (byte) ((sample >> 8) & 0xFF);
            byte low = (byte) (sample & 0xFF);
            audioDataBytes[i * 2] = low;
            audioDataBytes[i * 2 + 1] = high;
        }

        return audioDataBytes;
    }

    // saves the audio data given in audioDataBytes to a .wav file
    private void writeWavFile(final byte[] audioDataBytes, final int storedSamples, final String fileName) {
        AudioFormat audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, SAMPLERATE, 16, 1, 2, SAMPLERATE, false);
        AudioInputStream audioInputStream = new AudioInputStream(new ByteArrayInputStream(audioDataBytes), audioFormat, storedSamples);

        try {
            FileOutputStream fileOutputStream = new FileOutputStream(fileName);
            AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, fileOutputStream);
            audioInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private double lowPassFilter(double in) {
        xv[0] = xv[1];
        xv[1] = xv[2];
        xv[2] = xv[3];
        xv[3] = xv[4];
        xv[4] = xv[5];
        xv[5] = in / IIR_GAIN;
        yv[0] = yv[1];
        yv[1] = yv[2];
        yv[2] = yv[3];
        yv[3] = yv[4];
        yv[4] = yv[5];
        yv[5] = (xv[0] + xv[5]) + 5 * (xv[1] + xv[4]) + 10 * (xv[2] + xv[3]) + (0.0000000000 * yv[0]) + (-0.0557280900 * yv[1]) + (-0.0000000000 * yv[2]) + (-0.6334368540 * yv[3]) + (-0.0000000000 * yv[4]);
        return yv[5];
    }

    @Override
    public void run() {
        byte[] abBuffer = new byte[tdl.getBufferSize()];
        double[] abBufferDouble = new double[abBuffer.length / 2];
        ByteArrayOutputStream baos = new ByteArrayOutputStream(); // this will store sound data

        xv = new double[IIR_NZEROS + 1];
        yv = new double[IIR_NPOLES + 1];

        tdl.start();

        try {
            while (!Thread.interrupted()) {
                // waiting for the buffer to get filled
                while (tdl.available() < tdl.getBufferSize() * 0.5)
                    Thread.sleep(0, 1); // without this, the audio will be choppy

                int bytesRead = tdl.read(abBuffer, 0, tdl.available());

                // converting frames stored as bytes to double values
                int samplesRead = bytesRead / tdl.getFormat().getFrameSize();
                for (int i = 0; i < samplesRead; i++)
                    abBufferDouble[i] = ((abBuffer[i * 2] & 0xFF) | (abBuffer[i * 2 + 1] << 8)) / 32768.0;

                for (int i = 0; i < samplesRead; i++)
                    abBufferDouble[i] = lowPassFilter(abBufferDouble[i]);

                baos.write(getBytesFromDoubles(abBufferDouble, samplesRead), 0, samplesRead * 2);
            }
        } catch (InterruptedException e) {
        }

        tdl.stop();
        tdl.close();

        writeWavFile(baos.toByteArray(), baos.size() / 2, "output.wav");
    }

    public static void main(String[] args) {
        AudioFormat audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, SAMPLERATE, 16, 1, 2, SAMPLERATE, false);
        DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat, BUFFERSIZE);

        TargetDataLine targetDataLine = null;
        try {
            targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
            targetDataLine.open(audioFormat, BUFFERSIZE);
            System.out.println("Buffer size: " + targetDataLine.getBufferSize());
        } catch (LineUnavailableException e1) {
            e1.printStackTrace();
        }

        // creating the recorder thread from this class' instance
        LowPassFilterIIR lowPassFilter = new LowPassFilterIIR(targetDataLine);
        Thread lowPassFilterThread = new Thread(lowPassFilter);

        // we use this to read line from the standard input
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        lowPassFilterThread.setPriority(Thread.MAX_PRIORITY);
        lowPassFilterThread.start();

        System.out.println("Recording... press ENTER to stop recording!");
        try {
            br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }

        lowPassFilterThread.interrupt();

        try {
            // waiting for the recorder thread to stop
            lowPassFilterThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Recording stopped.");
    }

}

download (mirror2) (129.2 kb, 41 dls, today: 0)

Dude 2012-03-26 14:33:54

Where do you specifcy the cutoff frequency?

Nonoo 2012-03-26 14:36:32

When designing the filter on the filter design page.

 
 
Dude 2012-03-26 14:45:08

Hmmm, how would you go ahead if you would do that fluently instead? Like setting 300 hz directly in the code?

Nonoo 2012-03-26 15:03:49

Then you must calculate the coefficients of the filter again for the new frequency. This subject will be covered in one of my upcoming tutorials.

Hao 2016-01-28 08:36:21

great tutorial, really waiting for that

 
 
 
Dude 2012-03-26 15:14:46

Thats awesome! I learned alot by reading what you have now :)
I’ll be looking forward to it!

 
Lynn 2015-12-14 05:47:02

Hello,

Chanced upon this tutorial and I find it useful. However,may I know how to implement bandpass filter using IIR Filter?

Regards,
Lindsey

 
Hao 2016-01-28 10:59:37

Can you show me how to calculate frequency dominant of it

 
Name (required)
E-mail (required - never shown publicly)
Webpage URL
Comment:
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> in your comment.

Trackback responses to this post

About me

Nonoo
I'm Nonoo. This is my blog about music, sounds, filmmaking, amateur radio, computers, programming, electronics and other things I'm obsessed with. ... »

Twitter

Listening now

My favorite artists