From 7a2e20caac9db6f789a7b3fab344b9758af45335 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sun, 19 Apr 2015 21:02:06 -0700 Subject: j3dcore: flatten the directory structure a bit Signed-off-by: Harvey Harrison --- src/javax/media/j3d/SoundRetained.java | 1316 ++++++++++++++++++++++++++++++++ 1 file changed, 1316 insertions(+) create mode 100644 src/javax/media/j3d/SoundRetained.java (limited to 'src/javax/media/j3d/SoundRetained.java') 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 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 + * 1) the Sound node has a non-null sound data and this data has + * sucessfully been loaded/opened/copied/attached;
+ * 2) the Sound node is live;
+ * 3) there is at least one active View in the Universe; and
+ * 4) an instance of an AudioDevice is attached to the current + * PhysicalEnvironment. + * + * + * @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 + * 1) the Sound node has a non-null sound data and this data has + * sucessfully been loaded/opened/copied/attached;
+ * 2) the Sound node is live;
+ * 3) the given View is active in the Universe; and
+ * 4) an instance of an AudioDevice is attached to the current + * PhysicalEnvironment. + * + * + * @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