001: /*
002: * $RCSfile: JSSample.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: * Java Sound Sample object
047: *
048: * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
049: * to be rewritten.
050: */
051:
052: package com.sun.j3d.audioengines.javasound;
053:
054: import java.net.URL;
055: import java.io.InputStream;
056: import javax.media.j3d.*;
057: import javax.sound.sampled.*;
058: import com.sun.j3d.audioengines.*;
059:
060: /**
061: * The Sample Class extended for Java Sound Mixer specific audio device.
062: */
063:
064: class JSSample extends com.sun.j3d.audioengines.Sample {
065: /*
066: * NOTE: for this device type there is exactly one sample associated
067: * with each sound.
068: */
069:
070: /**
071: * Sound Data Types
072: *
073: * Samples can be processed as streaming or buffered data.
074: * Fully spatializing sound sources may require data to be buffered.
075: *
076: * Sound data specified as Streaming is not copied by the AudioDevice
077: * driver implementation. It is up the application to ensure that
078: * this data is continuously accessible during sound rendering.
079: * Futhermore, full sound spatialization may not be possible, for
080: * all AudioDevice implementations on unbuffered sound data.
081: */
082: static final int STREAMING_AUDIO_DATA = 1;
083: /**
084: * Sound data specified as Buffered is copied by the AudioDevice
085: * driver implementation.
086: */
087: static final int BUFFERED_AUDIO_DATA = 2;
088: /**
089: * MIDI data
090: * TODO: differentiate between STREAMING and BUFFERED MIDI data
091: * right now all MIDI data is buffered
092: */
093: static final int STREAMING_MIDI_DATA = 3;
094: static final int BUFFERED_MIDI_DATA = 3;
095: static final int UNSUPPORTED_DATA_TYPE = -1;
096:
097: static final int NULL_SAMPLE = -1;
098:
099: /**
100: * sound data types: BUFFERED (cached) or STREAMING (non-cached)
101: */
102: int dataType = BUFFERED_AUDIO_DATA;
103:
104: JSChannel channel = null;
105:
106: /**
107: * Offset pointer within currently playing sample data
108: */
109: long dataOffset = 0;
110:
111: /*
112: * Maintain continuously playing silent sound sources.
113: */
114: long timeDeactivated = 0;
115: long positionDeactivated = 0;
116:
117: long sampleLength = 0;
118: long loopStartOffset = 0; // for most this will be 0
119: long loopLength = 0; // for most this is end sample - sampleLength
120: long attackLength = 0; // portion of sample before loop section
121: long releaseLength = 0; // portion of sample after loop section
122:
123: float rateRatio = 1.0f;
124: float currentRateRatio = -1.0f; // last actual rate ratio send to device
125: float targetRateRatio = -1.0f;
126: boolean rampRateFlag = false;
127:
128: public JSSample() {
129: super ();
130: if (debugFlag)
131: debugPrintln("JSSample constructor");
132: }
133:
134: // the only public methods are those declared in the audioengines
135: // package as public
136:
137: /*
138: * This excutes code necessary to set current fields to their current
139: * correct values before JavaSoundMixer either start or updates the
140: * sample thru calls to JSThread.
141: */
142: public void render(int dirtyFlags, View view,
143: AuralParameters attribs) {
144: if (debugFlag)
145: debugPrint("JSSample.render ");
146: // if this is starting set gain, delay (for Pos), freq rate ...
147: // TODO: NOT SURE - leaving this in for now
148: float freqScaleFactor = attribs.frequencyScaleFactor;
149: if (attribs != null) {
150: if (freqScaleFactor <= 0.0f) {
151: // TODO: Pause Sample
152: } else
153: rateRatio = currentRateRatio * freqScaleFactor;
154: } else
155: rateRatio = currentRateRatio;
156: }
157:
158: /**
159: * Clears/re-initialize fields associated with sample data for
160: * this sound,
161: * and frees any device specific data associated with this sample.
162: */
163: public void clear() {
164: super .clear();
165: if (debugFlag)
166: debugPrintln("JSSample.clear() entered");
167: // TODO: unload sound data at device
168: // null out samples element that points to this?
169: // would this cause samples list size to shrink?
170: // if sample elements are never freed then does this mean
171: // a have a memory leak?
172: dataType = UNSUPPORTED_DATA_TYPE;
173: dataOffset = 0;
174: timeDeactivated = 0;
175: positionDeactivated = 0;
176: sampleLength = 0;
177: loopStartOffset = 0;
178: loopLength = 0;
179: attackLength = 0;
180: releaseLength = 0;
181: rateRatio = 1.0f;
182: channel = null;
183: if (debugFlag)
184: debugPrintln("JSSample.clear() exited");
185: }
186:
187: // @return error true if error occurred
188: boolean load(MediaContainer soundData) {
189: /**
190: * Get the AudioInputStream first.
191: * MediaContiner passed to method assumed to be a clone of the
192: * application node with the query capability bits set on.
193: */
194: String path = soundData.getURLString();
195: URL url = soundData.getURLObject();
196: InputStream inputStream = soundData.getInputStream();
197: boolean cacheFlag = soundData.getCacheEnable();
198: AudioInputStream ais = null;
199: DataLine dataLine = null;
200:
201: // TODO: How do we determine if the file is a MIDI file???
202: // for now set dataType to BUFFERED_ or STREAMING_AUDIO_DATA
203: // used to test for ais instanceof AudioMidiInputStream ||
204: // ais instanceof AudioRmfInputStream )
205: // then set dataType = JSSample.BUFFERED_MIDI_DATA;
206: // QUESTION: can non-cached MIDI files ever be supported ?
207: /****************
208: // TODO: when we have a way to determine data type use code below
209: if (dataType==UNSUPPORTED_DATA_TYPE OR error_occurred)
210: clearSound(index);
211: if (debugFlag)
212: debugPrintln("JavaSoundMixer.prepareSound get dataType failed");
213: return true;
214: }
215: *****************/
216: // ...for now just check cacheFlag
217: if (cacheFlag)
218: dataType = BUFFERED_AUDIO_DATA;
219: else
220: dataType = STREAMING_AUDIO_DATA;
221:
222: if ((url == null) && (inputStream == null) && (path == null)) {
223: if (debugFlag)
224: debugPrint("JavaSoundMixer.loadSound null data - return error");
225: return true;
226: }
227:
228: // get ais
229: if (path != null) {
230: // generate url from string, and pass url to driver
231: if (debugFlag) {
232: debugPrint("JavaSoundMixer.loadSound with path = "
233: + path);
234: }
235: try {
236: url = new URL(path);
237: } catch (Exception e) {
238: // do not throw an exception while rendering
239: return true;
240: }
241: }
242:
243: // get DataLine channel based on data type
244: if (dataType == BUFFERED_AUDIO_DATA) {
245: if (debugFlag)
246: debugPrintln("JSSample.load dataType = BUFFERED ");
247: channel = new JSClip();
248: if (debugFlag)
249: debugPrintln(" calls JSClip.initAudioInputStream");
250: if (url != null)
251: ais = channel.initAudioInputStream(url, cacheFlag);
252: else if (inputStream != null)
253: ais = channel.initAudioInputStream(inputStream,
254: cacheFlag);
255: if (ais == null) {
256: if (debugFlag)
257: debugPrintln("JavaSoundMixer.prepareSound "
258: + "initAudioInputStream() failed");
259: return true;
260: }
261: if (debugFlag)
262: debugPrintln(" calls JSClip.initDataLine");
263: dataLine = channel.initDataLine(ais);
264: } else if (dataType == STREAMING_AUDIO_DATA) {
265: if (debugFlag)
266: debugPrintln("JSSample.load dataType = STREAMING ");
267: channel = new JSStream();
268: if (debugFlag)
269: debugPrintln(" calls JSStream.initAudioInputStream");
270: if (url != null)
271: ais = channel.initAudioInputStream(url, cacheFlag);
272: else if (inputStream != null)
273: ais = channel.initAudioInputStream(inputStream,
274: cacheFlag);
275: if (ais == null) {
276: if (debugFlag)
277: debugPrintln("JavaSoundMixer.prepareSound "
278: + "initAudioInputStream() failed");
279: return true;
280: }
281: if (debugFlag)
282: debugPrintln(" calls JSStream.initDataLine");
283: dataLine = channel.initDataLine(ais);
284: } else {
285: if (debugFlag)
286: debugPrintln("JSSample.load doesn't support MIDI yet");
287: }
288: if (dataLine == null) {
289: if (debugFlag)
290: debugPrint("JSSample.load initDataLine failed ");
291: channel = null;
292: return true;
293: }
294: duration = channel.getDuration();
295: if (debugFlag)
296: debugPrint("JSSample.load channel duration = " + duration);
297: /*
298: * Since no error occurred while loading, save all the characteristics
299: * for the sound in the sample.
300: */
301: setDirtyFlags(0xFFFF);
302: setSoundType(soundType);
303: setSoundData(soundData);
304:
305: if (debugFlag)
306: debugPrintln("JSSample.load returned without error");
307: return false;
308: }
309:
310: void reset() {
311: if (debugFlag)
312: debugPrint("JSSample.reset() exit");
313: rateRatio = 1.0f;
314: }
315:
316: // TODO: NEED methods for any field accessed by both JSThread and
317: // JavaSoundMixer so that we can make these MT safe??
318: /*
319: * Process request for Filtering fields
320: */
321: boolean getFilterFlag() {
322: return false;
323: }
324:
325: float getFilterFreq() {
326: return -1.0f;
327: }
328:
329: void setCurrentRateRatio(float ratio) {
330: currentRateRatio = ratio;
331: }
332:
333: float getCurrentRateRatio() {
334: return currentRateRatio;
335: }
336:
337: void setTargetRateRatio(float ratio) {
338: targetRateRatio = ratio;
339: }
340:
341: float getTargetRateRatio() {
342: return targetRateRatio;
343: }
344:
345: void setRampRateFlag(boolean flag) {
346: rampRateFlag = flag;
347: }
348:
349: boolean getRampRateFlag() {
350: return rampRateFlag;
351: }
352:
353: void setDataType(int type) {
354: dataType = type;
355: }
356:
357: int getDataType() {
358: return dataType;
359: }
360:
361: }
|