javax.media.j3d.SoundRetained Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java3d-core Show documentation
Show all versions of java3d-core Show documentation
Java3D Core And Java3D Util Libraries
The newest version!
/*
* 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