diff options
Diffstat (limited to 'src/javax/media/j3d/SoundSchedulerAtom.java')
-rw-r--r-- | src/javax/media/j3d/SoundSchedulerAtom.java | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/src/javax/media/j3d/SoundSchedulerAtom.java b/src/javax/media/j3d/SoundSchedulerAtom.java new file mode 100644 index 0000000..3be6637 --- /dev/null +++ b/src/javax/media/j3d/SoundSchedulerAtom.java @@ -0,0 +1,707 @@ +/* + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * A SoundSchedulerAtom is the smallest object representing a Sound within + * SoundScheduler. This class contains View-Depedent fields. Some of these + * fields may appear to over lap fields in the Sound Node classes, but + * remember that the Sound Node fields are universal, user-defined fields + * and do not take into account specific Audio Device view-dependent + * conditions. + */ + +class SoundSchedulerAtom extends Object { + + /** + * The mirror sound node component of this sound scheduler atom + */ + SoundRetained sound = null; + + /** + * MediaContainer currently loaded for this atom + */ + MediaContainer soundData = null; + + // Maintain continuously playing silent sound sources. + long startTime = 0; + long endTime = 0; + + long sampleLength = 0; + long loopStartOffset = 0; // for most this will be 0 + long loopLength = 0; // for most this is end sample - sampleLength + long attackLength = 0; // portion of sample before loop section + long releaseLength = 0; // portion of sample after loop section + + int loadStatus = SoundRetained.LOAD_NULL; + boolean playing = false; + int numberChannels = 0; + + /** + * Is this sound in an active scheduling region + */ + boolean activated = false; + + /** + * Switch for turning sound on or off while the sound is "active" + */ + static final int OFF = 0; + static final int ON = 1; + static final int PENDING_ON = 2; + static final int PENDING_OFF = 3; + int enabled = OFF; + + /** + * Switch for muting and unmuting sound while it is playing + */ + static final int UNMUTED = 0; + static final int MUTED = 1; + static final int PENDING_UNMUTE = 2; + static final int PENDING_MUTE = 3; + int muted = UNMUTED; + + /** + * Switch for pausing and unpausing sound while it is playing + */ + static final int UNPAUSED = 0; // or resumed + static final int PAUSED = 1; + static final int PENDING_UNPAUSE = 2; // or pending resume + static final int PENDING_PAUSE = 3; + int paused = UNPAUSED; + + + /** + * Pending action for this sound determined by the SoundScheduler + */ + static final int DO_NOTHING = 0; + static final int LEAVE_OFF = 1; + static final int LEAVE_SILENT = 2; + static final int LEAVE_AUDIBLE = 3; + static final int LEAVE_PAUSED = 4; + + static final int RESTART_AUDIBLE = 5; + static final int START_AUDIBLE = 6; + static final int RESTART_SILENT = 7; + static final int START_SILENT = 8; + + static final int MAKE_AUDIBLE = 11; + static final int MAKE_SILENT = 12; + static final int PAUSE_AUDIBLE = 13; + static final int PAUSE_SILENT = 14; + static final int RESUME_AUDIBLE = 15; + static final int RESUME_SILENT = 16; + static final int TURN_OFF = 17; + static final int UPDATE = 18; + static final int COMPLETE = 19; + int schedulingAction = DO_NOTHING; + + /** + * This status flag is used for sound scheduling + */ + static final int SOUND_OFF = 0; // The sound is not playing + static final int SOUND_AUDIBLE = 1; // The sound is potentially audible + static final int SOUND_SILENT = 2; // The sound is playing silently + static final int SOUND_PAUSED = 3; // The sound is playing silently + static final int SOUND_COMPLETE = 4; // The sound is finished playing + int status = SOUND_OFF; + + // Sound atoms have two dirty flags: attribsDirty for sound node fields + // and stateDirty for changes to sound state not reflected by sound fields. + // When the field/parameter associated with the dirty bit has been: + // passed to all SoundSchedulers to update sound rendering or 'run' state + // the bit for that field is cleared by the SoundStructure thread. + + /** + * attribsDirty bit field + * This bitmask is set when sound node attribute is changed by the user. + */ + int attribsDirty = 0x0000; + + /** + * stateDirty bit field + * This bitmask is set when scene graph state is changed. + */ + int stateDirty = 0x0000; + + // Load Sound Data Status maintained in SoundRetained class + + /** + * Identifiers of sample associated with sound source + */ + int sampleId = SoundRetained.NULL_SOUND; + + /** + * reference to Sound Scheduler this atom is associated with + */ + SoundScheduler soundScheduler = null; + + + /** + * Calculate absolute time at which sample completes + * Checks playing flag denoting if sound is started already or not: + * false - calcalutes endTime in relation to startTime + * true - re-calculates endTime based on current position in + * loop portion of sample plus release length + */ + synchronized void calculateEndTime() { + SoundRetained sgSound = sound.sgSound; + int loops = sgSound.loopCount; + if (debugFlag) + debugPrint("calculateEndTime: loop count = " + loops); + // test lengths for <= 0; this includes DURATION_UNKNOWN + if ( (sampleLength <= 0 || loopLength <= 0 || loops < 0 ) +// QUESTION: removed? but what was this trying to avoid +// changing endTime when that is already set? +// but what happens when user changes LoopCount AFTER +// sound is started - should be able to do this +// && (enabled == OFF || enabled == PENDING_OFF) + ) { + endTime = -1; + if (debugFlag) + debugPrint("calculateEndTime: set to -1"); + } + else { +// QUESTION: if "&& playing" is in above test; won't we have to test for +// length unknown and loop = -1?? + if (playing && (startTime > 0)) { + endTime = startTime + attackLength + + (loopLength * (loops+1)) + releaseLength; if (debugFlag) + debugPrint("calculateEndTime: isPlaying so = " + endTime); } + else { + // Called when release flag is true + // Determine where within the loop portion sample the + // sound is currently playing, then set endTime to + // play remaining portion of loop portion plus the + // release portion. + long currentTime = J3dClock.currentTimeMillis(); + endTime = currentTime + ( (loopLength - + ((currentTime - startTime - attackLength) % loopLength)) + + releaseLength ); + if (debugFlag) + debugPrint("calculateEndTime: NOT Playing so = " + endTime); + } + } + } + + + void enable(boolean enabled) { + if (enabled) { + setEnableState(PENDING_ON); + if (debugFlag) + debugPrint(" enableSound calls soundAtom " + + this + " setEnableState PENDING_ON"); + } + else { + setEnableState(PENDING_OFF); + if (debugFlag) + debugPrint(" enableSound calls soundAtom " + + this + " setEnableState PENDING_OFF"); + } + } + + + void mute(boolean muted) { + if (muted) { + setMuteState(PENDING_MUTE); + if (debugFlag) + debugPrint(" muteSound() calls soundAtom " + + this + " setMuteState PENDING_ON"); + } + else { + setMuteState(PENDING_UNMUTE); + if (debugFlag) + debugPrint(" muteSound() calls soundAtom " + + this + " setMuteState PENDING_UNMUTE"); + } + } + + void pause(boolean paused) { + if (paused) { + setPauseState(PENDING_PAUSE); + if (debugFlag) + debugPrint(this + ".pause calls setPauseState(PENDING_PAUSE)"); + } + else { + setPauseState(PENDING_UNPAUSE); + if (debugFlag) + debugPrint(this +".pause calls setPauseState(PENDING_UNPAUSE)"); + } + } + + +// XXXX: remove this +// just set the state after debug no longer needed + void setEnableState(int state) { + enabled = state; + switch (state) { + case PENDING_ON: + if (debugFlag) + debugPrint("set enabled to PENDING_ON"); + break; + case ON: + if (debugFlag) + debugPrint("set enabled to ON"); + break; + case PENDING_OFF: + if (debugFlag) + debugPrint("set enabled to PENDING_OFF"); + break; + case OFF: + if (debugFlag) + debugPrint("set enabled to OFF"); + break; + default: + if (debugFlag) + debugPrint("state = " + state); + break; + } + } + +// XXXX: remove this +// just set the state after debug no longer needed + void setMuteState(int state) { + muted = state; + switch (state) { + case PENDING_MUTE: + if (debugFlag) + debugPrint("set mute to PENDING_MUTE"); + break; + case MUTED: + if (debugFlag) + debugPrint("set mute to MUTE"); + break; + case PENDING_UNMUTE: + if (debugFlag) + debugPrint("set mute to PENDING_UNMUTE"); + break; + case UNMUTED: + if (debugFlag) + debugPrint("set mute to UNMUTE"); + break; + default: + if (debugFlag) + debugPrint("state = " + state); + break; + } + } + +// XXXX: remove this +// just set the state after debug no longer needed + void setPauseState(int state) { + paused = state; + switch (state) { + case PENDING_PAUSE: + if (debugFlag) + debugPrint("set pause to PENDING_PAUSE"); + break; + case PAUSED: + if (debugFlag) + debugPrint("set pause to PAUSE"); + break; + case PENDING_UNPAUSE: + if (debugFlag) + debugPrint("set pause to PENDING_UNPAUSE"); + break; + case UNPAUSED: + if (debugFlag) + debugPrint("set pause to UNPAUSE"); + break; + default: + if (debugFlag) + debugPrint("state = " + state); + break; + } + } + + + /** + * calcActiveSchedAction() + * Calculate Sound Scheduler Action for Active sound (it's region + * intersects the viewPlatform). + * + * A big switch testing various SoundRetained fields to determine + * what SoundScheduler action to perform when sound is Active + * set sound active flag true + * switch on enable value, to set pending scheduling action + * depending on continuous and release flags and sound status + */ + synchronized int calcActiveSchedAction() { + SoundRetained sgSound = sound.sgSound; + int action = DO_NOTHING; + activated = true; + switch (enabled) { + case PENDING_ON: + setEnableState(ON); + if (debugFlag) + debugPrint(" calcActiveSchedAction: PENDING_ON"); + if (status == SOUND_OFF || + status == SOUND_PAUSED) + action = START_AUDIBLE; + else + action = RESTART_AUDIBLE; + break; + case ON: + if (debugFlag) + debugPrint(" calcActiveSchedAction: ON"); + if (status == SOUND_OFF) + // should NOT see this, but if we do... + action = START_AUDIBLE; + else if (status == SOUND_SILENT) + action = MAKE_AUDIBLE; + else // status == SOUND_AUDIBLE + action = LEAVE_AUDIBLE; + break; + case PENDING_OFF: + setEnableState(OFF); + if (debugFlag) + debugPrint("enable = " + enabled + + "enabled set to OFF"); + // fail thru + case OFF: + // QUESTION: Why would enable status ever be OFF yet + // status SOUND_AUDIBLE or _SILENT? + if (status == SOUND_AUDIBLE) { + if (sgSound.release) { + if (debugFlag) + debugPrint("enable = " + enabled + + ", AUDIBLE, released, " + + "action <- LEAVE_AUDIBLE"); + if (enabled == PENDING_OFF) { + // re-calculate EndTime + calculateEndTime(); + } + action = LEAVE_AUDIBLE; + } + else { + if (debugFlag) + debugPrint("enable = " + enabled + + ", AUDIBLE, not released, "+ + "action <- TURN_OFF"); + action = TURN_OFF; + } + } + else if (status == SOUND_SILENT) { + if (sgSound.release) { + if (debugFlag) + debugPrint("enable = " + enabled + + ", SILENT, released, " + + "action <- MAKE_AUDIBLE"); + // re-calculate EndTime + calculateEndTime(); + action = MAKE_AUDIBLE; + } + else { + if (debugFlag) + debugPrint("enable = " + enabled + + ", SILENT, not released, " + + "action <- TURN_OFF"); + action = TURN_OFF; + } + } + else { // status == SOUND_OFF + action = LEAVE_OFF; + } + break; + } // switch on enabled flag + + // if sounds pause state is PENDING_PAUSE modify action to perform. + if (paused == PENDING_PAUSE) { + // if this pause state is set to PAUSE then assume the sound is + // already paused, so any incoming action that leave the state + // as it already is, leaves the sound paused. + if (debugFlag) + debugPrint(" PENDING_PAUSE"); + switch (action) { + case MAKE_AUDIBLE: + case LEAVE_AUDIBLE: + case RESUME_AUDIBLE: + action = PAUSE_AUDIBLE; + break; + case MAKE_SILENT: + case LEAVE_SILENT: + case RESUME_SILENT: + action = PAUSE_SILENT; + break; + default: + // don't change action for any other cases + break; + } + } + // if sounds pause state is PENDING_UNPAUSE modify action + else if (paused == PENDING_UNPAUSE) { + debugPrint(" PENDING_UNPAUSE"); + switch (action) { + // When restart (audible or silent) pause flag is checked and + // explicitly set in SoundScheduler + case MAKE_AUDIBLE: + case LEAVE_AUDIBLE: + case PAUSE_AUDIBLE: + action = RESUME_AUDIBLE; + break; + case MAKE_SILENT: + case LEAVE_SILENT: + case PAUSE_SILENT: + action = RESUME_SILENT; + break; + default: + // don't change action for any other cases + break; + } + } + return(action); + } // end of calcActiveSchedAction + + + /** + * calcInactiveSchedAction() + * Calculate Sound Scheduler action for Inactive sound + * + * A big switch testing various SoundRetained fields to determine + * what SoundScheduler action to perform when sound is inactive. + * set sound active flag false + * switch on enable value, to set pending scheduling action + * depending on continuous and release flags and sound status + */ + synchronized int calcInactiveSchedAction() { + int action = DO_NOTHING; + SoundRetained sgSound = sound.sgSound; + + // Sound is Inactive + // Generally, sound is OFF unless continuous flag true + // then sound is silently playing if on. + activated = false; + + switch (enabled) { + case PENDING_ON: + if (debugFlag) + debugPrint(" calcInactiveSchedAction: PENDING_ON "); + setEnableState(ON); + if (sgSound.continuous) { + if (status == SOUND_OFF) + action = START_SILENT; + else // status == SOUND_AUDIBLE or SOUND_SILENT + action = RESTART_SILENT; + } + else { // sound is not continuous + if (status == SOUND_OFF) + action = LEAVE_OFF; + else // status == SOUND_SILENT || SOUND_AUDIBLE + action = TURN_OFF; + } + break; + case ON: + if (debugFlag) + debugPrint(" calcInactiveSchedActio: ON "); + if (sgSound.continuous) { + if (status == SOUND_AUDIBLE) + action = MAKE_SILENT; + else if (status == SOUND_OFF) + action = START_SILENT; + else // status == SOUND_SILENT + action = LEAVE_SILENT; + } + else { // sound is not continuous + // nothing to do if already off + if (status == SOUND_OFF) + action = LEAVE_OFF; + else // status == SOUND_SILENT or SOUND_AUDIBLE + action = TURN_OFF; + } + break; + case PENDING_OFF: + setEnableState(OFF); + if (debugFlag) + debugPrint("Enable = " + enabled + + "enabled set to OFF"); + // fall thru + + case OFF: + if (sgSound.release && sgSound.continuous) { + if (enabled == PENDING_OFF) { + // re-calculate EndTime + calculateEndTime(); + } + if (status == SOUND_AUDIBLE) { + if (debugFlag) + debugPrint("Enable = " + enabled + + ", AUDIBLE, released & continuous - " + + "action <- MAKE_SILENT"); + action = MAKE_SILENT; + } + else if (status == SOUND_SILENT) { + if (debugFlag) + debugPrint("Enable = " + enabled + + ", SILENT, released & continuous - " + + "action <- TURN_OFF"); + action = LEAVE_SILENT; + } + else { + if (debugFlag) + debugPrint("Enable = " + enabled + + ", already OFF, action <- LEAVE_OFF"); + action = LEAVE_OFF; + } + } + else { // continuous and release flag not both true + if (status == SOUND_OFF) { + if (debugFlag) + debugPrint("Enable = " + enabled + + ", already OFF, action <- LEAVE_OFF"); + action = LEAVE_OFF; + } + else { + if (debugFlag) + debugPrint("Enable = " + enabled + + ", not already OFF, action <- TURN_OFF"); + action = TURN_OFF; + } + } + break; + default: + break; + } // switch + + // if sounds pause state is PENDING_PAUSE modify action to perform. + if (paused == PENDING_PAUSE) { + // if this pause state is set to PAUSE then assume the sound is + // already paused, so any incoming action that leave the state + // as it already is, leaves the sound paused. + switch (action) { + case MAKE_SILENT: + case LEAVE_SILENT: + case RESUME_SILENT: + action = PAUSE_SILENT; + break; + default: + // don't change action for any other cases + break; + } + } + // if sounds pause state is PENDING_UNPAUSE modify action + else if (paused == PENDING_UNPAUSE) { + switch (action) { + case LEAVE_SILENT: + action = RESUME_SILENT; + break; + default: + // don't change action for any other cases + break; + } + } + return (action); + } // end of calcInactiveSchedAction + +// XXXX: isPLaying +// XXXX: setLoadingState + + // Debug print mechanism for Sound nodes + static final boolean debugFlag = false; + static final boolean internalErrors = false; + + void debugPrint(String message) { + if (debugFlag) { + System.err.println(message); + } + } + + + /** + * Set bit(s) in soundDirty field + * @param binary flag denotes bits to set ON + */ + void setAttribsDirtyFlag(int bitFlag) { + attribsDirty |= bitFlag; + if (debugFlag) + debugPrint("setAttribsDirtyFlag = " + bitFlag); + return ; + } + void setStateDirtyFlag(int bitFlag) { + stateDirty |= bitFlag; + if (debugFlag) + debugPrint("setStateDirtyFlag = " + bitFlag); + return ; + } + + /** + * Clear sound's dirty flag bit value. + * @param binary flag denotes bits to set OFF + */ + void clearAttribsDirtyFlag(int bitFlag) { + if (debugFlag) + debugPrint("clearAttribsDirtyFlag = " + bitFlag); + attribsDirty &= ~bitFlag; + return ; + } + void clearAttribsDirtyFlag() { + // clear all bits + if (debugFlag) + debugPrint("clearAttribsDirtyFlag = ALL"); + attribsDirty = 0x0; + return ; + } + void clearStateDirtyFlag(int bitFlag) { + if (debugFlag) + debugPrint("clearStateDirtyFlag = " + bitFlag); + stateDirty &= ~bitFlag; + return ; + } + void clearStateDirtyFlag() { + if (debugFlag) + debugPrint("clearStateDirtyFlag = ALL"); + stateDirty = 0x0; + return ; + } + + + /** + * Test sound's dirty flag bit(s) + * @param field denotes which bitmask to set into + * @param binary flag denotes bits to set Test + * @return true if bit(s) in bitFlag are set dirty (on) + */ + boolean testDirtyFlag(int field, int bitFlag) { + if ((field & bitFlag) > 0) + return true; + else + return false; + } + + /** + * Test sound's dirty flags for ANY bits on + * @return true if any bit in bitFlag is flipped on + */ + boolean testDirtyFlags() { + if ((attribsDirty & 0xFFFF) > 0) + return true; + else if ((stateDirty & 0xFFFF) > 0) + return true; + else + return false; + } + +} |