001: /*
002: * Copyright (c) 2000 Silvere Martin-Michiellot All Rights Reserved.
003: *
004: * Silvere Martin-Michiellot grants you ("Licensee") a non-exclusive,
005: * royalty free, license to use, modify and redistribute this
006: * software in source and binary code form,
007: * provided that i) this copyright notice and license appear on all copies of
008: * the software; and ii) Licensee does not utilize the software in a manner
009: * which is disparaging to Silvere Martin-Michiellot.
010: *
011: * This software is provided "AS IS," without a warranty of any kind. ALL
012: * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
013: * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
014: * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. Silvere Martin-Michiellot
015: * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
016: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
017: * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
018: * Silvere Martin-Michiellot OR ITS LICENSORS BE LIABLE
019: * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
020: * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
021: * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
022: * OR INABILITY TO USE SOFTWARE, EVEN IF Silvere Martin-Michiellot HAS BEEN
023: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
024: *
025: * This software is not designed or intended for use in on-line control of
026: * aircraft, air traffic, aircraft navigation or aircraft communications; or in
027: * the design, construction, operation or maintenance of any nuclear
028: * facility. Licensee represents and warrants that it will not use or
029: * redistribute the Software for such purposes.
030: *
031: */
032:
033: // This code is repackaged after the code from Craig A. Lindley, from Digital Audio with Java
034: // Site ftp://ftp.prenhall.com/pub/ptr/professional_computer_science.w-022/digital_audio/
035: // Email
036: package com.db.media.audio.dsp.processors;
037:
038: /*
039: This reverb module is called a Schroeder reverb because
040: the organization of parallel comb filters and series connected
041: allpass filters was suggested by M.R. Schroeder. See the book,
042: "Computer Music -- Synthesis, Composition and Performance" by
043: Charles Dodge and Thomas Jerse for details.
044: */
045:
046: public class SchroederReverb {
047:
048: // Parameters below were chosen to simulate the characteristics of
049: // a medium-sized concert hall. See book quoted above for details.
050: public static final double COMB1DELAYMSDEF = 29.7;
051: public static final double COMB2DELAYMSDEF = 37.1;
052: public static final double COMB3DELAYMSDEF = 41.1;
053: public static final double COMB4DELAYMSDEF = 43.7;
054:
055: public static final double ALLPASS1DELAYMSDEF = 5.0;
056: public static final double ALLPASS2DELAYMSDEF = 1.7;
057:
058: public static final double ALLPASS1SUSTAINMSDEF = 96.8;
059: public static final double ALLPASS2SUSTAINMSDEF = 32.9;
060:
061: private static final double SUSTAINTIMEMSMIN = 1;
062: private static final double SUSTAINTIMEMSMAX = 1000;
063: public static final double SUSTAINTIMEMSDEF = 500;
064: public static final double MIXDEF = 0.25;
065:
066: private static final double MINCOMBDELAYMS = 1.0;
067: private static final double MAXCOMBDELAYMS = 100.0;
068: private static final double MINALLPASSDELAYMS = 1.0;
069: private static final double MAXALLPASSDELAYMS = 50.0;
070:
071: // Private class data
072: private double mix;
073: private CombFilter comb1;
074: private CombFilter comb2;
075: private CombFilter comb3;
076: private CombFilter comb4;
077: private AllpassNetwork allpass1;
078: private AllpassNetwork allpass2;
079: private double[] dBuffer = new double[1];
080:
081: public SchroederReverb(int sampleRate, int numberOfChannels) {
082:
083: // Instantiate the comb filters and the allpass networks
084: comb1 = new CombFilter(sampleRate, numberOfChannels,
085: COMB1DELAYMSDEF);
086: comb2 = new CombFilter(sampleRate, numberOfChannels,
087: COMB2DELAYMSDEF);
088: comb3 = new CombFilter(sampleRate, numberOfChannels,
089: COMB3DELAYMSDEF);
090: comb4 = new CombFilter(sampleRate, numberOfChannels,
091: COMB4DELAYMSDEF);
092:
093: allpass1 = new AllpassNetwork(sampleRate, numberOfChannels,
094: ALLPASS1DELAYMSDEF);
095: allpass2 = new AllpassNetwork(sampleRate, numberOfChannels,
096: ALLPASS2DELAYMSDEF);
097:
098: // Set initial value for sustain
099: setSustainInMs(this .SUSTAINTIMEMSDEF);
100:
101: // Set dry/wet mix to initial value
102: mix = this .MIXDEF;
103:
104: }
105:
106: // Set the comb filter delays
107: public void setComb1Delay(double delay) {
108:
109: comb1.setDelayInMs(delay);
110:
111: }
112:
113: public void setComb2Delay(double delay) {
114:
115: comb2.setDelayInMs(delay);
116:
117: }
118:
119: public void setComb3Delay(double delay) {
120:
121: comb3.setDelayInMs(delay);
122:
123: }
124:
125: public void setComb4Delay(double delay) {
126:
127: comb4.setDelayInMs(delay);
128:
129: }
130:
131: // Set the allpass filter delays
132: public void setAllpass1Delay(double delay) {
133:
134: allpass1.setDelayInMs(delay);
135:
136: }
137:
138: public void setAllpass2Delay(double delay) {
139:
140: allpass2.setDelayInMs(delay);
141:
142: }
143:
144: public void setSustainInMs(double sustainInMs) {
145:
146: // Set sustain in all comb filters
147: comb1.setSustainTimeInMs(sustainInMs);
148: comb2.setSustainTimeInMs(sustainInMs);
149: comb3.setSustainTimeInMs(sustainInMs);
150: comb4.setSustainTimeInMs(sustainInMs);
151:
152: // Allpass filter sustain is set by model
153: allpass1.setSustainTimeInMs(ALLPASS1SUSTAINMSDEF);
154: allpass2.setSustainTimeInMs(ALLPASS2SUSTAINMSDEF);
155:
156: }
157:
158: // Set the mix between the dry and the wet signal
159: public void setDryWetMix(double mix) {
160:
161: this .mix = mix;
162:
163: }
164:
165: // Process a buffer of samples at a time thru the reverb
166: public int doReverb(short[] inBuf, int length) {
167:
168: // Allocate buffer as required. Buffer must be initialized
169: // to zeros.
170: if (length != -1)
171: dBuffer = new double[length];
172:
173: // Apply the combs in parallel, get the possibly new length.
174: // All combs should return the same length.
175:
176: int newLength = comb1.doFilter(inBuf, dBuffer, length);
177: comb2.doFilter(inBuf, dBuffer, length);
178: comb3.doFilter(inBuf, dBuffer, length);
179: comb4.doFilter(inBuf, dBuffer, length);
180:
181: boolean inputExhausted = (newLength == -1);
182:
183: if (!inputExhausted) {
184: // Scale the data
185: for (int i = 0; i < newLength; i++)
186: dBuffer[i] *= 0.25;
187:
188: } else {
189: newLength = dBuffer.length;
190: }
191: double[] dBuffer1 = new double[newLength];
192:
193: // Apply the allpass networks
194: length = allpass1.doFilter(dBuffer, dBuffer1,
195: inputExhausted ? -1 : newLength);
196:
197: length = allpass2.doFilter(dBuffer1, dBuffer, length);
198:
199: // Apply the mix
200: if (!inputExhausted) {
201: // Mix the dry input samples with the processed samples
202: for (int i = 0; i < length; i++) {
203: double s = (inBuf[i] * (1.0 - mix))
204: + (dBuffer[i] * mix);
205: if (s > 32767.0)
206: s = 32767.0;
207: else if (s < -32768.0)
208: s = -32768.0;
209:
210: inBuf[i] = (short) s;
211: }
212:
213: } else {
214:
215: // Only wet samples are available
216: for (int i = 0; i < length; i++) {
217: double s = dBuffer[i] * mix;
218: if (s > 32767.0)
219: s = 32767.0;
220: else if (s < -32768.0)
221: s = -32768.0;
222:
223: inBuf[i] = (short) s;
224: }
225: }
226:
227: return length;
228:
229: }
230:
231: }
|