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;
}
}
}