package jass.generators; import jass.engine.*; import java.io.*; /** Vibration model of object, capable of playing sound. @author Kees van den Doel (kvdoel@cs.ubc.ca) */ public class ModalObjectWithOneContact extends InOut { /** Sampling rate in Hertz. */ public float srate; /** Modal data. */ public ModalModel modalModel; /** Current barycentric location points. */ protected int p1=0,p2=0,p3=0; /** Current barycentric coordinates of location. */ protected float b1=1,b2=0,b3=0; /** State of filters. */ protected float [] yt_1, yt_2; private float [] yt_1_secondary, yt_2_secondary; /** The transfer function of a reson filter is H(z) = 1/(1-twoRCosTheta/z + R2/z*z). */ protected float[] R2; /** The transfer function of a reson filter is H(z) = 1/(1-twoRCosTheta/z + R2/z*z). */ protected float[] twoRCosTheta; /** Reson filter gain. */ protected float[] ampR; /** Cached values. */ protected float[] c_i; /** Temp storage */ protected float[] tmpBuf = null; /** Add a Source. Overrides Sink interface implementation from InOut. Allow only one Source. @param s Source to add. */ public Object addSource(Source s) throws SinkIsFullException { if(sourceContainer.size() > 0) { throw new SinkIsFullException(); } else { sourceContainer.addElement(s); } return null; } /** Scale dampings. @param d damping scale. */ public void setDamping(float dscale) { modalModel.dscale = dscale; System.out.println("asd"); computeFilter(); } /** Scale gains. @param a gain scale. */ public void setGain(float a) { modalModel.ascale = a; computeFilter(); } /** Scale frequencies. @param fscale frequency scale. */ public void setFrequencyScale(float fscale) { modalModel.fscale = fscale; computeFilter(); } /** Constructor for derived classes to call super @param bufferSize Buffer size used for real-time rendering. */ public ModalObjectWithOneContact(int bufferSize) { super(bufferSize); } /** Create and initialize, but don't set any modal parameters. @param srate sampling rate in Hertz. @param nf number of modes. @param np number of locations. @param bufferSize Buffer size used for real-time rendering. */ public ModalObjectWithOneContact(float srate,int nf,int np,int bufferSize) { super(bufferSize); this.srate = srate; modalModel = new ModalModel(nf,np); allocate(nf,np); tmpBuf = new float[bufferSize]; } /** Create and initialize with provided modal data. @param m modal model to load. @param srate sampling rate in Hertz. @param bufferSize Buffer size used for real-time rendering. */ public ModalObjectWithOneContact(ModalModel m,float srate,int bufferSize) { super(bufferSize); this.srate = srate; modalModel = m; allocate(modalModel.nf,modalModel.np); computeFilter(); tmpBuf = new float[bufferSize]; } /** Reduce number of modes used. @param nf number of modes to use. */ public void setNf(int nf) { if(nf < modalModel.nf) { modalModel.nfUsed = nf; } } /** Allocate data. @param nf number of modes. @param np number of locations. */ protected void allocate(int nf,int np) { R2 = new float[nf]; twoRCosTheta = new float[nf]; ampR = new float[nf]; yt_1 = new float[nf]; yt_2 = new float[nf]; yt_1_secondary = new float[nf]; yt_2_secondary = new float[nf]; c_i = new float[nf]; clearHistory(); } /** Compute the filter coefficients used for real-time rendering from the modal model parameters. */ public void computeFilter() { computeResonCoeff(); computeLocation(); } /** Compute the reson coefficients from the modal model parameters. Cache values used in {@link #setLocation}. */ public void computeResonCoeff() { for(int i=0;i<modalModel.nf;i++) { float tmp_r = (float)(Math.exp(-modalModel.dscale*modalModel.d[i]/srate)); R2[i] = tmp_r*tmp_r; twoRCosTheta[i] = (float)(2*Math.cos(2*Math.PI*modalModel.fscale* modalModel.f[i]/srate)*tmp_r); c_i[i] = (float)(Math.sin(2*Math.PI*modalModel.fscale* modalModel.f[i]/srate)*tmp_r); } } /** Compute gains. Check also if any frequency is above Nyquist rate. If so set its gain to zero. */ protected void computeLocation() { for(int i=0;i<modalModel.nf;i++) { if(modalModel.fscale*modalModel.f[i] < srate/2) { ampR[i] = modalModel.ascale *c_i[i] * (b1* modalModel.a[p1][i] + b2* modalModel.a[p2][i] + b3* modalModel.a[p3][i]); } else { ampR[i] = 0; // kill stuff above nyquist rate } } } /** Compute the gain coefficients from the modal model parameters at point p, given inside triangle of point p1,p2,p3, with barycentric coordinated b1,b2,b3 @param p1 location index 1. @param p2 location index 2. @param p3 location index 3. @param b1 barycentric coordinate 1. @param b2 barycentric coordinate 2. @param b3 barycentric coordinate 3. */ public void setLocation(int p1, int p2, int p3, float b1, float b2, float b3) { this.p1 = p1; this.p2 = p2; this.p3 = p3; this.b1 = b1; this.b2 = b2; this.b3 = b3; computeLocation(); } /** Set state to non-vibrating. */ public void clearHistory() { for(int i=0;i<modalModel.nf;i++) { yt_1[i] = yt_2[i] = 0; yt_1_secondary[i] = yt_2_secondary[i] = 0; } } protected float rollGain = .0f; // mix ratio of roll comp /** Compute the next buffer and store in member float[] buf. */ protected void computeBuffer() { computeModalFilterBank(this.buf, srcBuffers[0], getBufferSize()); } // 0.001 works to prevent strange underflow (??) bug static final float eps = 0.001f; /** Apply external force[] and compute response through bank of modal filters. @param output user provided output buffer. @param force input force. @param nsamples number of samples to compute. */ protected void computeModalFilterBank(float[] output, float[] force, int nsamples) { boolean isnul = true; for(int k=0;k<nsamples;k++) { output[k] = 0; if(Math.abs(force[k])>=eps) { isnul = false; } } int nf = modalModel.nfUsed; if(isnul) { for(int i=0;i<nf;i++) { if(Math.abs(yt_1[i]) >= eps || Math.abs(yt_2[i]) >= eps) { isnul = false; break; } } } if(isnul) { return; } for(int i=0;i<nf;i++) { float tmp_twoRCosTheta = twoRCosTheta[i]; float tmp_R2 = R2[i]; float tmp_a = ampR[i]; float tmp_yt_1 = yt_1[i]; float tmp_yt_2 = yt_2[i]; for(int k=0;k<nsamples;k++) { float ynew = tmp_twoRCosTheta * tmp_yt_1 - tmp_R2 * tmp_yt_2 + tmp_a * force[k]; // commenting out the force[k] changes performance 650->718 tmp_yt_2 = tmp_yt_1; tmp_yt_1 = ynew; output[k] += ynew; } yt_1[i] = tmp_yt_1; yt_2[i] = tmp_yt_2; } } /** Apply external force[] and compute response through bank of modal filters. Has different state. @param output user provided output buffer. @param force input force. @param nsamples number of samples to compute. */ private void computeModalFilterBank_2(float[] output, float[] force, int nsamples) { for(int k=0;k<nsamples;k++) { output[k] = 0; } int nf = modalModel.nfUsed; for(int i=0;i<nf;i++) { float tmp_twoRCosTheta = twoRCosTheta[i]; float tmp_R2 = R2[i]; float tmp_a = ampR[i]; float tmp_yt_1 = yt_1_secondary[i]; float tmp_yt_2 = yt_2_secondary[i]; for(int k=0;k<nsamples;k++) { float ynew = tmp_twoRCosTheta * tmp_yt_1 - tmp_R2 * tmp_yt_2 + tmp_a * force[k]; // commenting out the force[k] changes performance 650->718 tmp_yt_2 = tmp_yt_1; tmp_yt_1 = ynew; output[k] += ynew; } yt_1_secondary[i] = tmp_yt_1; yt_2_secondary[i] = tmp_yt_2; } } }