package jass.generators;
/** Kelly-Lochbaum filter. Follow sample code from
http://people.ee.ethz.ch/~jniederh/VocSynth/
@author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class KellyLochbaumFilter implements Filter {
private static final double DEFAULT_dampingCoeff = 1;
/** How much damping in system (1 == no damping)*/
protected double dampingCoeff=DEFAULT_dampingCoeff;
/** Sampling rate in Hertz. */
protected float srate;
/** State of filter. */
protected double[] li,lo,gi,go;
/** This many cylinder segments */
protected int nTubeSections;
/** Radii of the segments */
protected double[] cylRadius;
/** Filter coefficients derived form cylinder radii */
protected double[] kCoeff;
/** Create and initialize.
@param srate sampling rate in Hertz.
@param nTubeSection number of sections
*/
public KellyLochbaumFilter(float srate, int nTubeSections) {
this.srate = srate;
this.nTubeSections = nTubeSections;
allocate();
resetFilter();
System.out.println("ns="+nTubeSections);
}
public KellyLochbaumFilter() {}
private void allocate() {
li=new double[nTubeSections+1]; //to lips input to reflection --(z-1)--li----lo--
lo=new double[nTubeSections+1]; //to lips output of reflection |refl|
gi=new double[nTubeSections+1]; //to glottis input to reflection ------go----gi--
go=new double[nTubeSections+1]; //to glottis output of reflection
cylRadius = new double[nTubeSections+1];
kCoeff = new double[nTubeSections+1]; //reflections coefficients
for(int i=0;i<=nTubeSections;i++) {
cylRadius[i] = 1;
li[i]=lo[i]=gi[i]=go[i]=0;
kCoeff[i]=0;
}
computeKCoeff();
}
/** Compute low level filter values from geometry */
protected void computeKCoeff() {
kCoeff[0]=1.0; //Zgl=0
for(int i=1;i<nTubeSections;i++) {
kCoeff[i] = (cylRadius[i]*cylRadius[i]-cylRadius[i-1]*
cylRadius[i-1])/(cylRadius[i]*cylRadius[i]+cylRadius[i-1]*cylRadius[i-1]);
}
kCoeff[nTubeSections]=1.0; //Zl=inf
}
/** Set an individual segment radius
@param k index of segment (0,...)
@param r radius to set
*/
public void setCylinderRadius(int k,double r) {
cylRadius[k]=r;
computeKCoeff();
}
/** Set all radii
@param array of r radii
*/
public void setAllCylinderRadii(double[] r) {
for(int k=0;k<nTubeSections;k++) {
cylRadius[k]=r[k];
}
computeKCoeff();
}
/** Set damping coeff. (1 == no damping)
@param val damping coefficient
*/
public void setDampingCoeff(double val) {
dampingCoeff = val;
}
/** Clear filter of past history */
public void resetFilter() {
for(int i=0;i<=nTubeSections;i++) {
li[i]=lo[i]=gi[i]=go[i]=0;
}
}
/** Set the glottal reflection coeff.
@param val glottal reflection coefficient
*/
public void setGlottalReflectionCoeff(double val) {
}
/** 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.
*/
public void filter(float [] output, float[] input, int nsamples, int inputOffset) {
int inputLen = input.length;
int ii = inputOffset;
for (int k=0;k<nsamples;k++) {
//Input into system
li[0]=input[k]/2.0;
//Calculate all reflections
for (int i=nTubeSections;i>=0;i--)
{
//to lips
lo[i]=dampingCoeff*((1+kCoeff[i])*li[i]+kCoeff[i]*gi[i]);
//to glottis
go[i]=dampingCoeff*((1-kCoeff[i])*gi[i]-kCoeff[i]*li[i]);
//To glottis without delay!
if(i>1)
{
gi[i-1]=dampingCoeff*go[i];
}
}
//calculate delays towards lips
for (int i=0;i<nTubeSections;i++) {
li[i+1]=dampingCoeff*lo[i];
}
//Lip output
output[k]=(float)lo[nTubeSections];
}
}
}