package jass.contact;
import jass.engine.*;
import jass.generators.*;
/**
A force model with impact, slide, and slide modes based on looping wav files
Roll force is fed through lowpass filter.
@author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class ContactForce extends Out {
// Has these protected sources:
protected BangForce bangForce;
protected LoopBuffer slideForce;
protected LoopBuffer rollForceRaw;
protected FilterContainer rollForce;
// lowpass filter for rolling
protected Butter2LowFilter lowPassFilter;
// Cutoff frequency of lowpas filter in Hertz
protected float fLowPass=100.f;
protected float dryRollGain = 0; // 1 for raw wav force, 0 for pure filtered
// properties defining how physical parameters are mapped to audio parameters
// [x0,x1] is range of x
// maximum audio speeds, 1 is original wav file
protected float slideSpeed1=1f,rollSpeed1=1f;
// physical speed ranges
protected float vslide0=.1f,vslide1=1f,vroll0=.1f,vroll1=1f;
// gain ratios
protected float physicalToAudioGainSlide=1,physicalToAudioGainRoll=1,physicalToAudioGainImpact=1;
/** Set model parameters mapping physical units to audio units
@param slideSpeed1 maximum audio loop speed (1 = original recording)
@param rollSpeed1 maximum audio loop speed (1 = original recording)
@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 slideSpeed1, float rollSpeed1, float vslide0,
float vslide1, float vroll0, float vroll1,
float physicalToAudioGainSlide,
float physicalToAudioGainRoll,float physicalToAudioGainImpact) {
this.slideSpeed1 = slideSpeed1;
this.rollSpeed1 = rollSpeed1;
this.vslide0 = vslide0;
this.vslide1 = vslide1;
this.vroll0 = vroll0;
this.vroll1 = vroll1;
this.physicalToAudioGainSlide = physicalToAudioGainSlide;
this.physicalToAudioGainRoll = physicalToAudioGainRoll;
this.physicalToAudioGainImpact = physicalToAudioGainImpact;
}
/** Contructor intended only for subclass constructors (super(bufferSize);)
*/
protected ContactForce(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.)
*/
public ContactForce(float srate, int bufferSize, String fnImpact,String fnSlide,String fnRoll) {
super(bufferSize);
bangForce = new BangForce(srate,bufferSize,fnImpact);
slideForce = new LoopBuffer(srate,bufferSize,fnSlide);
rollForceRaw = new LoopBuffer(srate,bufferSize,fnRoll);
bangForce.setTime(getTime());
slideForce.setTime(getTime());
rollForceRaw.setTime(getTime());
lowPassFilter = new Butter2LowFilter(srate);
lowPassFilter.setCutoffFrequency(fLowPass);
rollForce = new FilterContainer(srate,bufferSize,lowPassFilter);
try {
rollForce.addSource(rollForceRaw);
} catch(SinkIsFullException e) {
System.out.println(this+" "+e);
}
rollForce.setTime(getTime());
}
/** 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);
}
/** 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) {
if(speed > vslide1) {
speed = vslide1;
} else if(speed < vslide0) {
speed = 0;
force = 0;
}
slideForce.setSpeed(speed * slideSpeed1/vslide1);
float vol = (float)(Math.sqrt(force * speed/vslide1));
slideForce.setVolume(physicalToAudioGainSlide*vol);
}
/** 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) {
if(speed > vroll1) {
speed = vroll1;
} else if(speed < vroll0) {
speed = 0;
force = 0;
}
rollForceRaw.setSpeed(speed * rollSpeed1/vroll1);
float vol = (float)(Math.sqrt(force * speed/vroll1));
rollForceRaw.setVolume(physicalToAudioGainRoll*vol);
}
/** Set roll force filter properties: cutoff and drygain.
*/
public synchronized void setRollFilter(float fLowPass, float dryRollGain) {
this.fLowPass = fLowPass;
lowPassFilter.setCutoffFrequency(fLowPass);
this.dryRollGain = dryRollGain;
}
/** Compute the next buffer.
*/
public synchronized void computeBuffer() {
try {
float[] b1 = bangForce.getBuffer(getTime());
float[] b2 = slideForce.getBuffer(getTime());
float[] b3 = rollForceRaw.getBuffer(getTime());
float[] b4 = rollForce.getBuffer(getTime());
int bufsz = getBufferSize();
for(int k=0;k<bufsz;k++) {
buf[k] = b1[k] + b2[k] + dryRollGain*b3[k] + (1 - dryRollGain)*b4[k];
}
} catch(BufferNotAvailableException e) {
System.out.println(this+" "+e);
}
}
}