import java.io.*; import jass.render.*; import jass.engine.*; import jass.generators.*; import jass.patches.*; /** CombReverb using Moorers reverb. See e.g. @book{Steiglitz96, title = {A Digital Signal Processing Primer with Applications to Digital Audio and Computer Music}, author = {Ken Steiglitz}, publisher = {Addison-Wesley}, address = {New York}, year = {1996}, pages = {290--295}} Defaults are for 25Khz sampling rate from Steiglitz book. @author Kees van den Doel (kvdoel@cs.ubc.ca) */ public class Reverb { int bufferSize = 16; // no feedback loop can have smaller delay than this int bufferSizeJavaSound = 0; float srate = 44100; SourcePlayer player; AudioIn input; ConstantLoopBuffer loopbuf; boolean useMike = true; float impulseT = 1f; //second CombReverb reverb; Controller controlPanel; final int nReflections = 6; final int nsliders = nReflections*3 + 3; final int nbuttons = 2; // Reverb parameters float[] combDelays = {.05f,.056f,.061f,.068f,.072f,.078f}; // delays in seonds float allpassDelay = .006f; // delay in seconds float a = .7071068f; // allpass parameter float[] R = {.4897f,.6142f,.5976f,.5893f,.581f,.5644f}; // feedback comb parameters float[] g = {.24f,.26f,.28f,.29f,.3f,.32f}; // low-pass comb parameters float dryToWet = .1f; // 1 is dry only double minDelay = 1.3*(bufferSize/srate); // smalles feedback delay possible double[] max,min,val; // for ui String[] names; // for ui String preferredMixer; public static void main (String args[]) throws Exception { String file = null; String m = null; if(args.length == 2) { file = args[0]; m = args[1]; } else if(args.length == 1) { m = args[0]; } else { System.out.println("usg: Reverb [file] mixer\nIf not recognizes mixer uses default"); System.exit(0); } Reverb app = new Reverb(file,m); } public Reverb(String file,String preferredMixer) { this.preferredMixer = preferredMixer; if(file != null) { useMike = false; } createPatch(file); createUI(); player.start(); } protected void createUI() { names = new String[] { "DryToWet ", "Allpass a ", "Allpass delay ", "Comb1 delay ","Comb1 feedback ","Comb1 lowpass coeff. g ", "Comb2 delay ","Comb2 feedback ","Comb2 lowpass coeff. g ", "Comb3 delay ","Comb3 feedback ","Comb3 lowpass coeff. g ", "Comb4 delay ","Comb4 feedback ","Comb4 lowpass coeff. g ", "Comb5 delay ","Comb5 feedback ","Comb5 lowpass coeff. g ", "Comb6 delay ","Comb6 feedback ","Comb6 lowpass coeff. g ", }; val = new double[] { dryToWet, a, allpassDelay, combDelays[0],R[0],g[0], combDelays[1],R[1],g[1], combDelays[2],R[2],g[2], combDelays[3],R[3],g[3], combDelays[4],R[4],g[4], combDelays[5],R[5],g[5] }; min = new double[] { 0, 0, minDelay, minDelay,0,0, minDelay,0,0, minDelay,0,0, minDelay,0,0, minDelay,0,0, minDelay,0,0 }; max = new double[] { 1, 1, 1, 1,1,1, 1,1,1, 1,1,1, 1,1,1, 1,1,1, 1,1,1 }; controlPanel = new ReverbController(new java.awt.Frame ("Reverb"),false,nsliders,nbuttons); /* for(int i=0;i<nsliders;i++) { controlPanel.onSlider(i); // set values } */ controlPanel.setButtonNames (new String[] {"Reset AGC","Defaults"}); controlPanel.setSliders(val,min,max,names); controlPanel.setVisible(true); } protected void createPatch(String file) { int numRtAudioBuffers = -1; System.out.println(preferredMixer); player = new SourcePlayer(bufferSize,bufferSizeJavaSound,srate,preferredMixer); if(useMike) { input = new AudioIn(srate,bufferSize,bufferSizeJavaSound,preferredMixer,"javasound",numRtAudioBuffers); } else { loopbuf = new ConstantLoopBuffer(srate,bufferSize,file); } reverb = new CombReverb(bufferSize,srate,nReflections); try { if(useMike) { reverb.addSource(input); } else { reverb.addSource(loopbuf); } player.addSource(reverb); } catch(SinkIsFullException e) { System.out.println(e); System.exit(0); } } class ReverbController extends Controller { public ReverbController(java.awt.Frame parent,boolean modal,int nsl,int nbut) { super(parent,modal,nsl,nbut); onButton(1); // set defaults } public void onButton(int k) { switch(k) { case 0: player.resetAGC(); break; case 1: this.val[0] = dryToWet; this.val[1] = a; this.val[2] = allpassDelay; int kk = 3; for(int i=0;i<nReflections;i++,kk+=3) { this.val[kk] = combDelays[i]; this.val[kk+1] = R[i]; this.val[kk+2] = g[i]; } setValues(this.val,this.min,this.max,this.names); for(int i=0;i<this.nsliders;i++) { onSlider(i); } break; } } public void onSlider(int k) { if(k<3) { // allpass parameters and drymix ratio switch(k) { case 0: reverb.setDryToWet((float)(this.val[0])); break; case 1: reverb.setA((float)(this.val[1])); break; case 2: reverb.setM((float)(this.val[2])); break; } } else { // comb parameters int icomb = (k-3)/3; // comb index int n = (k-3) - 3 * icomb; // labels 3 comb parameters switch(n) { case 0: reverb.setL((float)(this.val[k]),icomb); break; case 1: reverb.setR((float)(this.val[k]),icomb); break; case 2: reverb.setG((float)(this.val[k]),icomb); break; } } } } }