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