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 Panner extends AbstractAudio {
039:
040: // Private class data
041: private boolean monoSource;
042: private int preferredChannels;
043: private double leftPanFactor = 0.5;
044: private double rightPanFactor = 0.5;
045: private boolean mixMode = false;
046:
047: public Panner() {
048:
049: super ("Panner", AbstractAudio.PROCESSOR);
050:
051: // Assume a mono source
052: monoSource = true;
053:
054: }
055:
056: // Convert pot value into an attenuation factor for left and right
057: // channels.
058: public void setPanValue(int panValue) {
059:
060: this .leftPanFactor = (100.0 - panValue) / 100.0;
061: this .rightPanFactor = ((double) panValue) / 100.0;
062:
063: }
064:
065: // If set, stereo signals are mixed before panning is applied. If
066: // not set, stereo separation is retained. Mix mode has no affect
067: // on mono signals.
068: public void setMixMode(boolean state) {
069:
070: this .mixMode = state;
071:
072: }
073:
074: // Apply the panner effect to the samples passing through this
075: // stage.
076: public int getSamples(short[] buffer, int length) {
077:
078: // If the previous stage constitutes a mono (single channel)
079: // source, then halve the number of samples requested. This
080: // allows the use of a single buffer for processing.
081:
082: int halfLength = length / 2;
083:
084: // Request samples from previous stage
085: int len = previous.getSamples(buffer, monoSource ? halfLength
086: : length);
087:
088: // Was EOF indication returned?
089: if (len == -1)
090: return -1;
091:
092: // If bypass in effect and we have a stereo source, don't do
093: // anything as samples are already in the buffer. If we have
094: // a mono source, copy mono samples to both the left and right
095: // channels.
096: if (getByPass()) {
097: if (monoSource) {
098: // We have a mono source to process. Work from back to front
099: // of buffer to prevent over writing unprocessed data.
100: int sourceIndex = halfLength - 1;
101: int destIndex = length - 2;
102:
103: for (int i = 0; i < halfLength; i++) {
104: short s = buffer[sourceIndex--]; // Read mono sample
105: buffer[destIndex] = s; // Write left channel
106: buffer[destIndex + 1] = s; // Write right channel
107: destIndex -= 2;
108: }
109: }
110: return length;
111: }
112: // Bypass not in effect, do some panning
113:
114: // What is done depends upon source and mode
115: if (monoSource) {
116: // We have a mono source to process. Work from back to front
117: // of buffer to prevent over writing unprocessed data.
118: int sourceIndex = halfLength - 1;
119: int destIndex = length - 2;
120:
121: for (int i = 0; i < halfLength; i++) {
122: short s = buffer[sourceIndex--];
123: buffer[destIndex] = (short) (s * leftPanFactor);
124: buffer[destIndex + 1] = (short) (s * rightPanFactor);
125: destIndex -= 2;
126: }
127:
128: } else {
129:
130: // We have a stereo source to process. Check the mode.
131: if (mixMode) {
132: // Mix left and right before panning
133: for (int i = 0; i < length; i += 2) {
134: double s = (buffer[i] + buffer[i + 1]) / 2.0;
135: buffer[i] = (short) (s * leftPanFactor);
136: buffer[i + 1] = (short) (s * rightPanFactor);
137: }
138:
139: } else {
140: // Leave stereo separation intact
141: for (int i = 0; i < length; i += 2) {
142: buffer[i] = (short) (buffer[i] * leftPanFactor);
143: buffer[i + 1] = (short) (buffer[i + 1] * rightPanFactor);
144: }
145: }
146: }
147:
148: return length;
149:
150: }
151:
152: public void stopUI() {
153:
154: setByPass(true);
155:
156: }
157:
158: // Override AbstractAudio methods as required to influence the
159: // number of channels negotiation.
160:
161: // Override this method to capture the preferred number
162: // of channels from the stages preceeding the panner but
163: // return a stereo preference from this panner stage. This is
164: // done because the output of the panner is always stereo.
165: public void minMaxChannels(int min, int max, int preferred) {
166:
167: // Propagate call towards the source
168: if (previous != null)
169: previous.minMaxChannels(min, max, preferred);
170:
171: // Save the preferred value from previous stages
172: preferredChannels = preferred;
173:
174: // Set flag to indicate source mode
175: monoSource = (preferredChannels == 1);
176:
177: // Set up for stereo as the output of the panner is
178: // always stereo.
179: min = 2;
180: max = 2;
181: preferred = 2;
182:
183: }
184:
185: // Override this method so that all stages before the panner
186: // use their negotiated preference (stereo or mono). All
187: // stages afterwards are stereo.
188: public void setChannelsRecursive(int ch) {
189:
190: ch = preferredChannels;
191:
192: super.setChannelsRecursive(ch);
193:
194: }
195:
196: }
|