aboutsummaryrefslogtreecommitdiffstats
path: root/src/javax/media/j3d/SoundRetained.java
diff options
context:
space:
mode:
authorHarvey Harrison <[email protected]>2015-04-19 21:02:06 -0700
committerHarvey Harrison <[email protected]>2015-04-19 21:02:06 -0700
commit7a2e20caac9db6f789a7b3fab344b9758af45335 (patch)
treeb5236ff2570178de356eab569225108948eb4d30 /src/javax/media/j3d/SoundRetained.java
parentf76ce302c4bb2a9f03bbee571ec5d05c29633023 (diff)
j3dcore: flatten the directory structure a bit
Signed-off-by: Harvey Harrison <[email protected]>
Diffstat (limited to 'src/javax/media/j3d/SoundRetained.java')
-rw-r--r--src/javax/media/j3d/SoundRetained.java1316
1 files changed, 1316 insertions, 0 deletions
diff --git a/src/javax/media/j3d/SoundRetained.java b/src/javax/media/j3d/SoundRetained.java
new file mode 100644
index 0000000..246e9dc
--- /dev/null
+++ b/src/javax/media/j3d/SoundRetained.java
@@ -0,0 +1,1316 @@
+/*
+ * Copyright 1996-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;
+
+import java.util.ArrayList;
+
+
+
+/**
+ * SoundRetained is an abstract class that contains instance varables common
+ * to all retained sounds.
+ */
+
+abstract class SoundRetained extends LeafRetained
+{
+
+ /**
+ * Null Sound identifier denotes sound is not created or initialized
+ */
+ static final int NULL_SOUND = -1;
+
+ /**
+ * sound data associated with sound source
+ */
+ MediaContainer soundData = null;
+
+ /**
+ * Overall Scale Factor applied to sound.
+ */
+ float initialGain = 1.0f; // Valid values are >= 0.0.
+
+ /**
+ * Number of times sound is looped/repeated during play
+ */
+ int loopCount = 0; // Range from 0 to POSITIVE_INFINITY(-1)
+
+ /**
+ * Switch for turning sound on or off while the sound is "active"
+ */
+ boolean enable = false;
+
+ /**
+ * Type of release when sound is disabled.
+ * If true, sound plays thru to end of sample before disabled
+ * Otherwise, sound is disabled immediately.
+ */
+ boolean release = false;
+
+ /**
+ * Flag denoting if sound silently continues playing when it's deactivated.
+ */
+ boolean continuous = false;
+
+ /**
+ * Flag denoting if sound is explicitly muted, so that if begins playing
+ * it will be played silently.
+ */
+ boolean mute = false;
+
+ /**
+ * Flag denoting if sound is paused from playing - waiting to be resumed
+ */
+ boolean pause = false;
+
+ /**
+ * Sound priority ranking value.
+ * Valid values are 0.0 to 1.0
+ */
+ float priority = 1.0f;
+
+ /**
+ * Rate Scale Factor applied to sounds playback sample rate in Hertz.
+ * Valid values are 0.0 to 1.0
+ */
+ float rate = 1.0f;
+
+ /**
+ * The Boundary object defining the sound's scheduling region.
+ */
+ Bounds schedulingRegion = null;
+
+ /**
+ * The bounding leaf reference
+ */
+ BoundingLeafRetained boundingLeaf = null;
+
+ /**
+ * The transformed bounds from either schedulingRegion or boundingLeaf
+ */
+ Bounds transformedRegion = null;
+
+ // Dirty bit flags used to pass change as part of message, and are
+ // acclummuated/stored in SoundSchedulerAtoms.
+ // These flags are grouped into two catagories:
+ // attribsDirty for sound node fields
+ // stateDirty for changes to sound state not reflected by sound fields.
+
+ // Attributes Dirty bit flags
+ // This bitmask is set when sound node attribute is changed by the user.
+ static final int SOUND_DATA_DIRTY_BIT = 0x0001;
+ static final int INITIAL_GAIN_DIRTY_BIT = 0x0002;
+ static final int LOOP_COUNT_DIRTY_BIT = 0x0004;
+ static final int BOUNDS_DIRTY_BIT = 0x0008;
+ static final int BOUNDING_LEAF_DIRTY_BIT = 0x0010;
+ static final int PRIORITY_DIRTY_BIT = 0x0020;
+ static final int POSITION_DIRTY_BIT = 0x0040;
+ static final int DISTANCE_GAIN_DIRTY_BIT = 0x0080;
+ static final int BACK_DISTANCE_GAIN_DIRTY_BIT = 0x0100;
+ static final int DIRECTION_DIRTY_BIT = 0x0200;
+ static final int ANGULAR_ATTENUATION_DIRTY_BIT = 0x0400;
+ static final int RATE_DIRTY_BIT = 0x0800;
+
+ static final int BOUNDS_CHANGED =
+ BOUNDS_DIRTY_BIT | BOUNDING_LEAF_DIRTY_BIT;
+
+ static final int ATTRIBUTE_DIRTY_BITS =
+ SOUND_DATA_DIRTY_BIT | INITIAL_GAIN_DIRTY_BIT |
+ LOOP_COUNT_DIRTY_BIT | PRIORITY_DIRTY_BIT |
+ RATE_DIRTY_BIT;
+
+ static final int POSITIONAL_DIRTY_BITS =
+ ATTRIBUTE_DIRTY_BITS |
+ POSITION_DIRTY_BIT | DISTANCE_GAIN_DIRTY_BIT;
+
+ static final int DIRECTIONAL_DIRTY_BITS =
+ POSITIONAL_DIRTY_BITS | BACK_DISTANCE_GAIN_DIRTY_BIT |
+ DIRECTION_DIRTY_BIT | ANGULAR_ATTENUATION_DIRTY_BIT;
+
+ // All attribute bits that are specifically set or cleared for any node */
+ static final int ALL_ATTIBS_DIRTY_BITS = 0x0FFF;
+
+ // State Dirty bit flags
+ // This bitmask is set when scene graph state is changed.
+ static final int LIVE_DIRTY_BIT = 0x0001;
+ static final int IMMEDIATE_MODE_DIRTY_BIT = 0x0002;
+ static final int LOAD_SOUND_DIRTY_BIT = 0x0004;
+ static final int RELEASE_DIRTY_BIT = 0x0008;
+ static final int CONTINUOUS_DIRTY_BIT = 0x0010;
+ static final int ENABLE_DIRTY_BIT = 0x0020;
+ static final int MUTE_DIRTY_BIT = 0x0040;
+ static final int PAUSE_DIRTY_BIT = 0x0080;
+ static final int XFORM_DIRTY_BIT = 0x8000;
+
+ // All attribute bits that are specifically set or cleared for any node */
+ static final int ALL_STATE_DIRTY_BITS = 0x80FF;
+
+ // The type of sound node: Background, Point, Cone
+ int soundType = NULL_SOUND;
+
+ // A back reference to the scene graph sound, when this is a mirror sound
+ SoundRetained sgSound = null;
+
+ // A HashKey for sounds in a shared group
+ HashKey key = null;
+
+ // An array of mirror sounds, one for each instance of this sound in a
+ // shared group. Entry 0 is the only one valid if we are not in a shared
+ // group.
+ SoundRetained[] mirrorSounds = new SoundRetained[1];
+
+ // The number of valid sounds in mirrorSounds
+ int numMirrorSounds = 0;
+
+ /**
+ * Array of references to sound scheduler atoms associated with this node.
+ * For each view that a sound node is associated with a sound scheduler
+ * atom is created and maintained
+ */
+ // for a particular view that are playing either audibly or silently.
+ private SoundSchedulerAtom[] loadedAtoms = new SoundSchedulerAtom[1];
+ private int atomCount = 0;
+
+ /**
+ * This is true when this sound is referenced in an immediate mode context
+ */
+ boolean inImmCtx = false;
+
+ /**
+ * Load Sound Data Status
+ */
+ static final int LOAD_COMPLETE = 2;
+ // load requested but could not be performed due because sound not live
+ static final int LOAD_PENDING = 1;
+ static final int LOAD_NULL = 0;
+ static final int LOAD_FAILED = -1;
+ int loadStatus = LOAD_NULL;
+ long duration = Sound.DURATION_UNKNOWN;
+
+ // Static initializer for SoundRetained class
+ static {
+ VirtualUniverse.loadLibraries();
+ }
+
+ // Target threads to be notified when sound changes
+ static final int targetThreads = J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER;
+
+ // Is true, if the mirror light is viewScoped
+ boolean isViewScoped = false;
+
+
+ /**
+ * Dispatch a message about a sound attribute change
+ */
+ void dispatchAttribChange(int dirtyBit, Object argument) {
+ // Send message including a integer argument
+ J3dMessage createMessage = new J3dMessage();
+ createMessage.threads = J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER;
+ createMessage.type = J3dMessage.SOUND_ATTRIB_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(dirtyBit);
+ if (inSharedGroup)
+ createMessage.args[2] = new Integer(numMirrorSounds);
+ else
+ createMessage.args[2] = new Integer(1);
+ createMessage.args[3] = mirrorSounds.clone();
+ createMessage.args[4] = argument;
+ if (debugFlag)
+ debugPrint("dispatchAttribChange with " + dirtyBit);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ /**
+ * Dispatch a message about a sound state change
+ */
+ void dispatchStateChange(int dirtyBit, Object argument) {
+ // Send message including a integer argument
+ J3dMessage createMessage = new J3dMessage();
+ createMessage.threads = J3dThread.UPDATE_SOUND |
+ J3dThread.SOUND_SCHEDULER;
+ createMessage.type = J3dMessage.SOUND_STATE_CHANGED;
+ createMessage.universe = universe;
+ createMessage.args[0] = this;
+ createMessage.args[1]= new Integer(dirtyBit);
+ if (inSharedGroup)
+ createMessage.args[2] = new Integer(numMirrorSounds);
+ else
+ createMessage.args[2] = new Integer(1);
+ createMessage.args[3] = mirrorSounds.clone();
+ createMessage.args[4] = argument;
+ if (debugFlag)
+ debugPrint("dispatchStateChange with " + dirtyBit);
+ VirtualUniverse.mc.processMessage(createMessage);
+ }
+
+ /**
+ * Assign value into sound data field
+ * @param soundData description of sound source data
+ */
+ void setSoundDataState(MediaContainer soundData) {
+ this.soundData = soundData;
+ }
+
+ /**
+ * Associates sound data with this sound source node
+ * Attempt to load sound
+ * @param soundData descrition of sound source data
+ */
+ void setSoundData(MediaContainer soundData) {
+ // if resetting soundData to the same value don't bother doing anything
+ if (this.soundData == soundData) {
+ return;
+ }
+
+ if (this.soundData != null) {
+ // this sound node had older sound data; clear it out
+ ((MediaContainerRetained)this.soundData.retained).removeUser(this);
+ }
+
+ if (source != null && source.isLive()) {
+ if (this.soundData != null) {
+ ((MediaContainerRetained)this.soundData.retained).clearLive(refCount);
+ }
+
+ if (soundData != null) {
+ ((MediaContainerRetained)soundData.retained).setLive(inBackgroundGroup, refCount);
+ ((MediaContainerRetained)soundData.retained).addUser(this);
+ }
+ }
+
+ this.soundData = soundData;
+ dispatchAttribChange(SOUND_DATA_DIRTY_BIT, soundData);
+
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound data associated with this sound source node
+ * @return sound source data container
+ */
+ MediaContainer getSoundData() {
+ return ( this.soundData );
+ }
+
+
+ /**
+ * Set the gain scale factor applied to this sound
+ * @param amplitude gain scale factor
+ */
+ void setInitialGain(float scaleFactor) {
+ if (scaleFactor < 0.0f)
+ this.initialGain = 0.0f;
+ else
+ this.initialGain = scaleFactor;
+
+ dispatchAttribChange(INITIAL_GAIN_DIRTY_BIT, (new Float(scaleFactor)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+ /**
+ * Get the overall gain (applied to the sound data associated with source).
+ * @return overall gain of sound source
+ */
+ float getInitialGain() {
+ return (float) this.initialGain;
+ }
+
+
+ /**
+ * Sets the sound's loop count
+ * @param loopCount number of times sound is looped during play
+ */
+ void setLoop(int loopCount) {
+ if (loopCount < -1)
+ this.loopCount = -1;
+ else
+ this.loopCount = (int) loopCount;
+ if (debugFlag)
+ debugPrint("setLoopCount called with " + this.loopCount);
+
+ dispatchAttribChange(LOOP_COUNT_DIRTY_BIT, (new Integer(loopCount)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves the loop count
+ * @return loop count for data associated with sound
+ */
+ int getLoop() {
+ return (int) this.loopCount;
+ }
+
+ /**
+ * Enable or disable the release flag for this sound source
+ * @param state flag denoting release sound before stopping
+ */
+ void setReleaseEnable(boolean state) {
+ this.release = state;
+ dispatchAttribChange(RELEASE_DIRTY_BIT, (state ? Boolean.TRUE: Boolean.FALSE));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves release flag for sound associated with this source node
+ * @return sound's release flag
+ */
+ boolean getReleaseEnable() {
+ return (boolean) this.release;
+ }
+
+ /**
+ * Enable or disable continuous play flag
+ * @param state denotes if sound continues playing silently when deactivated
+ */
+ void setContinuousEnable(boolean state) {
+ this.continuous = state;
+ dispatchAttribChange(CONTINUOUS_DIRTY_BIT, (state ? Boolean.TRUE: Boolean.FALSE));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound's continuous play flag
+ * @return flag denoting if deactivated sound silently continues playing
+ */
+ boolean getContinuousEnable() {
+ return (boolean) this.continuous;
+ }
+
+ /**
+ * Sets the flag denotine sound enabled/disabled and sends a message
+ * for the following to be done:
+ * If state is true:
+ * if sound is not playing, sound is started.
+ * if sound is playing, sound is stopped, then re-started.
+ * If state is false:
+ * if sound is playing, sound is stopped
+ * @param state true or false to enable or disable the sound
+ */
+ void setEnable(boolean state) {
+ enable = state;
+ // QUESTION: Is this still valid code?
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ dispatchStateChange(ENABLE_DIRTY_BIT, (new Boolean(enable)));
+ }
+
+ /**
+ * Retrieves sound's enabled flag
+ * @return sound enabled flag
+ */
+ boolean getEnable() {
+ return enable;
+ }
+
+ /**
+ * Set the Sound's scheduling region.
+ * @param region a region that contains the Sound's new scheduling region
+ */
+ void setSchedulingBounds(Bounds region) {
+ if (region != null) {
+ schedulingRegion = (Bounds) region.clone();
+ if (staticTransform != null) {
+ schedulingRegion.transform(staticTransform.transform);
+ }
+ // QUESTION: Clone into transformedRegion IS required. Why?
+ transformedRegion = (Bounds) schedulingRegion.clone();
+ if (debugFlag)
+ debugPrint("setSchedulingBounds for a non-null region");
+ }
+ else {
+ schedulingRegion = null;
+ // QUESTION: Is transformedRegion of node (not mirror node)
+ // even looked at???
+ transformedRegion = null;
+ if (debugFlag)
+ debugPrint("setSchedulingBounds for a NULL region");
+ }
+ // XXXX: test that this works - could not new Bounds() since
+ // Bounds is an abstract class and can't be instantiated
+ dispatchAttribChange(BOUNDS_DIRTY_BIT, region);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Get the Sound's scheduling region.
+ * @return this Sound's scheduling region information
+ */
+ Bounds getSchedulingBounds() {
+ Bounds b = null;
+
+ if (this.schedulingRegion != null) {
+ b = (Bounds) schedulingRegion.clone();
+ if (staticTransform != null) {
+ Transform3D invTransform = staticTransform.getInvTransform();
+ b.transform(invTransform);
+ }
+ }
+ return b;
+ }
+
+ /**
+ * Set the Sound's scheduling region to the specified Leaf node.
+ */
+ void setSchedulingBoundingLeaf(BoundingLeaf region) {
+ int i;
+ int numSnds = numMirrorSounds;
+ if (numMirrorSounds == 0)
+ numSnds = 1;
+
+ if ((boundingLeaf != null) &&
+ (source != null && source.isLive())) {
+ // Remove the mirror lights as users of the original bounding leaf
+ for (i = 0; i < numSnds; i++) {
+ boundingLeaf.mirrorBoundingLeaf.removeUser(mirrorSounds[i]);
+ }
+ }
+
+ if (region != null) {
+ boundingLeaf = (BoundingLeafRetained)region.retained;
+ // Add all mirror sounds as user of this bounding leaf
+ if (source != null && source.isLive()) {
+ for (i = 0; i < numSnds; i++) {
+ boundingLeaf.mirrorBoundingLeaf.addUser(mirrorSounds[i]);
+ }
+ }
+ } else {
+ boundingLeaf = null;
+ }
+ // XXXX: since BoundingLeaf constructor only takes Bounds
+ // test if region passed into dispatchAttribChange correctly.
+ dispatchAttribChange(BOUNDING_LEAF_DIRTY_BIT, region);
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Get the Sound's scheduling region
+ */
+ BoundingLeaf getSchedulingBoundingLeaf() {
+ if (boundingLeaf != null) {
+ return((BoundingLeaf)boundingLeaf.source);
+ } else {
+ return null;
+ }
+ }
+
+ // The update Object function.
+ @Override
+ synchronized void updateMirrorObject(Object[] objs) {
+ Transform3D trans = null;
+ int component = ((Integer)objs[1]).intValue();
+ if (component == -1) { // update everything
+ // object 2 contains the mirror object that needs to be
+ // updated
+ initMirrorObject(((SoundRetained)objs[2]));
+ }
+
+ // call the parent's mirror object update routine
+ super.updateMirrorObject(objs);
+
+ }
+
+ void updateBoundingLeaf(long refTime) {
+ // This is necessary, if for example, the region
+ // changes from sphere to box.
+ if (boundingLeaf != null && boundingLeaf.switchState.currentSwitchOn) {
+ transformedRegion = boundingLeaf.transformedRegion;
+ } else { // evaluate schedulingRegion if not null
+ if (schedulingRegion != null) {
+ transformedRegion = schedulingRegion.copy(transformedRegion);
+ transformedRegion.transform(schedulingRegion,
+ getLastLocalToVworld());
+ } else {
+ transformedRegion = null;
+ }
+ }
+ }
+
+
+ /**
+ * Set sound's proirity value.
+ * @param priority value used to order sound's importance for playback.
+ */
+ void setPriority(float rank) {
+ if (rank == this.priority)
+ // changing priority is expensive in the sound scheduler(s)
+ // so only dispatch a message if 'new' priority value is really
+ // different
+ return;
+
+ this.priority = rank;
+ dispatchAttribChange(PRIORITY_DIRTY_BIT, (new Float(rank)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound's priority value.
+ * @return sound priority value
+ */
+ float getPriority() {
+ return (this.priority);
+ }
+
+
+ /**
+ * Retrieves sound's duration in milliseconds
+ * @return sound's duration, returns DURATION_UNKNOWN if duration could
+ * not be queried from the audio device
+ */
+ long getDuration() {
+ return (duration);
+ }
+
+
+ /**
+ * Set scale factor
+ * @param scaleFactor applied to sound playback rate
+ */
+ void setRateScaleFactor(float scaleFactor) {
+ this.rate = scaleFactor;
+ dispatchAttribChange(RATE_DIRTY_BIT, (new Float(scaleFactor)));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound's rate scale factor
+ * @return sound rate scale factor
+ */
+ float getRateScaleFactor() {
+ return (this.rate);
+ }
+
+ void changeAtomList(SoundSchedulerAtom atom, int loadStatus) {
+ if (atom == null)
+ return;
+ if (loadStatus == SoundRetained.LOAD_COMPLETE) {
+ // atom is successfully loaded, so add this atom to array of atoms
+ // associated with this sound, if not already in list
+ for (int i=0; i<atomCount; i++) {
+ if (atom == loadedAtoms[i])
+ return;
+ }
+ // add atom to list
+ atomCount++;
+ int currentArrayLength = loadedAtoms.length;
+ if (atomCount > currentArrayLength) {
+ // expand array - replace with a larger array
+ loadedAtoms = new SoundSchedulerAtom[2*currentArrayLength];
+ }
+ loadedAtoms[atomCount-1] = atom; // store reference to new atom
+ // all atoms sample durations SHOULD be the same so store it in node
+ this.duration = atom.sampleLength; // XXXX: refine later? in ms
+ }
+ else { // atom is NOT loaded or has been unloaded; remove from list
+ if (atomCount == 0)
+ return;
+
+ // remove atom from array of playing atoms if it is in list
+ boolean atomFound = false;
+ int i;
+ for (i=0; i<atomCount; i++) {
+ if (atom == loadedAtoms[i]) {
+ atomFound = true;
+ continue;
+ }
+ }
+ if (!atomFound)
+ return;
+
+ // otherwise remove atom from list by close up list
+ for (int j=i; j<atomCount; j++) {
+ loadedAtoms[j] = loadedAtoms[j+1];
+ }
+ atomCount--;
+ if (atomCount == 0)
+ this.duration = Sound.DURATION_UNKNOWN; // clear sound duration
+ }
+ }
+
+ /**
+ * Retrieves sound's ready state for ALL active views.
+ * For this node, the list of sound scheduler atoms associated with
+ * each view is maintained. The 'loaded' (=is ready) state is
+ * true only if the following are true for all views/sound schedulers:
+ *
+ * <ul>
+ * 1) the Sound node has a non-null sound data and this data has
+ * sucessfully been loaded/opened/copied/attached;<br>
+ * 2) the Sound node is live;<br>
+ * 3) there is at least one active View in the Universe; and<br>
+ * 4) an instance of an AudioDevice is attached to the current
+ * PhysicalEnvironment.
+ * </ul>
+ *
+ * @return true if potentially playable (audibly or silently); false otherwise
+ */
+ boolean isReady() {
+ // all the atoms in the atom list must be are ready for this
+ // method to return true
+ // if any non-null atoms are found NOT ready, return false.
+ boolean atomFoundReady = true;
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ else
+ if (atom.loadStatus == SoundRetained.LOAD_COMPLETE) {
+ atomFoundReady = true;
+ continue;
+ }
+ else
+ return false;
+ }
+ if (atomFoundReady) // at least on atom found ready
+ return true;
+ else
+ // not even one atom is associated with node so none are loaded
+ return false;
+ }
+
+ /**
+ * Retrieves sound's ready state for a particular view.
+ * For this node, the list of sound scheduler atoms associated with
+ * each view is maintained. The 'loaded' (=is ready) state is
+ * true only if the following are true for the given view:
+ *
+ * <ul>
+ * 1) the Sound node has a non-null sound data and this data has
+ * sucessfully been loaded/opened/copied/attached;<br>
+ * 2) the Sound node is live;<br>
+ * 3) the given View is active in the Universe; and<br>
+ * 4) an instance of an AudioDevice is attached to the current
+ * PhysicalEnvironment.
+ * </ul>
+ *
+ * @param viewRef view to test sound readiness for
+ * @return true if potentially playable (audibly or silently); false otherwise
+ */
+ boolean isReady(View viewRef) {
+ // if an atom in the atom list that is associated with the
+ // given view is found and has been loaded than return true,
+ // otherwise return false.
+ if (viewRef == null)
+ return false;
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == viewRef)
+ if (atom.loadStatus != SoundRetained.LOAD_COMPLETE)
+ return false;
+ else
+ return true;
+ else // atom is not associated with given referenced view
+ continue;
+ }
+ return false; // sound scheduler atom for given view not found
+
+ }
+
+ // *******************************
+ // Play Status - isPlaying states
+ // *******************************
+
+ /**
+ * Retrieves sound's playing status
+ * true if potentially audible (enabled and active) on ANY audio device
+ * false otherwise
+ * @return sound playing flag
+ */
+ boolean isPlaying() {
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.status == SoundSchedulerAtom.SOUND_AUDIBLE)
+ return true;
+ else
+ continue; // look for at lease one atom that is playing
+ }
+ // not even one atom is associated with this node so none are playing
+ return false;
+ }
+
+ /**
+ * Retrieves sound's playing status for a particular view
+ * true if potentially audible (enabled and active) on audio device
+ * associated with the given view
+ * false otherwise
+ * @param viewRef view to test sound playing state for
+ * @return sound playing flag
+ */
+ boolean isPlaying(View viewRef) {
+ if (viewRef == null)
+ return false;
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == viewRef) {
+ if (atom.status == SoundSchedulerAtom.SOUND_AUDIBLE)
+ return true;
+ else
+ return false;
+ }
+ else // atom is not associated with given referenced view
+ continue;
+ }
+ return false; // atom associated with this view not found in list
+ }
+
+ /**
+ * Retrieves sound's playing silently status
+ * true if enabled but not active (on any device)
+ * false otherwise
+ * @return sound playing flag
+ */
+ boolean isPlayingSilently() {
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.status == SoundSchedulerAtom.SOUND_SILENT)
+ return true;
+ else
+ return false;
+ }
+ return false; // atom not found in list or not playing audibilly
+ }
+
+ /**
+ * Retrieves sound's playing silently status for a particular view
+ * true if potentially audible (enabled and active) on audio device
+ * associated with the given view
+ * false otherwise
+ * @param viewRef view to test sound playing silently state for
+ * @return sound playing flag
+ */
+ boolean isPlayingSilently(View viewRef) {
+ if (viewRef == null)
+ return false;
+ for (int i=0; i<atomCount; i++) {
+ SoundSchedulerAtom atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == viewRef) {
+ if (atom.status == SoundSchedulerAtom.SOUND_SILENT)
+ return true;
+ else
+ return false;
+ }
+ else // atom is not associated with given referenced view
+ continue;
+ }
+ return false; // atom associated with this view not found in list
+ }
+
+ /**
+ * Retrieves number of channels allocated for this sound on the primary
+ * view's audio device.
+ * @return number of channels used by sound across all devices
+ */
+ int getNumberOfChannelsUsed() {
+ // retrieves the number of channels used by the atom that is:
+ // loaded, and
+ // playing either audibily or silently
+ // on the device associated with the primary view.
+ View primaryView = this.universe.getCurrentView();
+ if (primaryView == null)
+ return 0;
+
+ // find atom associated with primary view (VirtualUniverse currentView)
+ // then return the number of channels associated with that atom
+ SoundSchedulerAtom atom;
+ for (int i=0; i<atomCount; i++) {
+ atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == primaryView) {
+ return atom.numberChannels;
+ }
+ }
+ return 0; // atom associated with primary view not found
+ }
+
+ /**
+ * Retrieves number of channels allocated for this sound on the audio
+ * devices associated with a given view.
+ * @param viewRef view to test sound playing silently state for
+ * @return number of channels used by this sound on a particular device
+ */
+ int getNumberOfChannelsUsed(View viewRef) {
+ // retrieves the number of channels used by the atom that is:
+ // loaded, and
+ // playing either audibily or silently
+ // on the device associated with the given view.
+ if (viewRef == null)
+ return 0;
+ SoundSchedulerAtom atom;
+ for (int i=0; i<atomCount; i++) {
+ atom = loadedAtoms[i];
+ if (atom == null || atom.soundScheduler == null)
+ continue;
+ if (atom.soundScheduler.view == viewRef) {
+ return atom.numberChannels;
+ }
+ }
+ return 0; // atom associated with primary view not found
+ }
+
+ /**
+ * Set mute state flag. If the sound is playing it will be set to
+ * play silently
+ * @param state flag
+ * @since Java 3D 1.3
+ */
+ void setMute(boolean state) {
+ this.mute = state;
+ dispatchAttribChange(MUTE_DIRTY_BIT, (state ? Boolean.TRUE: Boolean.FALSE));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound Mute state.
+ * A return value of true does not imply that the sound has
+ * been started playing or is still playing silently.
+ * @return mute state flag
+ * @since Java 3D 1.3
+ */
+ boolean getMute() {
+ return (boolean) this.mute;
+ }
+
+ /**
+ * Set pause state flag. If the sound is playing it will be paused
+ * @param state flag
+ * @since Java 3D 1.3
+ */
+ void setPause(boolean state) {
+ this.pause = state;
+ dispatchAttribChange(PAUSE_DIRTY_BIT, (state ? Boolean.TRUE: Boolean.FALSE));
+ if (source != null && source.isLive()) {
+ notifySceneGraphChanged(false);
+ }
+ }
+
+ /**
+ * Retrieves sound Pause state.
+ * A return value of true does not imply that the sound has
+ * been started playing auditibly or silently.
+ * @return mute state flag
+ * @since Java 3D 1.3
+ */
+ boolean getPause() {
+ return (boolean) this.pause;
+ }
+
+
+ /**
+ * This sets the immedate mode context flag
+ */
+ void setInImmCtx(boolean inCtx) {
+ inImmCtx = inCtx;
+ }
+
+ /**
+ * This gets the immedate mode context flag
+ */
+ boolean getInImmCtx() {
+ return (inImmCtx);
+ }
+
+ /**
+ * This gets the mirror sound for this sound given the key.
+ */
+ SoundRetained getMirrorSound(HashKey key) {
+ int i;
+ SoundRetained[] newSounds;
+
+ if (inSharedGroup) {
+ for (i=0; i<numMirrorSounds; i++) {
+ if (mirrorSounds[i].key.equals(key)) {
+ return(mirrorSounds[i]);
+ }
+ }
+ if (numMirrorSounds == mirrorSounds.length) {
+ newSounds = new SoundRetained[numMirrorSounds*2];
+ for (i=0; i<numMirrorSounds; i++) {
+ newSounds[i] = mirrorSounds[i];
+ }
+ mirrorSounds = newSounds;
+ }
+ // mirrorSounds[numMirrorSounds] = (SoundRetained) this.clone();
+ mirrorSounds[numMirrorSounds] = (SoundRetained) this.clone();
+ //mirrorSounds[numMirrorSounds].key = new HashKey(key);
+ mirrorSounds[numMirrorSounds].key = key;
+ mirrorSounds[numMirrorSounds].sgSound = this;
+ return(mirrorSounds[numMirrorSounds++]);
+ } else {
+ if (mirrorSounds[0] == null) {
+ // mirrorSounds[0] = (SoundRetained) this.clone(true);
+ mirrorSounds[0] = (SoundRetained) this.clone();
+ mirrorSounds[0].sgSound = this;
+ }
+ return(mirrorSounds[0]);
+ }
+ }
+
+ synchronized void initMirrorObject(SoundRetained ms) {
+ GroupRetained group;
+ Transform3D trans;
+ Bounds region = null;
+
+ ms.setSchedulingBounds(getSchedulingBounds());
+ ms.setSchedulingBoundingLeaf(getSchedulingBoundingLeaf());
+ ms.sgSound = sgSound;
+/*
+// QUESTION: these are not set in LightRetained???
+ ms.key = null;
+ ms.mirrorSounds = new SoundRetained[1];
+ ms.numMirrorSounds = 0;
+*/
+ ms.inImmCtx = inImmCtx;
+ ms.setSoundData(getSoundData());
+
+// XXXX: copy ms.atoms array from this.atoms
+
+ ms.parent = parent;
+ ms.inSharedGroup = false;
+ ms.locale = locale;
+ ms.parent = parent;
+ ms.localBounds = (Bounds)localBounds.clone();
+
+ ms.transformedRegion = null;
+ if (boundingLeaf != null) {
+ if (ms.boundingLeaf != null)
+ ms.boundingLeaf.removeUser(ms);
+ ms.boundingLeaf = boundingLeaf.mirrorBoundingLeaf;
+ // Add this mirror object as user
+ ms.boundingLeaf.addUser(ms);
+ ms.transformedRegion = ms.boundingLeaf.transformedRegion;
+ }
+ else {
+ ms.boundingLeaf = null;
+ }
+
+ if (schedulingRegion != null) {
+ ms.schedulingRegion = (Bounds) schedulingRegion.clone();
+ // Assign region only if bounding leaf is null
+ if (ms.transformedRegion == null) {
+ ms.transformedRegion = (Bounds) ms.schedulingRegion.clone();
+ ms.transformedRegion.transform(ms.schedulingRegion,
+ ms.getLastLocalToVworld());
+ }
+
+ }
+ else {
+ ms.schedulingRegion = null;
+ }
+ }
+
+ @Override
+ void setLive(SetLiveState s) {
+ SoundRetained ms;
+ int i, j;
+
+ if (debugFlag)
+ debugPrint("Sound.setLive");
+
+ if (inImmCtx) {
+ throw new
+ IllegalSharingException(J3dI18N.getString("SoundRetained2"));
+ }
+ super.setLive(s);
+ if (inBackgroundGroup) {
+ throw new
+ IllegalSceneGraphException(J3dI18N.getString("SoundRetained3"));
+ }
+
+ if (this.loadStatus == LOAD_PENDING) {
+ if (debugFlag)
+ debugPrint("Sound.setLive load Sound");
+ dispatchStateChange(LOAD_SOUND_DIRTY_BIT, soundData);
+ }
+
+ if (this.soundData != null) {
+ ((MediaContainerRetained)this.soundData.retained).setLive(inBackgroundGroup, s.refCount);
+ }
+
+ if (s.inSharedGroup) {
+ for (i=0; i<s.keys.length; i++) {
+ ms = this.getMirrorSound(s.keys[i]);
+ ms.localToVworld = new Transform3D[1][];
+ ms.localToVworldIndex = new int[1][];
+
+ j = s.keys[i].equals(localToVworldKeys, 0,
+ localToVworldKeys.length);
+ if(j < 0) {
+ System.err.println("SoundRetained : Can't find hashKey");
+ }
+
+ ms.localToVworld[0] = localToVworld[j];
+ ms.localToVworldIndex[0] = localToVworldIndex[j];
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ms);
+ s.scopedNodesViewList.add(s.viewLists.get(i));
+ } else {
+ s.nodeList.add(ms);
+ }
+ // Initialization of the mirror object during the INSERT_NODE
+ // message (in updateMirrorObject)
+ if (s.switchTargets != null && s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(ms, Targets.SND_TARGETS);
+ }
+ ms.switchState = s.switchStates.get(j);
+ if (s.transformTargets != null &&
+ s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ms, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ }
+ } else {
+ ms = this.getMirrorSound(null);
+ ms.localToVworld = new Transform3D[1][];
+ ms.localToVworldIndex = new int[1][];
+ ms.localToVworld[0] = this.localToVworld[0];
+ ms.localToVworldIndex[0] = this.localToVworldIndex[0];
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ms);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(ms);
+ }
+ // Initialization of the mirror object during the INSERT_NODE
+ // message (in updateMirrorObject)
+ if (s.switchTargets != null && s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(ms, Targets.SND_TARGETS);
+ }
+ ms.switchState = s.switchStates.get(0);
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ms, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ }
+ dispatchStateChange(LIVE_DIRTY_BIT, soundData);
+ s.notifyThreads |= targetThreads;
+ }
+
+ @Override
+ void clearLive(SetLiveState s) {
+ SoundRetained ms;
+
+ super.clearLive(s);
+
+// XXXX: if (inSharedGroup)
+
+ if (s.inSharedGroup) {
+ for (int i=0; i<s.keys.length; i++) {
+ ms = this.getMirrorSound(s.keys[i]);
+ if (s.switchTargets != null &&
+ s.switchTargets[i] != null) {
+ s.switchTargets[i].addNode(ms, Targets.SND_TARGETS);
+ }
+ if (s.transformTargets != null && s.transformTargets[i] != null) {
+ s.transformTargets[i].addNode(ms, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ms);
+ s.scopedNodesViewList.add(s.viewLists.get(i));
+ } else {
+ s.nodeList.add(ms);
+ }
+ }
+ } else {
+ ms = this.getMirrorSound(null);
+ if (s.switchTargets != null &&
+ s.switchTargets[0] != null) {
+ s.switchTargets[0].addNode(ms, Targets.SND_TARGETS);
+ }
+ if (s.transformTargets != null &&
+ s.transformTargets[0] != null) {
+ s.transformTargets[0].addNode(ms, Targets.SND_TARGETS);
+ s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
+ }
+ // If its view Scoped, then add this list
+ // to be sent to Sound Structure
+ if ((s.viewScopedNodeList != null) && (s.viewLists != null)) {
+ s.viewScopedNodeList.add(ms);
+ s.scopedNodesViewList.add(s.viewLists.get(0));
+ } else {
+ s.nodeList.add(ms);
+ }
+ }
+ s.notifyThreads |= targetThreads;
+
+ if (this.soundData != null) {
+ ((MediaContainerRetained)this.soundData.retained).clearLive(s.refCount);
+ }
+ }
+
+ @Override
+ void mergeTransform(TransformGroupRetained xform) {
+ super.mergeTransform(xform);
+ if (schedulingRegion != null) {
+ schedulingRegion.transform(xform.transform);
+ }
+ }
+
+/*
+ // This makes passed in sound look just like this sound
+// QUESTION: DOesn't appread to be called
+// XXXX: ...if so, remove...
+ synchronized void update(SoundRetained sound) {
+ if (debugFlag)
+ debugPrint("Sound.update ******** entered ***** this = " + this +
+ ", and sound param = " + sound);
+
+ sound.soundData = soundData;
+ sound.initialGain = initialGain;
+ sound.loopCount = loopCount;
+ sound.release = release;
+ sound.continuous = continuous;
+ sound.enable = enable; // used to be 'on'
+ sound.inImmCtx = inImmCtx;
+
+// QUESTION:
+// This line removed from 1.1.1 version; why ???
+ sound.currentSwitchOn = currentSwitchOn;
+
+// NEW:
+ sound.priority = priority;
+
+// QUESTION: With code below, no sound schedulingRegion found
+// sound.schedulingRegion = schedulingRegion;
+// sound.boundingLeaf = boundingLeaf;
+// XXXX: clone of region used in Traverse code, why not here???
+// if (schedulingRegion != null)
+// sound.schedulingRegion = (Bounds)schedulingRegion.clone();
+// XXXX: BoundingLeafRetained boundingLeaf ...
+// WHAT ABOUT transformedRegion??
+
+// XXXX: Update ALL fields
+// ALL THE BELOW USED TO COMMENTED OUT vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ sound.sampleLength = sampleLength;
+ sound.loopStartOffset = loopStartOffset;
+ sound.loopLength = loopLength;
+ sound.attackLength = attackLength;
+ sound.releaseLength = releaseLength;
+
+ sound.sgSound = sgSound;
+ sound.key = key;
+ sound.numMirrorSounds = numMirrorSounds;
+ for (int index=0; index<numMirrorSounds; index++)
+ sound.mirrorSounds = mirrorSounds;
+ sound.universe = universe;
+ if (universe.sounds.contains(sound) == false) {
+ universe.sounds.addElement(sound);
+ }
+ if (debugFlag)
+ debugPrint("update****************************** exited");
+^^^^^^^^^^^ COMMENTED OUT
+ }
+*/
+
+
+ // Called on mirror object
+// QUESTION: doesn't transformed region need to be saved???
+ @Override
+ void updateTransformChange() {
+ // If bounding leaf is null, tranform the bounds object
+ if (debugFlag)
+ debugPrint("SoundRetained.updateTransformChange()");
+ if (boundingLeaf == null) {
+ if (schedulingRegion != null) {
+ transformedRegion = schedulingRegion.copy(transformedRegion);
+ transformedRegion.transform(schedulingRegion,
+ getLastLocalToVworld());
+ }
+ }
+ dispatchStateChange(XFORM_DIRTY_BIT, null);
+ }
+
+// QUESTION:
+// Clone method (from 1.1.1 version) removed!?!?!? yet LightRetained has it
+
+
+
+ // 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);
+ }
+ }
+ @Override
+ void getMirrorObjects(ArrayList leafList, HashKey key) {
+ if (key == null) {
+ leafList.add(mirrorSounds[0]);
+ }
+ else {
+ for (int i=0; i<numMirrorSounds; i++) {
+ if (mirrorSounds[i].key.equals(key)) {
+ leafList.add(mirrorSounds[i]);
+ break;
+ }
+ }
+
+ }
+ }
+
+}