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: // This version of CompExpWithUI.java illustrates implementation
039: // of a hard transition between the no compression and compression states.
040: // The soft knee version is contained in the file CompExpWithUI.java.
041:
042: public class CompExpHard extends AbstractAudio {
043:
044: private static final double MAXTHRESHOLDDB = 0;
045: private static final double MINTHRESHOLDDB = -60;
046: public static final double THRESHOLDDEF = -16;
047:
048: private static final double MAXBTRATIO = 1.0;
049: public static final double MINBTRATIO = 25.0;
050: public static final double BTRATIODEF = 1.0;
051:
052: public static final double MAXATRATIO = +11.0;
053: public static final double MINATRATIO = -11.0;
054: public static final double ATRATIODEF = 0.0;
055:
056: private static final double MAXATTACKMS = 500;
057: private static final double MINATTACKMS = 0;
058: public static final double ATTACKMSDEF = 50;
059:
060: private static final double MAXRELEASEMS = 2000;
061: private static final double MINRELEASEMS = 0;
062: public static final double RELEASEMSDEF = 100;
063:
064: public static final double MAXGAININDB = +12.0;
065: private static final double MINGAININDB = -12.0;
066: public static final double GAINDBDEF = 0.0;
067:
068: // Private class data
069: private boolean initializationComplete;
070: private int sampleRate = 0;
071: private int channels = 1;
072: private double thresholdValue = 32767.0;
073: private double btRatio = 1.0;
074: private double atRatio = 1.0;
075: private double attackInMs = 0;
076: private double releaseInMs = 0;
077: private double attackCount = 0;
078: private double releaseCount = 0;
079: private double gain = 1.0;
080: private boolean limiting = false;
081: private boolean gating = false;
082:
083: private int calcAttackCount = 0;
084: private int calcReleaseCount = 0;
085: private boolean attackExpired = false;
086:
087: // Class constructor
088: public CompExpHard() {
089:
090: super ("Compressor/Expander/Limiter/Noise Gate Processor",
091: AbstractAudio.PROCESSOR);
092:
093: // Initialization will take place after sample rate is known
094: initializationComplete = false;
095:
096: }
097:
098: public int getSamples(short[] buffer, int length) {
099:
100: // Get samples from previous stage
101: int len = previous.getSamples(buffer, length);
102:
103: // If bypass is enabled, short circuit processing
104: if (getByPass() || !initializationComplete)
105: return len;
106:
107: // We have samples to process
108: for (int i = 0; i < len; i++) {
109:
110: // Get a sample
111: double sample = (double) buffer[i];
112:
113: if (Math.abs(sample) >= thresholdValue) {
114: // Sample value exceeds threshold
115:
116: releaseCount++;
117: releaseCount %= (calcReleaseCount + 1);
118:
119: if (attackExpired) {
120: // Attack satisfied, process sample
121: if (!limiting)
122: sample *= atRatio;
123: else
124: sample = (sample < 0) ? -thresholdValue
125: : thresholdValue;
126:
127: } else {
128: attackCount--;
129: if (attackCount <= 0) {
130: attackExpired = true;
131: releaseCount = calcReleaseCount;
132: }
133: }
134:
135: } else {
136: // Sample value did not exceed threshold
137: if (attackExpired) {
138: if (!limiting)
139: sample *= atRatio;
140:
141: releaseCount--;
142: if (releaseCount <= 0) {
143: attackExpired = false;
144: attackCount = calcAttackCount;
145: }
146: } else {
147: attackCount++;
148: attackCount %= (calcAttackCount + 1);
149: }
150: // Now process below threshold noise gating
151: sample *= btRatio;
152: }
153: // Apply gain
154: sample *= gain;
155:
156: // Range check results
157: if (sample > 32767.0)
158: sample = 32767.0;
159: else if (sample < -32768.0)
160: sample = -32768.0;
161:
162: // Store sample back into buffer
163: buffer[i] = (short) sample;
164: }
165: // Return count of sample processed
166: return len;
167:
168: }
169:
170: // These methods called when UI controls are manipulated
171: public void setThreshold(double thresholdInDB) {
172:
173: // thresholdValue is the sample value which is thresholdInDB
174: // below the maximum value of 32767.0
175: thresholdValue = Math.pow(10, thresholdInDB / 20.0) * 32767.0;
176:
177: }
178:
179: public void setBelowThresholdRatio(double ratio) {
180:
181: // Check for noise gating function
182: gating = (ratio >= this .MINBTRATIO);
183:
184: // A noise gate clamps output to zero
185: if (gating)
186: btRatio = 0.0;
187: else
188: btRatio = 1.0 / ratio;
189:
190: }
191:
192: public void setAboveThresholdRatio(double dBRatio) {
193:
194: limiting = (dBRatio <= this .MINATRATIO);
195:
196: atRatio = Math.pow(10, dBRatio / 20);
197:
198: }
199:
200: public void setAttack(double attackInMs) {
201:
202: this .attackInMs = attackInMs;
203: calcAttackCount = (int) (channels * attackInMs * sampleRate / 1000);
204: attackCount = calcAttackCount;
205:
206: }
207:
208: public void setRelease(double releaseInMs) {
209:
210: this .releaseInMs = releaseInMs;
211: calcReleaseCount = (int) (channels * releaseInMs * sampleRate / 1000);
212: releaseCount = calcReleaseCount;
213:
214: }
215:
216: public void setGain(double gainInDb) {
217:
218: this .gain = Math.pow(10, gainInDb / 20);
219:
220: }
221:
222: // Perform calculations that require a known sample rate
223: private void doInitialization() {
224:
225: calcAttackCount = (int) (channels * attackInMs * sampleRate / 1000);
226: attackCount = calcAttackCount;
227:
228: calcReleaseCount = (int) (channels * releaseInMs * sampleRate / 1000);
229: releaseCount = calcReleaseCount;
230:
231: // Indicate initialization is complete
232: initializationComplete = true;
233:
234: }
235:
236: public void minMaxSamplingRate(int min, int max, int preferred) {
237:
238: super .minMaxSamplingRate(min, max, preferred);
239: sampleRate = preferred;
240: doInitialization();
241:
242: }
243:
244: // Negotiate the number of channels
245: public void minMaxChannels(int min, int max, int preferred) {
246:
247: super.minMaxChannels(min, max, preferred);
248: channels = preferred;
249:
250: }
251:
252: }
|