/**
* Copyright 2004-2006 DFKI GmbH.
* All Rights Reserved. Use is subject to license terms.
*
* This file is part of MARY TTS.
*
* MARY TTS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package marytts.signalproc.filter;
import marytts.signalproc.process.InlineDataProcessor;
import marytts.util.data.BufferedDoubleDataSource;
import marytts.util.data.DoubleDataSource;
/**
* @author Marc Schröder A purely recursive filter, following the equation:
* <code>x[n] = e[n] + a[1]*x[n-1] + a[2]*x[n-2] + ... + a[p]*x[n-p]</code>
*/
public class RecursiveFilter {
/**
* The prediction coefficients, as in * <code>x[n] = e[n] + a[1]*x[n-1] + a[2]*x[n-2] + ... + a[p]*x[n-p]</code>
*/
protected final double[] a;
/**
* Create a new recursive filter.
*
* @param a
* the recursive prediction coefficients
*/
public RecursiveFilter(double[] a) {
this.a = a;
}
/**
* Apply this filter to the given input signal. The input signal is filtered piece by piece, as it is read from the data
* source returned by this method. This is the recommended way to filter longer signals.
*
* @param signal
* the signal to which this filter should be applied
* @return a DoubleDataSource from which the data can be read.
*/
public DoubleDataSource apply(DoubleDataSource signal) {
return new BufferedDoubleDataSource(signal, new Processor(a));
}
/**
* Apply this filter to the given input signal. This method filters the entire signal, and returns the entire filtered signal.
* For long signals, it is better to use apply(DoubleDataSource).
*
* @param signal
* the signal to which this filter should be applied
* @return the filtered signal.
*/
public double[] apply(double[] signal) {
return new BufferedDoubleDataSource(signal, new Processor(a)).getAllData();
}
public static class Processor implements InlineDataProcessor {
/**
* The prediction coefficiednts, as in * <code>x[n] = e[n] + a[1]*x[n-1] + a[2]*x[n-2] + ... + a[p]*x[n-p]</code>
*/
protected final double[] a;
/**
* The prediction order, i.e. the length of a.
*/
protected final int p;
/**
* A memory of <code>x[n-1]...x[n-p]</code>, when reading data in chunks.
*/
protected double[] memory;
public Processor(double[] a) {
this.a = a;
this.p = a.length;
this.memory = new double[p];
}
/**
* Perform recursive filter processing on the data.
*
* @param data
* data to filter, e.g. a residual
* @param off
* position in data to start processing
* @param len
* number of sample points to process
*/
public void applyInline(double[] data, int off, int len) {
if (off < 0 || len <= 0 || off + len > data.length)
throw new IllegalArgumentException("off or len out of bounds");
for (int n = 0; n < len; n++) {
int offn = off + n;
for (int i = 1; i <= p; i++) {
if (n < i) // n-i<0, i.e. we need to look to the left of the current data chunk
data[offn] += a[i - 1] * memory[p + n - i]; // don't be fooled -- this is a[i]*x[n-i]
else
data[offn] += a[i - 1] * data[offn - i]; // don't be fooled -- this is a[i]*x[n-i]
}
}
// Remember last p points in memory
if (len < p) { // "Pathological" case: read less than p samples
// Can only take len new samples into memory
System.arraycopy(memory, len, memory, 0, p - len);
System.arraycopy(data, off, memory, p - len, len);
} else { // Normal processing: read at least p samples
// Copy last p samples into memory
System.arraycopy(data, off + len - p, memory, 0, p);
}
}
}
}