001: /*
002: * $RCSfile: JSClip.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.4 $
041: * $Date: 2007/02/09 17:20:03 $
042: * $State: Exp $
043: */
044:
045: /*
046: * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
047: * to be rewritten.
048: */
049:
050: package com.sun.j3d.audioengines.javasound;
051:
052: import java.applet.*;
053: import java.util.*;
054: import java.lang.String;
055: import java.net.*;
056: import java.io.*;
057: import java.io.InputStream;
058: import javax.sound.sampled.*;
059:
060: /**
061: * The JSClip Class defines an audio output methods that call JavaSound
062: * Hae mixer methods.
063: */
064:
065: class JSClip extends JSChannel {
066:
067: Clip line;
068:
069: // TODO: separate left and right channel required until I write into
070: // stereo buffer!
071: Clip otherChannel = null;
072:
073: // TODO: Reverb channel that is centered and not delayed is maintained separately
074: // until a way to set stereo reverb send (panned and attenuated to give
075: // the same affect) is implemented
076: Clip reverbChannel = null;
077:
078: /**
079: * Create data line for outputting audio input stream.
080: * for a stream that is a sourceDataline
081: * @return true is successful in initiallizing DataLine
082: */
083: DataLine initDataLine(AudioInputStream ais) {
084: if (debugFlag)
085: debugPrintln("JSClip: initDataLine(" + ais + ")");
086:
087: try {
088: if (debugFlag)
089: debugPrintln("JSClip: loadSample - try getting new line ");
090: /*
091: * From the AudioInputStream fetch information about the format
092: * of the audio data - including sampling frequency, number of
093: * channels, size of samples,...
094: */
095: audioFormat = ais.getFormat();
096:
097: /*
098: * we can't yet open the device for ALAW/ULAW playback,
099: * convert ALAW/ULAW to PCM
100: */
101: if ((audioFormat.getEncoding() == AudioFormat.Encoding.ULAW)
102: || (audioFormat.getEncoding() == AudioFormat.Encoding.ALAW)) {
103:
104: AudioFormat tmp = new AudioFormat(
105: AudioFormat.Encoding.PCM_SIGNED, audioFormat
106: .getSampleRate(), audioFormat
107: .getSampleSizeInBits() * 2, audioFormat
108: .getChannels(), audioFormat
109: .getFrameSize() * 2, audioFormat
110: .getFrameRate(), true);
111: ais = AudioSystem.getAudioInputStream(tmp, ais);
112: audioFormat = tmp;
113: }
114:
115: /*
116: * ask JavaSound for outline with a format suitable for our
117: * AudioInputStream. In order to ask for a line, a Info object
118: * with the desired properties must be constructed.
119: * Clip is used for outputing buffered data.
120: * We have to pass the line the AudioFormat object so it knows
121: * format will be.
122: *
123: * TODO: we could give JavaSound a hint about how big the
124: * internal buffer for the line should be, rather than use the
125: * default.
126: */
127: DataLine.Info info = new DataLine.Info(Clip.class,
128: audioFormat);
129: line = (Clip) AudioSystem.getLine(info);
130: /*****
131: // TODO: JSClip can't be a listener (do we need to do this in the thread?)
132: if (debugFlag)
133: debugPrintln("JSClip: addLineListener for clip");
134: line.addLineListener(this);
135: ******/
136:
137: if (debugFlag)
138: debugPrintln("JSClip: open sound Clip");
139:
140: // Make line ready to receive data.
141: line.open(ais);
142:
143: // Line can now receive data but still needs to be
144: // activated (opened) so it will pass data on to the
145: // audio device. This is done at "startSample" time.
146: } catch (Exception e) {
147: if (debugFlag) {
148: debugPrint("JSClip: Internal Error loadSample ");
149: debugPrintln("get stream failed");
150: }
151: e.printStackTrace();
152: // TODO: clean up vector elements that were set up for
153: // failed sample
154: return null;
155: }
156: return (DataLine) line;
157: } // initDataLine
158:
159: /**
160: * Start TWO Samples
161: *
162: * used when two samples are associated with a single Point or Cone
163: * sound. This method handles starting both samples, rather than
164: * forcing the caller to make two calls to startSample, so that the
165: * actual Java Sound start methods called are as immediate (without
166: * delay between as possible.
167: */
168: boolean startSamples(int loopCount, float leftGain,
169: float rightGain, int leftDelay, int rightDelay) {
170: // loop count is ignored for Stream and MIDI
171: // TODO: loop count isn't implemented for MIDI yet
172:
173: // left and rightDelay parameters are in terms of Samples
174: if (debugFlag) {
175: debugPrint("JSClip: startSamples ");
176: debugPrintln("start stream for Left called with ");
177: debugPrintln(" gain = " + leftGain + " delay = "
178: + leftDelay);
179: debugPrintln("start stream for Right called with ");
180: debugPrintln(" gain = " + rightGain + " delay = "
181: + rightDelay);
182: }
183:
184: // This is called assuming that the Stream is allocated for a
185: // Positional sample, but if it is not then fall back to
186: // starting the single sample associated with this Stream
187: if (otherChannel == null || reverbChannel == null)
188: startSample(loopCount, leftGain, leftDelay);
189:
190: /*
191: * ais for Left and Right streams should be same so just get ais
192: * left stream
193: */
194: if (ais == null) {
195: if (debugFlag) {
196: debugPrint("JSClip: Internal Error startSamples: ");
197: debugPrintln("either left or right ais is null");
198: }
199: return false;
200: }
201: Clip leftLine;
202: Clip rightLine;
203: leftLine = line;
204: rightLine = otherChannel;
205: // left line only for background sounds...
206: // TODO:
207: /***********
208: for now just care about the left
209: if (leftLine == null || rightLine == null) {
210: if (debugFlag) {
211: debugPrint("JSClip: startSamples Internal Error: ");
212: debugPrintln("either left or right line null");
213: }
214: return false;
215: }
216: ************/
217:
218: // we know that were processing TWO channels
219: double ZERO_EPS = 0.0039; // approx 1/256 - twice MIDI precision
220: double leftVolume = (double) leftGain;
221: double rightVolume = (double) rightGain;
222:
223: // TODO: if not reading/writing done for Clips then I can't do
224: // stereo trick (reading mono file and write to stereo buffer)
225: // Save time sound started, only in left
226: startTime = System.currentTimeMillis();
227: if (debugFlag)
228: debugPrintln("*****start Stream with new start time "
229: + startTime);
230: try {
231: // QUESTION: Offset clip is done how???
232: /*******
233: // TODO:
234: offset delayed sound
235: set volume
236: set pan??
237: set reverb
238: boolean reverbLeft = false; // off; reverb has it own channel
239: boolean reverbRight = reverbLeft;
240:
241: if (leftDelay < rightDelay) {
242: XXXX audioLeftStream.start(leftVolume, panLeft, reverbLeft);
243: XXXX audioRightStream.start(rightVolume, panRight, reverbRight);
244: }
245: else {
246: XXXX audioRightStream.start(rightVolume, panRight, reverbRight);
247: XXXX audioLeftStream.start(leftVolume, panLeft, reverbLeft);
248: }
249: ******/
250: line.setLoopPoints(0, -1); // Loop the entire sound sample
251: line.loop(loopCount); // plays clip loopCount + 1 times
252: line.start(); // start the sound
253: } catch (Exception e) {
254: if (debugFlag) {
255: debugPrint("JSClip: startSamples ");
256: debugPrintln("audioInputStream.read failed");
257: }
258: e.printStackTrace();
259: startTime = 0;
260: return false;
261: }
262:
263: if (debugFlag)
264: debugPrintln("JSClip: startSamples returns");
265: return true;
266: } // end of startSamples
267:
268: /*
269: * This method is called specifically for BackgroundSounds.
270: * There is exactly ONE sample (mono or stereo) associated with
271: * this type of sound. Consequently, delay is not applicable.
272: * Since the sound has no auralAttributes applied to it reverb
273: * is not applied to the sample.
274: */
275: boolean startSample(int loopCount, float gain, int delay) {
276: /*
277: if (debugFlag) {
278: debugPrint("JSClip: startSample ");
279: debugPrintln("start stream called with ");
280: debugPrintln(" gain = " + gain + ", delay is zero");
281: }
282:
283: // Since only one sample is processed in startSample, just call
284: // this more general method passing duplicate information
285: // We don't really want to do this in the long term.
286: return startSamples(loopCount, gain, gain, 0, 0);
287: */
288:
289: // TODO: The following is temporary until we have fully
290: // functional startSample and startSamples methods
291: if (debugFlag)
292: debugPrintln("JSClip.startSample(): starting sound Clip");
293: line.setFramePosition(0); // Start playing from the beginning
294: line.setLoopPoints(0, -1); // Loop the entire sound sample
295: line.loop(loopCount);
296: line.start();
297: return true;
298: } // end of start (single) Sample
299:
300: int stopSample() {
301: // This will tell thread to stop reading and writing
302: // reload with old URL - reloadSample()???
303:
304: if (debugFlag)
305: debugPrintln("JSClip.stopSample(): stopping sound Clip");
306: line.stop();
307:
308: startTime = 0;
309: return 0;
310: }
311:
312: int stopSamples() {
313: // This will tell thread to stop reading and writing
314: // TODO: For muting, stop sound but don't clear startTime...
315: // QUESTION: what does it mean for replaying that .stop "frees memory"
316:
317: // reloadSample
318: // QUESTION: set stop state WHERE??!!
319:
320: if (debugFlag)
321: debugPrintln("JSClip.stopSample(): stopping sound Clip");
322: line.stop();
323:
324: startTime = 0;
325: return 0;
326: }
327:
328: /*
329: * called by LineListener class
330: */
331: public void update(LineEvent event) {
332: if (event.getType().equals(LineEvent.Type.STOP)) {
333: line.close(); // really a stop??
334: } else if (event.getType().equals(LineEvent.Type.CLOSE)) {
335: // this forces a system exit in example code
336: // TODO: what should be done to close line
337: if (debugFlag)
338: debugPrint("JSClip.update(CLOSE) entered ");
339: }
340: }
341:
342: }
|