package jass.generators; import jass.engine.*; import java.io.*; /** Vibration model of object, capable of playing sound. Changes in freq damping and gains are linearly changed over one buffer rather than abruptly @author Kees van den Doel (kvdoel@cs.ubc.ca) */ public class ModalObjectWithOneContactInterpolated extends ModalObjectWithOneContact { /** The transfer function of a reson filter is H(z) = 1/(1-twoRCosTheta/z + R2/z*z). */ protected float[] R2_new; protected float[] twoRCosTheta_new; /** Reson filter gain. */ protected float[] ampR_new; /** Constructor for derived classes to call super @param bufferSize Buffer size used for real-time rendering. */ public ModalObjectWithOneContactInterpolated(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 ModalObjectWithOneContactInterpolated(float srate,int nf,int np,int bufferSize) { super(bufferSize); this.srate = srate; modalModel = new ModalModel(nf,np); allocate(nf,np); allocate_new(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 ModalObjectWithOneContactInterpolated(ModalModel m,float srate,int bufferSize) { super(bufferSize); this.srate = srate; modalModel = m; allocate(modalModel.nf,modalModel.np); allocate_new(m.nf,m.np); computeFilter(); tmpBuf = new float[bufferSize]; } /** Allocate data for new filter values @param nf number of modes. @param np number of locations. */ protected void allocate_new(int nf,int np) { R2_new = new float[nf]; twoRCosTheta_new = new float[nf]; ampR_new = new float[nf]; } /** 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_new[i] = tmp_r*tmp_r; twoRCosTheta_new[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 && modalModel.fscale*modalModel.f[i] > 50) { ampR_new[i] = modalModel.ascale *c_i[i] * (b1* modalModel.a[p1][i] + b2* modalModel.a[p2][i] + b3* modalModel.a[p3][i]); } else { ampR_new[i] = 0; // kill stuff above nyquist rate } } } /** Apply external force[] and compute response through bank of modal filters. Interpolate filter parameters over the buffer. c[k]=k*(c_new-c_old)/nsamples + c_old @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) { // filter parameters are: // twoRCosTheta[nf]; R2[nf]; ampR[nf] 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_old = twoRCosTheta[i]; float tmp_twoRCosTheta_new = twoRCosTheta_new[i]; float tmp_twoRCosTheta_coeff = (tmp_twoRCosTheta_new-tmp_twoRCosTheta_old)/nsamples; float tmp_R2_old = R2[i]; float tmp_R2_new = R2_new[i]; float tmp_R2_coeff = (tmp_R2_new-tmp_R2_old)/nsamples; float tmp_a_old = ampR[i]; float tmp_a_new = ampR_new[i]; float tmp_a_coeff = (tmp_a_new-tmp_a_old)/nsamples; float tmp_yt_1 = yt_1[i]; float tmp_yt_2 = yt_2[i]; for(int k=0;k<nsamples;k++) { // optimize by taking */ out ofthe loop float ynew = (tmp_twoRCosTheta_old+tmp_twoRCosTheta_coeff*k/nsamples)*tmp_yt_1 - (tmp_R2_old+tmp_R2_coeff*k/nsamples) * tmp_yt_2 + (tmp_a_old + tmp_a_coeff*k/nsamples) * force[k]; 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; twoRCosTheta[i] = twoRCosTheta_new[i]; R2[i] = R2_new[i]; ampR[i] = ampR_new[i]; } } }