001: /*
002: * $RCSfile: SoundSchedulerAtom.java,v $
003: *
004: * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
006: *
007: * This code is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License version 2 only, as
009: * published by the Free Software Foundation. Sun designates this
010: * particular file as subject to the "Classpath" exception as provided
011: * by Sun in the LICENSE file that accompanied this code.
012: *
013: * This code is distributed in the hope that it will be useful, but WITHOUT
014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: * version 2 for more details (a copy is included in the LICENSE file that
017: * accompanied this code).
018: *
019: * You should have received a copy of the GNU General Public License version
020: * 2 along with this work; if not, write to the Free Software Foundation,
021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
024: * CA 95054 USA or visit www.sun.com if you need additional information or
025: * have any questions.
026: *
027: * $Revision: 1.8 $
028: * $Date: 2008/02/28 20:17:30 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import java.util.ArrayList;
035:
036: /**
037: * A SoundSchedulerAtom is the smallest object representing a Sound within
038: * SoundScheduler. This class contains View-Depedent fields. Some of these
039: * fields may appear to over lap fields in the Sound Node classes, but
040: * remember that the Sound Node fields are universal, user-defined fields
041: * and do not take into account specific Audio Device view-dependent
042: * conditions.
043: */
044:
045: class SoundSchedulerAtom extends Object {
046:
047: /**
048: * The mirror sound node component of this sound scheduler atom
049: */
050: SoundRetained sound = null;
051:
052: /**
053: * MediaContainer currently loaded for this atom
054: */
055: MediaContainer soundData = null;
056:
057: // Maintain continuously playing silent sound sources.
058: long startTime = 0;
059: long endTime = 0;
060:
061: long sampleLength = 0;
062: long loopStartOffset = 0; // for most this will be 0
063: long loopLength = 0; // for most this is end sample - sampleLength
064: long attackLength = 0; // portion of sample before loop section
065: long releaseLength = 0; // portion of sample after loop section
066:
067: int loadStatus = SoundRetained.LOAD_NULL;
068: boolean playing = false;
069: int numberChannels = 0;
070:
071: /**
072: * Is this sound in an active scheduling region
073: */
074: boolean activated = false;
075:
076: /**
077: * Switch for turning sound on or off while the sound is "active"
078: */
079: static final int OFF = 0;
080: static final int ON = 1;
081: static final int PENDING_ON = 2;
082: static final int PENDING_OFF = 3;
083: int enabled = OFF;
084:
085: /**
086: * Switch for muting and unmuting sound while it is playing
087: */
088: static final int UNMUTED = 0;
089: static final int MUTED = 1;
090: static final int PENDING_UNMUTE = 2;
091: static final int PENDING_MUTE = 3;
092: int muted = UNMUTED;
093:
094: /**
095: * Switch for pausing and unpausing sound while it is playing
096: */
097: static final int UNPAUSED = 0; // or resumed
098: static final int PAUSED = 1;
099: static final int PENDING_UNPAUSE = 2; // or pending resume
100: static final int PENDING_PAUSE = 3;
101: int paused = UNPAUSED;
102:
103: /**
104: * Pending action for this sound determined by the SoundScheduler
105: */
106: static final int DO_NOTHING = 0;
107: static final int LEAVE_OFF = 1;
108: static final int LEAVE_SILENT = 2;
109: static final int LEAVE_AUDIBLE = 3;
110: static final int LEAVE_PAUSED = 4;
111:
112: static final int RESTART_AUDIBLE = 5;
113: static final int START_AUDIBLE = 6;
114: static final int RESTART_SILENT = 7;
115: static final int START_SILENT = 8;
116:
117: static final int MAKE_AUDIBLE = 11;
118: static final int MAKE_SILENT = 12;
119: static final int PAUSE_AUDIBLE = 13;
120: static final int PAUSE_SILENT = 14;
121: static final int RESUME_AUDIBLE = 15;
122: static final int RESUME_SILENT = 16;
123: static final int TURN_OFF = 17;
124: static final int UPDATE = 18;
125: static final int COMPLETE = 19;
126: int schedulingAction = DO_NOTHING;
127:
128: /**
129: * This status flag is used for sound scheduling
130: */
131: static final int SOUND_OFF = 0; // The sound is not playing
132: static final int SOUND_AUDIBLE = 1; // The sound is potentially audible
133: static final int SOUND_SILENT = 2; // The sound is playing silently
134: static final int SOUND_PAUSED = 3; // The sound is playing silently
135: static final int SOUND_COMPLETE = 4; // The sound is finished playing
136: int status = SOUND_OFF;
137:
138: // Sound atoms have two dirty flags: attribsDirty for sound node fields
139: // and stateDirty for changes to sound state not reflected by sound fields.
140: // When the field/parameter associated with the dirty bit has been:
141: // passed to all SoundSchedulers to update sound rendering or 'run' state
142: // the bit for that field is cleared by the SoundStructure thread.
143:
144: /**
145: * attribsDirty bit field
146: * This bitmask is set when sound node attribute is changed by the user.
147: */
148: int attribsDirty = 0x0000;
149:
150: /**
151: * stateDirty bit field
152: * This bitmask is set when scene graph state is changed.
153: */
154: int stateDirty = 0x0000;
155:
156: // Load Sound Data Status maintained in SoundRetained class
157:
158: /**
159: * Identifiers of sample associated with sound source
160: */
161: int sampleId = SoundRetained.NULL_SOUND;
162:
163: /**
164: * reference to Sound Scheduler this atom is associated with
165: */
166: SoundScheduler soundScheduler = null;
167:
168: /**
169: * Calculate absolute time at which sample completes
170: * Checks playing flag denoting if sound is started already or not:
171: * false - calcalutes endTime in relation to startTime
172: * true - re-calculates endTime based on current position in
173: * loop portion of sample plus release length
174: */
175: synchronized void calculateEndTime() {
176: SoundRetained sgSound = sound.sgSound;
177: int loops = sgSound.loopCount;
178: if (debugFlag)
179: debugPrint("calculateEndTime: loop count = " + loops);
180: // test lengths for <= 0; this includes DURATION_UNKNOWN
181: if ((sampleLength <= 0 || loopLength <= 0 || loops < 0)
182: // QUESTION: removed? but what was this trying to avoid
183: // changing endTime when that is already set?
184: // but what happens when user changes LoopCount AFTER
185: // sound is started - should be able to do this
186: // && (enabled == OFF || enabled == PENDING_OFF)
187: ) {
188: endTime = -1;
189: if (debugFlag)
190: debugPrint("calculateEndTime: set to -1");
191: } else {
192: // QUESTION: if "&& playing" is in above test; won't we have to test for
193: // length unknown and loop = -1??
194: if (playing && (startTime > 0)) {
195: endTime = startTime + attackLength
196: + (loopLength * (loops + 1)) + releaseLength;
197: if (debugFlag)
198: debugPrint("calculateEndTime: isPlaying so = "
199: + endTime);
200: } else {
201: // Called when release flag is true
202: // Determine where within the loop portion sample the
203: // sound is currently playing, then set endTime to
204: // play remaining portion of loop portion plus the
205: // release portion.
206: long currentTime = J3dClock.currentTimeMillis();
207: endTime = currentTime
208: + ((loopLength - ((currentTime - startTime - attackLength) % loopLength)) + releaseLength);
209: if (debugFlag)
210: debugPrint("calculateEndTime: NOT Playing so = "
211: + endTime);
212: }
213: }
214: }
215:
216: void enable(boolean enabled) {
217: if (enabled) {
218: setEnableState(PENDING_ON);
219: if (debugFlag)
220: debugPrint(" enableSound calls soundAtom " + this
221: + " setEnableState PENDING_ON");
222: } else {
223: setEnableState(PENDING_OFF);
224: if (debugFlag)
225: debugPrint(" enableSound calls soundAtom " + this
226: + " setEnableState PENDING_OFF");
227: }
228: }
229:
230: void mute(boolean muted) {
231: if (muted) {
232: setMuteState(PENDING_MUTE);
233: if (debugFlag)
234: debugPrint(" muteSound() calls soundAtom " + this
235: + " setMuteState PENDING_ON");
236: } else {
237: setMuteState(PENDING_UNMUTE);
238: if (debugFlag)
239: debugPrint(" muteSound() calls soundAtom " + this
240: + " setMuteState PENDING_UNMUTE");
241: }
242: }
243:
244: void pause(boolean paused) {
245: if (paused) {
246: setPauseState(PENDING_PAUSE);
247: if (debugFlag)
248: debugPrint(this
249: + ".pause calls setPauseState(PENDING_PAUSE)");
250: } else {
251: setPauseState(PENDING_UNPAUSE);
252: if (debugFlag)
253: debugPrint(this
254: + ".pause calls setPauseState(PENDING_UNPAUSE)");
255: }
256: }
257:
258: // XXXX: remove this
259: // just set the state after debug no longer needed
260: void setEnableState(int state) {
261: enabled = state;
262: switch (state) {
263: case PENDING_ON:
264: if (debugFlag)
265: debugPrint("set enabled to PENDING_ON");
266: break;
267: case ON:
268: if (debugFlag)
269: debugPrint("set enabled to ON");
270: break;
271: case PENDING_OFF:
272: if (debugFlag)
273: debugPrint("set enabled to PENDING_OFF");
274: break;
275: case OFF:
276: if (debugFlag)
277: debugPrint("set enabled to OFF");
278: break;
279: default:
280: if (debugFlag)
281: debugPrint("state = " + state);
282: break;
283: }
284: }
285:
286: // XXXX: remove this
287: // just set the state after debug no longer needed
288: void setMuteState(int state) {
289: muted = state;
290: switch (state) {
291: case PENDING_MUTE:
292: if (debugFlag)
293: debugPrint("set mute to PENDING_MUTE");
294: break;
295: case MUTED:
296: if (debugFlag)
297: debugPrint("set mute to MUTE");
298: break;
299: case PENDING_UNMUTE:
300: if (debugFlag)
301: debugPrint("set mute to PENDING_UNMUTE");
302: break;
303: case UNMUTED:
304: if (debugFlag)
305: debugPrint("set mute to UNMUTE");
306: break;
307: default:
308: if (debugFlag)
309: debugPrint("state = " + state);
310: break;
311: }
312: }
313:
314: // XXXX: remove this
315: // just set the state after debug no longer needed
316: void setPauseState(int state) {
317: paused = state;
318: switch (state) {
319: case PENDING_PAUSE:
320: if (debugFlag)
321: debugPrint("set pause to PENDING_PAUSE");
322: break;
323: case PAUSED:
324: if (debugFlag)
325: debugPrint("set pause to PAUSE");
326: break;
327: case PENDING_UNPAUSE:
328: if (debugFlag)
329: debugPrint("set pause to PENDING_UNPAUSE");
330: break;
331: case UNPAUSED:
332: if (debugFlag)
333: debugPrint("set pause to UNPAUSE");
334: break;
335: default:
336: if (debugFlag)
337: debugPrint("state = " + state);
338: break;
339: }
340: }
341:
342: /**
343: * calcActiveSchedAction()
344: * Calculate Sound Scheduler Action for Active sound (it's region
345: * intersects the viewPlatform).
346: *
347: * A big switch testing various SoundRetained fields to determine
348: * what SoundScheduler action to perform when sound is Active
349: * set sound active flag true
350: * switch on enable value, to set pending scheduling action
351: * depending on continuous and release flags and sound status
352: */
353: synchronized int calcActiveSchedAction() {
354: SoundRetained sgSound = sound.sgSound;
355: int action = DO_NOTHING;
356: activated = true;
357: switch (enabled) {
358: case PENDING_ON:
359: setEnableState(ON);
360: if (debugFlag)
361: debugPrint(" calcActiveSchedAction: PENDING_ON");
362: if (status == SOUND_OFF || status == SOUND_PAUSED)
363: action = START_AUDIBLE;
364: else
365: action = RESTART_AUDIBLE;
366: break;
367: case ON:
368: if (debugFlag)
369: debugPrint(" calcActiveSchedAction: ON");
370: if (status == SOUND_OFF)
371: // should NOT see this, but if we do...
372: action = START_AUDIBLE;
373: else if (status == SOUND_SILENT)
374: action = MAKE_AUDIBLE;
375: else
376: // status == SOUND_AUDIBLE
377: action = LEAVE_AUDIBLE;
378: break;
379: case PENDING_OFF:
380: setEnableState(OFF);
381: if (debugFlag)
382: debugPrint("enable = " + enabled + "enabled set to OFF");
383: // fail thru
384: case OFF:
385: // QUESTION: Why would enable status ever be OFF yet
386: // status SOUND_AUDIBLE or _SILENT?
387: if (status == SOUND_AUDIBLE) {
388: if (sgSound.release) {
389: if (debugFlag)
390: debugPrint("enable = " + enabled
391: + ", AUDIBLE, released, "
392: + "action <- LEAVE_AUDIBLE");
393: if (enabled == PENDING_OFF) {
394: // re-calculate EndTime
395: calculateEndTime();
396: }
397: action = LEAVE_AUDIBLE;
398: } else {
399: if (debugFlag)
400: debugPrint("enable = " + enabled
401: + ", AUDIBLE, not released, "
402: + "action <- TURN_OFF");
403: action = TURN_OFF;
404: }
405: } else if (status == SOUND_SILENT) {
406: if (sgSound.release) {
407: if (debugFlag)
408: debugPrint("enable = " + enabled
409: + ", SILENT, released, "
410: + "action <- MAKE_AUDIBLE");
411: // re-calculate EndTime
412: calculateEndTime();
413: action = MAKE_AUDIBLE;
414: } else {
415: if (debugFlag)
416: debugPrint("enable = " + enabled
417: + ", SILENT, not released, "
418: + "action <- TURN_OFF");
419: action = TURN_OFF;
420: }
421: } else { // status == SOUND_OFF
422: action = LEAVE_OFF;
423: }
424: break;
425: } // switch on enabled flag
426:
427: // if sounds pause state is PENDING_PAUSE modify action to perform.
428: if (paused == PENDING_PAUSE) {
429: // if this pause state is set to PAUSE then assume the sound is
430: // already paused, so any incoming action that leave the state
431: // as it already is, leaves the sound paused.
432: if (debugFlag)
433: debugPrint(" PENDING_PAUSE");
434: switch (action) {
435: case MAKE_AUDIBLE:
436: case LEAVE_AUDIBLE:
437: case RESUME_AUDIBLE:
438: action = PAUSE_AUDIBLE;
439: break;
440: case MAKE_SILENT:
441: case LEAVE_SILENT:
442: case RESUME_SILENT:
443: action = PAUSE_SILENT;
444: break;
445: default:
446: // don't change action for any other cases
447: break;
448: }
449: }
450: // if sounds pause state is PENDING_UNPAUSE modify action
451: else if (paused == PENDING_UNPAUSE) {
452: debugPrint(" PENDING_UNPAUSE");
453: switch (action) {
454: // When restart (audible or silent) pause flag is checked and
455: // explicitly set in SoundScheduler
456: case MAKE_AUDIBLE:
457: case LEAVE_AUDIBLE:
458: case PAUSE_AUDIBLE:
459: action = RESUME_AUDIBLE;
460: break;
461: case MAKE_SILENT:
462: case LEAVE_SILENT:
463: case PAUSE_SILENT:
464: action = RESUME_SILENT;
465: break;
466: default:
467: // don't change action for any other cases
468: break;
469: }
470: }
471: return (action);
472: } // end of calcActiveSchedAction
473:
474: /**
475: * calcInactiveSchedAction()
476: * Calculate Sound Scheduler action for Inactive sound
477: *
478: * A big switch testing various SoundRetained fields to determine
479: * what SoundScheduler action to perform when sound is inactive.
480: * set sound active flag false
481: * switch on enable value, to set pending scheduling action
482: * depending on continuous and release flags and sound status
483: */
484: synchronized int calcInactiveSchedAction() {
485: int action = DO_NOTHING;
486: SoundRetained sgSound = sound.sgSound;
487:
488: // Sound is Inactive
489: // Generally, sound is OFF unless continuous flag true
490: // then sound is silently playing if on.
491: activated = false;
492:
493: switch (enabled) {
494: case PENDING_ON:
495: if (debugFlag)
496: debugPrint(" calcInactiveSchedAction: PENDING_ON ");
497: setEnableState(ON);
498: if (sgSound.continuous) {
499: if (status == SOUND_OFF)
500: action = START_SILENT;
501: else
502: // status == SOUND_AUDIBLE or SOUND_SILENT
503: action = RESTART_SILENT;
504: } else { // sound is not continuous
505: if (status == SOUND_OFF)
506: action = LEAVE_OFF;
507: else
508: // status == SOUND_SILENT || SOUND_AUDIBLE
509: action = TURN_OFF;
510: }
511: break;
512: case ON:
513: if (debugFlag)
514: debugPrint(" calcInactiveSchedActio: ON ");
515: if (sgSound.continuous) {
516: if (status == SOUND_AUDIBLE)
517: action = MAKE_SILENT;
518: else if (status == SOUND_OFF)
519: action = START_SILENT;
520: else
521: // status == SOUND_SILENT
522: action = LEAVE_SILENT;
523: } else { // sound is not continuous
524: // nothing to do if already off
525: if (status == SOUND_OFF)
526: action = LEAVE_OFF;
527: else
528: // status == SOUND_SILENT or SOUND_AUDIBLE
529: action = TURN_OFF;
530: }
531: break;
532: case PENDING_OFF:
533: setEnableState(OFF);
534: if (debugFlag)
535: debugPrint("Enable = " + enabled + "enabled set to OFF");
536: // fall thru
537:
538: case OFF:
539: if (sgSound.release && sgSound.continuous) {
540: if (enabled == PENDING_OFF) {
541: // re-calculate EndTime
542: calculateEndTime();
543: }
544: if (status == SOUND_AUDIBLE) {
545: if (debugFlag)
546: debugPrint("Enable = " + enabled
547: + ", AUDIBLE, released & continuous - "
548: + "action <- MAKE_SILENT");
549: action = MAKE_SILENT;
550: } else if (status == SOUND_SILENT) {
551: if (debugFlag)
552: debugPrint("Enable = " + enabled
553: + ", SILENT, released & continuous - "
554: + "action <- TURN_OFF");
555: action = LEAVE_SILENT;
556: } else {
557: if (debugFlag)
558: debugPrint("Enable = " + enabled
559: + ", already OFF, action <- LEAVE_OFF");
560: action = LEAVE_OFF;
561: }
562: } else { // continuous and release flag not both true
563: if (status == SOUND_OFF) {
564: if (debugFlag)
565: debugPrint("Enable = " + enabled
566: + ", already OFF, action <- LEAVE_OFF");
567: action = LEAVE_OFF;
568: } else {
569: if (debugFlag)
570: debugPrint("Enable = "
571: + enabled
572: + ", not already OFF, action <- TURN_OFF");
573: action = TURN_OFF;
574: }
575: }
576: break;
577: default:
578: break;
579: } // switch
580:
581: // if sounds pause state is PENDING_PAUSE modify action to perform.
582: if (paused == PENDING_PAUSE) {
583: // if this pause state is set to PAUSE then assume the sound is
584: // already paused, so any incoming action that leave the state
585: // as it already is, leaves the sound paused.
586: switch (action) {
587: case MAKE_SILENT:
588: case LEAVE_SILENT:
589: case RESUME_SILENT:
590: action = PAUSE_SILENT;
591: break;
592: default:
593: // don't change action for any other cases
594: break;
595: }
596: }
597: // if sounds pause state is PENDING_UNPAUSE modify action
598: else if (paused == PENDING_UNPAUSE) {
599: switch (action) {
600: case LEAVE_SILENT:
601: action = RESUME_SILENT;
602: break;
603: default:
604: // don't change action for any other cases
605: break;
606: }
607: }
608: return (action);
609: } // end of calcInactiveSchedAction
610:
611: // XXXX: isPLaying
612: // XXXX: setLoadingState
613:
614: // Debug print mechanism for Sound nodes
615: static final boolean debugFlag = false;
616: static final boolean internalErrors = false;
617:
618: void debugPrint(String message) {
619: if (debugFlag) {
620: System.err.println(message);
621: }
622: }
623:
624: /**
625: * Set bit(s) in soundDirty field
626: * @param binary flag denotes bits to set ON
627: */
628: void setAttribsDirtyFlag(int bitFlag) {
629: attribsDirty |= bitFlag;
630: if (debugFlag)
631: debugPrint("setAttribsDirtyFlag = " + bitFlag);
632: return;
633: }
634:
635: void setStateDirtyFlag(int bitFlag) {
636: stateDirty |= bitFlag;
637: if (debugFlag)
638: debugPrint("setStateDirtyFlag = " + bitFlag);
639: return;
640: }
641:
642: /**
643: * Clear sound's dirty flag bit value.
644: * @param binary flag denotes bits to set OFF
645: */
646: void clearAttribsDirtyFlag(int bitFlag) {
647: if (debugFlag)
648: debugPrint("clearAttribsDirtyFlag = " + bitFlag);
649: attribsDirty &= ~bitFlag;
650: return;
651: }
652:
653: void clearAttribsDirtyFlag() {
654: // clear all bits
655: if (debugFlag)
656: debugPrint("clearAttribsDirtyFlag = ALL");
657: attribsDirty = 0x0;
658: return;
659: }
660:
661: void clearStateDirtyFlag(int bitFlag) {
662: if (debugFlag)
663: debugPrint("clearStateDirtyFlag = " + bitFlag);
664: stateDirty &= ~bitFlag;
665: return;
666: }
667:
668: void clearStateDirtyFlag() {
669: if (debugFlag)
670: debugPrint("clearStateDirtyFlag = ALL");
671: stateDirty = 0x0;
672: return;
673: }
674:
675: /**
676: * Test sound's dirty flag bit(s)
677: * @param field denotes which bitmask to set into
678: * @param binary flag denotes bits to set Test
679: * @return true if bit(s) in bitFlag are set dirty (on)
680: */
681: boolean testDirtyFlag(int field, int bitFlag) {
682: if ((field & bitFlag) > 0)
683: return true;
684: else
685: return false;
686: }
687:
688: /**
689: * Test sound's dirty flags for ANY bits on
690: * @return true if any bit in bitFlag is flipped on
691: */
692: boolean testDirtyFlags() {
693: if ((attribsDirty & 0xFFFF) > 0)
694: return true;
695: else if ((stateDirty & 0xFFFF) > 0)
696: return true;
697: else
698: return false;
699: }
700:
701: }
|