Source Code Cross Referenced for SoundScheduler.java in  » 6.0-JDK-Modules » java-3d » javax » media » j3d » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » java 3d » javax.media.j3d 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.