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.
It allows for N scraping textures, represented by N wav files. You can set which
wav file you want to use for scraping, or any linear superposition of the N wav files.
Since rolling still has only one wav file this generator is really useful only for
scraping at present.
@author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class ContactForceN extends Out {
// Has these protected sources:
protected BangForce bangForce;
protected LoopNBuffers slideForce;
protected int nSlideForces = 1;
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
private float [] tmpSpeed, tmpForce; // temp vars
// 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;
protected float[] slideBalance; // determines gains of components of slide force
// 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 ContactForceN(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 names for slide. (For example grid.wav.)
@param fnRoll Audio file name for slide. (For example roll.wav.)
*/
public ContactForceN(float srate, int bufferSize, String fnImpact,String[] fnSlide,String fnRoll) {
super(bufferSize);
bangForce = new BangForce(srate,bufferSize,fnImpact);
slideForce = new LoopNBuffers(srate,bufferSize,fnSlide);
nSlideForces = fnSlide.length;
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());
tmpSpeed = new float[nSlideForces];
tmpForce = new float[nSlideForces];
slideBalance = new float[nSlideForces];
slideBalance[0] = 1f;
for(int i=1;i<nSlideForces;i++) {
slideBalance[i] = 1f;
}
}
/**
Set balance verctor for slide force components
@param bal vector of balances
*/
public synchronized void setSlideBalance(float[] bal) {
for(int i=0;i<nSlideForces;i++) {
slideBalance[i] = bal[i];
}
}
/** get balance
@return array of balances
*/
public float[] getSlideBalance() {
return slideBalance;
}
/** 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;
}
float vol = (float)(Math.sqrt(force * speed/vslide1));
for(int i=0;i<nSlideForces;i++) {
tmpSpeed[i] = speed * slideSpeed1/vslide1;
tmpForce[i] = physicalToAudioGainSlide*vol*slideBalance[i];
}
slideForce.setSpeed(tmpSpeed);
//System.out.println("f0= "+tmpForce[0]+" f1= "+tmpForce[1]);
slideForce.setVolume(tmpForce);
}
/** 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);
}
}
}