0001: /*
0002: * $RCSfile: SoundScheduler.java,v $
0003: *
0004: * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0006: *
0007: * This code is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU General Public License version 2 only, as
0009: * published by the Free Software Foundation. Sun designates this
0010: * particular file as subject to the "Classpath" exception as provided
0011: * by Sun in the LICENSE file that accompanied this code.
0012: *
0013: * This code is distributed in the hope that it will be useful, but WITHOUT
0014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0016: * version 2 for more details (a copy is included in the LICENSE file that
0017: * accompanied this code).
0018: *
0019: * You should have received a copy of the GNU General Public License version
0020: * 2 along with this work; if not, write to the Free Software Foundation,
0021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0022: *
0023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0024: * CA 95054 USA or visit www.sun.com if you need additional information or
0025: * have any questions.
0026: *
0027: * $Revision: 1.11 $
0028: * $Date: 2008/02/28 20:17:30 $
0029: * $State: Exp $
0030: */
0031:
0032: package javax.media.j3d;
0033:
0034: import javax.vecmath.*;
0035: import java.lang.Math;
0036: import java.util.Vector;
0037: import java.util.ArrayList;
0038: import java.net.URL;
0039: import java.io.InputStream;
0040: import java.util.Enumeration;
0041: import java.util.Arrays;
0042:
0043: import java.awt.*;
0044: import java.awt.event.*;
0045:
0046: /**
0047: * This structure parallels the RenderBin structure and
0048: * is used for sounds
0049: */
0050: class SoundScheduler extends J3dStructure {
0051:
0052: /**
0053: * The View that owns this SoundScheduler
0054: */
0055: View view = null;
0056:
0057: /**
0058: * This boolean tells the thread to suspend itself.
0059: * This is true ONLY when everythings ready to render using run loop
0060: */
0061: boolean ready = false;
0062:
0063: /**
0064: * The ViewPlatform that is associated with this SoundScheduler
0065: */
0066: ViewPlatformRetained viewPlatform = null;
0067:
0068: /**
0069: * The GraphicContext3D that we are currently unning in.
0070: */
0071: GraphicsContext3D graphicsCtx = null;
0072:
0073: /**
0074: * Maintain what reference to the last AuralAttributes found active
0075: * was so that only if there was a change do we need to reset these
0076: * parameters in the AudioDevice3D.
0077: */
0078: AuralAttributesRetained lastAA = null;
0079:
0080: /**
0081: * Since AuralAttribute gain scale factor is multipled with sound's
0082: * initialGain scale factor, any change in AuralAttrib gain scale
0083: * factor should force an update of all active sounds' gains
0084: * Also, change in AuralAttributes should force a sound update
0085: * even if no other sound field changes occurred.
0086: */
0087: boolean resetAA = true;
0088:
0089: /**
0090: * Audio Device
0091: */
0092: AudioDevice audioDevice = null;
0093: AudioDevice3D audioDevice3D = null;
0094: AudioDevice3DL2 audioDevice3DL2 = null;
0095: int totalChannels = 0;
0096:
0097: /**
0098: * Array of SoundScapeRetained nodes that intersect the viewPlatform
0099: * This list is a subset of the soundscapes array, and is used when
0100: * selecting the closest Soundscape.
0101: * Maintained as an expandable array.
0102: */
0103: SoundscapeRetained[] intersectedSoundscapes = new SoundscapeRetained[32];
0104:
0105: /**
0106: * Array of Bounds nodes for the corresponding intersectedSoundscapes
0107: * array. This array is used when selecting the closest Soundscape.
0108: * This list is used when selecting the closest Soundscape.
0109: * Maintained as an expandable array.
0110: */
0111: Bounds[] intersectedRegions = new Bounds[32];
0112:
0113: /**
0114: * Reference to last processed region within run().
0115: * Maintained to avoid re-transforming this bounds.
0116: */
0117: Bounds region = null;
0118:
0119: /**
0120: * An array of prioritized sounds currently playing "live" sounds.
0121: * This prioritized sound list is NO longer re-create instead sounds
0122: * are insert, shuffled or removed as messages are processed.
0123: */
0124: // XXXX: (Enhancement) should have a seperate list for
0125: // background sound and a list for positional sounds
0126: ArrayList prioritizedSounds = new ArrayList();
0127:
0128: /**
0129: * Current number of scene graph sound nodes in the universe
0130: */
0131: int nRetainedSounds = -1; // none calculated yet
0132:
0133: /**
0134: * Current number of immediate mode sound nodes in the universe
0135: */
0136: int nImmedSounds = -1; // none calculated yet
0137:
0138: /**
0139: * Current active (selected) attribute node in the sceneGraph
0140: */
0141: AuralAttributesRetained aaRetained = null;
0142:
0143: // variables for processing transform messages
0144: boolean transformMsg = false;
0145: UpdateTargets targets = null;
0146:
0147: /**
0148: * Current active (selected) attribute node in the sceneGraph
0149: */
0150: AuralAttributesRetained aaImmed = null;
0151:
0152: // Dirty flags for fields and parameters that are unique to the
0153: // Sound Scheduler or the Audio Device
0154: // Any listener (body) and/or view transform changes processed in
0155: // CanvasViewCache force one of these flags to be set.
0156: static final int EAR_POSITIONS_CHANGED = 0x0001;
0157: static final int EYE_POSITIONS_CHANGED = 0x0002;
0158: static final int IMAGE_PLATE_TO_VWORLD_CHANGED = 0x0004;
0159: static final int HEAD_TO_VWORLD_CHANGED = 0x0008;
0160: static final int LISTENER_CHANGED = 0x000F;// all of the above
0161: private int listenerUpdated = LISTENER_CHANGED;
0162:
0163: /**
0164: * Temporary flag that's denotes that a positional sound was processed
0165: * in the current loop of renderChange().
0166: */
0167: private boolean positionalSoundUpdated = false;
0168:
0169: /**
0170: * Temporary flag that's denotes that some field auralAttribute was changed
0171: */
0172: private boolean auralAttribsChanged = true; // force processing 1st x
0173:
0174: private boolean stallThread = false;
0175:
0176: int lastEventReceived = WindowEvent.WINDOW_CLOSED;
0177:
0178: /**
0179: * Constructs a new SoundScheduler
0180: */
0181: SoundScheduler(VirtualUniverse u, View v) {
0182: super (u, J3dThread.SOUND_SCHEDULER);
0183:
0184: // Assertion check view & universe
0185: if (v == null) {
0186: System.err
0187: .println("WARNING: SoundScheduler constructed with null view");
0188: }
0189: if (u == null) {
0190: System.err
0191: .println("WARNING: SoundScheduler constructed with null universe");
0192: }
0193:
0194: universe = u;
0195: view = v;
0196: reset();
0197: }
0198:
0199: // NOTE: processMessage only called with updatethread.active true
0200: void processMessages(long referenceTime) {
0201: J3dMessage[] messages = getMessages(referenceTime);
0202: int nMsg = getNumMessage();
0203: J3dMessage m;
0204: int nSounds;
0205:
0206: if (nMsg > 0) {
0207: for (int i = 0; i < nMsg; i++) {
0208: m = messages[i];
0209:
0210: switch (m.type) {
0211: case J3dMessage.INSERT_NODES:
0212: insertNodes(m);
0213: break;
0214: case J3dMessage.REMOVE_NODES:
0215: removeNodes(m);
0216: break;
0217: case J3dMessage.SOUND_ATTRIB_CHANGED:
0218: changeNodeAttrib(m);
0219: break;
0220: case J3dMessage.SOUND_STATE_CHANGED:
0221: changeNodeState(m);
0222: break;
0223: case J3dMessage.BOUNDINGLEAF_CHANGED:
0224: processBoundingLeafChanged(m);
0225: break;
0226: case J3dMessage.SOUNDSCAPE_CHANGED:
0227: SoundscapeRetained ss = (SoundscapeRetained) m.args[0];
0228: if (universe.soundStructure
0229: .isSoundscapeScopedToView(ss, view)) {
0230: auralAttribsChanged = true;
0231: changeNodeAttrib(m);
0232: }
0233: break;
0234: case J3dMessage.AURALATTRIBUTES_CHANGED:
0235: auralAttribsChanged = true;
0236: changeNodeAttrib(m);
0237: break;
0238: case J3dMessage.MEDIA_CONTAINER_CHANGED:
0239: changeNodeAttrib(m);
0240: break;
0241: case J3dMessage.TRANSFORM_CHANGED:
0242: transformMsg = true;
0243: auralAttribsChanged = true;
0244: break;
0245: case J3dMessage.RENDER_IMMEDIATE:
0246: processImmediateNodes(m.args, referenceTime);
0247: break;
0248: case J3dMessage.VIEWSPECIFICGROUP_CHANGED:
0249: processViewSpecificGroupChanged(m);
0250: break;
0251: case J3dMessage.UPDATE_VIEW:
0252: if (debugFlag)
0253: debugPrint(".processMessage() UPDATE_VIEW");
0254: // NOTE: can only rely on seeing UPDATE_VIEW when canvas [re]Created
0255: // AND when view deactivated...
0256: // NOTE:
0257: // temp work-around
0258: // calling prioritizeSounds() wipes out old atom fields
0259: // QUESTION: prioritizedSound is NEVER empty - why if size is 0 can
0260: // .isEmpty return anything but TRUE???
0261: //
0262: if (prioritizedSounds.isEmpty()) {
0263: nSounds = prioritizeSounds();
0264: }
0265: break;
0266: case J3dMessage.SWITCH_CHANGED:
0267: if (debugFlag)
0268: debugPrint(".processMessage() "
0269: + "SWITCH_CHANGED ignored");
0270: break;
0271: } // switch
0272: m.decRefcount();
0273: } // for
0274: if (transformMsg) {
0275: targets = universe.transformStructure.getTargetList();
0276: updateTransformChange(targets, referenceTime);
0277: transformMsg = false;
0278: targets = null;
0279: }
0280: Arrays.fill(messages, 0, nMsg, null);
0281: }
0282:
0283: // Call renderChanges within try/catch so errors won't kill
0284: // the SoundScheduler.
0285: try {
0286: renderChanges();
0287: } catch (RuntimeException e) {
0288: System.err.println("Exception occurred "
0289: + "during Sound rendering:");
0290: e.printStackTrace();
0291: } catch (Error e) {
0292: // Issue 264 - catch Error
0293: System.err.println("Error occurred "
0294: + "during Sound rendering:");
0295: e.printStackTrace();
0296: }
0297:
0298: // what if the user/app makes no change to scenegraph?
0299: // must still re-render after retest for sound complete
0300: // calculate which sound will finished first and set a
0301: // wait time to this shortest time so that scheduler is
0302: // re-entered to process sound complete.
0303:
0304: long waitTime = shortestTimeToFinish();
0305:
0306: if (waitTime == 0L) {
0307: // come right back
0308: if (debugFlag)
0309: debugPrint(".processMessage calls sendRunMessage "
0310: + "for immediate processing");
0311: VirtualUniverse.mc.sendRunMessage(universe,
0312: J3dThread.SOUND_SCHEDULER);
0313: } else if (waitTime > 0L) {
0314: // Use TimerThread to send message with sounds complete.
0315: // This uses waitForElapse time to sleep for at least the duration
0316: // returned by shortestTimeToFinish method.
0317: if (debugFlag)
0318: debugPrint(".processMessage calls sendRunMessage "
0319: + "with wait time = " + waitTime);
0320: // QUESTION (ISSUE): even when this is set to a large time
0321: // processMessage is reentered immediately.
0322: // Why is timer thread not waiting??
0323: VirtualUniverse.mc.sendRunMessage(waitTime, view,
0324: J3dThread.SOUND_SCHEDULER);
0325: }
0326: }
0327:
0328: void insertNodes(J3dMessage m) {
0329: Object[] nodes = (Object[]) m.args[0];
0330: ArrayList viewScopedNodes = (ArrayList) m.args[3];
0331: ArrayList scopedNodesViewList = (ArrayList) m.args[4];
0332: Object node;
0333:
0334: for (int i = 0; i < nodes.length; i++) {
0335: node = (Object) nodes[i];
0336: if (node instanceof SoundRetained) {
0337: nRetainedSounds++;
0338: // insert sound node into sound scheduler's prioritized list
0339: addSound((SoundRetained) node);
0340: } else if (node instanceof SoundscapeRetained) {
0341: auralAttribsChanged = true;
0342: } else if (node instanceof AuralAttributesRetained) {
0343: auralAttribsChanged = true;
0344: } else if (node instanceof ViewPlatformRetained) {
0345: // XXXX: don't support multiple viewPlatforms per scheduler
0346: /*
0347: // useful for resetting VP ??
0348: addViewPlatform((ViewPlatformRetained) node);
0349: */
0350: if (debugFlag) {
0351: debugPrint(".insertNodes() viewPlatformRetained not supported yet");
0352: }
0353: }
0354: }
0355:
0356: // Handle ViewScoped Nodes
0357: if (viewScopedNodes != null) {
0358: int size = viewScopedNodes.size();
0359: int vlsize;
0360: for (int i = 0; i < size; i++) {
0361: node = (NodeRetained) viewScopedNodes.get(i);
0362: ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
0363: // If the node object is scoped to this view, then ..
0364: if (vl.contains(view)) {
0365: if (node instanceof SoundRetained) {
0366: nRetainedSounds++;
0367: // insert sound node into sound scheduler's prioritized list
0368: addSound((SoundRetained) node);
0369: } else if (node instanceof SoundscapeRetained) {
0370: auralAttribsChanged = true;
0371: }
0372: }
0373: }
0374: }
0375: }
0376:
0377: /**
0378: * Add sound to sounds list.
0379: */
0380: void addSound(SoundRetained sound) {
0381: if (sound == null)
0382: return;
0383: if (debugFlag)
0384: debugPrint(".addSound()");
0385: synchronized (prioritizedSounds) {
0386: addPrioritizedSound(sound);
0387: }
0388: } // end addSound
0389:
0390: /**
0391: * Node removed from tree
0392: */
0393: void removeNodes(J3dMessage m) {
0394: Object[] nodes = (Object[]) m.args[0];
0395: ArrayList viewScopedNodes = (ArrayList) m.args[3];
0396: ArrayList scopedNodesViewList = (ArrayList) m.args[4];
0397: Object node;
0398:
0399: for (int i = 0; i < nodes.length; i++) {
0400: node = (Object) nodes[i];
0401: if (node instanceof SoundRetained) {
0402: // sound is deactivated but NOT deleted
0403: // incase sound is reattached
0404:
0405: // QUESTION: what's the difference in messages between really deleting
0406: // a node and just deactivating it.
0407: /*
0408: //
0409: // can't delete atom in case it's reactivitated
0410: //
0411: nRetainedSounds--;
0412: deleteSound((SoundRetained) node);
0413: //
0414: // until there's a difference in messages between detaching and deleting
0415: // a sound node, sound is stopped and atom enable state changed but atom
0416: // is NOT deleted from list.
0417: */
0418: SoundSchedulerAtom soundAtom = null;
0419: for (int arrIndx = 1;; arrIndx++) {
0420: soundAtom = findSoundAtom((SoundRetained) node,
0421: arrIndx);
0422: if (soundAtom == null)
0423: break;
0424: stopSound(soundAtom, false);
0425: }
0426: } else if (node instanceof SoundscapeRetained) {
0427: auralAttribsChanged = true;
0428: }
0429: }
0430: // Handle ViewScoped Nodes
0431: if (viewScopedNodes != null) {
0432: int size = viewScopedNodes.size();
0433: int vlsize;
0434: for (int i = 0; i < size; i++) {
0435: node = (NodeRetained) viewScopedNodes.get(i);
0436: ArrayList vl = (ArrayList) scopedNodesViewList.get(i);
0437: // If the node object is scoped to this view, then ..
0438: if (vl.contains(view)) {
0439: if (node instanceof SoundRetained) {
0440: SoundSchedulerAtom soundAtom = null;
0441: for (int arrIndx = 1;; arrIndx++) {
0442: soundAtom = findSoundAtom(
0443: (SoundRetained) node, arrIndx);
0444: if (soundAtom == null)
0445: break;
0446: stopSound(soundAtom, false);
0447: }
0448: } else if (node instanceof SoundscapeRetained) {
0449: auralAttribsChanged = true;
0450: }
0451: }
0452: }
0453: }
0454:
0455: }
0456:
0457: // deletes all instances of the sound nodes from the priority list
0458: void deleteSound(SoundRetained sound) {
0459: if (sound != null)
0460: return;
0461: if (debugFlag)
0462: debugPrint(".deleteSound()");
0463: synchronized (prioritizedSounds) {
0464: if (!prioritizedSounds.isEmpty()) {
0465: // find sound in list and remove it
0466: int arrSize = prioritizedSounds.size();
0467: for (int index = 0; index < arrSize; index++) {
0468: SoundSchedulerAtom soundAtom = (SoundSchedulerAtom) prioritizedSounds
0469: .get(index);
0470: // QUESTION: which???
0471: if (soundAtom.sound == sound
0472: || soundAtom.sound.sgSound == sound) {
0473: stopSound(soundAtom, false);
0474: prioritizedSounds.remove(index);
0475: }
0476: }
0477: }
0478: }
0479: }
0480:
0481: void changeNodeAttrib(J3dMessage m) {
0482: Object node = m.args[0];
0483: Object value = m.args[1];
0484: int attribDirty = ((Integer) value).intValue();
0485: if (debugFlag)
0486: debugPrint(".changeNodeAttrib:");
0487:
0488: if (node instanceof SoundRetained
0489: && universe.soundStructure.isSoundScopedToView(node,
0490: view)) {
0491:
0492: this .setAttribsDirtyFlag((SoundRetained) node, attribDirty);
0493: if (debugFlag)
0494: debugPrint(" Sound node dirty bit = "
0495: + attribDirty);
0496: if ((attribDirty & SoundRetained.PRIORITY_DIRTY_BIT) > 0) {
0497: shuffleSound((SoundRetained) node);
0498: }
0499: if ((attribDirty & SoundRetained.SOUND_DATA_DIRTY_BIT) > 0) {
0500: if (debugFlag)
0501: debugPrint(".changeNodeAttrib "
0502: + "SOUND_DATA_DIRTY_BIT calls loadSound");
0503: loadSound((SoundRetained) node, true);
0504: }
0505: if ((attribDirty & SoundRetained.MUTE_DIRTY_BIT) > 0) {
0506: if (debugFlag)
0507: debugPrint(" MuteDirtyBit is on");
0508: muteSound((SoundRetained) node);
0509: }
0510: if ((attribDirty & SoundRetained.PAUSE_DIRTY_BIT) > 0) {
0511: if (debugFlag)
0512: debugPrint(" PauseDirtyBit is on");
0513: pauseSound((SoundRetained) node);
0514: }
0515: } else if (node instanceof SoundscapeRetained
0516: && universe.soundStructure.isSoundscapeScopedToView(
0517: node, view)) {
0518: auralAttribsChanged = true;
0519: } else if (node instanceof AuralAttributesRetained) {
0520: auralAttribsChanged = true;
0521: } else if (node instanceof MediaContainerRetained) {
0522: int listSize = ((Integer) m.args[2]).intValue();
0523: ArrayList userList = (ArrayList) m.args[3];
0524: for (int i = 0; i < listSize; i++) {
0525: SoundRetained sound = (SoundRetained) userList.get(i);
0526: if (sound != null) {
0527: loadSound(sound, true);
0528: if (debugFlag)
0529: debugPrint(".changeNodeAttrib "
0530: + "MEDIA_CONTAINER_CHANGE calls loadSound");
0531: }
0532: }
0533: }
0534: }
0535:
0536: void changeNodeState(J3dMessage m) {
0537: Object node = m.args[0];
0538: Object value = m.args[1];
0539: if (debugFlag)
0540: debugPrint(".changeNodeState:");
0541: if (node instanceof SoundRetained
0542: && universe.soundStructure.isSoundScopedToView(node,
0543: view)) {
0544: int stateDirty = ((Integer) value).intValue();
0545: setStateDirtyFlag((SoundRetained) node, stateDirty);
0546: if (debugFlag)
0547: debugPrint(" Sound node dirty bit = "
0548: + stateDirty);
0549: if ((stateDirty & SoundRetained.LIVE_DIRTY_BIT) > 0) {
0550: if (debugFlag)
0551: debugPrint(".changeNodeState LIVE_DIRTY_BIT "
0552: + "calls loadSound");
0553: loadSound((SoundRetained) node, false);
0554: }
0555: if ((stateDirty & SoundRetained.ENABLE_DIRTY_BIT) > 0) {
0556: if (debugFlag)
0557: debugPrint(" EnableDirtyBit is on");
0558: if (((Boolean) m.args[4]).booleanValue()) {
0559: enableSound((SoundRetained) node);
0560: } else {
0561: SoundSchedulerAtom soundAtom;
0562: SoundRetained soundRetained = (SoundRetained) node;
0563: for (int i = prioritizedSounds.size() - 1; i >= 0; i--) {
0564: soundAtom = ((SoundSchedulerAtom) prioritizedSounds
0565: .get(i));
0566: if (soundAtom.sound.sgSound == soundRetained) {
0567: // ignore soundRetained.release
0568: // flag which is not implement
0569: turnOff(soundAtom);
0570: // Fix to Issue 431.
0571: soundAtom.enable(soundRetained.enable);
0572: }
0573: }
0574: }
0575: }
0576: }
0577: }
0578:
0579: void shuffleSound(SoundRetained sound) {
0580: // Find sound atom that references this sound node and
0581: // reinsert it into prioritized sound list by removing atom for
0582: // this sound from priority list, then re-add it.
0583: // Assumes priority has really changed since a message is not sent
0584: // to the scheduler if the 'new' priority value isn't different.
0585: deleteSound(sound); // remove atom for this sound
0586: addSound(sound); // then re-insert it back into list in new position
0587: }
0588:
0589: void loadSound(SoundRetained sound, boolean forceReload) {
0590: // find sound atom that references this sound node
0591: // QUESTION: "node" probably not mirror node?
0592: SoundSchedulerAtom soundAtom = null;
0593: for (int i = 1;; i++) {
0594: soundAtom = findSoundAtom(sound, i);
0595: if (soundAtom == null)
0596: break;
0597: MediaContainer mediaContainer = sound.getSoundData();
0598: if (forceReload
0599: || soundAtom.loadStatus != SoundRetained.LOAD_COMPLETE) {
0600: if (debugFlag)
0601: debugPrint(": not LOAD_COMPLETE - try attaching");
0602: attachSoundData(soundAtom, mediaContainer, forceReload);
0603: }
0604: }
0605: }
0606:
0607: void enableSound(SoundRetained sound) {
0608: if (debugFlag)
0609: debugPrint(".enableSound " + sound);
0610: // find sound atom that references this sound node
0611: SoundSchedulerAtom soundAtom = null;
0612: for (int i = 1;; i++) {
0613: soundAtom = findSoundAtom(sound, i);
0614: if (soundAtom == null)
0615: break;
0616: // Set atom enabled field based on current Sound node
0617: // enable boolean flag
0618: soundAtom.enable(sound.enable);
0619: }
0620: }
0621:
0622: void muteSound(SoundRetained sound) {
0623: // make mute pending
0624: // mute -> MAKE-SILENT
0625: // unmute -> MAKE-AUDIBLE
0626: if (debugFlag)
0627: debugPrint(".muteSound " + sound);
0628: // find sound atom that references this sound node
0629: SoundSchedulerAtom soundAtom = null;
0630: for (int i = 1;; i++) {
0631: soundAtom = findSoundAtom(sound, i);
0632: if (soundAtom == null)
0633: break;
0634: // Set atom mute field based on node current
0635: // mute boolean flag
0636: soundAtom.mute(sound.mute);
0637: }
0638: }
0639:
0640: void pauseSound(SoundRetained sound) {
0641: // make pause pending
0642: // Pause is a separate action
0643: // When resumed it has to reset its state
0644: // PAUSE_AUDIBLE
0645: // PAUSE_SILENT
0646: // RESUME_AUDIBLE
0647: // RESUME_SILENT
0648: // to whatever it was before
0649: if (debugFlag)
0650: debugPrint(".pauseSound " + sound);
0651: // find sound atom that references this sound node
0652: SoundSchedulerAtom soundAtom = null;
0653: for (int i = 1;; i++) {
0654: soundAtom = findSoundAtom(sound, i);
0655: if (soundAtom == null)
0656: break;
0657: // Set atom pause field based on node's current
0658: // pause boolean flag
0659: soundAtom.pause(sound.pause);
0660: }
0661: }
0662:
0663: void processImmediateNodes(Object[] args, long referenceTime) {
0664: Object command = args[0];
0665: Object newNode = args[1];
0666: Object oldNode = args[2];
0667: Sound oldSound = (Sound) oldNode;
0668: Sound newSound = (Sound) newNode;
0669: int action = ((Integer) command).intValue();
0670: if (debugFlag)
0671: debugPrint(".processImmediateNodes() - action = " + action);
0672: switch (action) {
0673: case GraphicsContext3D.ADD_SOUND:
0674: case GraphicsContext3D.INSERT_SOUND:
0675: addSound((SoundRetained) newSound.retained);
0676: nImmedSounds++;
0677: break;
0678: case GraphicsContext3D.REMOVE_SOUND:
0679: deleteSound((SoundRetained) oldSound.retained);
0680: nImmedSounds--;
0681: break;
0682: case GraphicsContext3D.SET_SOUND:
0683: deleteSound((SoundRetained) oldSound.retained);
0684: addSound((SoundRetained) newSound.retained);
0685: break;
0686: }
0687: }
0688:
0689: void updateTransformChange(UpdateTargets targets, long referenceTime) {
0690: // node.updateTransformChange() called immediately rather than
0691: // waiting for updateObject to be called and process xformChangeList
0692: // which apprears to only happen when sound started...
0693:
0694: UnorderList arrList = targets.targetList[Targets.SND_TARGETS];
0695: if (arrList != null) {
0696: int j, i;
0697: Object nodes[], nodesArr[];
0698: int size = arrList.size();
0699: nodesArr = arrList.toArray(false);
0700:
0701: for (j = 0; j < size; j++) {
0702: nodes = (Object[]) nodesArr[j];
0703:
0704: for (i = 0; i < nodes.length; i++) {
0705: if (nodes[i] instanceof ConeSoundRetained
0706: && universe.soundStructure
0707: .isSoundScopedToView(nodes[i], view)) {
0708: ConeSoundRetained cnSndNode = (ConeSoundRetained) nodes[i];
0709: synchronized (cnSndNode) {
0710: cnSndNode.updateTransformChange();
0711: }
0712: // set XFORM_DIRTY_BIT in corresponding atom
0713: setStateDirtyFlag((SoundRetained) nodes[i],
0714: SoundRetained.XFORM_DIRTY_BIT);
0715: } else if (nodes[i] instanceof PointSoundRetained
0716: && universe.soundStructure
0717: .isSoundScopedToView(nodes[i], view)) {
0718: PointSoundRetained ptSndNode = (PointSoundRetained) nodes[i];
0719: synchronized (ptSndNode) {
0720: ptSndNode.updateTransformChange();
0721: }
0722: // set XFORM_DIRTY_BIT in corresponding atom
0723: setStateDirtyFlag((SoundRetained) nodes[i],
0724: SoundRetained.XFORM_DIRTY_BIT);
0725: } else if (nodes[i] instanceof SoundscapeRetained
0726: && universe.soundStructure
0727: .isSoundscapeScopedToView(nodes[i],
0728: view)) {
0729: SoundscapeRetained sndScapeNode = (SoundscapeRetained) nodes[i];
0730: synchronized (sndScapeNode) {
0731: sndScapeNode.updateTransformChange();
0732: }
0733: }
0734: }
0735: }
0736: }
0737: }
0738:
0739: void updateTransformedFields(SoundRetained mirSound) {
0740: if (mirSound instanceof ConeSoundRetained
0741: && universe.soundStructure.isSoundScopedToView(
0742: mirSound, view)) {
0743: ConeSoundRetained cnSndNode = (ConeSoundRetained) mirSound;
0744: synchronized (cnSndNode) {
0745: cnSndNode.updateTransformChange();
0746: }
0747: } else if (mirSound instanceof PointSoundRetained
0748: && universe.soundStructure.isSoundScopedToView(
0749: mirSound, view)) {
0750: PointSoundRetained ptSndNode = (PointSoundRetained) mirSound;
0751: synchronized (ptSndNode) {
0752: ptSndNode.updateTransformChange();
0753: }
0754: }
0755: }
0756:
0757: void activate() {
0758: updateThread.active = true;
0759: if (debugFlag)
0760: debugPrint(".activate(): calls sendRunMessage for immediate processing");
0761: VirtualUniverse.mc.sendRunMessage(universe,
0762: J3dThread.SOUND_SCHEDULER);
0763: // renderChanges() called indirectly thru processMessage now
0764: }
0765:
0766: // deactivate scheduler only if it state has such that it can not perform
0767: // sound processing
0768: void deactivate() {
0769: if (debugFlag)
0770: debugPrint(".deactivate()");
0771: //
0772:
0773: // XXXX: The following code is clearly erroneous.
0774: // The indendation, along with the 2nd test of
0775: // "if (debugFlag)" in the else clause, suggests that
0776: // the intent was to make the else clause apply to
0777: // "if (checkState())". However, the else clause
0778: // actually applies to the first "if (debugFlag)".
0779: // This is a textbook example of why one should
0780: // *ALWAYS* enclose the target of an "if", "while", or
0781: // "else" in curly braces -- even when the target
0782: // consists of a single statement.
0783: //
0784: // The upshot of this, is that the else clause is
0785: // unconditionally executed, since "debugFlag" is a
0786: // static final constant that is set to false for
0787: // production builds. We won't fix it now, because
0788: // The SoundScheduler may actually be relying on the
0789: // fact that all sounds are unconditionally
0790: // deactivated when this method is called, and we
0791: // don't want to introduce a new bug.
0792: //
0793: if (checkState())
0794: if (debugFlag)
0795: debugPrint(" checkState returns true");
0796: else {
0797: if (debugFlag)
0798: debugPrint(" checkState returns false; deactive scheduler");
0799: // Call deactivateAllSounds within try/catch so
0800: // errors won't kill the SoundScheduler.
0801: try {
0802: deactivateAllSounds();
0803: } catch (RuntimeException e) {
0804: System.err.println("Exception occurred "
0805: + "during sound deactivation:");
0806: e.printStackTrace();
0807: } catch (Error e) {
0808: // Issue 264 - catch Error
0809: System.err.println("Error occurred "
0810: + "during sound deactivation:");
0811: e.printStackTrace();
0812: }
0813: updateThread.active = false;
0814: }
0815: }
0816:
0817: // Check the ready state and return true if ready
0818: boolean checkState() {
0819: boolean runState = false;
0820: if (stallThread) {
0821: if (debugFlag)
0822: debugPrint(" checkState stallThread true");
0823: runState = false;
0824: }
0825:
0826: if (ready) {
0827: if (debugFlag)
0828: debugPrint(" checkState ready to run");
0829: runState = true;
0830: } else {
0831: // previous not ready, force reset call to see if everything
0832: // ready now or not
0833: reset();
0834: if (ready) {
0835: if (debugFlag)
0836: debugPrint(" checkState Now ready to run");
0837: runState = true;
0838: } else {
0839: if (debugFlag) {
0840: debugPrint(" checkState NOT ready to run");
0841: }
0842: runState = false;
0843: }
0844: }
0845: return runState;
0846: }
0847:
0848: synchronized void reset() {
0849: // Return quickly if universe, view, physical env, or audio
0850: // device are null
0851: if (universe == null || view == null
0852: || view.physicalEnvironment == null
0853: || view.physicalEnvironment.audioDevice == null) {
0854:
0855: audioDevice = null;
0856: ready = false;
0857: return;
0858: }
0859:
0860: // Set AudioDevice
0861: audioDevice = view.physicalEnvironment.audioDevice;
0862:
0863: // Get viewPlatform; if it is null or not live, we can't render
0864: ViewPlatform vp = view.getViewPlatform();
0865: if (vp == null || vp.retained == null) {
0866: // System.err.println(" vp is null");
0867: viewPlatform = null;
0868: ready = false;
0869: return;
0870: }
0871:
0872: viewPlatform = (ViewPlatformRetained) vp.retained;
0873: if (!vp.isLive()) {
0874: ready = false;
0875: return;
0876: }
0877:
0878: // XXXX: Does not support multiple canvases per view, thus
0879: // multiple GraphicsContext3Ds
0880: // QUESTION: what does that mean for sound -
0881: // being applied to only ONE graphics context?
0882: // GET FIRST Canvas
0883: Canvas3D canvas = view.getFirstCanvas();
0884: if (canvas != null) {
0885: graphicsCtx = canvas.getGraphicsContext3D();
0886: }
0887:
0888: // now the render loop can be run successfully
0889: audioDevice3DL2 = null;
0890: audioDevice3D = null;
0891: if (audioDevice instanceof AudioDevice3DL2) {
0892: audioDevice3DL2 = (AudioDevice3DL2) audioDevice;
0893: }
0894: if (audioDevice instanceof AudioDevice3D) {
0895: audioDevice3D = (AudioDevice3D) audioDevice;
0896: if (debugFlag)
0897: debugPrint(".reset: audioDevice3D.setView");
0898: audioDevice3D.setView(view);
0899: totalChannels = audioDevice.getTotalChannels();
0900: if (debugFlag)
0901: debugPrint(" audioDevice3D.getTotalChannels returned "
0902: + totalChannels);
0903: } else {
0904: if (internalErrors)
0905: debugPrint(": AudioDevice implementation not supported");
0906: totalChannels = 0;
0907: }
0908:
0909: if (totalChannels == 0) {
0910: ready = false;
0911: return;
0912: }
0913:
0914: ready = true;
0915: // since audio device is ready; set enable flag for continuous
0916: // calculating userHead-to-VirtualWorld transform
0917: view.setUserHeadToVworldEnable(true);
0918: return;
0919: }
0920:
0921: void receiveAWTEvent(AWTEvent evt) {
0922: int eventId = evt.getID();
0923: if (debugFlag)
0924: debugPrint(".receiveAWTEvent " + eventId);
0925: if (ready && eventId == WindowEvent.WINDOW_ICONIFIED) {
0926: lastEventReceived = eventId;
0927: } else if (ready
0928: && (lastEventReceived == WindowEvent.WINDOW_ICONIFIED && eventId == WindowEvent.WINDOW_DEICONIFIED)) {
0929: lastEventReceived = eventId;
0930: // used to notify
0931: }
0932: }
0933:
0934: /**
0935: * The main loop for the Sound Scheduler.
0936: */
0937: void renderChanges() {
0938: int nSounds = 0;
0939: int totalChannelsUsed = 0;
0940: int nPrioritizedSound = 0;
0941: int numActiveSounds = 0;
0942: if (debugFlag)
0943: debugPrint(" renderChanges begun");
0944: // XXXX: BUG?? should wait if audioDevice is NULL or nSounds = 0
0945: // when a new sound is added or deleted from list, or
0946: // when the audioDevice is set into PhysicalEnvironment
0947:
0948: if (!checkState()) {
0949: if (debugFlag)
0950: debugPrint(".workToDo() checkState failed");
0951: return;
0952: }
0953:
0954: /*
0955: synchronized (prioritizedSounds) {
0956: */
0957: nPrioritizedSound = prioritizedSounds.size();
0958: if (debugFlag)
0959: debugPrint(" nPrioritizedSound = " + nPrioritizedSound);
0960: if (nPrioritizedSound == 0)
0961: return;
0962:
0963: if (auralAttribsChanged) {
0964: // Find closest active aural attributes in scene graph
0965: int nIntersected = findActiveSoundscapes();
0966: if (nIntersected > 0) {
0967: if (debugFlag)
0968: debugPrint(" " + nIntersected
0969: + " active SoundScapes found");
0970: // XXXX: (Performance) calling clone everytime, even
0971: // though closest AA has NOT changed, is expensive
0972: aaRetained = (AuralAttributesRetained) (findClosestAAttribs(nIntersected))
0973: .clone();
0974: } else {
0975: if (debugFlag)
0976: debugPrint(" NO active SoundScapes found");
0977: }
0978: }
0979: if (nPrioritizedSound > 0) {
0980: calcSchedulingAction();
0981: muteSilentSounds();
0982:
0983: // short term flag set within performActions->update()
0984: positionalSoundUpdated = false;
0985:
0986: // if listener parameters changed re-set View parameters
0987: if (testListenerFlag()) {
0988: if (debugFlag)
0989: debugPrint(" audioDevice3D.setView");
0990: audioDevice3D.setView(view);
0991: }
0992:
0993: numActiveSounds = performActions();
0994:
0995: if (positionalSoundUpdated) {
0996: // if performActions updated at least one positional sound
0997: // was processed so the listener/view changes were processed,
0998: // thus we can clear the SoundScheduler dirtyFlag, otherwise
0999: // leave the flag dirty until a positional sound is updated
1000: clearListenerFlag(); // clears listenerUpdated flag
1001: }
1002: }
1003: /*
1004: }
1005: */
1006: }
1007:
1008: /**
1009: * Prioritize all sounds associated with SoundScheduler (view)
1010: * This only need be done once when scheduler is initialized since
1011: * the priority list is updated when:
1012: * a) PRIORITY_DIRTY_BIT in soundDirty field set; or
1013: * b) sound added or removed from live array list
1014: */
1015: int prioritizeSounds() {
1016: int size;
1017: synchronized (prioritizedSounds) {
1018: if (!prioritizedSounds.isEmpty()) {
1019: prioritizedSounds.clear();
1020: }
1021: // XXXX: sync soundStructure sound list
1022: UnorderList retainedSounds = universe.soundStructure
1023: .getSoundList(view);
1024: // QUESTION: what is in this sound list??
1025: // mirror node or actual node???
1026: nRetainedSounds = 0;
1027: nImmedSounds = 0;
1028: if (debugFlag)
1029: debugPrint(" prioritizeSound , num retained sounds"
1030: + retainedSounds.size());
1031: for (int i = 0; i < retainedSounds.size(); i++) {
1032: addPrioritizedSound((SoundRetained) retainedSounds
1033: .get(i));
1034: nRetainedSounds++;
1035: }
1036: // XXXX: sync canvases
1037: Enumeration canvases = view.getAllCanvas3Ds();
1038: while (canvases.hasMoreElements()) {
1039: Canvas3D canvas = (Canvas3D) canvases.nextElement();
1040: GraphicsContext3D graphicsContext = canvas
1041: .getGraphicsContext3D();
1042: Enumeration nonretainedSounds = graphicsContext
1043: .getAllSounds();
1044: while (nonretainedSounds.hasMoreElements()) {
1045: if (debugFlag)
1046: debugPrint(" prioritizeSound , get non-retained sound");
1047: Sound sound = (Sound) nonretainedSounds
1048: .nextElement();
1049: if (sound == null) {
1050: if (debugFlag)
1051: debugPrint(" prioritizeSound , sound element is null");
1052: // QUESTION: why should I have to do this?
1053: continue;
1054: }
1055: addPrioritizedSound((SoundRetained) sound.retained);
1056: nImmedSounds++;
1057: }
1058: }
1059: if (debugFlag)
1060: debugPrint(" prioritizeSound , num of processed retained sounds"
1061: + nRetainedSounds);
1062: debugPrint(" prioritizeSound , num of processed non-retained sounds"
1063: + nImmedSounds);
1064: size = prioritizedSounds.size();
1065: } // sync
1066: return size;
1067: }
1068:
1069: // methods that call this should synchronize prioritizedSounds
1070: void addPrioritizedSound(SoundRetained mirSound) {
1071: SoundRetained sound = mirSound.sgSound;
1072: if (sound == null) { // this mirSound is a nonretained sound
1073: // pad the "child" sg sound pointer with itself
1074: mirSound.sgSound = mirSound;
1075: sound = mirSound;
1076: if (debugFlag)
1077: debugPrint(":addPritorizedSound() sound NULL");
1078: }
1079: boolean addAtom = false;
1080: // see if this atom is in the list already
1081: // covers the case where the node was detached or unswitched but NOT
1082: // deleted (so sample already loaded
1083: // QUESTION: is above logic correct???
1084: SoundSchedulerAtom atom = null;
1085: atom = findSoundAtom(mirSound, 1); // look thru list for 1st instance
1086: if (atom == null) {
1087: atom = new SoundSchedulerAtom();
1088: atom.soundScheduler = this ; // save scheduler atom is associated with
1089: addAtom = true;
1090: }
1091:
1092: // update fields in atom based on sound nodes state
1093: atom.sound = mirSound; // new mirror sound
1094: updateTransformedFields(mirSound);
1095:
1096: if (!addAtom) {
1097: return;
1098: }
1099:
1100: // if this atom being added then set the enable state
1101: atom.enable(sound.enable);
1102:
1103: if (prioritizedSounds.isEmpty()) {
1104: // List is currently empty, so just add it
1105: // insert into empty list of prioritizedSounds
1106: prioritizedSounds.add(atom);
1107: if (debugFlag)
1108: debugPrint(":addPritorizedSound() inset sound "
1109: + mirSound + " into empty priority list");
1110: } else {
1111: // something's in the proirity list already
1112: // Since list is not empty insert sound into list.
1113: //
1114: // Order is highest to lowest priority values, and
1115: // for sounds with equal priority values, sound
1116: // inserted first get in list given higher priority.
1117: SoundRetained jSound;
1118: SoundSchedulerAtom jAtom;
1119: int j;
1120: int jsounds = (prioritizedSounds.size() - 1);
1121: float soundPriority = sound.priority;
1122: for (j = jsounds; j >= 0; j--) {
1123: jAtom = (SoundSchedulerAtom) prioritizedSounds.get(j);
1124: jSound = jAtom.sound;
1125: if (debugFlag)
1126: debugPrint(": priority of sound " + jSound.sgSound
1127: + " element " + (j + 1)
1128: + " of prioritized list");
1129: if (soundPriority <= jSound.sgSound.priority) {
1130: if (j == jsounds) {
1131: // last element's priority is larger than
1132: // current sound's priority, so add this
1133: // sound to the end of the list
1134: prioritizedSounds.add(atom);
1135: if (debugFlag)
1136: debugPrint(": insert sound at list bottom");
1137: break;
1138: } else {
1139: if (debugFlag)
1140: debugPrint(": insert sound as list element "
1141: + (j + 1));
1142: prioritizedSounds.add(j + 1, atom);
1143: break;
1144: }
1145: }
1146: } // for loop
1147: if (j < 0) { // insert at the top of the list
1148: if (debugFlag)
1149: debugPrint(": insert sound at top of priority list");
1150: prioritizedSounds.add(0, atom);
1151: }
1152: } // else list not empty
1153: }
1154:
1155: /**
1156: * Process active Soundscapes (if there are any) and intersect these
1157: * soundscapes with the viewPlatform.
1158: *
1159: * Returns the number of soundscapes that intesect with
1160: * view volume.
1161: */
1162: int findActiveSoundscapes() {
1163: int nSscapes = 0;
1164: int nSelectedSScapes = 0;
1165: SoundscapeRetained ss = null;
1166: SoundscapeRetained lss = null;
1167: boolean intersected = false;
1168: int nUnivSscapes = 0;
1169: UnorderList soundScapes = null;
1170:
1171: // Make a copy of references to the soundscapes in the universe
1172: // that are both switch on and have non-null (transformed) regions,
1173: // don't bother testing for intersection with view.
1174: if (universe == null) {
1175: if (debugFlag)
1176: debugPrint(".findActiveSoundscapes() univ=null");
1177: return 0;
1178: }
1179: soundScapes = universe.soundStructure.getSoundscapeList(view);
1180: if (soundScapes == null) {
1181: if (debugFlag)
1182: debugPrint(".findActiveSoundscapes() soundScapes null");
1183: return 0;
1184: }
1185:
1186: synchronized (soundScapes) {
1187: nUnivSscapes = soundScapes.size;
1188: if (nUnivSscapes == 0) {
1189: if (debugFlag)
1190: debugPrint(".findActiveSoundscapes() soundScapes size=0");
1191: return 0;
1192: }
1193:
1194: // increase arrays lengths by increments of 32 elements
1195: if (intersectedRegions.length < nSscapes) {
1196: intersectedRegions = new Bounds[nSscapes + 32];
1197: }
1198: if (intersectedSoundscapes.length < nSscapes) {
1199: intersectedSoundscapes = new SoundscapeRetained[nSscapes + 32];
1200: }
1201:
1202: // nSscapes is incremented for every Soundscape found
1203: if (debugFlag)
1204: debugPrint(".findActiveSoundscapes() nUnivSscapes="
1205: + nUnivSscapes);
1206: nSelectedSScapes = 0;
1207: for (int k = 0; k < nUnivSscapes; k++) {
1208: lss = (SoundscapeRetained) soundScapes.get(k);
1209:
1210: // Find soundscapes that intersect the view platform region
1211: if (lss.transformedRegion == null) {
1212: continue;
1213: }
1214: if ((region instanceof BoundingSphere && lss.transformedRegion instanceof BoundingSphere)
1215: || (region instanceof BoundingBox && lss.transformedRegion instanceof BoundingBox)
1216: || (region instanceof BoundingPolytope && lss.transformedRegion instanceof BoundingPolytope)) {
1217: lss.transformedRegion.getWithLock(region);
1218: if (debugFlag)
1219: debugPrint(" get tranformed region " + region);
1220: } else {
1221: region = (Bounds) lss.transformedRegion.clone();
1222: if (debugFlag)
1223: debugPrint(" clone tranformed region " + region);
1224: }
1225: if (region != null
1226: && viewPlatform.schedSphere.intersect(region)) {
1227: intersectedRegions[nSelectedSScapes] = (Bounds) region
1228: .clone();
1229: // test View Platform intersection(lss))
1230: intersectedSoundscapes[nSelectedSScapes] = lss;
1231: if (debugFlag)
1232: debugPrint(" store sscape " + lss
1233: + " and region " + region
1234: + " into array");
1235: nSelectedSScapes++;
1236: if (debugFlag)
1237: debugPrint(": region of intersection for "
1238: + "soundscape " + k + " found at "
1239: + J3dClock.currentTimeMillis());
1240: } else {
1241: if (debugFlag)
1242: debugPrint(": region of intersection for soundscape "
1243: + k
1244: + " not found at "
1245: + J3dClock.currentTimeMillis());
1246: }
1247: }
1248: }
1249: return (nSelectedSScapes);
1250: }
1251:
1252: AuralAttributesRetained findClosestAAttribs(int nSelectedSScapes) {
1253: AuralAttributes aa = null;
1254: SoundscapeRetained ss = null;
1255: boolean intersected = false;
1256:
1257: // Get auralAttributes from closest (non-null) soundscape
1258: ss = null;
1259: if (nSelectedSScapes == 1)
1260: ss = intersectedSoundscapes[0];
1261: else if (nSelectedSScapes > 1) {
1262: Bounds closestRegions;
1263: closestRegions = viewPlatform.schedSphere
1264: .closestIntersection(intersectedRegions);
1265: for (int j = 0; j < intersectedRegions.length; j++) {
1266: if (debugFlag)
1267: debugPrint(" element " + j
1268: + " in intersectedSoundsscapes is "
1269: + intersectedRegions[j]);
1270: if (intersectedRegions[j] == closestRegions) {
1271: ss = intersectedSoundscapes[j];
1272: if (debugFlag)
1273: debugPrint(" element " + j
1274: + " is closest");
1275: break;
1276: }
1277: }
1278: }
1279:
1280: if (ss != null) {
1281: if (debugFlag)
1282: debugPrint(" closest SoundScape found is " + ss);
1283: aa = ss.getAuralAttributes();
1284: if (aa != null) {
1285: if (debugFlag)
1286: debugPrint(": AuralAttribute for "
1287: + "soundscape is NOT null");
1288: } else {
1289: if (debugFlag)
1290: debugPrint(": AuralAttribute for " + "soundscape "
1291: + ss + " is NULL");
1292: }
1293: } else {
1294: if (debugFlag)
1295: debugPrint(": AuralAttribute is null "
1296: + "since soundscape is NULL");
1297: }
1298:
1299: if (debugFlag)
1300: debugPrint(" auralAttrib for closest SoundScape found is "
1301: + aa);
1302: return ((AuralAttributesRetained) aa.retained);
1303: }
1304:
1305: /**
1306: * Send current aural attributes to audio device
1307: *
1308: * Note that a AA's dirtyFlag is clear only after parameters are sent to
1309: * audio device.
1310: */
1311: void updateAuralAttribs(AuralAttributesRetained attribs) {
1312: if (auralAttribsChanged) {
1313: if (attribs != null) {
1314: synchronized (attribs) {
1315: /*
1316: // XXXX: remove use of aaDirty from AuralAttrib node
1317: if ((attribs != lastAA) || attribs.aaDirty)
1318: */
1319: if (debugFlag) {
1320: debugPrint(" set real updateAuralAttribs because");
1321: }
1322:
1323: // Send current aural attributes to audio device
1324: // Assumes that aural attribute parameter is NOT null.
1325: audioDevice3D.setRolloff(attribs.rolloff);
1326: if (debugFlag)
1327: debugPrint(" rolloff " + attribs.rolloff);
1328:
1329: // Distance filter parameters
1330: int arraySize = attribs.getDistanceFilterLength();
1331: if ((attribs.filterType == AuralAttributesRetained.NO_FILTERING)
1332: || arraySize == 0) {
1333: audioDevice3D.setDistanceFilter(
1334: attribs.NO_FILTERING, null, null);
1335: if (debugFlag)
1336: debugPrint(" no filtering");
1337: } else {
1338: Point2f[] attenuation = new Point2f[arraySize];
1339: for (int i = 0; i < arraySize; i++)
1340: attenuation[i] = new Point2f();
1341: attribs.getDistanceFilter(attenuation);
1342: double[] distance = new double[arraySize];
1343: float[] cutoff = new float[arraySize];
1344: for (int i = 0; i < arraySize; i++) {
1345: distance[i] = attenuation[i].x;
1346: cutoff[i] = attenuation[i].y;
1347: }
1348: audioDevice3D.setDistanceFilter(
1349: attribs.filterType, distance, cutoff);
1350: if (debugFlag) {
1351: debugPrint(" filtering parameters: "
1352: + " distance, cutoff arrays");
1353: for (int jj = 0; jj < arraySize; jj++)
1354: debugPrint(" " + distance[jj]
1355: + ", " + cutoff[jj]);
1356: }
1357: }
1358: audioDevice3D
1359: .setFrequencyScaleFactor(attribs.frequencyScaleFactor);
1360: if (debugFlag)
1361: debugPrint(" freq.scale "
1362: + attribs.frequencyScaleFactor);
1363: audioDevice3D
1364: .setVelocityScaleFactor(attribs.velocityScaleFactor);
1365: if (debugFlag)
1366: debugPrint(" velocity scale "
1367: + attribs.velocityScaleFactor);
1368: audioDevice3D
1369: .setReflectionCoefficient(attribs.reflectionCoefficient);
1370: if (audioDevice3DL2 != null) {
1371: audioDevice3DL2
1372: .setReverbCoefficient(attribs.reverbCoefficient);
1373: audioDevice3DL2
1374: .setDecayFilter(attribs.decayFilter);
1375: audioDevice3DL2.setDiffusion(attribs.diffusion);
1376: audioDevice3DL2.setDensity(attribs.density);
1377: if (debugFlag) {
1378: debugPrint(" reflect coeff "
1379: + attribs.reflectionCoefficient);
1380: debugPrint(" reverb coeff "
1381: + attribs.reverbCoefficient);
1382: debugPrint(" decay filter "
1383: + attribs.decayFilter);
1384: debugPrint(" diffusion "
1385: + attribs.diffusion);
1386: debugPrint(" density "
1387: + attribs.density);
1388: }
1389: }
1390:
1391: // Precidence for determining Reverb Delay
1392: // 1) If ReverbBounds set, bounds used to calculate delay.
1393: // Default ReverbBounds is null meaning it's not defined.
1394: // 2) If ReverbBounds is null, ReverbDelay used
1395: // (implitic default value is defined as 40ms)
1396:
1397: // If Bounds null, use Reverb Delay
1398: // else calculate Reverb Delay from Bounds
1399: Bounds reverbVolume = attribs.reverbBounds;
1400: if (reverbVolume == null) {
1401: audioDevice3D
1402: .setReverbDelay(attribs.reverbDelay);
1403: if (debugFlag)
1404: debugPrint(" reverbDelay "
1405: + attribs.reverbDelay);
1406: } else {
1407: float reverbDelay;
1408: if (reverbVolume instanceof BoundingSphere) {
1409: // worst (slowest) case is if sound in center of
1410: // bounding sphere
1411: reverbDelay = (float) ((2.0 * ((BoundingSphere) reverbVolume).radius) / AuralAttributesRetained.SPEED_OF_SOUND);
1412: } else {
1413: // create a bounding sphere the surrounds the bounding
1414: // object then calcalate the worst case as above
1415: BoundingSphere tempSphere = new BoundingSphere(
1416: reverbVolume);
1417: reverbDelay = (float) ((2.0 * tempSphere.radius) / AuralAttributesRetained.SPEED_OF_SOUND);
1418: }
1419: audioDevice3D.setReverbDelay(reverbDelay);
1420: if (debugFlag)
1421: debugPrint(" reverbDelay "
1422: + reverbDelay);
1423: }
1424:
1425: // Audio device makes calculations for reverb decay
1426: // using API's precidence where positive reverb order
1427: // is used to clamp reverb decay time.
1428: // Because of that the values are just passed along.
1429: audioDevice3D.setReverbOrder(attribs.reverbOrder);
1430: if (audioDevice3DL2 != null)
1431: audioDevice3DL2.setDecayTime(attribs.decayTime);
1432: } // sync attribs
1433: resetAA = true;
1434: } // attribs not null
1435:
1436: else if (lastAA != null) {
1437: // Even when there are no active auralAttributes, still check
1438: // if a set of auralAttributes was passed to the AudioDevice,
1439: // thus is now inactive and must be cleared out (set to
1440: // default values).
1441: // Gain scale factor of 1.0 implicit by default - this value
1442: // passed to AudioDevice3D as part of InitialScaleFactor value.
1443: if (debugFlag)
1444: debugPrint(": set updateDefaultAAs");
1445: audioDevice3D.setRolloff(1.0f);
1446: audioDevice3D.setReflectionCoefficient(0.0f);
1447: audioDevice3D.setReverbDelay(40.0f);
1448: audioDevice3D.setReverbOrder(0);
1449: // Distance filter parameters
1450: audioDevice3D.setDistanceFilter(
1451: AuralAttributesRetained.NO_FILTERING, null,
1452: null);
1453: audioDevice3D.setFrequencyScaleFactor(1.0f);
1454: audioDevice3D.setVelocityScaleFactor(0.0f);
1455: if (audioDevice3DL2 != null) {
1456: audioDevice3DL2.setReverbCoefficient(0.0f);
1457: audioDevice3DL2.setReflectionDelay(20.0f);
1458: audioDevice3DL2.setDecayTime(1000.0f);
1459: audioDevice3DL2.setDecayFilter(5000.0f);
1460: audioDevice3DL2.setDiffusion(1.0f);
1461: audioDevice3DL2.setDensity(1.0f);
1462: }
1463:
1464: // Any change in AuralAttrib should force an update of all
1465: // active sounds as passed to AudioDevice3D.
1466: resetAA = true;
1467: } else {
1468: if (debugFlag)
1469: debugPrint(" updateAuralAttribs: none set or cleared");
1470: }
1471: /*
1472: attribs.aaDirty = false;
1473: */
1474: lastAA = attribs;
1475: auralAttribsChanged = false;
1476: }
1477: }
1478:
1479: void processSoundAtom(SoundSchedulerAtom soundAtom) {
1480: SoundRetained mirSound = soundAtom.sound;
1481: SoundRetained sound = mirSound.sgSound;
1482:
1483: // Sounds that have finished playing are not put into list
1484: if ((soundAtom.status == SoundSchedulerAtom.SOUND_COMPLETE)
1485: && (soundAtom.enabled != SoundSchedulerAtom.PENDING_ON)) {
1486: // XXXX:/QUESTION test for immediate mode (?)
1487:
1488: // Unless the sound has been re-started, there's no need
1489: // to process sound the finished playing the last time thru
1490: // the run() loop
1491: return; // don't need to process unless re-started
1492: }
1493:
1494: // Sound must be loaded if it hasn't been successfully loaded already
1495: if (soundAtom.loadStatus != SoundRetained.LOAD_COMPLETE) {
1496: // should be OK for soundData to be NULL - this will unclear
1497: if (debugFlag)
1498: debugPrint(": LOAD_PENDING - try attaching");
1499: attachSoundData(soundAtom, sound.soundData, false);
1500: }
1501:
1502: // if the loadStatus is STILL NOT COMPLETE, wait to schedule
1503: if (soundAtom.loadStatus != SoundRetained.LOAD_COMPLETE) {
1504: if (debugFlag)
1505: debugPrint(": not LOAD_COMPLETE yet, so bail");
1506: // if sound is still not loaded, or loaded with null
1507: return; // don't process this sound now
1508: }
1509:
1510: if (resetAA) { // explicitly force update of gain for sound
1511: soundAtom
1512: .setAttribsDirtyFlag(SoundRetained.INITIAL_GAIN_DIRTY_BIT);
1513: }
1514:
1515: // Determine if sound is "active"
1516: // Immediate mode sounds are always active.
1517: // Sounds (both background and positional) are active only when their
1518: // scheduling region intersects the viewPlatform.
1519: boolean intersected = false;
1520: if (!updateThread.active) {
1521: region = null;
1522: intersected = false; // force sound to be made inactive
1523: if (debugFlag)
1524: debugPrint(": updateThread NOT active");
1525: } else if (sound.getInImmCtx()) {
1526: region = null;
1527: intersected = true;
1528: if (debugFlag)
1529: debugPrint(": sound.getInImmCtx TRUE");
1530: } else {
1531: if (sound.schedulingRegion != null
1532: && mirSound.transformedRegion != null) {
1533: // QUESTION: shouldn't mirror sound transformedRegion be
1534: // set to null when sound node's region set null?
1535: if ((region instanceof BoundingSphere && mirSound.transformedRegion instanceof BoundingSphere)
1536: || (region instanceof BoundingBox && mirSound.transformedRegion instanceof BoundingBox)
1537: || (region instanceof BoundingPolytope && mirSound.transformedRegion instanceof BoundingPolytope)) {
1538: mirSound.transformedRegion.getWithLock(region);
1539: } else {
1540: region = (Bounds) mirSound.transformedRegion
1541: .clone();
1542: }
1543: } else {
1544: region = null;
1545: }
1546:
1547: if (region != null) {
1548: if (debugFlag)
1549: debugPrint(": region is " + region);
1550: intersected = viewPlatform.schedSphere
1551: .intersect(region);
1552: if (debugFlag)
1553: debugPrint(" intersection with viewPlatform is "
1554: + intersected);
1555: } else {
1556: intersected = false;
1557: if (debugFlag)
1558: debugPrint(": region is null, "
1559: + "so region does NOT intersect viewPlatform");
1560: }
1561: }
1562:
1563: // Get scheduling action based on sound state (flags, status)
1564: // Sound must be unmuted or pending unmuting and
1565: // either immediate mode node, or if retained
1566: // then intersecting active region and switch state must be on.
1567: if (debugFlag) {
1568: debugPrint(": intersected = " + intersected);
1569: debugPrint(": switchState = " + mirSound.switchState);
1570: if (mirSound.switchState != null)
1571: debugPrint(": switchOn = "
1572: + mirSound.switchState.currentSwitchOn);
1573: debugPrint(": soundAtom.muted = " + soundAtom.muted);
1574: }
1575: if ((sound.getInImmCtx() || (intersected
1576: && mirSound.switchState != null && mirSound.switchState.currentSwitchOn))
1577: && (soundAtom.muted == soundAtom.UNMUTED || soundAtom.muted == soundAtom.PENDING_UNMUTE)) {
1578: if (debugFlag)
1579: debugPrint(": calcActiveSchedAction");
1580: soundAtom.schedulingAction = soundAtom
1581: .calcActiveSchedAction();
1582: } else {
1583: if (debugFlag)
1584: debugPrint(": calcInactiveSchedAction");
1585: soundAtom.schedulingAction = soundAtom
1586: .calcInactiveSchedAction();
1587: }
1588:
1589: if (debugFlag) {
1590: debugPrint(": scheduling action calculated " + "as "
1591: + soundAtom.schedulingAction);
1592: debugPrint(" dirtyFlag test of LISTENER_CHANGED "
1593: + testListenerFlag());
1594: }
1595:
1596: // If state has not changed but parameters have, set
1597: // action to UPDATE.
1598: // This test includes checking that SoundScheduler dirtyFlag
1599: // set when Eye, Ear or ImagePlate-to-Vworld Xform changed
1600: // even for non-BackgroundSounds
1601: if ((soundAtom.schedulingAction == SoundSchedulerAtom.LEAVE_AUDIBLE)
1602: && (soundAtom.testDirtyFlags() || (testListenerFlag() && !(sound instanceof BackgroundSoundRetained)))) {
1603: if (debugFlag) {
1604: if (testListenerFlag()) {
1605: debugPrint(" testListenerFlag = "
1606: + testListenerFlag());
1607: }
1608: debugPrint(": action changed from "
1609: + "as LEAVE_AUDIBLE to UPDATE");
1610: }
1611: soundAtom.schedulingAction = SoundSchedulerAtom.UPDATE;
1612: }
1613:
1614: // Update prioritized list of sounds whose scheduling action
1615: // demands processing...
1616:
1617: // Ensure sound are not stopped while looping thru prioritized sounds
1618: switch (soundAtom.schedulingAction) {
1619: case SoundSchedulerAtom.TURN_OFF:
1620: soundAtom.status = SoundSchedulerAtom.SOUND_OFF;
1621: turnOff(soundAtom); // Stop sound that need to be turned off
1622: if (debugFlag)
1623: debugPrint(": sound " + soundAtom.sampleId
1624: + " action OFF results in call to stop");
1625: soundAtom.schedulingAction = SoundSchedulerAtom.LEAVE_OFF;
1626: break;
1627:
1628: case SoundSchedulerAtom.MAKE_AUDIBLE:
1629: case SoundSchedulerAtom.MAKE_SILENT:
1630: case SoundSchedulerAtom.LEAVE_AUDIBLE:
1631: case SoundSchedulerAtom.LEAVE_SILENT:
1632: case SoundSchedulerAtom.PAUSE_AUDIBLE:
1633: case SoundSchedulerAtom.PAUSE_SILENT:
1634: case SoundSchedulerAtom.RESUME_AUDIBLE:
1635: case SoundSchedulerAtom.RESUME_SILENT:
1636: case SoundSchedulerAtom.UPDATE:
1637:
1638: // Test for sound finishing playing since the last
1639: // thru the run() loop.
1640: //
1641: // test current time against endTime of sound to determine
1642: // if sound is Completely finished playing
1643: long currentTime = J3dClock.currentTimeMillis();
1644: if (soundAtom.endTime > 0
1645: && soundAtom.endTime <= currentTime) {
1646: // sound's completed playing, force action
1647: soundAtom.schedulingAction = SoundSchedulerAtom.COMPLETE;
1648: if (debugFlag)
1649: debugPrint(": sample complete;" + " endTime = "
1650: + soundAtom.endTime + ", currentTime = "
1651: + currentTime + " so turned off");
1652: soundAtom.status = SoundSchedulerAtom.SOUND_COMPLETE;
1653: turnOff(soundAtom); // Stop sound in device that are complete
1654: if (debugFlag)
1655: debugPrint(": sound "
1656: + soundAtom.sampleId
1657: + " action COMPLETE results in call to stop");
1658: }
1659: break;
1660:
1661: case SoundSchedulerAtom.RESTART_AUDIBLE:
1662: case SoundSchedulerAtom.START_AUDIBLE:
1663: case SoundSchedulerAtom.RESTART_SILENT:
1664: case SoundSchedulerAtom.START_SILENT:
1665: break;
1666:
1667: default: // includes COMPLETE, DO_NOTHING
1668: soundAtom.schedulingAction = SoundSchedulerAtom.DO_NOTHING;
1669: break;
1670: } // switch
1671:
1672: if (debugFlag)
1673: debugPrint(": final scheduling action " + "set to "
1674: + soundAtom.schedulingAction);
1675: }
1676:
1677: /**
1678: * Determine scheduling action for each live sound
1679: */
1680: int calcSchedulingAction() {
1681: // Temp variables
1682: SoundRetained sound;
1683: SoundRetained mirSound;
1684: SoundSchedulerAtom soundAtom;
1685: SoundRetained jSound;
1686: int nSounds = 0;
1687: boolean processSound;
1688: // number of sounds to process including scene graph and immediate nodes
1689: int numSoundsToProcess = 0;
1690:
1691: if (universe == null) {
1692: if (debugFlag)
1693: debugPrint(": calcSchedulingAction: univ NULL");
1694: return 0;
1695: }
1696: if (universe.soundStructure == null) {
1697: if (debugFlag)
1698: debugPrint(": calcSchedulingAction: soundStructure NULL");
1699: return 0;
1700: }
1701:
1702: // List of prioritized "live" sounds taken from universe list of sounds.
1703: // Maintained as an expandable array - start out with a small number of
1704: // elements for this array then grow the list larger if necessary...
1705: synchronized (prioritizedSounds) {
1706: nSounds = prioritizedSounds.size();
1707: if (debugFlag)
1708: debugPrint(": calcSchedulingAction: soundsList size = "
1709: + nSounds);
1710:
1711: // (Large) Loop over all switched on sounds and conditionally put
1712: // these into a order prioritized list of sound.
1713: // Try throw out as many sounds as we can:
1714: // Sounds finished playing (reached end before stopped)
1715: // Sounds still yet to be loaded
1716: // Positional sounds whose regions don't intersect view
1717: // Sound to be stopped
1718: // Those sounds remaining are inserted into a prioritized list
1719:
1720: for (int i = 0; i < nSounds; i++) {
1721: soundAtom = ((SoundSchedulerAtom) prioritizedSounds
1722: .get(i));
1723: mirSound = soundAtom.sound;
1724: sound = mirSound.sgSound;
1725: if (debugFlag) {
1726: debugPrint(" calcSchedulingAction: sound at "
1727: + sound);
1728: printAtomState(soundAtom);
1729: }
1730: // First make a list of switched on live sounds
1731: // make sure to process turned-off sounds even if they are
1732: // NOT active
1733: processSound = false;
1734: if ((!sound.source.isLive() && !sound.getInImmCtx())) {
1735: if (debugFlag) {
1736: debugPrint(" calcSchedulingAction sound "
1737: + sound + " is NOT Live");
1738: if (mirSound.source != null) {
1739: if (mirSound.source.isLive() != sound.source
1740: .isLive())
1741: debugPrint(" !=!=!= sound.isLive != mirSound");
1742: }
1743: }
1744: if (soundAtom.playing
1745: || (soundAtom.enabled == SoundSchedulerAtom.ON)) {
1746: soundAtom
1747: .setEnableState(SoundSchedulerAtom.PENDING_OFF);
1748: processSound = true;
1749: if (debugFlag)
1750: debugPrint(" calcSchedulingAction !isLive: "
1751: + "sound playing or ON, so set PENDING_OFF");
1752: } else if (soundAtom.enabled == SoundSchedulerAtom.PENDING_OFF) {
1753: processSound = true;
1754: if (debugFlag)
1755: debugPrint(" calcSchedulingAction !isLive: "
1756: + "sound == PENDING_OFF so process");
1757: } else if (soundAtom.enabled == SoundSchedulerAtom.PENDING_ON) {
1758: soundAtom
1759: .setEnableState(SoundSchedulerAtom.OFF);
1760: if (debugFlag)
1761: debugPrint(" calcSchedulingAction !isLive: "
1762: + "sound == PENDING_ON so set OFF");
1763: }
1764: } else { // live and switched on retained node or
1765: // non-retained (immediate mode) node
1766: processSound = true;
1767: }
1768:
1769: if (processSound) {
1770: numSoundsToProcess++;
1771: if (debugFlag) {
1772: debugPrint(".testListenerFlag = "
1773: + testListenerFlag() + "....");
1774: debugPrint(" contents of live sound "
1775: + soundAtom.sampleId
1776: + " before processing,");
1777: debugPrint(" >>>>>>sound using sgSound at "
1778: + sound);
1779: printAtomState(soundAtom);
1780: }
1781: processSoundAtom(soundAtom);
1782: } // end of process sound
1783: else {
1784: soundAtom.schedulingAction = SoundSchedulerAtom.DO_NOTHING;
1785: } // end of not process sound
1786:
1787: } // end loop over all sound in soundList
1788: } // sync
1789:
1790: if (debugFlag) {
1791: if (numSoundsToProcess > 0)
1792: debugPrint(": number of liveSounds = "
1793: + numSoundsToProcess);
1794: else
1795: debugPrint(": number of liveSounds <= 0");
1796: }
1797:
1798: return numSoundsToProcess;
1799: }
1800:
1801: /**
1802: * Mute sounds that are to be played silently.
1803: *
1804: * Not all the sound in the prioritized enabled sound list
1805: * may be able to be played. Due to low priority, some sounds
1806: * must be muted/silenced (if such an action frees up channel
1807: * resources) to make way for sounds with higher priority.
1808: * For each sound in priority list:
1809: * For sounds whose actions are X_SILENT:
1810: * Mute sounds to be silenced
1811: * Add the number of channels used by this muted sound to
1812: * current total number of channels used
1813: * For all remaining sounds (with actions other than above)
1814: * The number of channels that 'would be used' to play
1815: * potentially audible sounds is compared with
1816: * the number left on the device:
1817: * If this sound would use more channels than available
1818: * Change it's X_AUDIBLE action to X_SILENT
1819: * Mute sounds to be silenced
1820: * Add the number of channels used by this sound, muted
1821: * or not, to current total number of channels used
1822: *
1823: * NOTE: requests for sounds to play beyond channel capability of
1824: * the audio device do NOT throw an exception when more sounds are
1825: * started than can be played. Rather the unplayable sounds are
1826: * muted. It is up to the AudioDevice3D implementation to determine
1827: * how muted/silent sounds are implememted (playing with gain zero
1828: * and thus using up channel resources, or stop and restarted with
1829: * correct offset when inactivated then re-actived.
1830: */
1831: void muteSilentSounds() {
1832: // Temp variables
1833: SoundRetained sound;
1834: SoundRetained mirSound;
1835: int totalChannelsUsed = 0;
1836: SoundSchedulerAtom soundAtom;
1837: int nAtoms;
1838: synchronized (prioritizedSounds) {
1839: nAtoms = prioritizedSounds.size();
1840: if (debugFlag)
1841: debugPrint(".muteSilentSounds(): Loop over prioritizedSounds list, "
1842: + "size = " + nAtoms);
1843: for (int i = 0; i < nAtoms; i++) {
1844: soundAtom = (SoundSchedulerAtom) prioritizedSounds
1845: .get(i);
1846: mirSound = (SoundRetained) soundAtom.sound;
1847: sound = mirSound.sgSound;
1848: int sampleId = soundAtom.sampleId;
1849: int status = soundAtom.status;
1850:
1851: if (debugFlag) {
1852: debugPrint(": contents of current sound "
1853: + soundAtom.sampleId
1854: + " before switch on sAction");
1855: printAtomState(soundAtom);
1856: }
1857:
1858: if (soundAtom.status == SoundSchedulerAtom.SOUND_COMPLETE) {
1859: continue;
1860: }
1861: if (soundAtom.schedulingAction == SoundSchedulerAtom.DO_NOTHING) {
1862: continue;
1863: }
1864: if (sampleId == SoundRetained.NULL_SOUND) {
1865: // skip it until next time thru calcSchedulingAction
1866: continue;
1867: }
1868: if ((soundAtom.schedulingAction == SoundSchedulerAtom.MAKE_SILENT)
1869: || (soundAtom.schedulingAction == SoundSchedulerAtom.RESTART_SILENT)
1870: || (soundAtom.schedulingAction == SoundSchedulerAtom.LEAVE_SILENT)
1871: || (soundAtom.schedulingAction == SoundSchedulerAtom.START_SILENT)) {
1872: // Mute sounds that are not already silent
1873: if (status != SoundSchedulerAtom.SOUND_SILENT) {
1874: // old status is not already muted/silent
1875: audioDevice3D.muteSample(sampleId);
1876: if (debugFlag)
1877: debugPrint(": sound "
1878: + sampleId
1879: + " action is x_SILENT, sound muted");
1880: }
1881: // now that the exact muting state is known get the actual
1882: // number of channels used by this sound and add to total
1883: int numberChannels = audioDevice3D
1884: .getNumberOfChannelsUsed(sampleId);
1885: soundAtom.numberChannels = numberChannels; // used in audio device
1886: totalChannelsUsed += numberChannels; // could return zero
1887: } // scheduling is for silent sound
1888:
1889: else {
1890: // First, test to see if the sound can play as unmuted.
1891: int numberChannels = audioDevice3D
1892: .getNumberOfChannelsUsed(sampleId, false);
1893: // Mute sounds that have too low priority
1894: if ((totalChannelsUsed + numberChannels) > totalChannels) {
1895: if ((soundAtom.schedulingAction == SoundSchedulerAtom.MAKE_AUDIBLE)
1896: || (soundAtom.schedulingAction == SoundSchedulerAtom.LEAVE_AUDIBLE)) {
1897: soundAtom.schedulingAction = SoundSchedulerAtom.MAKE_SILENT;
1898: } else if (soundAtom.schedulingAction == SoundSchedulerAtom.RESTART_AUDIBLE)
1899: soundAtom.schedulingAction = SoundSchedulerAtom.RESTART_SILENT;
1900: else if (soundAtom.schedulingAction == SoundSchedulerAtom.START_AUDIBLE)
1901: soundAtom.schedulingAction = SoundSchedulerAtom.START_SILENT;
1902: else if (soundAtom.schedulingAction == SoundSchedulerAtom.PAUSE_AUDIBLE)
1903: soundAtom.schedulingAction = SoundSchedulerAtom.PAUSE_SILENT;
1904: else if (soundAtom.schedulingAction == SoundSchedulerAtom.RESUME_AUDIBLE)
1905: soundAtom.schedulingAction = SoundSchedulerAtom.RESUME_SILENT;
1906: audioDevice3D.muteSample(sampleId);
1907: if (debugFlag) {
1908: debugPrint(": sound " + sampleId
1909: + "number of channels needed is "
1910: + numberChannels);
1911: debugPrint(": sound "
1912: + sampleId
1913: + " action is x_AUDIBLE but "
1914: + "not enough channels free ("
1915: + (totalChannels - totalChannelsUsed)
1916: + ") so, sound muted");
1917: }
1918: }
1919: // sound has enough channels to play
1920: else if (status != SoundSchedulerAtom.SOUND_AUDIBLE) {
1921: // old status is not already unmuted/audible
1922: audioDevice3D.unmuteSample(sampleId);
1923: if (debugFlag)
1924: debugPrint(": sound "
1925: + sampleId
1926: + " action is x_AUDIBLE and channels free so, "
1927: + "sound unmuted");
1928: }
1929: // now that the exact muting state is known (re-)get actual
1930: // number of channels used by this sound and add to total
1931: numberChannels = audioDevice3D
1932: .getNumberOfChannelsUsed(sampleId);
1933: soundAtom.numberChannels = numberChannels; // used in audio device
1934: totalChannelsUsed += numberChannels;
1935: } // otherwise, scheduling is for potentally audible sound
1936: // No sound in list should have action TURN_ or LEAVE_OFF
1937: } // of for loop over sounds in list
1938: }
1939: }
1940:
1941: void muteSilentSound(SoundSchedulerAtom soundAtom) {
1942: // Temp variables
1943: SoundRetained sound;
1944: SoundRetained mirSound;
1945: mirSound = (SoundRetained) soundAtom.sound;
1946: sound = mirSound.sgSound;
1947: int sampleId = soundAtom.sampleId;
1948: int status = soundAtom.status;
1949:
1950: if (status == SoundSchedulerAtom.SOUND_COMPLETE) {
1951: return;
1952: }
1953: if (sampleId == SoundRetained.NULL_SOUND) {
1954: return;
1955: }
1956: if (debugFlag) {
1957: debugPrint(": contents of current sound "
1958: + soundAtom.sampleId + " before switch on sAction");
1959: printAtomState(soundAtom);
1960: }
1961:
1962: if ((soundAtom.schedulingAction == SoundSchedulerAtom.MAKE_SILENT)
1963: || (soundAtom.schedulingAction == SoundSchedulerAtom.RESTART_SILENT)
1964: || (soundAtom.schedulingAction == SoundSchedulerAtom.LEAVE_SILENT)
1965: || (soundAtom.schedulingAction == SoundSchedulerAtom.START_SILENT)) {
1966: // Mute sounds that are not already silent
1967: if (status != SoundSchedulerAtom.SOUND_SILENT) {
1968: // old status is not already muted/silent
1969: audioDevice3D.muteSample(sampleId);
1970: if (debugFlag)
1971: debugPrint(": sound " + sampleId
1972: + " action is x_SILENT, sound muted");
1973: }
1974: } // scheduling is for silent sound
1975: }
1976:
1977: /**
1978: * Determine amount of time before next playing sound will be
1979: * is complete.
1980: *
1981: * find the atom that has the least amount of time before is
1982: * finished playing and return this time
1983: * @return length of time in millisecond until the next active sound
1984: * will be complete. Returns -1 if no sounds are playing (or all are
1985: * complete).
1986: */
1987: long shortestTimeToFinish() {
1988: long currentTime = J3dClock.currentTimeMillis();
1989: long shortestTime = -1L;
1990: SoundSchedulerAtom soundAtom;
1991: synchronized (prioritizedSounds) {
1992: int nAtoms = prioritizedSounds.size();
1993: for (int i = 0; i < nAtoms; i++) {
1994: soundAtom = (SoundSchedulerAtom) prioritizedSounds
1995: .get(i);
1996: if (soundAtom.status == SoundSchedulerAtom.SOUND_OFF
1997: || soundAtom.status == SoundSchedulerAtom.SOUND_COMPLETE)
1998: continue;
1999: long endTime = soundAtom.endTime;
2000: if (endTime < 0)
2001: // skip sounds that are to play infinitely (until stopped)
2002: continue;
2003: if (debugFlag) {
2004: if (endTime == 0)
2005: debugPrint(".shortestTimeToFinish: "
2006: + "Internal Error - endTime 0 while sound playing");
2007: }
2008: // for all playing sounds (audible and silent) find how much
2009: // time is left before the sound completed playing
2010: long timeLeft = endTime - currentTime;
2011: if (debugFlag)
2012: debugPrint(" shortestTimeToFinish timeLeft = "
2013: + timeLeft);
2014: if (timeLeft < 0L) {
2015: // normalize completed sounds; force to zero
2016: // so no waiting occurs before scheduler re-entered
2017: timeLeft = 0L;
2018: }
2019: if (shortestTime < 0L) {
2020: // save first atom's time as shortest
2021: shortestTime = timeLeft;
2022: } else if (timeLeft < shortestTime) {
2023: shortestTime = timeLeft;
2024: }
2025: }
2026: }
2027: if (debugFlag)
2028: debugPrint(".shortestTimeToFinish returns " + shortestTime);
2029: return shortestTime;
2030: }
2031:
2032: /**
2033: * Perform the scheduling action for each prioritized sound.
2034: *
2035: * Now, finally, the scheduling action value reflects what is
2036: * requested and what is physically posible to perform.
2037: * So, for each sound in list of prioritized enabled sounds,
2038: * start/update sounds that are REALLY supposed to be either
2039: * playing audibly or playing silently.
2040: * @return number of active (audible and silent) sounds
2041: */
2042: int performActions() {
2043: // Temp variables
2044: SoundRetained sound;
2045: SoundRetained mirSound;
2046: int nAtoms;
2047: SoundSchedulerAtom soundAtom;
2048: AuralAttributesRetained attribs;
2049: int numActiveSounds = 0;
2050: int sampleId;
2051:
2052: synchronized (prioritizedSounds) {
2053: nAtoms = prioritizedSounds.size();
2054: for (int i = 0; i < nAtoms; i++) {
2055: // XXXX: (Enhancement) Get all sound node fields here
2056: // and store locally for performance
2057: soundAtom = (SoundSchedulerAtom) prioritizedSounds
2058: .get(i);
2059: mirSound = soundAtom.sound;
2060: sound = mirSound.sgSound;
2061: sampleId = soundAtom.sampleId;
2062:
2063: if (sampleId == SoundRetained.NULL_SOUND) {
2064: // skip it until next time thru calcSchedulingAction
2065: continue;
2066: }
2067:
2068: // Two flags denoting that AuralAttributes have be changed and thus
2069: // sounds have to potentially be rendered are maintained and set in
2070: // updateAuralAttribs().
2071: resetAA = false;
2072:
2073: // check to see if aural attributes changed and have to be updated
2074: // must be done before list of sound processed so that Aural Attributes
2075: // that affect Sound fields can be set in AudioDevice
2076: // XXXX: this is not effient if auralAttribs always the same
2077: if (sound.getInImmCtx()) {
2078: if (graphicsCtx != null
2079: && graphicsCtx.auralAttributes != null) {
2080: aaImmed = (AuralAttributesRetained) (graphicsCtx.auralAttributes.retained);
2081: attribs = aaImmed;
2082: } else {
2083: attribs = null;
2084: }
2085: } else {
2086: attribs = aaRetained;
2087: }
2088: updateAuralAttribs(attribs);
2089:
2090: if (debugFlag) {
2091: debugPrint(": contents of current sound "
2092: + sampleId + " before start/update ");
2093: printAtomState(soundAtom);
2094: }
2095:
2096: switch (soundAtom.schedulingAction) {
2097: case SoundSchedulerAtom.RESTART_AUDIBLE:
2098: // stop sound first then fall thru to re-start
2099: turnOff(soundAtom);
2100: case SoundSchedulerAtom.START_AUDIBLE:
2101: // Pause and Resume related actions are checked when sound
2102: // is to be started or restarted
2103: if (soundAtom.paused == soundAtom.PENDING_PAUSE)
2104: pause(soundAtom);
2105: if (soundAtom.paused == soundAtom.PENDING_UNPAUSE)
2106: unpause(soundAtom);
2107: if (soundAtom.paused == soundAtom.UNPAUSED) {
2108: // if its unpaused, start audible sound
2109: soundAtom.status = SoundSchedulerAtom.SOUND_AUDIBLE;
2110: render(true, soundAtom, attribs);
2111: } else { // sound paused
2112: soundAtom.status = SoundSchedulerAtom.SOUND_PAUSED;
2113: // start it after when the sound is not paused
2114: soundAtom
2115: .setEnableState(SoundSchedulerAtom.PENDING_ON);
2116: }
2117: numActiveSounds++;
2118: break;
2119:
2120: case SoundSchedulerAtom.RESTART_SILENT:
2121: // stop sound first then fall thru to re-start
2122: turnOff(soundAtom);
2123: case SoundSchedulerAtom.START_SILENT:
2124: // Pause and Resume related actions are checked when sound
2125: // is to be started or restarted
2126: if (soundAtom.paused == soundAtom.PENDING_PAUSE)
2127: pause(soundAtom);
2128: if (soundAtom.paused == soundAtom.PENDING_UNPAUSE)
2129: unpause(soundAtom);
2130: if (soundAtom.paused == soundAtom.UNPAUSED) {
2131: // if the sound is unpaused, start silent sound
2132: soundAtom.status = SoundSchedulerAtom.SOUND_SILENT;
2133: render(true, soundAtom, attribs);
2134: } else { // sound paused
2135: soundAtom.status = SoundSchedulerAtom.SOUND_PAUSED;
2136: // start it after when the sound is not paused
2137: soundAtom
2138: .setEnableState(SoundSchedulerAtom.PENDING_ON);
2139: }
2140: numActiveSounds++;
2141: break;
2142:
2143: case SoundSchedulerAtom.RESUME_AUDIBLE:
2144: // pause then fall thru set make audible
2145: unpause(soundAtom);
2146: case SoundSchedulerAtom.MAKE_AUDIBLE:
2147: // change status to audible then update sound
2148: soundAtom.status = SoundSchedulerAtom.SOUND_AUDIBLE;
2149: render(false, soundAtom, attribs);
2150: numActiveSounds++;
2151: break;
2152: case SoundSchedulerAtom.RESUME_SILENT:
2153: // pause then fall thru set make silent
2154: unpause(soundAtom);
2155: case SoundSchedulerAtom.MAKE_SILENT:
2156: // change status to silent AFTER calling render so
2157: // that a currently audible sound will be muted.
2158: // XXXX: why set status AFTER??
2159: render(false, soundAtom, attribs);
2160: soundAtom.status = SoundSchedulerAtom.SOUND_SILENT;
2161: numActiveSounds++;
2162: break;
2163:
2164: case SoundSchedulerAtom.PAUSE_AUDIBLE:
2165: case SoundSchedulerAtom.PAUSE_SILENT:
2166: pause(soundAtom);
2167: soundAtom.status = SoundSchedulerAtom.SOUND_PAUSED;
2168: numActiveSounds++;
2169: break;
2170:
2171: case SoundSchedulerAtom.UPDATE:
2172: render(false, soundAtom, attribs);
2173: numActiveSounds++;
2174: break;
2175:
2176: case SoundSchedulerAtom.LEAVE_AUDIBLE:
2177: case SoundSchedulerAtom.LEAVE_SILENT:
2178: case SoundSchedulerAtom.LEAVE_PAUSED:
2179: if (resetAA || soundAtom.testDirtyFlags())
2180: render(false, soundAtom, attribs);
2181: if (debugFlag)
2182: debugPrint(": LEAVE_AUDIBLE or _SILENT "
2183: + "seen");
2184: numActiveSounds++;
2185: break;
2186:
2187: case SoundSchedulerAtom.TURN_OFF:
2188: turnOff(soundAtom);
2189: break;
2190:
2191: case SoundSchedulerAtom.LEAVE_OFF:
2192: case SoundSchedulerAtom.COMPLETE:
2193: case SoundSchedulerAtom.DO_NOTHING:
2194: break;
2195:
2196: default:
2197: if (internalErrors)
2198: debugPrint(": Internal Error"
2199: + " unknown action");
2200: break;
2201: }
2202: // Clear atom state and attrib dirty flags
2203: soundAtom.clearStateDirtyFlag();
2204: soundAtom.clearAttribsDirtyFlag();
2205:
2206: } // for sounds in priority list
2207: }
2208:
2209: // Now that aural attribute change forced each processed sounds
2210: // to be updated, clear this special reset aural attrubute flag
2211: resetAA = false;
2212:
2213: return numActiveSounds;
2214: }
2215:
2216: /**
2217: * render (start or update) the oscillator associated with this sound
2218: */
2219: void render(boolean startFlag, SoundSchedulerAtom soundAtom,
2220: AuralAttributesRetained attribs) {
2221:
2222: SoundRetained mirrorSound = soundAtom.sound;
2223: SoundRetained sound = mirrorSound.sgSound;
2224: if (debugFlag)
2225: debugPrint(".render " + sound);
2226:
2227: if (soundAtom.sampleId == SoundRetained.NULL_SOUND
2228: || soundAtom.soundData == null) {
2229: if (internalErrors)
2230: debugPrint(".render - Internal Error: "
2231: + "null sample data");
2232: return;
2233: }
2234:
2235: int index = soundAtom.sampleId;
2236:
2237: // Depending on Mute and/or pause flags, set sound parameters
2238: if (startFlag) {
2239: if ((sound instanceof PointSoundRetained)
2240: || (sound instanceof ConeSoundRetained)) {
2241: updateXformedParams(true, soundAtom);
2242: }
2243: updateSoundParams(true, soundAtom, attribs);
2244: start(soundAtom);
2245: } else {
2246: if (soundAtom.status == SoundSchedulerAtom.SOUND_AUDIBLE) {
2247: if ((sound instanceof PointSoundRetained)
2248: || (sound instanceof ConeSoundRetained)) {
2249: updateXformedParams(false, soundAtom);
2250: }
2251: updateSoundParams(false, soundAtom, attribs);
2252: update(soundAtom);
2253: }
2254: } // if sound Audible
2255: } // render
2256:
2257: /**
2258: * Start the sample associated with this sound
2259: * Do everything necessary to start the sound:
2260: * set start time
2261: * the oscillator associated with this sound
2262: */
2263: void start(SoundSchedulerAtom soundAtom) {
2264: SoundRetained sound = soundAtom.sound.sgSound;
2265: int index = soundAtom.sampleId;
2266: int startStatus = -1;
2267: if (index != SoundRetained.NULL_SOUND
2268: && (startStatus = audioDevice3D.startSample(index)) >= 0) {
2269: if (debugFlag)
2270: debugPrint(".start: " + index);
2271: soundAtom.playing = true;
2272: soundAtom.startTime = audioDevice3D.getStartTime(index);
2273: soundAtom.calculateEndTime();
2274: if (debugFlag)
2275: debugPrint(".start: begintime = " + soundAtom.startTime
2276: + ", endtime " + soundAtom.endTime);
2277: } else { // error returned by audio device when trying to start
2278: soundAtom.startTime = 0;
2279: soundAtom.endTime = 0;
2280: soundAtom.playing = false;
2281: if (debugFlag) {
2282: debugPrint(".start: error " + startStatus
2283: + " returned by audioDevice3D.startSample("
2284: + index + ")");
2285: debugPrint(" start/endTime set to zero");
2286: }
2287: }
2288: }
2289:
2290: /**
2291: * Exlicitly update the sound parameters associated with a sample
2292: */
2293: void update(SoundSchedulerAtom soundAtom) {
2294: int index = soundAtom.sampleId;
2295:
2296: if (index == SoundRetained.NULL_SOUND) {
2297: return;
2298: }
2299: SoundRetained sound = soundAtom.sound;
2300: audioDevice3D.updateSample(index);
2301: if (debugFlag) {
2302: debugPrint(".update: " + index);
2303: }
2304: soundAtom.calculateEndTime();
2305: if (sound instanceof PointSoundRetained
2306: || sound instanceof ConeSoundRetained) {
2307: positionalSoundUpdated = true;
2308: }
2309: }
2310:
2311: /**
2312: * stop playing one specific sound node
2313: *
2314: * If setPending flag true, sound is stopped but enable state
2315: * is set to pending-on so that it is restarted.
2316: */
2317: void stopSound(SoundSchedulerAtom soundAtom, boolean setPending) {
2318: if (audioDevice3D == null)
2319: return;
2320:
2321: if (debugFlag)
2322: debugPrint(":stopSound(" + soundAtom + "), enabled = "
2323: + soundAtom.enabled);
2324: switch (soundAtom.enabled) {
2325: case SoundSchedulerAtom.ON:
2326: if (setPending)
2327: soundAtom.setEnableState(SoundSchedulerAtom.PENDING_ON);
2328: else
2329: soundAtom.setEnableState(SoundSchedulerAtom.SOUND_OFF);
2330: break;
2331: case SoundSchedulerAtom.PENDING_OFF:
2332: soundAtom.setEnableState(SoundSchedulerAtom.SOUND_OFF);
2333: break;
2334: case SoundSchedulerAtom.PENDING_ON:
2335: if (!setPending)
2336: // Pending sounds to be stop from playing later
2337: soundAtom.setEnableState(SoundSchedulerAtom.SOUND_OFF);
2338: break;
2339: default:
2340: break;
2341: }
2342: soundAtom.status = SoundSchedulerAtom.SOUND_OFF;
2343: turnOff(soundAtom);
2344: }
2345:
2346: /**
2347: * Deactive all playing sounds
2348: * If the sound is continuous thendSilence it but leave it playing
2349: * otherwise stop sound
2350: */
2351: synchronized void deactivateAllSounds() {
2352: SoundRetained sound;
2353: SoundRetained mirSound;
2354: SoundSchedulerAtom soundAtom;
2355:
2356: if (audioDevice3D == null)
2357: return;
2358:
2359: if (debugFlag)
2360: debugPrint(".deactivateAllSounds");
2361:
2362: // sync this method from interrupting run() while loop
2363: synchronized (prioritizedSounds) {
2364: if (prioritizedSounds != null) {
2365: int nAtoms = prioritizedSounds.size();
2366: if (debugFlag)
2367: debugPrint("silenceAll " + nAtoms + " Sounds");
2368: for (int i = 0; i < nAtoms; i++) {
2369: // XXXX: (Enhancement) Get all sound node fields here
2370: // and store locally for performance
2371: soundAtom = (SoundSchedulerAtom) prioritizedSounds
2372: .get(i);
2373: mirSound = soundAtom.sound;
2374: sound = mirSound.sgSound;
2375: if (sound.continuous) {
2376: // make playing sound silent
2377: if (debugFlag)
2378: debugPrint("deactivateAll atomScheduling "
2379: + "before calcInactiveSchedAction"
2380: + soundAtom.schedulingAction);
2381: soundAtom.schedulingAction = soundAtom
2382: .calcInactiveSchedAction();
2383: if (debugFlag)
2384: debugPrint("deactivateAll atomScheduling "
2385: + "after calcInactiveSchedAction"
2386: + soundAtom.schedulingAction);
2387: // perform muting of sound
2388: muteSilentSound(soundAtom); // mark sound as silence
2389: } else {
2390: // stop playing sound but make pending on
2391: stopSound(soundAtom, true); // force pendingOn TRUE
2392: soundAtom.schedulingAction = soundAtom.LEAVE_OFF;
2393: if (debugFlag)
2394: debugPrint("deactivateAll atomScheduling "
2395: + "forced to TURN_OFF, set pending On");
2396: }
2397:
2398: } // for sounds in priority list
2399: }
2400: }
2401: performActions();
2402: }
2403:
2404: /**
2405: * Pause all activity playing sounds
2406: */
2407: synchronized void pauseAllSounds() {
2408: SoundRetained sound;
2409: SoundRetained mirSound;
2410:
2411: if (audioDevice3D == null)
2412: return;
2413:
2414: stallThread = true;
2415: if (debugFlag)
2416: debugPrint(".pauseAll stallThread set to true");
2417:
2418: // sync this method from interrupting run() while loop
2419: synchronized (prioritizedSounds) {
2420: if (prioritizedSounds != null) {
2421: int nAtoms = prioritizedSounds.size();
2422: if (debugFlag)
2423: debugPrint(":pauseAll " + nAtoms + " Sounds");
2424: for (int i = 0; i < nAtoms; i++) {
2425: // XXXX: (Enhancement) Get all sound node fields here
2426: // and store locally for performance
2427: SoundSchedulerAtom soundAtom = (SoundSchedulerAtom) prioritizedSounds
2428: .get(i);
2429: mirSound = soundAtom.sound;
2430: sound = mirSound.sgSound;
2431:
2432: switch (soundAtom.enabled) {
2433: case SoundSchedulerAtom.ON:
2434: case SoundSchedulerAtom.PENDING_OFF:
2435: pause(soundAtom);
2436: if (debugFlag)
2437: debugPrint(".pauseAllSounds PAUSE sound "
2438: + sound);
2439: break;
2440: default:
2441: break;
2442: }
2443: } // for sounds in priority list
2444: }
2445: }
2446: }
2447:
2448: /**
2449: * Resume playing all paused active sounds
2450: */
2451: synchronized void resumeAllSounds() {
2452: SoundRetained sound;
2453: SoundRetained mirSound;
2454:
2455: if (audioDevice3D == null)
2456: return;
2457:
2458: if (debugFlag)
2459: debugPrint(".resumeAll stallThread set to true");
2460:
2461: // sync this method from interrupting run() while loop
2462: synchronized (prioritizedSounds) {
2463: if (prioritizedSounds != null) {
2464: int nAtoms = prioritizedSounds.size();
2465: if (debugFlag)
2466: debugPrint(": resumeAll " + nAtoms + " Sounds ");
2467:
2468: for (int i = 0; i < nAtoms; i++) {
2469: // XXXX: (Enhancement) Get all sound node fields here
2470: // and store locally for performance
2471: SoundSchedulerAtom soundAtom = (SoundSchedulerAtom) prioritizedSounds
2472: .get(i);
2473: mirSound = soundAtom.sound;
2474: sound = mirSound.sgSound;
2475:
2476: switch (soundAtom.enabled) {
2477: case SoundSchedulerAtom.ON:
2478: case SoundSchedulerAtom.PENDING_OFF:
2479: unpause(soundAtom);
2480: if (debugFlag)
2481: debugPrint(".resumeAll - sound = " + sound);
2482: break;
2483: default:
2484: break;
2485: }
2486: } // for sounds in priority list
2487: }
2488: }
2489: stallThread = false;
2490: }
2491:
2492: /**
2493: * Stop all activity playing sounds
2494: */
2495: synchronized void stopAllSounds() {
2496: stopAllSounds(false);
2497: }
2498:
2499: synchronized void stopAllSounds(boolean setPlayingSoundsPending) {
2500: // QUESTION: how can I assure that all sounds on device
2501: // are stopped before thread paused/shutdown
2502: if (debugFlag)
2503: debugPrint(".stopAllSounds entered");
2504: SoundRetained sound;
2505: SoundRetained mirSound;
2506:
2507: if (audioDevice3D == null)
2508: return;
2509:
2510: if (lastEventReceived == WindowEvent.WINDOW_ICONIFIED) {
2511: return; // leave sounds playing
2512: }
2513:
2514: // sync this method from interrupting run() while loop
2515: synchronized (prioritizedSounds) {
2516: if (prioritizedSounds != null) {
2517: int nAtoms = prioritizedSounds.size();
2518: if (debugFlag)
2519: debugPrint(": stopAll " + nAtoms + " Sounds ");
2520:
2521: for (int i = 0; i < nAtoms; i++) {
2522: // XXXX: (Enhancement) Get all sound node fields here
2523: // and store locally for performance
2524: SoundSchedulerAtom soundAtom = (SoundSchedulerAtom) prioritizedSounds
2525: .get(i);
2526: if (debugFlag)
2527: debugPrint(" stop(" + soundAtom + ")");
2528: // stop playing Sound - optionally set pending enabled
2529: stopSound(soundAtom, setPlayingSoundsPending);
2530: } // for sounds in priority list
2531:
2532: // QUESTION: - removed the code that empties out prioritized
2533: // sound atom list. Are there cases when core calling
2534: // StopAllSounds expects sounds to be cleared??
2535: }
2536: }
2537: if (debugFlag)
2538: debugPrint(".stopAllSounds exited");
2539: }
2540:
2541: // XXXX: Mute All Sounds, complementary to Stop All Sounds
2542: // "should return from run loop - but simply WAIT until sounds
2543: // are unmuted. " ???
2544:
2545: /**
2546: * pause the sample associated with this sound
2547: */
2548: void pause(SoundSchedulerAtom soundAtom) {
2549: if (soundAtom.sampleId == SoundRetained.NULL_SOUND)
2550: return;
2551: // Ensure sound are not modified while looping thru prioritized sounds
2552: if (debugFlag)
2553: debugPrint(".pause");
2554: audioDevice3D.pauseSample(soundAtom.sampleId);
2555: soundAtom.setPauseState(soundAtom.PAUSED);
2556: }
2557:
2558: void unpause(SoundSchedulerAtom soundAtom) {
2559: if (soundAtom.sampleId == SoundRetained.NULL_SOUND)
2560: return;
2561: if (debugFlag)
2562: debugPrint(".unpause");
2563: audioDevice3D.unpauseSample(soundAtom.sampleId);
2564: soundAtom.setPauseState(soundAtom.UNPAUSED);
2565: }
2566:
2567: /**
2568: * stop the sample associated with this sound
2569: */
2570: void turnOff(SoundSchedulerAtom soundAtom) {
2571: // Ensure sound are not stopped while looping thru prioritized sounds
2572: if (soundAtom.sampleId == SoundRetained.NULL_SOUND)
2573: return;
2574: if (debugFlag)
2575: debugPrint(".turnOff");
2576: if (audioDevice3D.stopSample(soundAtom.sampleId) < 0) {
2577: if (internalErrors) {
2578: debugPrint("Internal Error: stop sample error");
2579: }
2580: }
2581: soundAtom.playing = false;
2582: soundAtom.startTime = 0;
2583: soundAtom.endTime = 0;
2584:
2585: }
2586:
2587: /**
2588: * Update VirtualWorld local transform, sound position and direction.
2589: *
2590: * This is done dynamically from PointSoundRetained as these fields
2591: * are updated (when soundAtom.status is AUDIBLE or SILENT), or by this.
2592: * render() method when sound is started (sound.enabled is true).
2593: *
2594: * This method should only be called if mirror sound is a Point or
2595: * ConeSound.
2596: *
2597: * Important: pre-transformed position and direction sent to AudioDevice.
2598: */
2599: void updateXformedParams(boolean updateAll,
2600: SoundSchedulerAtom soundAtom) {
2601: PointSoundRetained mirrorPtSound = (PointSoundRetained) soundAtom.sound;
2602: PointSoundRetained ptSound = (PointSoundRetained) mirrorPtSound.sgSound;
2603: int index = soundAtom.sampleId;
2604: if (index == SoundRetained.NULL_SOUND)
2605: return;
2606: PointSoundRetained ps = (PointSoundRetained) mirrorPtSound;
2607:
2608: // Set Transform
2609:
2610: /*
2611: // XXXX: per sound tranforms can now be passed to AudioDevice
2612: // modify and execute the code below
2613:
2614: // MoveAppBoundingLeaf > ~/Current/MoveAppBoundingLeaf.outted,
2615: // instead transformed position and direction
2616: // points/vectors will be passed to AudioDevice directly.
2617:
2618: // vvvvvvvvvvvvvvvvvvvvvvvvvvv
2619: if (updateAll || soundAtom.testDirtyFlag(SoundRetained.XFORM_DIRTY_BIT){
2620: Transform3D xform = new Transform3D();
2621: ps.trans.getWithLock(xform);
2622: if (debugFlag) {
2623: debugPrint(".updateXformedParams " +
2624: "setVworldXfrm for ps @ " + ps + ":");
2625: debugPrint(" xformPosition " +
2626: ps.xformPosition.x + ", " +
2627: ps.xformPosition.y + ", " +
2628: ps.xformPosition.z );
2629: debugPrint(" column-major transform ");
2630: debugPrint(" " +
2631: xform.mat[0]+", " + xform.mat[1]+", "+
2632: xform.mat[2]+", " + xform.mat[3]);
2633: debugPrint(" " +
2634: xform.mat[4]+", " + xform.mat[5]+", "+
2635: xform.mat[6]+", " + xform.mat[7]);
2636: debugPrint(" " +
2637: xform.mat[8]+", " + xform.mat[9]+", "+
2638: xform.mat[10]+", " + xform.mat[11]);
2639: debugPrint(" " +
2640: xform.mat[12]+", " + xform.mat[13]+", "+
2641: xform.mat[14]+", " + xform.mat[15]);
2642: }
2643: audioDevice3D.setVworldXfrm(index, xform);
2644: soundAtom.clearStateDirtyFlag( SoundRetained.XFORM_DIRTY_BIT);
2645: // XXXX: make sure position and direction are already transformed and stored
2646: // into xformXxxxxxx fields.
2647: }
2648: // ^^^^^^^^^^^^^^^^^^^^^
2649: */
2650:
2651: // Set Position
2652: if (updateAll
2653: || testListenerFlag()
2654: || soundAtom.testDirtyFlag(soundAtom.attribsDirty,
2655: SoundRetained.POSITION_DIRTY_BIT)
2656: || soundAtom.testDirtyFlag(soundAtom.stateDirty,
2657: SoundRetained.XFORM_DIRTY_BIT)) {
2658: Point3f xformLocation = new Point3f();
2659: mirrorPtSound.getXformPosition(xformLocation);
2660: Point3d positionD = new Point3d(xformLocation);
2661: if (debugFlag)
2662: debugPrint("xform'd Position: (" + positionD.x + ", "
2663: + positionD.y + ", " + positionD.z + ")");
2664: audioDevice3D.setPosition(index, positionD);
2665: }
2666:
2667: // Set Direction
2668: if (mirrorPtSound instanceof ConeSoundRetained) {
2669: ConeSoundRetained cn = (ConeSoundRetained) mirrorPtSound;
2670: ConeSoundRetained cnSound = (ConeSoundRetained) mirrorPtSound.sgSound;
2671: if (updateAll ||
2672: // XXXX: test for XFORM_DIRTY only in for 1.2
2673: soundAtom
2674: .testDirtyFlag(
2675: soundAtom.attribsDirty,
2676: (SoundRetained.DIRECTION_DIRTY_BIT | SoundRetained.XFORM_DIRTY_BIT))) {
2677:
2678: Vector3f xformDirection = new Vector3f();
2679: cn.getXformDirection(xformDirection);
2680: Vector3d directionD = new Vector3d(xformDirection);
2681: audioDevice3D.setDirection(index, directionD);
2682: }
2683: }
2684: }
2685:
2686: void updateSoundParams(boolean updateAll,
2687: SoundSchedulerAtom soundAtom,
2688: AuralAttributesRetained attribs) {
2689:
2690: SoundRetained mirrorSound = soundAtom.sound;
2691: SoundRetained sound = mirrorSound.sgSound;
2692: int index = soundAtom.sampleId;
2693: int arraySize;
2694:
2695: if (index == SoundRetained.NULL_SOUND)
2696: return;
2697: if (debugFlag)
2698: debugPrint(".updateSoundParams(dirytFlags="
2699: + soundAtom.attribsDirty + ", "
2700: + soundAtom.stateDirty + ")");
2701:
2702: // since the sound is audible, make sure that the parameter for
2703: // this sound are up-to-date.
2704: if (updateAll
2705: || soundAtom.testDirtyFlag(soundAtom.attribsDirty,
2706: SoundRetained.INITIAL_GAIN_DIRTY_BIT)) {
2707:
2708: if (attribs != null) {
2709: audioDevice3D.setSampleGain(index,
2710: (sound.initialGain * attribs.attributeGain));
2711: } else {
2712: audioDevice3D.setSampleGain(index, sound.initialGain);
2713: }
2714: }
2715:
2716: if (updateAll
2717: || soundAtom.testDirtyFlag(soundAtom.attribsDirty,
2718: SoundRetained.LOOP_COUNT_DIRTY_BIT)) {
2719: if (debugFlag)
2720: debugPrint(" audioDevice.setLoop(" + sound.loopCount
2721: + ") called");
2722: audioDevice3D.setLoop(index, sound.loopCount);
2723: }
2724:
2725: if (updateAll
2726: || soundAtom.testDirtyFlag(soundAtom.attribsDirty,
2727: SoundRetained.RATE_DIRTY_BIT)) {
2728: if (audioDevice3DL2 != null) {
2729: if (debugFlag)
2730: debugPrint(" audioDevice.setRateScaleFactor("
2731: + sound.rate + ") called");
2732: audioDevice3DL2.setRateScaleFactor(index, sound.rate);
2733: }
2734: }
2735:
2736: if (updateAll
2737: || soundAtom.testDirtyFlag(soundAtom.attribsDirty,
2738: SoundRetained.DISTANCE_GAIN_DIRTY_BIT)) {
2739: if (sound instanceof ConeSoundRetained) {
2740: ConeSoundRetained cnSound = (ConeSoundRetained) sound;
2741:
2742: // set distance attenuation
2743: arraySize = cnSound.getDistanceGainLength();
2744: if (arraySize == 0) {
2745: // send default
2746: audioDevice3D.setDistanceGain(index, null, null,
2747: null, null);
2748: } else {
2749: Point2f[] attenuation = new Point2f[arraySize];
2750: Point2f[] backAttenuation = new Point2f[arraySize];
2751: for (int i = 0; i < arraySize; i++) {
2752: attenuation[i] = new Point2f();
2753: backAttenuation[i] = new Point2f();
2754: }
2755: cnSound.getDistanceGain(attenuation,
2756: backAttenuation);
2757: double[] frontDistance = new double[arraySize];
2758: float[] frontGain = new float[arraySize];
2759: double[] backDistance = new double[arraySize];
2760: float[] backGain = new float[arraySize];
2761: for (int i = 0; i < arraySize; i++) {
2762: frontDistance[i] = attenuation[i].x;
2763: frontGain[i] = attenuation[i].y;
2764: backDistance[i] = backAttenuation[i].x;
2765: backGain[i] = backAttenuation[i].y;
2766: }
2767: audioDevice3D.setDistanceGain(index, frontDistance,
2768: frontGain, backDistance, backGain);
2769: }
2770: } // ConeSound distanceGain
2771: else if (sound instanceof PointSoundRetained) {
2772: PointSoundRetained ptSound = (PointSoundRetained) sound;
2773:
2774: // set distance attenuation
2775: arraySize = ptSound.getDistanceGainLength();
2776: if (arraySize == 0) {
2777: // send default
2778: audioDevice3D.setDistanceGain(index, null, null,
2779: null, null);
2780: } else {
2781: Point2f[] attenuation = new Point2f[arraySize];
2782: for (int i = 0; i < arraySize; i++)
2783: attenuation[i] = new Point2f();
2784: ptSound.getDistanceGain(attenuation);
2785: double[] frontDistance = new double[arraySize];
2786: float[] frontGain = new float[arraySize];
2787: for (int i = 0; i < arraySize; i++) {
2788: frontDistance[i] = attenuation[i].x;
2789: frontGain[i] = attenuation[i].y;
2790: }
2791: audioDevice3D.setDistanceGain(index, frontDistance,
2792: frontGain, null, null);
2793: }
2794: } // PointSound distanceGain
2795: }
2796:
2797: if ((sound instanceof ConeSoundRetained)
2798: && (updateAll || soundAtom.testDirtyFlag(
2799: soundAtom.attribsDirty,
2800: SoundRetained.ANGULAR_ATTENUATION_DIRTY_BIT))) {
2801:
2802: // set angular attenuation
2803: ConeSoundRetained cnSound = (ConeSoundRetained) sound;
2804: arraySize = cnSound.getAngularAttenuationLength();
2805: if (arraySize == 0) {
2806: // send default
2807: double[] angle = new double[2];
2808: float[] scaleFactor = new float[2];
2809: angle[0] = 0.0;
2810: angle[1] = (Math.PI) / 2.0;
2811: scaleFactor[0] = 1.0f;
2812: scaleFactor[1] = 0.0f;
2813: audioDevice3D.setAngularAttenuation(index,
2814: cnSound.NO_FILTERING, angle, scaleFactor, null);
2815: } else {
2816: Point3f[] attenuation = new Point3f[arraySize];
2817: for (int i = 0; i < arraySize; i++) {
2818: attenuation[i] = new Point3f();
2819: }
2820: cnSound.getAngularAttenuation(attenuation);
2821: double[] angle = new double[arraySize];
2822: float[] scaleFactor = new float[arraySize];
2823: float[] cutoff = new float[arraySize];
2824: for (int i = 0; i < arraySize; i++) {
2825: angle[i] = attenuation[i].x;
2826: scaleFactor[i] = attenuation[i].y;
2827: cutoff[i] = attenuation[i].z;
2828: }
2829: audioDevice3D.setAngularAttenuation(index,
2830: cnSound.filterType, angle, scaleFactor, cutoff);
2831: }
2832: }
2833: }
2834:
2835: /**
2836: * Check (and set if necessary) AudioDevice3D field
2837: */
2838: boolean checkAudioDevice3D() {
2839: if (universe != null) {
2840: if (universe.currentView != null)
2841: if (universe.currentView.physicalEnvironment != null) {
2842: audioDevice = universe.currentView.physicalEnvironment.audioDevice;
2843: if (audioDevice != null) {
2844: if (audioDevice instanceof AudioDevice3DL2) {
2845: audioDevice3DL2 = (AudioDevice3DL2) audioDevice;
2846: }
2847: if (audioDevice instanceof AudioDevice3D) {
2848: audioDevice3D = (AudioDevice3D) audioDevice;
2849: } else { // audioDevice is only an instance of AudioDevice
2850: if (internalErrors)
2851: debugPrint("AudioDevice implementation not supported");
2852: // audioDevice3D should already be null
2853: }
2854: } else {
2855: // if audioDevice is null, clear extended class fields
2856: audioDevice3DL2 = null;
2857: audioDevice3D = null;
2858: }
2859: }
2860: }
2861: if (audioDevice3D == null)
2862: return false;
2863:
2864: if (audioDevice3D.getTotalChannels() == 0)
2865: return false; // can not render sounds on AudioEngine that has no channels
2866:
2867: return true;
2868: }
2869:
2870: /**
2871: * Clears the fields associated with sample data for this sound.
2872: * Assumes soundAtom is non-null, and that non-null atom
2873: * would have non-null sound field.
2874: */
2875: void clearSoundData(SoundSchedulerAtom soundAtom) {
2876: if (checkAudioDevice3D()
2877: && soundAtom.sampleId != SoundRetained.NULL_SOUND) {
2878: stopSound(soundAtom, false); // force stop of playing sound
2879: // Unload sound data from AudioDevice
2880: audioDevice3D.clearSound(soundAtom.sampleId);
2881: }
2882:
2883: soundAtom.sampleId = SoundRetained.NULL_SOUND;
2884: // set load state into atom
2885: soundAtom.loadStatus = SoundRetained.LOAD_NULL;
2886: // NOTE: setting node load status not 1-to-1 w/actual load;
2887: // this is incorrect
2888: SoundRetained sound = soundAtom.sound;
2889: soundAtom.loadStatus = SoundRetained.LOAD_NULL;
2890: soundAtom.soundData = null;
2891: sound.changeAtomList(soundAtom, SoundRetained.LOAD_NULL);
2892: }
2893:
2894: /**
2895: * Attempts to load sound data for a particular sound source onto
2896: * the chosen/initialized audio device
2897: * If this called, it is assumed that SoundRetained.audioDevice is
2898: * NOT null.
2899: * If an error in loading occurs (an exception is caught,...)
2900: * an error is printed out to stderr - an exception is not thrown.
2901: * @param soundData descrition of sound source data
2902: */
2903: // QUESTION: should this method be synchronized?
2904: void attachSoundData(SoundSchedulerAtom soundAtom,
2905: MediaContainer soundData, boolean forceReload) {
2906:
2907: if (!forceReload && (soundAtom.soundData == soundData)) {
2908: return;
2909: }
2910: SoundRetained sound = soundAtom.sound.sgSound;
2911: if (!checkAudioDevice3D()) {
2912: if (debugFlag)
2913: debugPrint(".attachSoundData audioDevice3D null");
2914: soundAtom.loadStatus = SoundRetained.LOAD_PENDING;
2915: sound.changeAtomList(soundAtom, SoundRetained.LOAD_PENDING);
2916: return;
2917: }
2918: if (soundAtom.soundData != null) {
2919: // clear sound data field for view specific atom NOT sound node
2920: clearSoundData(soundAtom);
2921: if (soundData == null) {
2922: if (debugFlag)
2923: debugPrint(".attachSoundData with null soundData");
2924: return;
2925: }
2926: }
2927:
2928: URL url = ((MediaContainerRetained) sound.soundData.retained).url;
2929: String path = ((MediaContainerRetained) sound.soundData.retained).urlString;
2930: InputStream stream = ((MediaContainerRetained) sound.soundData.retained).inputStream;
2931: if (url == null && path == null && stream == null) {
2932: if (debugFlag)
2933: debugPrint(".attachSoundData with null soundData");
2934: // clear non-null sample associated with this soundData
2935: if (soundAtom.sampleId != SoundRetained.NULL_SOUND) {
2936: clearSoundData(soundAtom);
2937: }
2938: return;
2939: }
2940:
2941: int id;
2942: if (sound instanceof ConeSoundRetained)
2943: sound.soundType = AudioDevice3D.CONE_SOUND;
2944: else if (sound instanceof PointSoundRetained)
2945: sound.soundType = AudioDevice3D.POINT_SOUND;
2946: else
2947: sound.soundType = AudioDevice3D.BACKGROUND_SOUND;
2948: if (debugFlag) {
2949: debugPrint(".attachSoundData soundType = "
2950: + sound.soundType);
2951: debugPrint(".attachSoundData this is = " + sound);
2952: }
2953:
2954: // Clone the MediaContainer associated with this node and
2955: // set the capability bits for this clone to allow access to
2956: // all fields; this copy is passed to the audioDevice.
2957: // As the fields of the MediaContainer expands, this code must
2958: // be appended.
2959: MediaContainer cloneMediaContainer = new MediaContainer();
2960: cloneMediaContainer.duplicateAttributes(soundData, true);
2961: cloneMediaContainer
2962: .setCapability(MediaContainer.ALLOW_CACHE_READ);
2963: cloneMediaContainer
2964: .setCapability(MediaContainer.ALLOW_URL_READ);
2965:
2966: id = audioDevice3D.prepareSound(sound.soundType,
2967: cloneMediaContainer);
2968: if (debugFlag)
2969: debugPrint(".attachSoundData prepareSound returned " + id);
2970:
2971: if (id == SoundRetained.NULL_SOUND) {
2972: soundAtom.loadStatus = SoundRetained.LOAD_FAILED;
2973: // NOTE: setting node load status not 1-to-1 with actual load;
2974: // this is incorrect
2975: sound.changeAtomList(soundAtom, SoundRetained.LOAD_FAILED);
2976: //System.err.println(path + ": "+ J3dI18N.getString("SoundRetained1"));
2977: } else {
2978: if (debugFlag)
2979: debugPrint(".attachSoundData - sampleId set");
2980: soundAtom.sampleId = id;
2981:
2982: // For now loopLength=sampleLength, loop points not supported
2983: long duration = audioDevice3D.getSampleDuration(id);
2984: soundAtom.sampleLength = duration;
2985: soundAtom.loopLength = soundAtom.sampleLength;
2986:
2987: // XXXX: for most this will be 0 but not all
2988: soundAtom.loopStartOffset = 0;
2989: soundAtom.attackLength = 0; // portion of sample before loop section
2990: soundAtom.releaseLength = 0; // portion of sample after loop section
2991: soundAtom.loadStatus = SoundRetained.LOAD_COMPLETE;
2992: soundAtom.soundData = soundData;
2993: sound
2994: .changeAtomList(soundAtom,
2995: SoundRetained.LOAD_COMPLETE);
2996: if (debugFlag)
2997: debugPrint(" attachSoundData; index = "
2998: + soundAtom.sampleId);
2999: }
3000: }
3001:
3002: SoundSchedulerAtom findSoundAtom(SoundRetained node, int nthInstance) {
3003: // find nth sound atom in the list of prioritized sounds that
3004: // references this sound node
3005: // nthInstance=1 would look for first instance
3006: if (node == null)
3007: return null;
3008: SoundSchedulerAtom returnAtom = null;
3009: synchronized (prioritizedSounds) {
3010: if (!prioritizedSounds.isEmpty()) {
3011: SoundSchedulerAtom soundAtom = null;
3012: int atomFound = 0;
3013: // find sound in list and remove it
3014: int arrSize = prioritizedSounds.size();
3015: for (int index = 0; index < arrSize; index++) {
3016: soundAtom = (SoundSchedulerAtom) prioritizedSounds
3017: .get(index);
3018: if (soundAtom.sound == null)
3019: continue;
3020: // soundAtom.sound is address of mirror sound not org node
3021: if (soundAtom.sound.sgSound == node) {
3022: atomFound++;
3023: // orginal app node pass into method
3024: // QUESTION: is mirror node still correct?
3025: // XXXX: ensure only mirror nodes passed into method
3026: if (atomFound == nthInstance) {
3027: returnAtom = soundAtom;
3028: break;
3029: }
3030: } else if (soundAtom.sound.sgSound == node.sgSound) {
3031: atomFound++;
3032: // store potentially new mirror sound into soundAtom
3033: soundAtom.sound = node;
3034: if (atomFound == nthInstance) {
3035: returnAtom = soundAtom;
3036: break;
3037: }
3038: }
3039: }
3040: }
3041: }
3042: return returnAtom;
3043: }
3044:
3045: /**
3046: * 'Dirty' flag == listenerUpdated flag
3047: * The ambiguous name 'dirtyFlag' is a legacy from when there was only a
3048: * single dirty flag set by Core yet tested and cleared in this scheduler.
3049: * These methods specifically set/test/clear the local listenerUpdated flag.
3050: */
3051: // Called by CanvasViewCache when listener parameter(s) changes
3052: void setListenerFlag(int flag) {
3053: listenerUpdated |= flag;
3054: }
3055:
3056: void clearListenerFlag() {
3057: listenerUpdated = 0x0;
3058: }
3059:
3060: boolean testListenerFlag() {
3061: // Test if any bits are on
3062: if (listenerUpdated > 0)
3063: return true;
3064: else
3065: return false;
3066: }
3067:
3068: /**
3069: * set dirty flags associated with SoundSchedulerAtom
3070: */
3071: void setAttribsDirtyFlag(SoundRetained node, int dirtyFlag) {
3072: if (debugFlag)
3073: debugPrint(".setAttribsDirtyFlag " + node);
3074: // find sound atom that references this sound node
3075: SoundSchedulerAtom soundAtom = null;
3076: for (int i = 1;; i++) {
3077: soundAtom = findSoundAtom(node, i);
3078: if (soundAtom == null)
3079: break;
3080: soundAtom.setAttribsDirtyFlag(dirtyFlag);
3081: }
3082: }
3083:
3084: void setStateDirtyFlag(SoundRetained node, int dirtyFlag) {
3085: if (debugFlag)
3086: debugPrint(".setStateDirtyFlag " + node);
3087: // find sound atom that references this sound node
3088: SoundSchedulerAtom soundAtom = null;
3089: for (int i = 1;; i++) {
3090: soundAtom = findSoundAtom(node, i);
3091: if (soundAtom == null)
3092: break;
3093: soundAtom.setStateDirtyFlag(dirtyFlag);
3094: }
3095: }
3096:
3097: void printAtomState(SoundSchedulerAtom atom) {
3098: SoundRetained sound = atom.sound.sgSound;
3099: debugPrint(" this atom = " + atom + " ");
3100: debugPrint(" references sound = " + sound
3101: + " ");
3102: debugPrint(" enabled " + atom.enabled);
3103: debugPrint(" status " + atom.status);
3104: debugPrint(" activated " + atom.activated);
3105: debugPrint(" released " + sound.release);
3106: debugPrint(" continuous " + sound.continuous);
3107: debugPrint(" scheduling "
3108: + atom.schedulingAction);
3109: }
3110:
3111: // Debug print mechanism for Sound nodes
3112:
3113: static final boolean debugFlag = false;
3114: static final boolean internalErrors = false;
3115:
3116: void debugPrint(String message) {
3117: if (debugFlag)
3118: System.err.println("SS." + message);
3119: }
3120:
3121: void processViewSpecificGroupChanged(J3dMessage m) {
3122: int component = ((Integer) m.args[0]).intValue();
3123: Object[] objAry = (Object[]) m.args[1];
3124: if (((component & ViewSpecificGroupRetained.ADD_VIEW) != 0)
3125: || ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
3126: int i;
3127: Object obj;
3128: View v = (View) objAry[0];
3129: ArrayList leafList = (ArrayList) objAry[2];
3130: // View being added is this view
3131: if (v == view) {
3132: int size = leafList.size();
3133: for (i = 0; i < size; i++) {
3134: obj = leafList.get(i);
3135: if (obj instanceof SoundRetained) {
3136: nRetainedSounds++;
3137: addSound((SoundRetained) obj);
3138: } else if (obj instanceof SoundscapeRetained) {
3139: auralAttribsChanged = true;
3140: }
3141: }
3142:
3143: }
3144:
3145: }
3146: if (((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0)
3147: || ((component & ViewSpecificGroupRetained.SET_VIEW) != 0)) {
3148: int i;
3149: Object obj;
3150: ArrayList leafList;
3151: View v;
3152:
3153: if ((component & ViewSpecificGroupRetained.REMOVE_VIEW) != 0) {
3154: v = (View) objAry[0];
3155: leafList = (ArrayList) objAry[2];
3156: } else {
3157: v = (View) objAry[4];
3158: leafList = (ArrayList) objAry[6];
3159: }
3160: if (v == view) {
3161: int size = leafList.size();
3162: for (i = 0; i < size; i++) {
3163: obj = leafList.get(i);
3164: if (obj instanceof SoundRetained) {
3165: SoundSchedulerAtom soundAtom = null;
3166: for (int arrIndx = 1;; arrIndx++) {
3167: soundAtom = findSoundAtom(
3168: (SoundRetained) obj, arrIndx);
3169: if (soundAtom == null)
3170: break;
3171: stopSound(soundAtom, false);
3172: }
3173: } else if (obj instanceof SoundscapeRetained) {
3174: auralAttribsChanged = true;
3175: }
3176: }
3177: }
3178: }
3179:
3180: }
3181:
3182: void processBoundingLeafChanged(J3dMessage m) {
3183: // Notify all users of this bounding leaf, it may
3184: // result in the re-evaluation of the lights/fogs/backgrounds
3185: Object[] users = (Object[]) (m.args[3]);
3186: int i;
3187:
3188: for (i = 0; i < users.length; i++) {
3189: LeafRetained leaf = (LeafRetained) users[i];
3190: if (leaf instanceof SoundRetained
3191: && universe.soundStructure.isSoundScopedToView(
3192: leaf, view)) {
3193: auralAttribsChanged = true;
3194: } else if (leaf instanceof SoundscapeRetained
3195: && universe.soundStructure
3196: .isSoundscapeScopedToView(leaf, view)) {
3197: auralAttribsChanged = true;
3198: }
3199: }
3200: }
3201:
3202: void cleanup() {
3203: // clean up any messages that are queued up, since they are
3204: // irrelevant
3205: // clearMessages();
3206: }
3207: }
|