package jass.generators;
import jass.engine.*;
import java.io.*;
/** Vibration model of object, capable of playing sound. Modal parameters are
fed through a of butterworth filter so if you change them they change slowly.
@author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class StickyModalObjectWithOneContact extends ModalObjectWithOneContact {
protected Butter2LowFilter[] butterFiltersR2; // filter modal parameters
protected Butter2LowFilter[] butterFiltersAmp; // filter modal parameters
protected Butter2LowFilter[] butterFiltersCos; // filter modal parameters
protected float butterLowPassFreq = .25f; // lowpass frequency of butterworth filter in Hz
protected float[] parQueue = new float[2]; // for filtering
final static int QUEUESIZE = 2;
final static int OFFSET = 0;
final static int NRELAXATIONS = 100;
protected boolean hasBeenConverged = false;
/** Reset parameter filter
*/
public void resetParameterFilter() {
hasBeenConverged = false;
}
/** Set lowpass freq through which modal parameters are fed
@param freq lowpass cutoff of butterworth filter
*/
public void setLowPassControlFilter(float freq) {
butterLowPassFreq = freq;
int nf = modalModel.nfUsed;
for(int i=0;i<nf;i++) {
butterFiltersR2[i].setCutoffFrequency(freq);
butterFiltersCos[i].setCutoffFrequency(freq);
butterFiltersAmp[i].setCutoffFrequency(freq);
}
}
/** Get lowpass freq through which modal parameters are fed
@return lowpass cutoff of butterworth filter
*/
public float getLowPassControlFilter() {
return butterLowPassFreq;
}
/** old values of low-level parameters */
protected float[] R2_old;
protected float[] twoRCosTheta_old;
protected float[] ampR_old;
/** Constructor for derived classes to call super
@param bufferSize Buffer size used for real-time rendering.
*/
public StickyModalObjectWithOneContact(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 StickyModalObjectWithOneContact(float srate,int nf,int np,int bufferSize) {
super(srate,nf,np,bufferSize);
allocateOldData(nf,np);
}
/** 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 StickyModalObjectWithOneContact(ModalModel m,float srate,int bufferSize) {
super(m,srate,bufferSize);
allocateOldData(modalModel.nf,modalModel.np);
}
/** Allocate old data.
@param nf number of modes.
@param np number of locations.
*/
protected void allocateOldData(int nf,int np) {
R2_old = new float[nf];
twoRCosTheta_old = new float[nf];
ampR_old = new float[nf];
float controlRate = (float)(srate/bufferSize);
butterFiltersR2 = new Butter2LowFilter[nf];
butterFiltersCos = new Butter2LowFilter[nf];
butterFiltersAmp = new Butter2LowFilter[nf];
for(int i=0;i<nf;i++) {
butterFiltersR2[i] = new Butter2LowFilter(controlRate);
butterFiltersCos[i] = new Butter2LowFilter(controlRate);
butterFiltersAmp[i] = new Butter2LowFilter(controlRate);
butterFiltersR2[i].setCutoffFrequency(butterLowPassFreq);
butterFiltersCos[i].setCutoffFrequency(butterLowPassFreq);
butterFiltersAmp[i].setCutoffFrequency(butterLowPassFreq);
}
}
/** Apply external force[] and compute response through bank of modal filters.
Filter modal parameters through the butterworth filter so they change slowly
@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;
}
if(hasBeenConverged) {
for(int i=0;i<nf;i++) {
parQueue[1] = R2[i];
parQueue[0] = R2_old[i];
butterFiltersR2[i].filter(parQueue,parQueue,QUEUESIZE,OFFSET);
//System.out.println("0:"+parQueue[0]+" 1:"+parQueue[1]);
R2_old[i] = parQueue[1];
parQueue[1] = twoRCosTheta[i];
parQueue[0] = twoRCosTheta_old[i];
butterFiltersCos[i].filter(parQueue,parQueue,QUEUESIZE,OFFSET);
twoRCosTheta_old[i] = parQueue[1];
parQueue[1] = ampR[i];
parQueue[0] = ampR_old[i];
butterFiltersAmp[i].filter(parQueue,parQueue,QUEUESIZE,OFFSET);
ampR_old[i] = parQueue[1];
}
} else {
for(int i=0;i<nf;i++) {
for(int k=0;k<NRELAXATIONS;k++) {
parQueue[1] = R2[i];
parQueue[0] = R2[i];
butterFiltersR2[i].filter(parQueue,parQueue,QUEUESIZE,OFFSET);
}
for(int k=0;k<NRELAXATIONS;k++) {
parQueue[1] = twoRCosTheta[i];
parQueue[0] = twoRCosTheta[i];
butterFiltersCos[i].filter(parQueue,parQueue,QUEUESIZE,OFFSET);
}
for(int k=0;k<NRELAXATIONS;k++) {
parQueue[1] = ampR[i];
parQueue[0] = ampR[i];
butterFiltersAmp[i].filter(parQueue,parQueue,QUEUESIZE,OFFSET);
}
R2_old[i] = R2[i];
twoRCosTheta_old[i] = twoRCosTheta[i];
ampR_old[i] = ampR[i];
}
/*
for(int i=0;i<nf;i++) {
butterFiltersR2[i].reset(R2[i]);
butterFiltersCos[i].reset(twoRCosTheta[i]);
butterFiltersAmp[i].reset(ampR[i]);
}
*/
hasBeenConverged = true;
}
for(int i=0;i<nf;i++) {
float tmp_twoRCosTheta = twoRCosTheta_old[i];
float tmp_R2 = R2_old[i];
float tmp_a = ampR_old[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;
}
}
}