package jass.generators; /** Half sample delay Kelly-Lochbaum filter. See Master thesis of Siddarth Mathur, Univ. of Arizona 2003 for details of the algorithm. This implementation defines a tube of a certain maximum number of segments. the actual end segment depends on the actual length and can be modified at run-time. @author Kees van den Doel (kvdoel@cs.ubc.ca) */ public class HalfSampleDelayKLFilter implements Filter { /** Sampling rate in Hertz. */ protected float srate; protected State state; public class State { public double glottalRefl = 0.99; public double lipRefl = -0.99; public double dampingCoeff = 1.0; // overall damping in system public int nTubeSections; // maximum segments (must be even) public int nSectionsUsed; // actual used (may change dynamically) public double[] area; // Area of the segments public double[] f,b; // filter state } private double[] r; // reflection coefficients (not in State for efficiency) protected void allocate() { state.area = new double[state.nTubeSections]; r = new double[state.nTubeSections-1]; state.f = new double[state.nTubeSections]; state.b = new double[state.nTubeSections]; for(int i=0;i<state.nTubeSections;i++) { state.area[i] = 1; } computeReflectionCoeff(); } /** Clear filter of past history */ public void resetFilter() { for(int i=0;i<state.nTubeSections;i++) { state.f[i] = 0; state.b[i] = 0; } } /** Set actual number of segments used @param end number of segments (so last segment used has index end-1) */ public void setEnd(int end) { if(end > state.nTubeSections) { state.nSectionsUsed = state.nTubeSections; } else if(end > 2) { // do nothing if called with ridiculous value if(end != state.nSectionsUsed) { state.nSectionsUsed = end; } } computeReflectionCoeff(); } /** Compute reflection coefficients from areas */ protected void computeReflectionCoeff() { for(int i=0;i<state.nSectionsUsed-1;i++) { r[i] = (state.area[i] - state.area[i+1])/(state.area[i] + state.area[i+1]); } for(int i=state.nSectionsUsed;i<state.nTubeSections-1;i++) { r[i] = 0; } } /** Set the glottal reflection coeff. @param val glottal reflection coefficient */ public void setGlottalReflectionCoeff(double val) { state.glottalRefl = val; } /** Set the end (lip) reflection coeff. @param val glottal reflection coefficient (positive, actual coeff is negative) */ public void setLipReflectionCoeff(double val) { state.lipRefl = -val; } /** Set damping coeff. (1 == no damping) @param val damping coefficient */ public void setDampingCoeff(double val) { state.dampingCoeff = val; } /** Create and initialize. @param srate sampling rate in Hertz. @param nTubeSection maximum number of sections (must be even) */ public HalfSampleDelayKLFilter(float srate, int nTubeSections) { state = new State(); this.srate = srate; state.nTubeSections = 2*nTubeSections; // can double in size: always even state.nSectionsUsed = nTubeSections; allocate(); setEnd(state.nSectionsUsed); resetFilter(); System.out.println("nsections="+state.nTubeSections); System.out.println("nsectionsUsed="+state.nSectionsUsed); } public HalfSampleDelayKLFilter() {} /** Set an individual segment radius @param k index of segment (0,...) @param r radius to set */ public void setCylinderRadius(int k,double r) { state.area[k]=r*r; computeReflectionCoeff(); } /** Set all radii @param array of r radii */ public void setAllCylinderRadii(double[] r) { for(int k=0;k<state.nSectionsUsed;k++) { state.area[k]=r[k]*r[k]; } computeReflectionCoeff(); } /** Proces input (may be same as output). Implements Filter interface @param output user provided buffer for returned result. @param input user provided input buffer. @param nsamples number of samples written to output buffer. @param inputOffset where to start in circular buffer input (unused) */ public void filter(float [] output, float[] input, int nsamples, int inputOffset) { int nJunctions = state.nTubeSections-1; int nJunctionsUsed = state.nSectionsUsed-1; if(state.nSectionsUsed%2 == 1) { // odd # sections nJunctionsUsed++; // last segment is "virtual": nJunctionsUsed always odd } double damp = state.dampingCoeff; double delta; double [] f = state.f; double [] b = state.b; for (int k=0;k<nsamples;k++) { //Input into system f[0]=input[k]/state.area[0] + state.glottalRefl*b[0]; for (int i=1;i<nJunctionsUsed-1;i+=2) { delta = r[i]*(f[i] - b[i+1]); f[i+1] = damp*(f[i] + delta); b[i] = damp*(b[i+1] + delta); } for (int i=0;i<nJunctionsUsed;i+=2) { delta = r[i]*(f[i] - b[i+1]); f[i+1] = damp*(f[i] + delta); b[i] = damp*(b[i+1] + delta); } b[state.nSectionsUsed-1] = state.lipRefl*f[state.nSectionsUsed-1]; output[k]=(float)(f[state.nSectionsUsed-1] + b[state.nSectionsUsed-1]); //System.out.println(output[k]); } } }