001: /*
002: * $RCSfile: JSChannel.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; // import sun.applet.*;
058: import javax.sound.sampled.*;
059: import com.sun.j3d.audioengines.*;
060:
061: // import javax.media.j3d.*;
062:
063: /**
064: * The JSChannel Class defines an audio output methods that call JavaSound
065: * API methods common for all data line types: streams, clip and MIDI lines.
066: */
067:
068: class JSChannel {
069:
070: AudioInputStream ais = null;
071: long startTime = 0;
072: URL url = null;
073: InputStream inputStream = null;
074: AudioFormat audioFormat = null;
075: // WORKAROUND for (possibly old) bug in JavaSound
076: // JavaSound has left and right flipped
077: // TODO: verify whether this is still true
078: static double panLeft = 1.0;
079: static double panRight = -1.0;
080: float rateInHz = 0.0f;
081:
082: /**
083: * Debug print mechanism for Sound nodes
084: */
085: static final boolean debugFlag = false;
086:
087: static void debugPrint(String message) {
088: if (debugFlag)
089: System.out.print(message);
090: }
091:
092: static void debugPrintln(String message) {
093: if (debugFlag)
094: System.out.println(message);
095: }
096:
097: /**
098: * Code to initialize the device
099: * @return flag: true is initialized sucessfully, false if error
100: */
101: boolean initialize() {
102: // for now do nothing
103: return true;
104: }
105:
106: /**
107: * @return reference to newly created AudioInputStream
108: */
109: AudioInputStream initAudioInputStream(InputStream inputStream,
110: boolean cacheFlag) {
111: ais = null;
112: if (inputStream == null) {
113: if (debugFlag) {
114: debugPrint("JSChannel: Internal Error initAudioInputStream ");
115: debugPrintln("input stream given is null");
116: }
117: this .inputStream = null;
118: return null;
119: }
120: try {
121: if (debugFlag)
122: debugPrintln("JSChannel: initAudioInputStream - try getting stream ");
123: // open the sound data as an 'audio input stream'
124: // and read the header information at the start of the data.
125: ais = AudioSystem.getAudioInputStream(inputStream);
126: // add this new stream to vector list of streams
127: } catch (Exception e) {
128: if (debugFlag) {
129: debugPrint("JSChannel: Internal Error initAudioInputStream ");
130: debugPrintln("get stream failed");
131: }
132: e.printStackTrace();
133: this .inputStream = null;
134: return null;
135: }
136: // success, so save new inputStream and nullify url field
137: this .inputStream = inputStream;
138: url = null;
139: /******
140: // QUESTION: HOW do I figure out the data type of the file/url/inputStream????
141: if (ais instanceof AudioMidiInputStream ||
142: ais instanceof AudioRmfInputStream )
143: // QUESTION: can non-cached MIDI files ever be supported ?
144: *******/
145: return ais;
146: } // initAudioInputStream
147:
148: /**
149: * @return reference to newly created AudioInputStream
150: */
151: AudioInputStream initAudioInputStream(URL path, boolean cacheFlag) {
152: ais = null;
153: if (path == null) {
154: if (debugFlag) {
155: debugPrint("JSChannel: Internal Error initAudioInputStream ");
156: debugPrintln("URL given is null");
157: }
158: this .url = null;
159: return null;
160: }
161: try {
162: if (debugFlag)
163: debugPrintln("JSChannel: initAudioInputStream - try getting stream ");
164: ais = AudioSystem.getAudioInputStream(path.openStream());
165: } catch (Exception e) {
166: if (debugFlag) {
167: debugPrint("JSChannel: Internal Error initAudioInputStream ");
168: debugPrintln("get stream failed");
169: }
170: e.printStackTrace();
171: this .url = null;
172: return null;
173: }
174: // success, so save new url path and nullify input stream field
175: this .url = path;
176: inputStream = null;
177: return ais;
178: } // initAudioInputStream
179:
180: AudioInputStream reinitAudioInputStream(URL path) {
181: /*****
182: if (path == null) {
183: if (debugFlag) {
184: debugPrint("JSChannel: Internal Error reinitAudioInputStream ");
185: debugPrintln("URL given is null");
186: }
187: return null;
188: }
189: try {
190: if (debugFlag)
191: debugPrintln("JSChannel: reinitAudioInputStream - try getting stream ");
192: ais = AudioSystem.getAudioInputStream(path.openStream());
193: }
194: catch (Exception e) {
195: if (debugFlag) {
196: debugPrint("JSChannel: Internal Error reinitAudioInputStream ");
197: debugPrintln("get stream failed");
198: }
199: e.printStackTrace();
200: return null;
201: }
202: // Parametes stay the same except for start time which is changed later
203: return ais;
204: ******/
205: return null; // TODO: implement this
206:
207: } // reinitAudioInputStream
208:
209: AudioInputStream reinitAudioInputStream(InputStream inputStream) {
210: /******
211: AudioInputStream ais;
212: if (inputStream == null) {
213: if (debugFlag) {
214: debugPrint("JSChannel: Internal Error reinitAudioInputStream ");
215: debugPrintln("InputStream given is null");
216: }
217: return null;
218: }
219: try {
220: // Couldn't get this method to work!!!
221: if (debugFlag)
222: debugPrintln("JSChannel: reintAudioContainer - try closing stream ");
223: inputStream.close();
224:
225: if (debugFlag)
226: debugPrintln("JSChannel: reinitAudioInputStream - try getting stream ");
227: ais = AudioSystem.getAudioInputStream(inputStream);
228: }
229: catch (Exception e) {
230: if (debugFlag) {
231: debugPrint("JSChannel: Internal Error reinitAudioInputStream ");
232: debugPrintln("get stream failed");
233: }
234: e.printStackTrace();
235: return null;
236: }
237: // Parametes stay the same except for start time which is changed later
238: return ais; // >=0 if everythings OK
239: **************/
240: return null; // TODO: implement this
241:
242: } // reinitAudioInputStream
243:
244: DataLine initDataLine(AudioInputStream ais) {
245: if (debugFlag) {
246: debugPrintln("JSChannel: initDataLine(" + ais + ")");
247: debugPrintln(" must be overridden");
248: }
249: return null;
250: }
251:
252: long getDuration() {
253: // TODO: how should this really be done??
254: if (debugFlag)
255: debugPrintln("JSChannel:getDuration");
256:
257: if (ais == null || audioFormat == null) {
258: if (debugFlag)
259: debugPrintln("JSChannel: Internal Error getDuration");
260: return (long) Sample.DURATION_UNKNOWN;
261: }
262: // Otherwise we'll assume that we can calculate this duration
263:
264: // get "duration" of audio stream (wave file)
265: // TODO: For True STREAMing audio the size is unknown...
266: long numFrames = ais.getFrameLength();
267: if (debugFlag)
268: debugPrintln(" frame length = " + numFrames);
269: if (numFrames <= 0)
270: return (long) Sample.DURATION_UNKNOWN;
271:
272: float rateInFrames = audioFormat.getFrameRate();
273: rateInHz = audioFormat.getSampleRate();
274: if (debugFlag)
275: debugPrintln(" rate in Frames = " + rateInFrames);
276: if (numFrames <= 0)
277: return (long) Sample.DURATION_UNKNOWN;
278: long duration = (long) ((float) numFrames / rateInFrames);
279: if (debugFlag)
280: debugPrintln(" duration(based on ais) = "
281: + duration);
282: return duration;
283: }
284:
285: /**
286: * Start TWO Samples
287: */
288: boolean startSamples(int loopCount, float leftGain,
289: float rightGain, int leftDelay, int rightDelay) {
290: if (debugFlag)
291: debugPrint("JSChannel: startSamples must be overridden");
292: return false;
293: } // end of start Samples
294:
295: /*
296: * Starts a Sample
297: */
298: boolean startSample(int loopCount, float gain, int delay) {
299: if (debugFlag)
300: debugPrint("JSChannel: startSample must be overridden");
301: return false;
302: } // end of start (single) Sample
303:
304: int stopSample() {
305: // This will tell thread to stop reading and writing
306: // reload with old URL
307: // reloadSample
308: if (debugFlag)
309: debugPrint("JSChannel: stopSample must be overridden");
310: startTime = 0;
311: return 0;
312: }
313:
314: int stopSamples() {
315: // This will tell thread to stop reading and writing
316: // TODO: For muting, stop sound but don't clear startTime...
317: // QUESTION: what does it mean for replaying that .stop "frees memory"
318: if (debugFlag)
319: debugPrint("JSChannel: stopSample must be overridden");
320: // reloadSample
321:
322: startTime = 0;
323: return 0;
324: }
325:
326: void setSampleGain(float gain) {
327: // TODO: Must be done in thread
328: if (debugFlag)
329: debugPrint("JSChannel: setSampleGain must be overridden");
330: }
331:
332: void setSampleDelay(int delay) {
333: if (debugFlag)
334: debugPrint("JSChannel: setSampleDelay must be overridden");
335: /*
336: * null method
337: */
338: // dynamic changes to sample delay while playing is not implemented
339: }
340:
341: void setSampleReverb(int type, boolean on) {
342: if (debugFlag)
343: debugPrint("JSChannel: setSampleReverb must be overridden");
344: }
345:
346: void setSampleRate() {
347: if (debugFlag)
348: debugPrint("JSChannel: setSampleRate must be overridden");
349: }
350:
351: void scaleSampleRate(float scaleFactor) {
352: /**
353: * Change rate for Doppler affect or pitch shifting.
354: * Engine maximum sample rate is 48kHz so clamp to that
355: * max value.
356: */
357: if (debugFlag)
358: debugPrintln("JSChannel: scaleSampleRate");
359: if (ais == null) {
360: if (debugFlag) {
361: debugPrint("JSChannel: Internal Error scaleSampleRate: ");
362: debugPrintln("ais is null");
363: }
364: return;
365: }
366:
367: AudioFormat audioFormat = ais.getFormat();
368: float rate = audioFormat.getSampleRate();
369:
370: double newRate = rate * scaleFactor;
371: if (newRate > 48000.0) // clamp to 48K max
372: newRate = 48000.0;
373: /****
374: // NOTE: This doesn't work...
375: /// audioStream.setSampleRate(newRate);
376:
377: // need to set FloatControl.Type(SAMPLE_RATE) to new value somehow...
378:
379: if (debugFlag) {
380: debugPrintln("JSChannel: scaleSampleRate: new rate = " +
381: rate * scaleFactor);
382: debugPrintln(" >>>>>>>>>>>>>>> using scaleFactor = " +
383: scaleFactor);
384: }
385: ****/
386: }
387:
388: int pauseSamples() {
389: /**
390: * Pause playing samples
391: */
392: // TODO: Notify thread
393: return 0;
394: }
395:
396: int pauseSample() {
397: /**
398: * Pause playing a sample
399: */
400: // TODO: Notify thread
401: return 0;
402: }
403:
404: int unpauseSamples() {
405: /**
406: * Resume playing samples
407: */
408: // TODO: Notify thread
409: return 0;
410: }
411:
412: int unpauseSample() {
413: /**
414: * Resume playing a sample
415: */
416: // TODO: Notify thread
417: return 0;
418: }
419:
420: void setSampleFiltering(boolean filterFlag, float cutoffFreq) {
421: /**
422: * Set or clear low-pass filtering
423: */
424: /****
425: // QUESTION: how will this be done if data is written out one channel/sample at
426: a time??
427: ****/
428: // QUESTION: should filtering of Midi data be performed?
429: // ais.setFiltering(filterFlag, cutoffFreq);
430: }
431:
432: }
|