package jass.patches;
import jass.engine.*;
import jass.generators.*;
/** CombReverb as 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}}
Must have delays greater than the bufferSize.
Defaults are for 25Khz sampling rate from Steiglitz book.
BUGS: Does not support removal of source.
@author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class CombReverb extends InOut {
protected int nchannels = 1;
protected float srate;
protected int nCombs = 6;
protected float[] combDelays = {.05f,.056f,.061f,.068f,.072f,.078f}; // delays in seonds
protected float allpassDelay = .006f; // delay in seonds
protected float a = .7f; // allpass parameter
protected float[] R = {.4897f,.6142f,.5976f,.5893f,.581f,.5644f}; // feedback comb parameters
protected float[] g = {.24f,.26f,.28f,.29f,.3f,.32f}; // low-pass comb parameters
protected float dryToWet = .9f; // 1 is dry only
protected LowPassComb[] lpCombs;
protected AllPass allPass;
protected Mixer mixer; // mixes combs
protected Mixer endMixer; // mixes dry and wet
/** Create. For derived classes.
@param bufferSize Buffer size used for real-time rendering.
*/
public CombReverb(int bufferSize) {
super(bufferSize);
}
/** Create. For derived classes.
@param bufferSize Buffer size used for real-time rendering.
@param nchannels 1 for mono
*/
public CombReverb(int bufferSize,int nchannels) {
super(bufferSize);
this.nchannels = nchannels;
}
/** Create. For derived classes.
@param bufferSize Buffer size used for real-time rendering.
@param srate sampling rate in Hz
@param nCombs number of comb filters
*/
public CombReverb(int bufferSize,float srate,int nCombs) {
super(bufferSize);
this.srate = srate;
this.nCombs = nCombs;
init();
}
/** Create. For derived classes.
@param nchannels 1 for mono
@param bufferSize Buffer size used for real-time rendering.
@param srate sampling rate in Hz
@param nCombs number of comb filters
*/
public CombReverb(int bufferSize,float srate,int nCombs,int nchannels) {
super(bufferSize);
this.nchannels = nchannels;
this.srate = srate;
this.nCombs = nCombs;
init();
}
public void setNChannels(int n) {
nchannels = n;
}
/** Init and allocate. Defaults are usable.
*/
protected void init() {
mixer = new Mixer(bufferSize,nCombs);
endMixer = new Mixer(bufferSize,2);
allPass = new AllPass(bufferSize,srate);
lpCombs = new LowPassComb[nCombs];
for(int i=0;i<nCombs;i++) {
lpCombs[i] = new LowPassComb(bufferSize,srate);
}
try {
endMixer.addSource(allPass);
allPass.addSource(mixer);
for(int i=0;i<nCombs;i++) {
mixer.addSource(lpCombs[i]);
mixer.setGain(i,1f); // constant, not settable
}
} catch(SinkIsFullException e) {
System.out.println(this+" "+e);
}
long t = getTime();
endMixer.setTime(t);
mixer.setTime(t);
allPass.setTime(t);
for(int i=0;i<nCombs;i++) {
lpCombs[i].setTime(t);
}
setAllParameters();
}
/**
Set all filter parameters
*/
public void setAllParameters() {
endMixer.setGain(0,1-dryToWet);
endMixer.setGain(1,dryToWet);
allPass.setA(a);
allPass.setM(nchannels*allpassDelay);
for(int i=0;i<nCombs;i++) {
lpCombs[i].setL(nchannels*combDelays[i]);
lpCombs[i].setG(g[i]);
lpCombs[i].setR(R[i]);
}
}
/** Set feedback of filter k
@param r feedback
@param k index of comb filter
*/
public void setR(float r,int k) {
R[k] = r;
lpCombs[k].setR(R[k]);
}
/** Set one pole lowpass coefficient g; H(z) = 1/(1-g/z))
@param g lowpass filter coefficient
@param k index of comb filter
*/
public void setG(float g,int k) {
this.g[k] = g;
lpCombs[k].setG(this.g[k]);
}
/** Set dryToWet ratio
@param d dry to wet. 1 is dry only
*/
public void setDryToWet(float d) {
dryToWet = d;
endMixer.setGain(0,1-dryToWet);
endMixer.setGain(1,dryToWet);
}
/** Set allpass coeff. a: H(z) = (z^_{m} + a)/(1 + a*z^{-m})
@param a allpass coeff
*/
public void setA(float a) {
this.a = a;
allPass.setA(a);
}
/** Set allpass delay: H(z) = (z^_{m} + a)/(1 + a*z^{-m})
Make sure is not snaller than buffersize
@param del allpass delay in seconds
*/
public void setM(float del) {
allpassDelay = del;
allPass.setM(del);
}
/** Set comb delay. Make sure is not snaller than buffersize
@param del allpass delay in seconds
@param k index of comb filter
*/
public void setL(float del,int k) {
combDelays[k] = del;
lpCombs[k].setL(combDelays[k]);
}
/** Add source to Sink. Override to allow one input only and add to mixer with
gain coefficient a and to delay 2.
This will be called after init() so mixer will already have 2 inputs
@param s Source to add.
@return object representing Source in Sink (may be null).
*/
public Object addSource(Source s) throws SinkIsFullException {
if(getSources().length > 0) {
throw new SinkIsFullException();
} else {
endMixer.addSource(s);
for(int i=0;i<nCombs;i++) {
lpCombs[i].addSource(s);
}
// add to the superclass. THis is for administrative reasons only,
// the source cache is not used here.
return super.addSource(s);
}
}
/** Compute the next buffer and store in member float[] buf.
*/
protected void computeBuffer() {
// Time has now been incremented and external Sources
// have been called, the result of which is in the cache.
// But we won't use this and just call getBuffer on the
// contained subpatch. It will call getBuffer() on
// atached Sources but they are already at this time so will
// just return their cached buffer. We are wasting only an extra method call.
try {
buf = endMixer.getBuffer(getTime());
} catch(BufferNotAvailableException e) {
System.out.println(this+" "+e);
}
}
}