DSP tutorial: Lowpass filtering using an IIR filter
Last modified: November 16th, 2011This 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 (129.2 kb)
RSS feed for comments
Trackback URL
Trackback URL
8 Comments »
Trackback responses to this post
About me
I'm Nonoo. This is my blog about music, sounds, filmmaking, amateur radio, computers, programming, electronics and other things I'm obsessed with.
... »
Where do you specifcy the cutoff frequency?
When designing the filter on the filter design page.
Hmmm, how would you go ahead if you would do that fluently instead? Like setting 300 hz directly in the code?
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.
great tutorial, really waiting for that
Thats awesome! I learned alot by reading what you have now :)
I’ll be looking forward to it!
Hello,
Chanced upon this tutorial and I find it useful. However,may I know how to implement bandpass filter using IIR Filter?
Regards,
Lindsey
Can you show me how to calculate frequency dominant of it