001: /*
002: * $RCSfile: JavaSoundMixer.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:04 $
042: * $State: Exp $
043: */
044:
045: /*
046: * Audio device driver using Java Sound Mixer Engine.
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.vecmath.*;
057: import javax.media.j3d.*;
058: import com.sun.j3d.audioengines.*;
059: import java.util.ArrayList;
060: import java.lang.Thread;
061:
062: /**
063: * The JavaSoundMixer Class defines an audio output device that accesses
064: * JavaSound functionality stream data.
065: */
066: public class JavaSoundMixer extends AudioEngine3DL2 {
067:
068: // Debug print flags and methods
069: static final boolean debugFlag = false;
070: static final boolean internalErrors = false;
071:
072: void debugPrint(String message) {
073: if (debugFlag)
074: System.out.println(message);
075: }
076:
077: void debugPrintln(String message) {
078: if (debugFlag)
079: System.out.println(message);
080: }
081:
082: // Determines method to call for added or setting sound into ArrayList
083: static final int ADD_TO_LIST = 1;
084: static final int SET_INTO_LIST = 2;
085:
086: // current Aural Parameters = Aural Attributes from core + JavaSound
087: // specific fields, including reverberation parameters.
088: JSAuralParameters auralParams = null;
089:
090: // thread for dynamically changing audio parameters such as volume
091: // and sample rate.
092: JSThread thread = null;
093:
094: /*
095: * new fields in extended class
096: */
097: protected float deviceGain = 1.0f;
098:
099: protected static final int NOT_PAUSED = 0;
100: protected static final int PAUSE_PENDING = 1;
101: protected static final int PAUSED = 2;
102: protected static final int RESUME_PENDING = 3;
103: protected int pause = NOT_PAUSED;
104:
105: /*
106: * Construct a new JavaSoundMixer with the specified P.E.
107: * @param physicalEnvironment the physical environment object where we
108: * want access to this device.
109: */
110: public JavaSoundMixer(PhysicalEnvironment physicalEnvironment) {
111: super (physicalEnvironment);
112: thread = new JSThread(Thread.currentThread().getThreadGroup(),
113: this );
114: }
115:
116: /**
117: * Query total number of channels available for sound rendering
118: * for this audio device.
119: * Overridden method from AudioEngine.
120: * @return number of maximum voices play simultaneously on JavaSound Mixer.
121: */
122: public int getTotalChannels() {
123: if (thread != null)
124: return thread.getTotalChannels();
125: else
126: return 32;
127: }
128:
129: /**
130: * Code to initialize the device
131: * New interface to mixer/engine specific methods
132: * @return flag: true is initialized sucessfully, false if error
133: */
134: public boolean initialize() {
135: if (thread == null) {
136: return false;
137: }
138: // init JavaSound dynamic thread
139: thread.initialize();
140: auralParams = new JSAuralParameters();
141: if (debugFlag)
142: debugPrintln("JavaSoundMixer: JSStream.initialize returned true");
143: return true;
144: }
145:
146: /**
147: * Code to close the device.
148: * New interface to mixer/engine specific methods
149: * @return flag: true is closed sucessfully, false if error
150: */
151: public boolean close() {
152: if (thread == null)
153: return false;
154: if (thread.close()) {
155: if (debugFlag)
156: debugPrintln("JavaSoundMixer: JSStream.close returned true");
157: return true;
158: } else {
159: if (debugFlag)
160: debugPrintln("JavaSoundMixer: JSStream.close returned false");
161: return false;
162: }
163: }
164:
165: /**
166: * Code to load sound data into a channel of device mixer.
167: * Load sound as one or mores sample into the Java Sound Mixer:
168: * a) as either a STREAM or CLIP based on whether cached is enabled
169: * b) positional and directional sounds use three samples per
170: * sound
171: * Overriden method from AudioEngine3D.
172: *
173: * Sound type determines if this is a Background, Point or Cone
174: * sound source and thus the JSXxxxSample object type
175: * Call JSXxxxxSample.loadSample()
176: * If no error
177: * Get the next free index in the samples list.
178: * Store a reference to JSXxxxSample object in samples list.
179: * @return index to the sample in samples list.
180: */
181: public int prepareSound(int soundType, MediaContainer soundData) {
182: int index = JSSample.NULL_SAMPLE;
183: int methodType = ADD_TO_LIST;
184: if (soundData == null)
185: return JSSample.NULL_SAMPLE;
186: synchronized (samples) {
187: // for now force to just add to end of samples list
188: int samplesSize = samples.size();
189: index = samplesSize;
190: samples.ensureCapacity(index + 1);
191: boolean error = false;
192:
193: if (soundType == AudioDevice3D.CONE_SOUND) {
194: if (debugFlag)
195: debugPrintln("JavaSoundMixer.prepareSound type=CONE");
196: JSDirectionalSample dirSample = new JSDirectionalSample();
197: error = dirSample.load(soundData);
198: if (error)
199: return JSSample.NULL_SAMPLE;
200: if (methodType == SET_INTO_LIST)
201: samples.set(index, dirSample);
202: else
203: samples.add(index, dirSample);
204: /*
205: * Since no error occurred while loading, save all the
206: * characterstics for the sound in the sample.
207: */
208: dirSample.setDirtyFlags(0xFFFF);
209: dirSample.setSoundType(soundType);
210: dirSample.setSoundData(soundData);
211:
212: } else if (soundType == AudioDevice3D.POINT_SOUND) {
213: if (debugFlag)
214: debugPrintln("JavaSoundMixer.prepareSound type=POINT");
215: JSPositionalSample posSample = new JSPositionalSample();
216: error = posSample.load(soundData);
217: if (error)
218: return JSSample.NULL_SAMPLE;
219: if (methodType == SET_INTO_LIST)
220: samples.set(index, posSample);
221: else
222: samples.add(index, posSample);
223: posSample.setDirtyFlags(0xFFFF);
224: posSample.setSoundType(soundType);
225: posSample.setSoundData(soundData);
226: } else { // soundType == AudioDevice3D.BACKGROUND_SOUND
227: if (debugFlag)
228: debugPrintln("JavaSoundMixer.prepareSound type=BACKGROUND");
229: JSSample sample = null;
230: sample = new JSSample();
231: error = sample.load(soundData);
232: if (error)
233: return JSSample.NULL_SAMPLE;
234: if (methodType == SET_INTO_LIST)
235: samples.set(index, sample);
236: else
237: samples.add(index, sample);
238: sample.setDirtyFlags(0xFFFF);
239: sample.setSoundType(soundType);
240: sample.setSoundData(soundData);
241: }
242: }
243:
244: if (debugFlag) {
245: debugPrint(" prepareSound type = "
246: + soundType);
247: debugPrintln("JavaSoundMixer.prepareSound returned "
248: + index);
249: }
250: return index;
251: }
252:
253: /**
254: * Clears the fields associated with sample data for this sound.
255: * Overriden method from AudioEngine3D.
256: */
257: public void clearSound(int index) {
258: // TODO: call JSXXXX clear method
259: JSSample sample = null;
260: if ((sample = (JSSample) getSample(index)) == null)
261: return;
262: sample.clear();
263: synchronized (samples) {
264: samples.set(index, null);
265: }
266: }
267:
268: /**
269: * Save a reference to the local to virtual world coordinate space
270: * Overriden method from AudioEngine3D.
271: */
272: public void setVworldXfrm(int index, Transform3D trans) {
273: if (debugFlag)
274: debugPrintln("JavaSoundMixer: setVworldXfrm for index "
275: + index);
276: super .setVworldXfrm(index, trans);
277: if (debugFlag) {
278: double[] matrix = new double[16];
279: trans.get(matrix);
280: debugPrintln("JavaSoundMixer column-major transform ");
281: debugPrintln("JavaSoundMixer " + matrix[0] + ", "
282: + matrix[1] + ", " + matrix[2] + ", " + matrix[3]);
283: debugPrintln("JavaSoundMixer " + matrix[4] + ", "
284: + matrix[5] + ", " + matrix[6] + ", " + matrix[7]);
285: debugPrintln("JavaSoundMixer " + matrix[8] + ", "
286: + matrix[9] + ", " + matrix[10] + ", " + matrix[11]);
287: debugPrintln("JavaSoundMixer " + matrix[12] + ", "
288: + matrix[13] + ", " + matrix[14] + ", "
289: + matrix[15]);
290: }
291: JSSample sample = null;
292: if ((sample = (JSSample) getSample(index)) == null)
293: return;
294: int soundType = sample.getSoundType();
295:
296: if (soundType == AudioDevice3D.CONE_SOUND) {
297: JSDirectionalSample dirSample = null;
298: if ((dirSample = (JSDirectionalSample) getSample(index)) == null)
299: return;
300: dirSample.setXformedDirection();
301: dirSample.setXformedPosition();
302: // flag that VirtualWorld transform set
303: dirSample.setVWrldXfrmFlag(true);
304: } else if (soundType == AudioDevice3D.POINT_SOUND) {
305: JSPositionalSample posSample = null;
306: if ((posSample = (JSPositionalSample) getSample(index)) == null)
307: return;
308: posSample.setXformedPosition();
309: // flag that VirtualWorld transform set
310: posSample.setVWrldXfrmFlag(true);
311: }
312: return;
313: }
314:
315: /*
316: * Overriden method from AudioEngine3D.
317: */
318: public void setPosition(int index, Point3d position) {
319: if (debugFlag)
320: debugPrintln("JavaSoundMixer: setPosition for index "
321: + index);
322: super .setPosition(index, position);
323: JSPositionalSample posSample = null;
324: if ((posSample = (JSPositionalSample) getSample(index)) == null)
325: return;
326: int soundType = posSample.getSoundType();
327: if ((soundType == AudioDevice3D.POINT_SOUND)
328: || (soundType == AudioDevice3D.CONE_SOUND)) {
329: posSample.setXformedPosition();
330: }
331: return;
332: }
333:
334: /*
335: * Overriden method from AudioEngine3D.
336: */
337: public void setDirection(int index, Vector3d direction) {
338: if (debugFlag)
339: debugPrintln("JavaSoundMixer: setDirection for index "
340: + index);
341: super .setDirection(index, direction);
342: JSDirectionalSample dirSample = null;
343: if ((dirSample = (JSDirectionalSample) getSample(index)) == null)
344: return;
345: int soundType = dirSample.getSoundType();
346: if (soundType == AudioDevice3D.CONE_SOUND) {
347: dirSample.setXformedDirection();
348: }
349: return;
350: }
351:
352: /*
353: * Overriden method from AudioEngine3D.
354: */
355: public void setReflectionCoefficient(float coefficient) {
356: super .setReflectionCoefficient(coefficient);
357: auralParams.reverbDirty |= JSAuralParameters.REFLECTION_COEFF_CHANGED;
358: return;
359: }
360:
361: /*
362: * Overriden method from AudioEngine3D.
363: */
364: public void setReverbDelay(float reverbDelay) {
365: super .setReverbDelay(reverbDelay);
366: auralParams.reverbDirty |= JSAuralParameters.REVERB_DELAY_CHANGED;
367: return;
368: }
369:
370: /*
371: * Overriden method from AudioEngine3D.
372: */
373: public void setReverbOrder(int reverbOrder) {
374: super .setReverbOrder(reverbOrder);
375: auralParams.reverbDirty |= JSAuralParameters.REVERB_ORDER_CHANGED;
376: return;
377: }
378:
379: /*
380: * QUESTION: if this is used, for now, exclusively, to start a Background
381: * or any single sampled Sounds, why are there if-else cases to handle
382: * Point and Cone sounds??
383: *
384: * For now background sounds are not reverberated
385: *
386: * Overriden method from AudioEngine3D.
387: */
388: public int startSample(int index) {
389: // TODO: Rewrite this function
390:
391: if (debugFlag)
392: debugPrintln("JavaSoundMixer: STARTSample for index "
393: + index);
394:
395: JSSample sample = null;
396: if (((sample = (JSSample) getSample(index)) == null)
397: || thread == null)
398: return JSSample.NULL_SAMPLE;
399:
400: int soundType = sample.getSoundType();
401: boolean muted = sample.getMuteFlag();
402: if (muted) {
403: if (debugFlag)
404: debugPrintln(" MUTEd start");
405: thread.muteSample(sample);
406: if (soundType != AudioDevice3D.BACKGROUND_SOUND)
407: setFilter(index, false, Sound.NO_FILTER);
408: } else {
409: sample.render(sample.getDirtyFlags(), getView(),
410: auralParams);
411: this .scaleSampleRate(index, sample.rateRatio);
412: // filtering
413: if (soundType != AudioDevice3D.BACKGROUND_SOUND)
414: setFilter(index, sample.getFilterFlag(), sample
415: .getFilterFreq());
416: }
417:
418: boolean startSuccessful;
419: startSuccessful = thread.startSample(sample);
420:
421: sample.channel.startSample(sample.getLoopCount(), sample
422: .getGain(), 0);
423:
424: if (!startSuccessful) {
425: if (internalErrors)
426: debugPrintln("JavaSoundMixer: Internal Error startSample for index "
427: + index + " failed");
428: return JSSample.NULL_SAMPLE;
429: } else {
430: if (debugFlag)
431: debugPrintln(" startSample worked, "
432: + "returning " + startSuccessful);
433: // NOTE: Set AuralParameters AFTER sound started
434: // Setting AuralParameters before you start sound doesn't work
435: if (!muted) {
436: if (auralParams.reverbDirty > 0) {
437: if (debugFlag) {
438: debugPrintln("startSample: reverb settings are:");
439: debugPrintln(" coeff = "
440: + auralParams.reflectionCoefficient
441: + ", delay = "
442: + auralParams.reverbDelay
443: + ", order = "
444: + auralParams.reverbOrder);
445: }
446: float delayTime = auralParams.reverbDelay
447: * auralParams.rolloff;
448: calcReverb(sample);
449: }
450: // NOTE: it apprears that reverb has to be reset in
451: // JavaSound engine when sound re-started??
452: // force reset of reverb parameters when sound is started
453: setReverb(sample);
454: }
455: return index;
456: }
457: }
458:
459: /*
460: * Overriden method from AudioEngine3D.
461: */
462: public int stopSample(int index) {
463: // TODO: Rewrite this function
464:
465: if (debugFlag)
466: debugPrintln("JavaSoundMixer: STOPSample for index "
467: + index);
468: JSSample sample = null;
469: if ((sample = (JSSample) getSample(index)) == null)
470: return -1;
471:
472: int dataType = sample.getDataType();
473: int soundType = sample.getSoundType();
474:
475: boolean stopSuccessful = true;
476: stopSuccessful = thread.stopSample(sample);
477:
478: sample.channel.stopSample();
479:
480: if (!stopSuccessful) {
481: if (internalErrors)
482: debugPrintln("JavaSoundMixer: Internal Error stopSample(s) for index "
483: + index + " failed");
484: return -1;
485: } else {
486: // set fields in sample to reset for future start
487: sample.reset();
488: if (debugFlag)
489: debugPrintln("JavaSoundMixer: stopSample for index "
490: + index + " worked, returning "
491: + stopSuccessful);
492: return 0;
493: }
494: }
495:
496: /*
497: * Overriden method from AudioEngine3D.
498: */
499: public void pauseSample(int index) {
500: if (debugFlag)
501: debugPrintln("JavaSoundMixer: PAUSESample for index "
502: + index);
503: JSSample sample = null;
504: if ((sample = (JSSample) getSample(index)) == null)
505: return;
506: // check thread != null
507: thread.pauseSample(sample);
508: }
509:
510: /*
511: * Overriden method from AudioEngine3D.
512: */
513: public void unpauseSample(int index) {
514: if (debugFlag)
515: debugPrintln("JavaSoundMixer: UNPAUSESample for index "
516: + index);
517: JSSample sample = null;
518: if ((sample = (JSSample) getSample(index)) == null)
519: return;
520: thread.unpauseSample(sample);
521: }
522:
523: /*
524: * Force thread to update sample.
525: * Overriden method from AudioEngine3D.
526: */
527:
528: public void updateSample(int index) {
529: if (debugFlag)
530: debugPrintln("JavaSoundMixer: UPDATESample for index "
531: + index);
532: JSSample sample = null;
533: if (((sample = (JSSample) getSample(index)) == null)
534: || thread == null)
535: return;
536:
537: int soundType = sample.getSoundType();
538: boolean muted = sample.getMuteFlag();
539:
540: if (muted) {
541: if (soundType != AudioDevice3D.BACKGROUND_SOUND)
542: setFilter(index, false, Sound.NO_FILTER);
543: thread.muteSample(sample);
544: if (debugFlag)
545: debugPrintln(" Mute during update");
546: } else {
547: // If reverb parameters changed resend to audio device
548: if (auralParams.reverbDirty > 0) {
549: if (debugFlag) {
550: debugPrintln("updateSample: reverb settings are:");
551: debugPrintln(" coeff = "
552: + auralParams.reflectionCoefficient
553: + ", delay = " + auralParams.reverbDelay
554: + ", order = " + auralParams.reverbOrder);
555: }
556: float delayTime = auralParams.reverbDelay
557: * auralParams.rolloff;
558: calcReverb(sample);
559: }
560: // TODO: Only re-set reverb if values different
561: // For now force reset to ensure that reverb is currently correct
562: setReverb(sample); // ensure reverb is current/correct
563:
564: // TODO: For now sum left & rightGains for reverb gain
565: float reverbGain = 0.0f;
566: if (!muted && auralParams.reverbFlag) {
567: reverbGain = sample.getGain()
568: * auralParams.reflectionCoefficient;
569: }
570:
571: sample.render(sample.getDirtyFlags(), getView(),
572: auralParams);
573:
574: // filtering
575: if (soundType != AudioDevice3D.BACKGROUND_SOUND)
576: setFilter(index, sample.getFilterFlag(), sample
577: .getFilterFreq());
578: thread.setSampleGain(sample, auralParams);
579: thread.setSampleRate(sample, auralParams);
580: thread.setSampleDelay(sample, auralParams);
581: }
582: return;
583: }
584:
585: /*
586: * Overriden method from AudioEngine3D.
587: */
588: public void muteSample(int index) {
589: JSSample sample = null;
590: if ((sample = (JSSample) getSample(index)) == null)
591: return;
592:
593: if (debugFlag)
594: debugPrintln("JavaSoundMixer: muteSample");
595: sample.setMuteFlag(true);
596: thread.muteSample(sample);
597: return;
598: }
599:
600: /*
601: * Overriden method from AudioEngine3D.
602: */
603: public void unmuteSample(int index) {
604: JSSample sample = null;
605: if ((sample = (JSSample) getSample(index)) == null)
606: return;
607:
608: if (debugFlag)
609: debugPrintln("JavaSoundMixer: unmuteSample");
610: sample.setMuteFlag(false);
611:
612: // since while mute the reverb type and state was not updated...
613: // Reverb has to be recalculated when sound is unmuted .
614: auralParams.reverbDirty = 0xFFFF; // force an update of reverb params
615: sample.setDirtyFlags(0xFFFF); // heavy weight forcing of gain/delay update
616:
617: // TODO: force an update of ALL parameters that could have changed
618: // while muting disabled...
619:
620: thread.unmuteSample(sample);
621: return;
622: }
623:
624: /*
625: * Overriden method from AudioEngine3D.
626: */
627: public long getSampleDuration(int index) {
628: JSSample sample = null;
629: if ((sample = (JSSample) getSample(index)) == null)
630: return Sample.DURATION_UNKNOWN;
631: long duration;
632:
633: if (sample != null)
634: duration = sample.getDuration();
635: else
636: duration = Sample.DURATION_UNKNOWN;
637: if (debugFlag)
638: debugPrintln(" return duration " + duration);
639: return duration;
640: }
641:
642: /*
643: * Overriden method from AudioEngine3D.
644: */
645: public int getNumberOfChannelsUsed(int index) {
646: /*
647: * Calls same method with different signature containing the
648: * sample's mute flag passed as the 2nd parameter.
649: */
650: JSSample sample = null;
651: if ((sample = (JSSample) getSample(index)) == null)
652: return 0;
653: else
654: return getNumberOfChannelsUsed(index, sample.getMuteFlag());
655: }
656:
657: /**
658: * Overriden method from AudioEngine3D.
659: */
660: public int getNumberOfChannelsUsed(int index, boolean muted) {
661: /*
662: * The JavaSoundMixer implementation uses THREE channels to render
663: * the stereo image of each Point and Cone Sounds:
664: * Two for rendering the right and left portions of the rendered
665: * spatialized sound image - panned hard right or left respectively.
666: * This implementation uses one channel to render Background sounds
667: * whether the sample is mono or stereo.
668: *
669: * TODO: When muted is implemented, that flag should be check
670: * so that zero is returned.
671: */
672: JSSample sample = null;
673: if ((sample = (JSSample) getSample(index)) == null)
674: return 0;
675:
676: int soundType = sample.getSoundType();
677: int dataType = sample.getDataType();
678:
679: // TODO: for now positional Midi sound used only 1 sample
680: if (dataType == JSSample.STREAMING_MIDI_DATA
681: || dataType == JSSample.BUFFERED_MIDI_DATA)
682: return 1;
683:
684: if (soundType == BACKGROUND_SOUND)
685: return 1;
686: else
687: // for Point and Cone sounds
688: return 3;
689: }
690:
691: /*
692: * Overriden method from AudioEngine3D.
693: */
694: public long getStartTime(int index) {
695: JSSample sample = null;
696: if ((sample = (JSSample) getSample(index)) == null)
697: return 0L;
698: if (sample.channel == null)
699: return 0L;
700: return (long) sample.channel.startTime;
701: }
702:
703: /*
704: * Methods called during rendering
705: */
706: void scaleSampleRate(int index, float scaleFactor) {
707: if (debugFlag)
708: debugPrintln("JavaSoundMixer: scaleSampleRate index "
709: + index + ", scale factor = " + scaleFactor);
710: JSSample sample = null;
711: if ((sample = (JSSample) getSample(index)) == null
712: || thread == null)
713: return;
714: int dataType = sample.getDataType();
715: if (debugFlag)
716: debugPrintln(" scaleSampleRate.dataType = " + dataType
717: + "using sample " + sample + " from samples["
718: + index + "]");
719: int soundType = sample.getSoundType();
720:
721: if (dataType == JSSample.STREAMING_AUDIO_DATA
722: || dataType == JSSample.BUFFERED_AUDIO_DATA) {
723: thread.setSampleRate(sample, scaleFactor);
724: /**********
725: // TODO:
726: if (soundType != AudioDevice3D.BACKGROUND_SOUND) {
727: thread.setSampleRate( ((JSPositionalSample)sample).getSecondIndex(),
728: scaleFactor);
729: thread.setSampleRate(((JSPositionalSample)sample).getReverbIndex(),
730: scaleFactor);
731: }
732: **********/
733: } else if (dataType == JSSample.STREAMING_MIDI_DATA
734: || dataType == JSSample.BUFFERED_MIDI_DATA) {
735: thread.setSampleRate(sample, scaleFactor);
736: /**********
737: if (soundType != AudioDevice3D.BACKGROUND_SOUND) {
738: thread.setSampleRate(((JSPositionalSample)sample).getSecondIndex(),
739: scaleFactor);
740: thread.setSampleRate(((JSPositionalSample)sample).getReverbIndex(),
741: scaleFactor);
742: }
743: **********/
744: } else {
745: if (internalErrors)
746: debugPrintln("JavaSoundMixer: Internal Error scaleSampleRate dataType "
747: + dataType + " invalid");
748: }
749: }
750:
751: /*
752: * Methods called during rendering
753: */
754: void calcReverb(JSSample sample) {
755: /*
756: * Java Sound reverb parameters are a subset of Java 3D parameters
757: */
758: int dataType = sample.getDataType();
759: int soundType = sample.getSoundType();
760: float decay = auralParams.decayTime;
761: float delay = auralParams.reverbDelay * auralParams.rolloff;
762: float reflection = auralParams.reflectionCoefficient;
763: int order = auralParams.reverbOrder;
764: /*
765: * Remember Coeff change is choosen over Order change if BOTH made
766: * otherwise the last one changed take precidence.
767: */
768: if (auralParams.reflectionCoefficient == 0.0f
769: || auralParams.reverbCoefficient == 0.0f)
770: auralParams.reverbFlag = false;
771: else {
772: auralParams.reverbFlag = true;
773: if (order > 0) {
774: // clamp reverb decay time to order*delay
775: float clampedTime = order * delay;
776: if (clampedTime < decay)
777: decay = clampedTime;
778: }
779: if (delay < 100.0f) {
780: // "small" reverberant space
781: if (decay <= 1500.0f)
782: auralParams.reverbType = 2;
783: else
784: auralParams.reverbType = 4;
785: } else if (delay < 500.0f) {
786: // "medium" reverberant space
787: if (decay <= 1500.0f)
788: auralParams.reverbType = 3;
789: else
790: auralParams.reverbType = 6;
791: } else { // delay >= 500.0f
792: // "large" reverberant space
793: if (decay <= 1500.0f)
794: auralParams.reverbType = 6;
795: else
796: auralParams.reverbType = 5;
797: }
798: }
799:
800: if (debugFlag)
801: debugPrintln("JavaSoundMixer: setReverb for " + sample
802: + ", type = " + auralParams.reverbType
803: + ", flag = " + auralParams.reverbFlag);
804:
805: auralParams.reverbDirty = 0; // clear the attribute reverb dirty flags
806: }
807:
808: /*
809: * Interal method for setting reverb parameters called during rendering.
810: * This not called by SoundScheduler.
811: */
812: void setReverb(JSSample sample) {
813: /*
814: * Only third sample of multisample sounds has reverb parameters set.
815: * For now, only positional and directional sounds are reverberated.
816: */
817: int soundType = sample.getSoundType();
818: int dataType = sample.getDataType();
819:
820: // QUESTION: Should reverb be applied to background sounds?
821: if ((soundType == AudioDevice3D.CONE_SOUND)
822: || (soundType == AudioDevice3D.POINT_SOUND)) {
823: if (debugFlag)
824: debugPrintln("setReverb called with type, on = "
825: + auralParams.reverbType + ", "
826: + auralParams.reverbFlag);
827: if (sample == null)
828: return;
829: JSPositionalSample posSample = (JSPositionalSample) sample;
830: if (posSample.channel == null)
831: return;
832:
833: /**********
834: // NOTE: no support for reverb channel yet...
835: int reverbIndex = posSample.getReverbIndex();
836: **********/
837: if (dataType == JSSample.STREAMING_AUDIO_DATA) {
838: JSStream stream = (JSStream) posSample.channel;
839: stream.setSampleReverb(auralParams.reverbType,
840: auralParams.reverbFlag);
841: } else if (dataType == JSSample.BUFFERED_AUDIO_DATA) {
842: JSClip clip = (JSClip) posSample.channel;
843: clip.setSampleReverb(auralParams.reverbType,
844: auralParams.reverbFlag);
845: }
846: /**********
847: // TODO:
848: else if (dataType == JSSample.STREAMING_MIDI_DATA ||
849: dataType == JSSample.BUFFERED_MIDI_DATA) {
850: JSMidi.setSampleReverb(reverbIndex,
851: auralParams.reverbType, auralParams.reverbFlag);
852: }
853: **********/
854: else {
855: if (internalErrors)
856: debugPrintln("JavaSoundMixer: Internal Error setReverb "
857: + "dataType " + dataType + " invalid");
858: }
859: }
860: }
861:
862: // TEMPORARY: Override of method due to bug in Java Sound
863: public void setLoop(int index, int count) {
864: JSSample sample = null;
865: if ((sample = (JSSample) getSample(index)) == null)
866: return;
867: int dataType = sample.getDataType();
868:
869: // WORKAROUND:
870: // Bug in Java Sound engine hangs when INFINITE_LOOP count
871: // for Audio Wave data. Leave count unchanged for Midi data.
872: if (dataType == JSSample.STREAMING_AUDIO_DATA
873: || dataType == JSSample.BUFFERED_AUDIO_DATA) {
874: if (count == Sound.INFINITE_LOOPS) {
875: // LoopCount of 'loop Infinitely' forced to largest positive int
876: count = 0x7FFFFFF;
877: }
878: }
879: super .setLoop(index, count);
880: return;
881: }
882:
883: // Perform device specific filtering
884: // Assumes that this is called for positional and directional sounds
885: // not background sounds, so there are at lease two samples assigned
886: // per sound.
887: // TODO: remove assumption from method
888: void setFilter(int index, boolean filterFlag, float filterFreq) {
889: JSPositionalSample posSample = null;
890: if ((posSample = (JSPositionalSample) getSample(index)) == null)
891: return;
892: if (posSample.channel == null)
893: return;
894: int dataType = posSample.getDataType();
895:
896: // Filtering can NOT be performed on MIDI Songs
897: if (dataType == JSSample.STREAMING_MIDI_DATA
898: || dataType == JSSample.BUFFERED_MIDI_DATA) {
899: return;
900: }
901:
902: /****
903: // TODO: multiple clips per channel
904: int secondIndex = posSample.getSecondIndex();
905: *****/
906: if (dataType == JSSample.BUFFERED_AUDIO_DATA) {
907: JSClip clip = (JSClip) posSample.channel;
908: clip.setSampleFiltering(filterFlag, filterFreq);
909: /*****
910: JSClip.setSampleFiltering(econdIndex, filterFlag, filterFreq);
911: ******/
912: } else { // dataType == JSSample.STREAMING_AUDIO_DATA
913: JSStream stream = (JSStream) posSample.channel;
914: stream.setSampleFiltering(filterFlag, filterFreq);
915: /*****
916: JSStream.setSampleFiltering(secondIndex, ilterFlag, filterFreq);
917: ******/
918: }
919: // QUESTION: should reverb channel be filtered???
920:
921: if (debugFlag) {
922: debugPrintln("JavaSoundMixer:setFilter "
923: + "of non-backgroundSound by (" + filterFlag + ", "
924: + filterFreq + ")");
925: }
926: }
927:
928: //
929: // Set overall gain for device
930: // @since Java 3D 1.3
931: //
932: public void setGain(float scaleFactor) {
933: float oldDeviceGain = deviceGain;
934: float gainFactor = scaleFactor / oldDeviceGain;
935: // TODO: for each sample, change gain by gainFactor
936: deviceGain = scaleFactor; // set given scalefactor as new device gain
937: return;
938: }
939:
940: /*
941: * Set sample specific sample rate scale factor gain
942: * @since Java 3D 1.3
943: */
944: public void setRateScaleFactor(int index, float rateScaleFactor) {
945: JSSample sample = null;
946: if ((sample = (JSSample) getSample(index)) == null)
947: return;
948: sample.setRateScaleFactor(rateScaleFactor);
949: this .scaleSampleRate(index, rateScaleFactor);
950: }
951:
952: /**
953: * Pauses audio device engine without closing the device and associated
954: * threads.
955: * Causes all cached sounds to be paused and all streaming sounds to be
956: * stopped.
957: */
958: public void pause() {
959: pause = PAUSE_PENDING;
960: // TODO: pause all sounds
961: return;
962: }
963:
964: /**
965: * Resumes audio device engine (if previously paused) without reinitializing * the device.
966: * Causes all paused cached sounds to be resumed and all streaming sounds
967: * restarted.
968: */
969: public void resume() {
970: pause = RESUME_PENDING;
971: // TODO: unpause all sounds
972: return;
973: }
974: }
|