/* * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; /** * A sound structure is a object that organizes Sounds and * soundscapes. * This structure parallels the RenderingEnv structure and * is used for sounds */ class SoundStructure extends J3dStructure { /** * The list of Sound nodes */ UnorderList nonViewScopedSounds = new UnorderList(SoundRetained.class); HashMap viewScopedSounds = new HashMap(); /** * The list of Soundscapes */ UnorderList nonViewScopedSoundscapes = new UnorderList(SoundscapeRetained.class); HashMap viewScopedSoundscapes = new HashMap(); /** * The list of view platforms */ UnorderList viewPlatforms = new UnorderList(ViewPlatformRetained.class); /** * A bounds used for getting a view platform scheduling BoundingSphere */ BoundingSphere tempSphere = new BoundingSphere(); BoundingSphere vpsphere = new BoundingSphere(); // ArrayList of leafRetained object whose mirrorObjects // should be updated ArrayList objList = new ArrayList(); // ArrayList of leafRetained object whose boundingleaf xform // should be updated ArrayList xformChangeList = new ArrayList(); // ArrayList of switches that have changed ArrayList switchChangeLeafNodes = new ArrayList(); ArrayList switchChangeLeafMasks = new ArrayList(); // variables for processing transform messages boolean transformMsg = false; UpdateTargets targets = null; /** * This constructor does nothing */ SoundStructure(VirtualUniverse u) { super(u, J3dThread.UPDATE_SOUND); if (debugFlag) debugPrint("SoundStructure constructed"); } @Override void processMessages(long referenceTime) { J3dMessage messages[] = getMessages(referenceTime); int nMsg = getNumMessage(); J3dMessage m; if (nMsg <= 0) { return; } for (int i=0; i < nMsg; i++) { m = messages[i]; switch (m.type) { case J3dMessage.INSERT_NODES : // Prioritize retained and non-retained sounds for this view insertNodes(m); break; case J3dMessage.REMOVE_NODES: removeNodes(m); break; case J3dMessage.SOUND_ATTRIB_CHANGED: changeNodeAttrib(m); break; case J3dMessage.SOUND_STATE_CHANGED: changeNodeState(m); break; case J3dMessage.SOUNDSCAPE_CHANGED: case J3dMessage.AURALATTRIBUTES_CHANGED: // XXXX: this needs to be changed changeNodeAttrib(m); break; case J3dMessage.TRANSFORM_CHANGED: transformMsg = true; break; case J3dMessage.SWITCH_CHANGED: // This method isn't implemented yet. // processSwitchChanged(m); // may need to process dirty switched-on transform if (universe.transformStructure.getLazyUpdate()) { transformMsg = true; } break; case J3dMessage.VIEWSPECIFICGROUP_CHANGED: updateViewSpecificGroupChanged(m); break; // XXXX: case J3dMessage.BOUNDINGLEAF_CHANGED } /* // NOTE: this should already be handled by including/ORing // SOUND_SCHEDULER in targetThread for these message types!! // Dispatch a message about a sound change ViewPlatformRetained vpLists[] = (ViewPlatformRetained []) viewPlatforms.toArray(false); // QUESTION: can I just use this message to pass to all the Sound Bins for (int k=viewPlatforms.arraySize()- 1; k>=0; k--) { View[] views = vpLists[k].getViewList(); for (int j=(views.length-1); j>=0; j--) { View v = (View)(views[j]); m.view = v; VirtualUniverse.mc.processMessage(m); } } */ m.decRefcount(); } if (transformMsg) { targets = universe.transformStructure.getTargetList(); updateTransformChange(targets, referenceTime); transformMsg = false; targets = null; } Arrays.fill(messages, 0, nMsg, null); } void insertNodes(J3dMessage m) { Object[] nodes = (Object[])m.args[0]; ArrayList viewScopedNodes = (ArrayList)m.args[3]; ArrayList> scopedNodesViewList = (ArrayList>)m.args[4]; for (int i=0; i vl = scopedNodesViewList.get(i); int vsize = vl.size(); if (node instanceof SoundRetained) { ((SoundRetained)node).isViewScoped = true; for (int k = 0; k < vsize; k++) { View view = vl.get(k); addScopedSound((SoundRetained) node, view); } } else if (node instanceof SoundscapeRetained) { ((SoundscapeRetained)node).isViewScoped = true; for (int k = 0; k < vsize; k++) { View view = vl.get(k); addScopedSoundscape((SoundscapeRetained) node, view); } } } } /* // XXXX: if (node instanceof AuralAttributesRetained) { } else if (node instanceof ViewPlatformRetained) { addViewPlatform((ViewPlatformRetained) node); } */ } /** * Add sound to sounds list. */ void addScopedSound(SoundRetained mirSound, View view) { if (debugFlag) debugPrint("SoundStructure.addSound()"); ArrayList l = (ArrayList) viewScopedSounds.get(view); if (l == null) { l = new ArrayList(); viewScopedSounds.put(view, l); } l.add(mirSound); } // end addSound() void addNonScopedSound(SoundRetained mirSound) { if (debugFlag) debugPrint("SoundStructure.addSound()"); nonViewScopedSounds.add(mirSound); } // end addSound() void addScopedSoundscape(SoundscapeRetained soundscape, View view) { if (debugFlag) debugPrint("SoundStructure.addSoundscape()"); ArrayList l = (ArrayList) viewScopedSoundscapes.get(view); if (l == null) { l = new ArrayList(); viewScopedSoundscapes.put(view, l); } l.add(soundscape); } void addNonSoundscape(SoundscapeRetained soundscape) { if (debugFlag) debugPrint("SoundStructure.addSoundscape()"); nonViewScopedSoundscapes.add(soundscape); } @Override void removeNodes(J3dMessage m) { Object[] nodes = (Object[])m.args[0]; ArrayList viewScopedNodes = (ArrayList)m.args[3]; ArrayList> scopedNodesViewList = (ArrayList>)m.args[4]; for (int i=0; i vl = scopedNodesViewList.get(i); // If the node object is scoped to this view, then .. int vsize = vl.size(); if (node instanceof SoundRetained) { ((SoundRetained)node).isViewScoped = false; for (int k = 0; k < vsize; k++) { View view = vl.get(k); deleteScopedSound((SoundRetained) node, view); } } else if (node instanceof SoundscapeRetained) { ((SoundscapeRetained)node).isViewScoped = false; for (int k = 0; k < vsize; k++) { View view = vl.get(k); deleteScopedSoundscape((SoundscapeRetained) node, view); } } } } } void deleteNonScopedSound(SoundRetained sound) { if (!nonViewScopedSounds.isEmpty()) { // find sound in list and remove it int index = nonViewScopedSounds.indexOf(sound); nonViewScopedSounds.remove(index); } } void deleteNonScopedSoundscape(SoundscapeRetained soundscape) { boolean error = nonViewScopedSoundscapes.remove(soundscape); } void deleteScopedSound(SoundRetained sound, View view) { ArrayList l = (ArrayList) viewScopedSounds.get(view); if (!l.isEmpty()) { // find sound in list and remove it int index = l.indexOf(sound); l.remove(index); } if (l.isEmpty()) viewScopedSounds.remove(view); } void deleteScopedSoundscape(SoundscapeRetained soundscape, View view) { ArrayList l = (ArrayList) viewScopedSoundscapes.get(view); if (!l.isEmpty()) { // find sound in list and remove it int index = l.indexOf(soundscape); l.remove(index); } if (l.isEmpty()) viewScopedSoundscapes.remove(view); } void changeNodeAttrib(J3dMessage m) { int attribDirty; Object node = m.args[0]; Object value = m.args[1]; if (debugFlag) debugPrint("SoundStructure.changeNodeAttrib:"); if (node instanceof SoundRetained) { attribDirty = ((Integer)value).intValue(); if (debugFlag) debugPrint(" Sound node dirty bit = " + attribDirty); if ((attribDirty & SoundRetained.PRIORITY_DIRTY_BIT) > 0) { // XXXX: shuffle in SoundScheduler /* shuffleSound((SoundRetained) node); */ } if ((attribDirty & SoundRetained.SOUND_DATA_DIRTY_BIT) > 0) { loadSound((SoundRetained) node, true); } ((SoundRetained)node).updateMirrorObject(m.args); } if (node instanceof SoundscapeRetained) { /* attribDirty = ((Integer)value).intValue(); if (((attribDirty & SoundscapeRetained.BOUNDING_LEAF_CHANGED) != 0) || ((attribDirty & SoundscapeRetained.APPLICATION_BOUNDS_CHANGED) != 0) ) { */ ((SoundscapeRetained)node).updateTransformChange(); /* } */ // XXXX: have no dirty flag for soundscape, just auralAttributes... // what if reference to AA changes in soundscape??? } } void changeNodeState(J3dMessage m) { int stateDirty; Object node = m.args[0]; Object value = m.args[1]; if (debugFlag) debugPrint("SoundStructure.changeNodeState:"); if (node instanceof SoundRetained) { stateDirty = ((Integer)value).intValue(); if (debugFlag) debugPrint(" Sound node dirty bit = " + stateDirty); if ((stateDirty & SoundRetained.LIVE_DIRTY_BIT) > 0) { loadSound((SoundRetained) node, false); } if ((stateDirty & SoundRetained.ENABLE_DIRTY_BIT) > 0) { enableSound((SoundRetained) node); } ((SoundRetained)node).updateMirrorObject(m.args); } } // return true if one of ViewPlatforms intersect region boolean intersect(Bounds region) { if (region == null) return false; ViewPlatformRetained vpLists[] = (ViewPlatformRetained []) viewPlatforms.toArray(false); for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) { vpLists[i].schedSphere.getWithLock(tempSphere); if (tempSphere.intersect(region)) { return true; } } return false; } void loadSound(SoundRetained sound, boolean forceLoad) { // QUESTION: should not be calling into soundScheduler directly??? MediaContainer mediaContainer = sound.getSoundData(); ViewPlatformRetained vpLists[] = (ViewPlatformRetained []) viewPlatforms.toArray(false); for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) { View[] views = vpLists[i].getViewList(); for (int j=(views.length-1); j>=0; j--) { View v = views[j]; // XXXX: Shouldn't this be done with messages?? v.soundScheduler.loadSound(sound, forceLoad); } } } void enableSound(SoundRetained sound) { ViewPlatformRetained vpLists[] = (ViewPlatformRetained []) viewPlatforms.toArray(false); for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) { View[] views = vpLists[i].getViewList(); for (int j=(views.length-1); j>=0; j--) { View v = views[j]; v.soundScheduler.enableSound(sound); } } } void muteSound(SoundRetained sound) { ViewPlatformRetained vpLists[] = (ViewPlatformRetained []) viewPlatforms.toArray(false); for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) { View[] views = vpLists[i].getViewList(); for (int j=(views.length-1); j>=0; j--) { View v = views[j]; v.soundScheduler.muteSound(sound); } } } void pauseSound(SoundRetained sound) { ViewPlatformRetained vpLists[] = (ViewPlatformRetained []) viewPlatforms.toArray(false); for (int i=viewPlatforms.arraySize()- 1; i>=0; i--) { View[] views = vpLists[i].getViewList(); for (int j=(views.length-1); j>=0; j--) { View v = views[j]; v.soundScheduler.pauseSound(sound); } } } // Implementation be needed. void processSwitchChanged(J3dMessage m) { /* SoundRetained sound; LeafRetained leaf; UnorderList arrList; int size; Object[] nodes; UpdateTargets targets = (UpdateTargets)m.args[0]; arrList = targets.targetList[Targets.SND_TARGETS]; if (arrList != null) { size = arrList.size(); nodes = arrList.toArray(false); for (int i=size-1; i>=0; i--) { leaf = (LeafRetained)nodes[i]; sound = (SoundRetained) leaf; if (sound.switchState.currentSwitchOn) { // System.err.println("SoundStructure.switch on"); // add To Schedule List } else { // System.err.println("SoundStructure.switch off"); // remove From Schedule List } } } */ } // How can active flag (based on View orientataion) be set here for all Views?!? UnorderList getSoundList(View view) { ArrayList l = (ArrayList)viewScopedSounds.get(view); // No sounds scoped to this view if (l == null) return nonViewScopedSounds; UnorderList newS = (UnorderList) nonViewScopedSounds.clone(); int size = l.size(); for (int i = 0; i < size; i++) { newS.add(l.get(i)); } return newS; } UnorderList getSoundscapeList(View view) { ArrayList l = (ArrayList)viewScopedSoundscapes.get(view); // No sounds scoped to this view if (l == null) return nonViewScopedSoundscapes; UnorderList newS = (UnorderList) nonViewScopedSoundscapes.clone(); int size = l.size(); for (int i = 0; i < size; i++) { newS.add(l.get(i)); } return newS; } /* // XXXX: how is immediate mode handled? below code taken from SoundSchedule // Don't know how we'll process immediate mode sounds; // Append immediate mode sounds to live sounds list if (graphicsCtx != null) { synchronized (graphicsCtx.sounds) { nImmedSounds = graphicsCtx.numSounds(); numSoundsToProcess = nSounds + nImmedSounds; if (sounds.length < numSoundsToProcess) { // increase the array length of sounds array list // by added 32 elements more than universe list size sounds = new SoundRetained[numSoundsToProcess + 32]; } for (int i=0; i