/* * Copyright (c) 2007 - 2008 by Damien Di Fede <ddf@compartmental.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package ddf.minim.effects; import ddf.minim.AudioEffect; import ddf.minim.Minim; /** * <code>Convolver</code> is an effect that convolves a signal with a kernal. * The kernal can be thought of as the impulse response of an audio filter, or * simply as a set of weighting coefficients. <code>Convolver</code> performs * brute-force convolution, meaning that it is slow, relatively speaking. * However, the algorithm is very straighforward. Each output sample * <code>i</code> is calculated by multiplying each kernal value * <code>j</code> with the input sample <code>i - j</code> and then summing * the resulting values. The output will be * <code>kernal.length + signal.length - 1</code> samples long, so the extra * samples are stored in an overlap array. The overlap array from the previous * signal convolution is added into the beginning of the output array, which * results in a output signal without pops. * * @author Damien Di Fede * @see <a href="http://www.dspguide.com/ch6.htm">Convolution</a> * */ public class Convolver implements AudioEffect { protected float[] kernal; protected float[] outputL; protected float[] overlapL; protected float[] outputR; protected float[] overlapR; protected int sigLen; /** * Constructs a Convolver with the kernal <code>k</code> that expects buffer * of length <code>sigLength</code>. * * @param k * the kernal of the filter * @param sigLength * the length of the buffer that will be convolved with the kernal */ public Convolver(float[] k, int sigLength) { sigLen = sigLength; setKernal(k); } /** * Sets the kernal to <code>k</code>. The values in <code>k</code> are * copied so it is not possible to alter the kernal after it has been set * except by setting it again. * * @param k * the kernal to use */ public void setKernal(float[] k) { kernal = new float[k.length]; System.arraycopy(k, 0, kernal, 0, k.length); outputL = new float[sigLen + kernal.length - 1]; outputR = new float[sigLen + kernal.length - 1]; overlapL = new float[outputL.length - sigLen]; overlapR = new float[outputR.length - sigLen]; } public void process(float[] signal) { if (signal.length != sigLen) { Minim .error("Convolver.process: signal.length does not equal sigLen, no processing will occurr."); return; } // store the overlap from the previous convolution System.arraycopy(outputL, signal.length, overlapL, 0, overlapL.length); // convolve kernal with signal and put the result in outputL for (int i = 0; i < outputL.length; i++) { outputL[i] = 0; for (int j = 0; j < kernal.length; j++) { if (i - j < 0 || i - j > signal.length) continue; outputL[i] += kernal[j] * signal[i - j]; } } // copy the result into signal System.arraycopy(outputL, 0, signal, 0, signal.length); // add the overlap from the previous convolution to the beginning of signal for (int i = 0; i < overlapL.length; i++) { signal[i] += overlapL[i]; } } public void process(float[] sigLeft, float[] sigRight) { if (sigLeft.length != sigLen || sigRight.length != sigLen) { Minim .error("Convolver.process: signal.length does not equal sigLen, no processing will occurr."); return; } System.arraycopy(outputL, sigLeft.length, overlapL, 0, overlapL.length); System.arraycopy(outputR, sigRight.length, overlapR, 0, overlapR.length); for (int i = 0; i < outputL.length; i++) { outputL[i] = 0; outputR[i] = 0; for (int j = 0; j < kernal.length; j++) { if (i - j < 0 || i - j >= sigLeft.length) continue; outputL[i] += kernal[j] * sigLeft[i - j]; outputR[i] += kernal[j] * sigRight[i - j]; } } System.arraycopy(outputL, 0, sigLeft, 0, sigLeft.length); System.arraycopy(outputR, 0, sigRight, 0, sigRight.length); for (int i = 0; i < overlapL.length; i++) { sigLeft[i] += overlapL[i]; sigRight[i] += overlapR[i]; } } }