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: public class Chorus extends AbstractAudio {
039:
040: // Private class data
041: private boolean initializationComplete = false;
042: private int delayInMs;
043: private double halfDepth = 1;
044: private double halfDepthInSamples;
045: private double rateInHz;
046: private boolean isSinLFO;
047: private boolean invertPhase;
048: private double step;
049: private double sweepValue = 0;
050: private int sampleNumber = 0;
051: private double radiansPerSample;
052:
053: private int depthLevel;
054: private int dryLevel;
055: private int wetLevel;
056: private int feedbackLevel;
057:
058: private int sampleRate = 0;
059: private int numberOfChannels = 0;
060: private int delayBufferSize;
061:
062: private short[] localBuffer = null;
063: private int[] delayBuffer = null;
064: private int[] leftDelayBuffer = null;
065: private int[] rightDelayBuffer = null;
066:
067: private int readIndex;
068: private int writeIndex;
069:
070: public Chorus() {
071:
072: super ("Chorus", AbstractAudio.PROCESSOR);
073:
074: initializationComplete = false;
075: isSinLFO = false;
076: invertPhase = false;
077:
078: // Allocate local sample buffer
079: localBuffer = new short[AbstractAudio.SAMPLEBUFFERSIZE];
080:
081: }
082:
083: // Process the samples that pass thru this effect
084: public int getSamples(short[] buffer, int length) {
085:
086: if (getByPass() || !initializationComplete)
087: return previous.getSamples(buffer, length);
088:
089: // Read number of samples requested from previous stage
090: int len = previous.getSamples(localBuffer, length);
091: if (len == -1)
092: return -1;
093:
094: if (numberOfChannels == 1)
095: return processMonoSamples(localBuffer, buffer, len);
096: else
097: return processStereoSamples(localBuffer, buffer, len);
098:
099: }
100:
101: // Process mono samples
102: protected int processMonoSamples(short[] localBuffer,
103: short[] buffer, int len) {
104:
105: // Do the processing
106: for (int i = 0; i < len; i++) {
107:
108: // Fetch the input samples from the local buffer
109: int inputSample = (int) localBuffer[i];
110:
111: // Calculate sample offsets for fetching two samples
112: double sampleOffset1 = sweepValue - halfDepthInSamples;
113: double sampleOffset2 = sampleOffset1 - 1;
114:
115: // Calculate delta for linear interpolation
116: double delta = Math
117: .abs((int) sampleOffset1 - sampleOffset1);
118:
119: int actualIndex1 = readIndex + (int) sampleOffset1;
120: int actualIndex2 = readIndex++ + (int) sampleOffset2;
121: boolean underflow1 = (actualIndex1 < 0);
122: boolean underflow2 = (actualIndex2 < 0);
123:
124: // Adjust indices for possible under/over flow
125: if (underflow1)
126: actualIndex1 += delayBufferSize;
127: else
128: actualIndex1 %= delayBufferSize;
129:
130: if (underflow2)
131: actualIndex2 += delayBufferSize;
132: else
133: actualIndex2 %= delayBufferSize;
134:
135: // Fetch two samples and interpolate
136: int delaySample1 = (int) delayBuffer[actualIndex1];
137: int delaySample2 = (int) delayBuffer[actualIndex2];
138: int delaySample = (int) (delaySample2 * delta + delaySample1
139: * (1.0 - delta));
140: // Sum wet and dry portions of the output
141: int outputSample = ((inputSample * dryLevel) / 100)
142: + ((delaySample * wetLevel) / 100);
143:
144: // Clamp output to legal range
145: if (outputSample > 32767)
146: outputSample = 32767;
147: else if (outputSample < -32768)
148: outputSample = -32768;
149:
150: // Store output sample
151: buffer[i] = (short) outputSample;
152:
153: // Calculate sample for storage in delay buffer
154: inputSample += (delaySample * feedbackLevel * (invertPhase ? -1
155: : +1)) / 100;
156:
157: // Store sample
158: delayBuffer[writeIndex++] = inputSample;
159:
160: // Update indices
161: readIndex %= delayBufferSize;
162: writeIndex %= delayBufferSize;
163:
164: // Calculate new sweep value
165: if (isSinLFO) {
166: // LFO is sinusoidal
167: sampleNumber %= sampleRate;
168: sweepValue = halfDepthInSamples
169: * Math.sin(radiansPerSample * sampleNumber++);
170:
171: } else {
172:
173: // LFO is triangular
174: sweepValue += step;
175:
176: // Keep sweep in range
177: if ((sweepValue >= halfDepthInSamples)
178: || (sweepValue <= -halfDepthInSamples)) {
179: // Change direction of sweep
180: step *= -1;
181: }
182: }
183: }
184:
185: return len;
186:
187: }
188:
189: // Process stereo samples
190: protected int processStereoSamples(short[] localBuffer,
191: short[] buffer, int len) {
192:
193: // Do the processing
194: for (int i = 0; i < len / 2; i++) {
195:
196: // Fetch the input samples from the local buffer
197: int leftInputSample = (int) localBuffer[2 * i];
198: int rightInputSample = (int) localBuffer[2 * i + 1];
199:
200: // Calculate sample offsets for fetching two samples
201: double sampleOffset1 = sweepValue - halfDepthInSamples;
202: double sampleOffset2 = sampleOffset1 - 1;
203:
204: // Calculate delta for linear interpolation
205: double delta = Math
206: .abs((int) sampleOffset1 - sampleOffset1);
207:
208: int actualIndex1 = readIndex + (int) sampleOffset1;
209: int actualIndex2 = readIndex++ + (int) sampleOffset2;
210: boolean underflow1 = (actualIndex1 < 0);
211: boolean underflow2 = (actualIndex2 < 0);
212:
213: // Adjust indices for possible under/over flow
214: if (underflow1)
215: actualIndex1 += delayBufferSize;
216: else
217: actualIndex1 %= delayBufferSize;
218:
219: if (underflow2)
220: actualIndex2 += delayBufferSize;
221: else
222: actualIndex2 %= delayBufferSize;
223:
224: // Fetch two samples and interpolate
225: int leftDelaySample1 = (int) leftDelayBuffer[actualIndex1];
226: int leftDelaySample2 = (int) leftDelayBuffer[actualIndex2];
227: int leftDelaySample = (int) (leftDelaySample2 * delta + leftDelaySample1
228: * (1.0 - delta));
229:
230: int rightDelaySample1 = (int) rightDelayBuffer[actualIndex1];
231: int rightDelaySample2 = (int) rightDelayBuffer[actualIndex2];
232: int rightDelaySample = (int) (rightDelaySample2 * delta + rightDelaySample1
233: * (1.0 - delta));
234:
235: // Sum wet and dry portions of the output
236: int leftOutputSample = ((leftInputSample * dryLevel) / 100)
237: + ((leftDelaySample * wetLevel) / 100);
238:
239: int rightOutputSample = ((rightInputSample * dryLevel) / 100)
240: + ((rightDelaySample * wetLevel) / 100);
241:
242: // Clamp output to legal range
243: if (leftOutputSample > 32767)
244: leftOutputSample = 32767;
245: else if (leftOutputSample < -32768)
246: leftOutputSample = -32768;
247:
248: if (rightOutputSample > 32767)
249: rightOutputSample = 32767;
250: else if (rightOutputSample < -32768)
251: rightOutputSample = -32768;
252:
253: // Store in output samples
254: buffer[2 * i] = (short) leftOutputSample;
255: buffer[2 * i + 1] = (short) rightOutputSample;
256:
257: // Calculate samples for storage in delay buffer
258: leftInputSample += (leftDelaySample * feedbackLevel * (invertPhase ? -1
259: : +1)) / 100;
260: rightInputSample += (rightDelaySample * feedbackLevel * (invertPhase ? -1
261: : +1)) / 100;
262:
263: // Store samples
264: leftDelayBuffer[writeIndex] = leftInputSample;
265: rightDelayBuffer[writeIndex++] = rightInputSample;
266:
267: // Update indices
268: readIndex %= delayBufferSize;
269: writeIndex %= delayBufferSize;
270:
271: // Calculate new sweep value
272: if (isSinLFO) {
273: // LFO is sinusoidal
274: sampleNumber %= sampleRate;
275: sweepValue = halfDepthInSamples
276: * Math.sin(radiansPerSample * sampleNumber++);
277:
278: } else {
279:
280: // LFO is triangular
281: sweepValue += step;
282:
283: // Keep sweep in range
284: if ((sweepValue >= halfDepthInSamples)
285: || (sweepValue <= -halfDepthInSamples)) {
286: // Change direction of sweep
287: step *= -1;
288: }
289: }
290: }
291: return len;
292:
293: }
294:
295: // Set a new delay value from UI
296: public void setDelayInMs(int delayInMs) {
297:
298: this .delayInMs = delayInMs;
299:
300: initializationComplete = false;
301:
302: doInitialization();
303:
304: }
305:
306: // Set a new LFO rate from UI
307: public void setRateInHz(double rateInHz) {
308:
309: this .rateInHz = rateInHz;
310:
311: // Calculate step size
312: calculateStepSize();
313:
314: }
315:
316: // Set a new LFO mode from the UI
317: public void setLFOMode(boolean isSinLFO) {
318:
319: this .isSinLFO = isSinLFO;
320:
321: // Calculate step size
322: calculateStepSize();
323:
324: }
325:
326: // Set a new depth value from UI
327: public void setDepthLevel(double depthInMs) {
328:
329: halfDepth = depthInMs / 2.0;
330:
331: // Calculate step size
332: calculateStepSize();
333:
334: }
335:
336: // Calculate new sweep value from LFO rate and depth
337: private void calculateStepSize() {
338:
339: // Calculate half depth in samples
340: halfDepthInSamples = (halfDepth * sampleRate) / 1000;
341:
342: sweepValue = 0.0;
343:
344: // Calculations for triangle wave
345: double periodInSamples = (1.0 / rateInHz) * sampleRate;
346: double quarterPeriod = periodInSamples / 4.0;
347:
348: step = halfDepthInSamples / quarterPeriod;
349:
350: // Calculations for sin wave
351: sampleNumber = 0;
352: radiansPerSample = (2 * Math.PI * rateInHz) / sampleRate;
353:
354: }
355:
356: // Set a new dry level value from UI
357: public void setDryLevel(int dryLevel) {
358:
359: this .dryLevel = dryLevel;
360:
361: }
362:
363: // Set a new wet level value from UI
364: public void setWetLevel(int wetLevel) {
365:
366: this .wetLevel = wetLevel;
367:
368: }
369:
370: // Set feedback phase from UI
371: public void setFeedbackPhase(boolean invertPhase) {
372:
373: this .invertPhase = invertPhase;
374:
375: }
376:
377: // Set a new feedback level value from UI
378: public void setFeedbackLevel(int feedbackLevel) {
379:
380: this .feedbackLevel = feedbackLevel;
381:
382: }
383:
384: // Calculate buffer sizes from delay values
385: public void doInitialization() {
386:
387: // See if we have the necessary data to initialize delay
388: if ((sampleRate != 0) && (numberOfChannels != 0)
389: && (!initializationComplete)) {
390:
391: // Calculate number of samples required for delay
392: int delayOffset = (delayInMs * sampleRate) / 1000;
393:
394: if (numberOfChannels == 1) {
395: // We're doing a mono signal
396: // Calculate buffer size required
397: delayBufferSize = AbstractAudio.SAMPLEBUFFERSIZE
398: + delayOffset;
399:
400: // Allocate new delay buffer
401: delayBuffer = new int[delayBufferSize];
402:
403: // Initialize indices
404: // Index where dry sample is written
405: writeIndex = 0;
406:
407: // Index where wet sample is read
408: readIndex = AbstractAudio.SAMPLEBUFFERSIZE;
409:
410: } else {
411:
412: // We're doing a stereo signal
413: // Calculate buffer size required
414: int halfBufferSize = AbstractAudio.SAMPLEBUFFERSIZE / 2;
415: delayBufferSize = halfBufferSize + delayOffset;
416:
417: // Allocate new delay buffers
418: leftDelayBuffer = new int[delayBufferSize];
419: rightDelayBuffer = new int[delayBufferSize];
420:
421: // Initialize indices
422: // Index where dry sample is written
423: writeIndex = 0;
424:
425: // Index where wet sample is read
426: readIndex = halfBufferSize;
427: }
428: // Indicate initialization is complete
429: initializationComplete = true;
430: }
431:
432: }
433:
434: public void minMaxSamplingRate(int min, int max, int preferred) {
435:
436: super .minMaxSamplingRate(min, max, preferred);
437: sampleRate = preferred;
438: doInitialization();
439: calculateStepSize();
440:
441: }
442:
443: // Negotiate the number of channels
444: public void minMaxChannels(int min, int max, int preferred) {
445:
446: super.minMaxChannels(min, max, preferred);
447: numberOfChannels = preferred;
448:
449: }
450:
451: }
|