package jass.contact;
import jass.engine.*;
import jass.generators.*;
/**
A force model with impact, slide, and slide modes based on looping wav files,
fed through Reson.
Roll force is fed through modal filter.
@author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class FilteredWavContactForce extends Out {
// impact
protected BangForce bangForce;
// slide
protected ConstantLoopBuffer slideForceRaw;
protected FilterContainer slideForce;
protected ResonFilter resonSlideFilter;
// freq., damp. and gain of AR model (Reson).
protected float slideARd=1000f,slideARf=100f,slideARa=1f;
// min and max reson values
protected float slideFreq0=10f,slideFreq1=2000f;
// physical speed range
protected float vslide0=0,vslide1=1f;
// roll
protected ConstantLoopBuffer rollForceRaw;
protected FilterContainer rollReson; // reson container
protected ResonFilter resonRollFilter;
// freq., damp. and gain of AR model (Reson).
protected float rollARd=1000f,rollARf=100f,rollARa=1f;
// min and max reson values
protected float rollFreq0=10f,rollFreq1=2000f;
// modal filter for rolling
protected ModalObjectWithOneContact rollForce;
// Damping scale of roll filter
protected float rollModalDamping=1.f;
// physical speed range
protected float vroll0=0,vroll1=1f;
// gain ratios
protected float physicalToAudioGainSlide=1,
physicalToAudioGainRoll=1,
physicalToAudioGainImpact=1;
/** Set model parameters mapping physical units to audio units
@param slideFreq0 minimum reson freq.
@param slideFreq1 maximum reson freq.
@param rollFreq0 minimum reson freq.
@param rollFreq1 maximum reson freq.
@param vslide0 minimum physical speed (lower than this is considered to be zero)
@param vslide1 maximum physical speed (higher than this is set to this value)
@param vroll0 minimum physical speed (lower than this is considered to be zero)
@param vroll1 maximum physical speed (higher than this is set to this value)
@param physicalToAudioGainSlide multiplies normal force to get slide gain
@param physicalToAudioGainRoll multiplies normal force to get roll gain
@param physicalToAudioGainImpact multiplies impact force to get impact gain
*/
public void setStaticContactModelParameters(float slideFreq0, float slideFreq1,
float rollFreq0, float rollFreq1,
float vslide0, float vslide1,
float vroll0, float vroll1,
float physicalToAudioGainSlide,
float physicalToAudioGainRoll,
float physicalToAudioGainImpact) {
this.slideFreq0 = slideFreq0;
this.slideFreq1 = slideFreq1;
this.rollFreq0 = rollFreq0;
this.rollFreq1 = rollFreq1;
this.vslide0 = vslide0;
this.vslide1 = vslide1;
this.vroll0 = vroll0;
this.vroll1 = vroll1;
this.physicalToAudioGainSlide = physicalToAudioGainSlide;
this.physicalToAudioGainRoll = physicalToAudioGainRoll;
this.physicalToAudioGainImpact = physicalToAudioGainImpact;
}
/** Constructor intended only for subclass constructors (super(bufferSize);)
*/
protected FilteredWavContactForce(int bufferSize) {
super(bufferSize);
}
/** Construct contact force from named files.
@param srate sampling rate in Hertz.
@param bufferSize bufferSize of this Out.
@param fnImpact Audio file name for impact. (For example cos20ms.wav.)
@param fnSlide Audio file name for slide. (For example grid.wav.)
@param fnRoll Audio file name for slide. (For example roll.wav.)
@param mm ModalModel
*/
public FilteredWavContactForce(float srate, int bufferSize,
String fnImpact,String fnSlide,
String fnRoll, ModalModel mm) {
super(bufferSize);
// impact
bangForce = new BangForce(srate,bufferSize,fnImpact);
// slide filtergraph
slideForceRaw = new ConstantLoopBuffer(srate,bufferSize,fnSlide);
resonSlideFilter = new ResonFilter(srate);
resonSlideFilter.setResonCoeff(slideARf,slideARd,slideARa);
slideForce = new FilterContainer(srate,bufferSize,resonSlideFilter);
try {
slideForce.addSource(slideForceRaw);
} catch(SinkIsFullException e) {
System.out.println(this+" 1"+e);
}
// roll filtergraph
rollForceRaw = new ConstantLoopBuffer(srate,bufferSize,fnRoll);
resonRollFilter = new ResonFilter(srate);
resonRollFilter.setResonCoeff(rollARf,rollARd,rollARa);
rollReson = new FilterContainer(srate,bufferSize,resonRollFilter);
rollForce = new ModalObjectWithOneContact(mm,srate,bufferSize);
rollForce.setDamping(rollModalDamping);
try {
rollForce.addSource(rollReson);
} catch(SinkIsFullException e) {
System.out.println(this+" 2"+e);
}
try {
rollReson.addSource(rollForceRaw);
} catch(SinkIsFullException e) {
System.out.println(this+" 3"+e);
}
// synchronize
bangForce.setTime(getTime());
slideForceRaw.setTime(getTime());
slideForce.setTime(getTime());
rollForceRaw.setTime(getTime());
rollReson.setTime(getTime());
rollForce.setTime(getTime());
}
/** Set slide model damping (usually static property)
@param d damping
*/
public void setSlideModelDamping(float d) {
this.slideARd = d;
}
/** Set slide reson at low lvel */
public void setSlideReson(float f,float d,float a) {
if(f<1f) {
f=.1f;
a=0;
}
slideARf = f;
slideARd = d;
slideARa = a;
resonSlideFilter.setResonCoeff(slideARf,slideARd,slideARa);
}
/** Set slide speed and normal force in physical units.
@param force normal force.
@param speed relative surface velocity.
*/
public synchronized void setSlideProperties(float force,float speed) {
// f= a*v +b;
float a = (slideFreq1-slideFreq0)/(vslide1-vslide0);
float b = slideFreq0 - a*vslide0;
float f,d; // freq and gain
float vol = (float)(Math.sqrt(force * speed/vslide1));
if(speed > vslide1) {
speed = vslide1;
slideARf = slideFreq1;
slideARa = physicalToAudioGainSlide*vol;
} else if(speed < vslide0) {
slideARf = slideFreq1;
slideARa = 0;
} else {
slideARf = a*speed + b;
slideARa = physicalToAudioGainSlide*vol;
}
resonSlideFilter.setResonCoeff(slideARf,slideARd,slideARa);
}
/** Set roll model damping (usually static property)
@param d damping
*/
public void setRollModelDamping(float d) {
this.rollARd = d;
}
/** Set roll reson at low lvel */
public void setRollReson(float f,float d,float a) {
if(f<1f) {
f=.1f;
a=0;
}
rollARf = f;
rollARd = d;
rollARa = a;
if(f<.1f) {
f=.1f;
}
resonRollFilter.setResonCoeff(rollARf,rollARd,rollARa);
}
/** Set roll speed and normal force in physical units.
@param force normal force.
@param speed roll velocity.
*/
public synchronized void setRollProperties(float force,float speed) {
// f= a*v +b;
float a = (rollFreq1-rollFreq0)/(vroll1-vroll0);
float b = rollFreq0 - a*vroll0;
float f,d; // freq and gain
float vol = (float)(Math.sqrt(force * speed/vroll1));
if(speed > vroll1) {
speed = vroll1;
rollARf = rollFreq1;
rollARa = physicalToAudioGainRoll*vol;
} else if(speed < vroll0) {
rollARf = rollFreq1;
rollARa = 0;
} else {
rollARf = a*speed + b;
rollARa = physicalToAudioGainRoll*vol;
}
//System.out.println("roll fda="+rollARf+ " "+rollARd+" "+rollARa);
resonRollFilter.setResonCoeff(rollARf,rollARd,rollARa);
}
/** Set roll force modal filter properties: damping.
*/
public synchronized void setRollFilterModalDamping(float val) {
rollModalDamping = val;
rollForce.setDamping(rollModalDamping);
}
/** Generate impact force in physical units.
@param force magnitude.
@param dur duration in seconds of impact.
*/
public synchronized void bang(float force, float dur) {
bangForce.bang(physicalToAudioGainImpact*force,dur);
}
/** Compute the next buffer. Mix down the 3 contributions
*/
public synchronized void computeBuffer() {
try {
float[] b1 = bangForce.getBuffer(getTime());
float[] b2 = slideForce.getBuffer(getTime());
float[] b3 = rollForce.getBuffer(getTime());
int bufsz = getBufferSize();
for(int k=0;k<bufsz;k++) {
buf[k] = b1[k] + b2[k] + b3[k];
}
} catch(BufferNotAvailableException e) {
System.out.println(this+" "+e);
}
}
}