diff options
5 files changed, 2465 insertions, 233 deletions
@@ -63,6 +63,11 @@ <artifactId>j3dutils</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.jogamp.joal</groupId> + <artifactId>joal-main</artifactId> + <version>${jogamp.version}</version> + </dependency> </dependencies> <build> diff --git a/src/main/java/org/jdesktop/j3d/examples/sound/BackgroundSoundTest.java b/src/main/java/org/jdesktop/j3d/examples/sound/BackgroundSoundTest.java index 230a249..38cbd50 100644 --- a/src/main/java/org/jdesktop/j3d/examples/sound/BackgroundSoundTest.java +++ b/src/main/java/org/jdesktop/j3d/examples/sound/BackgroundSoundTest.java @@ -47,6 +47,7 @@ import java.awt.GraphicsConfiguration; import java.net.URL; import org.jdesktop.j3d.examples.Resources; +import org.jdesktop.j3d.examples.sound.audio.JOALMixer; import org.jogamp.java3d.AmbientLight; import org.jogamp.java3d.Appearance; import org.jogamp.java3d.BackgroundSound; @@ -81,6 +82,7 @@ import org.jogamp.vecmath.Vector3f; */ public class BackgroundSoundTest extends javax.swing.JFrame { + private JOALMixer mixer = null; private URL url = null; private SimpleUniverse univ = null; private BranchGroup scene = null; @@ -210,7 +212,7 @@ public class BackgroundSoundTest extends javax.swing.JFrame { Viewer viewer = univ.getViewer(); - viewer.createAudioDevice(); + viewer.getView().setBackClipDistance(1000.0f); // Ensure at least 50 msec per frame. @@ -224,6 +226,27 @@ public class BackgroundSoundTest extends javax.swing.JFrame { knb.setSchedulingBounds(b); bg.addChild(knb); univ.addBranchGraph(bg); + + //Note if you defined a "j3d.audiodevice" system property and gave it a class then you would call this function + //viewer.createAudioDevice(); + + //Here I use a direct setup + + //getUserHeadToVworldEnable must be enabled otherwise a restricted access exception occurs + if (mixer == null && viewer.getView().getUserHeadToVworldEnable()) + { + // create and adds a joalmixer as the audio device + mixer = new JOALMixer(viewer.getPhysicalEnvironment()); + + boolean success = mixer.initialize(); + + if (!success) + { + System.out.println("Open AL failed to init"); + // remove the audio device + viewer.getPhysicalEnvironment().setAudioDevice(null); + } + } return c; } diff --git a/src/main/java/org/jdesktop/j3d/examples/sound/PointSoundTest.java b/src/main/java/org/jdesktop/j3d/examples/sound/PointSoundTest.java index c5af22b..91cdb35 100644 --- a/src/main/java/org/jdesktop/j3d/examples/sound/PointSoundTest.java +++ b/src/main/java/org/jdesktop/j3d/examples/sound/PointSoundTest.java @@ -48,6 +48,7 @@ import java.awt.GraphicsConfiguration; import java.net.URL; import org.jdesktop.j3d.examples.Resources; +import org.jdesktop.j3d.examples.sound.audio.JOALMixer; import org.jogamp.java3d.AmbientLight; import org.jogamp.java3d.Appearance; import org.jogamp.java3d.BoundingSphere; @@ -82,237 +83,272 @@ import org.jogamp.vecmath.Vector3f; * contributed by David Grace ([email protected]). * */ -public class PointSoundTest extends javax.swing.JFrame { - - private URL url = null; - private SimpleUniverse univ = null; - private BranchGroup scene = null; - - //The activation radius for the ViewPlatform - private float activationRadius = 1; - - private Shape3D getDefaultGrid(int noOfLines, double size, double height){ - - Shape3D shape = new Shape3D(); - double lineLength = noOfLines * size / 2; - LineArray la = new LineArray(noOfLines * 4, LineArray.COORDINATES); - int count = 0; - for (int i=0; i<noOfLines; i++){ - la.setCoordinate(count, new Point3d(-lineLength, height, i*size - lineLength)); - count++; - la.setCoordinate(count, new Point3d(lineLength, height, i*size - lineLength)); - count++; - } - for (int i=0; i<noOfLines; i++){ - la.setCoordinate(count, new Point3d(i*size - lineLength, height, -lineLength)); - count++; - la.setCoordinate(count, new Point3d(i*size - lineLength, height, lineLength)); - count++; - } - shape.setGeometry(la); - Appearance a = new Appearance(); - ColoringAttributes ca = new ColoringAttributes(); - ca.setColor(0.3f, 0.3f, 0.3f); - a.setColoringAttributes(ca); - LineAttributes sla = new LineAttributes(); - sla.setLineWidth(1.0f); - a.setLineAttributes(sla); - shape.setAppearance(a); - - return shape; - } - - private Group createSoundBoundingGeometries(PointSound ps) { - Group group = new Group(); - Sphere sphere1 = getInnerBoundingSphere(ps); - group.addChild(sphere1); - - assert(ps.getDistanceGainLength() == 2); - - // create geometry for outer Bounding Sphere - float [] ds = new float [ps.getDistanceGainLength()]; - float [] as = new float [ps.getDistanceGainLength()]; - ps.getDistanceGain(ds, as); - float distanceAtZero = ds[ps.getDistanceGainLength() - 1]; - - Sphere sphere2 = getSphere(distanceAtZero, false); - group.addChild(sphere2); - - return group; - } - - private Sphere getInnerBoundingSphere(Sound sound){ - Bounds bounds = sound.getSchedulingBounds(); - assert ((bounds != null) && (bounds instanceof BoundingSphere)); - BoundingSphere bs = (BoundingSphere) bounds; - float radius = (float) bs.getRadius(); - - return getSphere(radius, true); - } - - private Sphere getSphere(float radius, boolean inner){ - - Appearance a = new Appearance(); - Material m = new Material(); - - if (inner) { - m.setDiffuseColor(1, 0, 0); - m.setAmbientColor(1, 0, 0); - m.setShininess(8); - } else { - m.setDiffuseColor(0, 1, 0); - m.setAmbientColor(0, 1, 0); - m.setShininess(8); - } - a.setMaterial(m); - - PolygonAttributes pa = new PolygonAttributes(); - pa.setPolygonMode(PolygonAttributes.POLYGON_LINE); - pa.setCullFace(PolygonAttributes.CULL_NONE); - a.setPolygonAttributes(pa); - return new Sphere(radius, a); - } - - private TransformGroup createSoundNodeGeometry(float x, float y, float z){ - - TransformGroup rootTransformGroup = new TransformGroup(); - Transform3D t3D = new Transform3D(); - t3D.setTranslation(new Vector3f(x, y, z)); - rootTransformGroup.setTransform(t3D); - ColorCube cc = new ColorCube(0.1); - rootTransformGroup.addChild(cc); - return rootTransformGroup; - } - - - public BranchGroup createSceneGraph() { - // Create the root of the branch graph - BranchGroup objRoot = new BranchGroup(); - - BoundingSphere bounds = - new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0); - - AmbientLight al = new AmbientLight(); - al.setInfluencingBounds(bounds); - - DirectionalLight dl = new DirectionalLight(); - dl.setDirection(-1, -1, -1); - dl.setInfluencingBounds(bounds); - objRoot.addChild(al); - objRoot.addChild(dl); - - /* - * Create Sound and Behavior objects that will play the sound +public class PointSoundTest extends javax.swing.JFrame +{ + + private JOALMixer mixer = null; + private URL url = null; + private SimpleUniverse univ = null; + private BranchGroup scene = null; + + //The activation radius for the ViewPlatform + private float activationRadius = 1; + + private Shape3D getDefaultGrid(int noOfLines, double size, double height) + { + + Shape3D shape = new Shape3D(); + double lineLength = noOfLines * size / 2; + LineArray la = new LineArray(noOfLines * 4, LineArray.COORDINATES); + int count = 0; + for (int i = 0; i < noOfLines; i++) + { + la.setCoordinate(count, new Point3d(-lineLength, height, i * size - lineLength)); + count++; + la.setCoordinate(count, new Point3d(lineLength, height, i * size - lineLength)); + count++; + } + for (int i = 0; i < noOfLines; i++) + { + la.setCoordinate(count, new Point3d(i * size - lineLength, height, -lineLength)); + count++; + la.setCoordinate(count, new Point3d(i * size - lineLength, height, lineLength)); + count++; + } + shape.setGeometry(la); + Appearance a = new Appearance(); + ColoringAttributes ca = new ColoringAttributes(); + ca.setColor(0.3f, 0.3f, 0.3f); + a.setColoringAttributes(ca); + LineAttributes sla = new LineAttributes(); + sla.setLineWidth(1.0f); + a.setLineAttributes(sla); + shape.setAppearance(a); + + return shape; + } + + private Group createSoundBoundingGeometries(PointSound ps) + { + Group group = new Group(); + Sphere sphere1 = getInnerBoundingSphere(ps); + group.addChild(sphere1); + + assert (ps.getDistanceGainLength() == 2); + + // create geometry for outer Bounding Sphere + float[] ds = new float[ps.getDistanceGainLength()]; + float[] as = new float[ps.getDistanceGainLength()]; + ps.getDistanceGain(ds, as); + float distanceAtZero = ds[ps.getDistanceGainLength() - 1]; + + Sphere sphere2 = getSphere(distanceAtZero, false); + group.addChild(sphere2); + + return group; + } + + private Sphere getInnerBoundingSphere(Sound sound) + { + Bounds bounds = sound.getSchedulingBounds(); + assert ((bounds != null) && (bounds instanceof BoundingSphere)); + BoundingSphere bs = (BoundingSphere) bounds; + float radius = (float) bs.getRadius(); + + return getSphere(radius, true); + } + + private Sphere getSphere(float radius, boolean inner) + { + + Appearance a = new Appearance(); + Material m = new Material(); + + if (inner) + { + m.setDiffuseColor(1, 0, 0); + m.setAmbientColor(1, 0, 0); + m.setShininess(8); + } + else + { + m.setDiffuseColor(0, 1, 0); + m.setAmbientColor(0, 1, 0); + m.setShininess(8); + } + a.setMaterial(m); + + PolygonAttributes pa = new PolygonAttributes(); + pa.setPolygonMode(PolygonAttributes.POLYGON_LINE); + pa.setCullFace(PolygonAttributes.CULL_NONE); + a.setPolygonAttributes(pa); + return new Sphere(radius, a); + } + + private TransformGroup createSoundNodeGeometry(float x, float y, float z) + { + + TransformGroup rootTransformGroup = new TransformGroup(); + Transform3D t3D = new Transform3D(); + t3D.setTranslation(new Vector3f(x, y, z)); + rootTransformGroup.setTransform(t3D); + ColorCube cc = new ColorCube(0.1); + rootTransformGroup.addChild(cc); + return rootTransformGroup; + } + + public BranchGroup createSceneGraph() + { + // Create the root of the branch graph + BranchGroup objRoot = new BranchGroup(); + + BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); + + AmbientLight al = new AmbientLight(); + al.setInfluencingBounds(bounds); + + DirectionalLight dl = new DirectionalLight(); + dl.setDirection(-1, -1, -1); + dl.setInfluencingBounds(bounds); + objRoot.addChild(al); + objRoot.addChild(dl); + + /* + * Create Sound and Behavior objects that will play the sound + */ + PointSound ps = new PointSound(); + PointSoundBehavior player = new PointSoundBehavior(ps, url, new Point3f(0.0f, 0.0f, 0.0f)); + player.setSchedulingBounds(bounds); + objRoot.addChild(ps); + objRoot.addChild(player); + + objRoot.addChild(getDefaultGrid(40, 1, -1)); + objRoot.addChild(createSoundNodeGeometry(0, 0, 0)); + objRoot.addChild(createSoundBoundingGeometries(ps)); + return objRoot; + } + + private Canvas3D createUniverse() + { + // Get the preferred graphics configuration for the default screen + GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); + + // Create a Canvas3D using the preferred configuration + Canvas3D c = new Canvas3D(config); + + // Create simple universe with view branch + univ = new SimpleUniverse(c); + + ViewingPlatform viewingPlatform = univ.getViewingPlatform(); + TransformGroup viewingPlatformTransformGroup = viewingPlatform.getViewPlatformTransform(); + + // This will move the ViewPlatform back a bit so the + // objects in the scene can be viewed. + viewingPlatform.setNominalViewingTransform(); + + Viewer viewer = univ.getViewer(); + + viewer.getView().setBackClipDistance(1000.0f); + + // Ensure at least 50 msec per frame. + viewer.getView().setMinimumFrameCycleTime(30); + + viewer.getView().getViewPlatform().setActivationRadius(activationRadius); + + BranchGroup bg = new BranchGroup(); + KeyNavigatorBehavior knb = new KeyNavigatorBehavior(c, viewingPlatformTransformGroup); + Bounds b = new BoundingSphere(new Point3d(), Double.POSITIVE_INFINITY); + knb.setSchedulingBounds(b); + bg.addChild(knb); + univ.addBranchGraph(bg); + + //Note if you defined a "j3d.audiodevice" system property and gave it a class then you would call this function + //viewer.createAudioDevice(); + + //Here I use a direct setup + + //getUserHeadToVworldEnable must be enabled otherwise a restricted access exception occurs + if (mixer == null && viewer.getView().getUserHeadToVworldEnable()) + { + // create and adds a joalmixer as the audio device + mixer = new JOALMixer(viewer.getPhysicalEnvironment()); + + boolean success = mixer.initialize(); + + if (!success) + { + System.out.println("Open AL failed to init"); + // remove the audio device + viewer.getPhysicalEnvironment().setAudioDevice(null); + } + } + + return c; + } + + /** + * Creates new form PointSoundTest */ - PointSound ps = new PointSound(); - PointSoundBehavior player = new PointSoundBehavior( ps, url, new Point3f(0.0f, 0.0f, 0.0f)); - player.setSchedulingBounds(bounds); - objRoot.addChild(ps); - objRoot.addChild(player); - - objRoot.addChild(getDefaultGrid(40, 1, -1)); - objRoot.addChild(createSoundNodeGeometry(0, 0, 0)); - objRoot.addChild(createSoundBoundingGeometries(ps)); - return objRoot; - } - - - private Canvas3D createUniverse() { - // Get the preferred graphics configuration for the default screen - GraphicsConfiguration config = - SimpleUniverse.getPreferredConfiguration(); - - // Create a Canvas3D using the preferred configuration - Canvas3D c = new Canvas3D(config); - - // Create simple universe with view branch - univ = new SimpleUniverse(c); - - ViewingPlatform viewingPlatform = univ.getViewingPlatform(); - TransformGroup viewingPlatformTransformGroup = viewingPlatform.getViewPlatformTransform(); - - // This will move the ViewPlatform back a bit so the - // objects in the scene can be viewed. - viewingPlatform.setNominalViewingTransform(); - - Viewer viewer = univ.getViewer(); - - viewer.createAudioDevice(); - viewer.getView().setBackClipDistance(1000.0f); - - // Ensure at least 50 msec per frame. - viewer.getView().setMinimumFrameCycleTime(30); - - viewer.getView().getViewPlatform().setActivationRadius(activationRadius); - - BranchGroup bg = new BranchGroup(); - KeyNavigatorBehavior knb = new KeyNavigatorBehavior(c, viewingPlatformTransformGroup); - Bounds b = new BoundingSphere(new Point3d(), Double.POSITIVE_INFINITY); - knb.setSchedulingBounds(b); - bg.addChild(knb); - univ.addBranchGraph(bg); - - return c; - } - - - /** - * Creates new form PointSoundTest - */ - public PointSoundTest() { - // Initialize the GUI components - initComponents(); - - url = Resources.getResource("main/resources/audio/magic_bells.wav"); - if (url == null) { - System.err.println("main/resources/audio/magic_bells.wav not found"); - System.exit(1); - } - - // Create Canvas3D and SimpleUniverse; add canvas to drawing panel - Canvas3D c = createUniverse(); - drawingPanel.add(c, java.awt.BorderLayout.CENTER); - - // Create the content branch and add it to the universe - scene = createSceneGraph(); - univ.addBranchGraph(scene); - - } - - // ---------------------------------------------------------------- - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents - private void initComponents() { - drawingPanel = new javax.swing.JPanel(); - - setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); - setTitle("PointSound Test"); - drawingPanel.setLayout(new java.awt.BorderLayout()); - - drawingPanel.setPreferredSize(new java.awt.Dimension(800, 600)); - getContentPane().add(drawingPanel, java.awt.BorderLayout.CENTER); - - pack(); - }// </editor-fold>//GEN-END:initComponents - - /** - * @param args the command line arguments - */ - public static void main(String args[]) {System.setProperty("sun.awt.noerasebackground", "true"); - java.awt.EventQueue.invokeLater(new Runnable() { - public void run() { - new PointSoundTest().setVisible(true); - } - }); - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JPanel drawingPanel; - // End of variables declaration//GEN-END:variables - + public PointSoundTest() + { + // Initialize the GUI components + initComponents(); + + url = Resources.getResource("main/resources/audio/magic_bells.wav"); + if (url == null) + { + System.err.println("main/resources/audio/magic_bells.wav not found"); + System.exit(1); + } + + // Create Canvas3D and SimpleUniverse; add canvas to drawing panel + Canvas3D c = createUniverse(); + drawingPanel.add(c, java.awt.BorderLayout.CENTER); + + // Create the content branch and add it to the universe + scene = createSceneGraph(); + univ.addBranchGraph(scene); + + } + + // ---------------------------------------------------------------- + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents + private void initComponents() + { + drawingPanel = new javax.swing.JPanel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + setTitle("PointSound Test"); + drawingPanel.setLayout(new java.awt.BorderLayout()); + + drawingPanel.setPreferredSize(new java.awt.Dimension(800, 600)); + getContentPane().add(drawingPanel, java.awt.BorderLayout.CENTER); + + pack(); + }// </editor-fold>//GEN-END:initComponents + + /** + * @param args the command line arguments + */ + public static void main(String args[]) + { + System.setProperty("sun.awt.noerasebackground", "true"); + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() + { + new PointSoundTest().setVisible(true); + } + }); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel drawingPanel; + // End of variables declaration//GEN-END:variables + } diff --git a/src/main/java/org/jdesktop/j3d/examples/sound/audio/JOALMixer.java b/src/main/java/org/jdesktop/j3d/examples/sound/audio/JOALMixer.java new file mode 100644 index 0000000..be78fad --- /dev/null +++ b/src/main/java/org/jdesktop/j3d/examples/sound/audio/JOALMixer.java @@ -0,0 +1,1232 @@ +/* + * Taken from j3d-optional-utils + * BSD License + * https://java.net/projects/j3d-optional-utils + */ + +package org.jdesktop.j3d.examples.sound.audio; + +import java.util.HashMap; + +import org.jogamp.java3d.AudioDevice; +import org.jogamp.java3d.AudioDevice3D; +import org.jogamp.java3d.AudioDevice3DL2; +import org.jogamp.java3d.AuralAttributes; +import org.jogamp.java3d.MediaContainer; +import org.jogamp.java3d.PhysicalEnvironment; +import org.jogamp.java3d.Sound; +import org.jogamp.java3d.Transform3D; +import org.jogamp.java3d.View; +import org.jogamp.java3d.audioengines.AudioEngine3DL2; +import org.jogamp.java3d.audioengines.Sample; +import org.jogamp.vecmath.Point3d; +import org.jogamp.vecmath.Vector3d; +import org.jogamp.vecmath.Vector3f; + +import com.jogamp.openal.AL; +import com.jogamp.openal.ALC; +import com.jogamp.openal.ALCConstants; +import com.jogamp.openal.ALCcontext; +import com.jogamp.openal.ALCdevice; +import com.jogamp.openal.ALConstants; +import com.jogamp.openal.ALException; +import com.jogamp.openal.ALFactory; +import com.jogamp.openal.util.ALut; + +/** + * This class is a concrete implementation of AudioEngine3DL2 that uses the JOAL/OpenAL sound library to provide + * rendering of the Sound nodes in Java3D. <br> + * Notes: <br> + * 1. getChannelsAvailable, getNumberOfChannelsUsed and getTotalChannels have not been implemented yet and thus if you + * try to play more sounds than the system has then they will not play. I am still trying to find out exactly how to + * implement these functions, possibly there is no limit when rendering sound using software rendering. Also at the + * moment I think I have limited the implementation to 64 sounds. <br> + * 2. This implementation only plays wav files. <br> + * 3. Other functions not implemented have the comment 'NOT IMPLEMENTED' in their respective JavaDoc comment, otherwise + * the function has been implemented. <br> + * 4. Webstart demos are available at http://www.dutchie.net/joal/default.htm <br> + * Usage: <br> + * 1. You must have the OpenAL drivers installed on your machine, these can be downloaded for Windows, MacOS, Linux at + * http://www.openal.org/downloads.html <br> + * 2. You must have JOAL installed which can be downloaded from https://joal.dev.java.net/ . This includes joal.zip and + * the native dll/so for your selected platform. <br> + * 3. To use in your application you simply use: <br> + * java -Dj3d.audiodevice=org.jdesktop.j3d.audioengines.joal.JOALMixer [your_application_class] and call + * viewer.createAudioDevice(); as normal. + * + * @author David Grace ([email protected]) + */ +@SuppressWarnings( +{ "unchecked", "unused", "hiding" }) +public class JOALMixer extends AudioEngine3DL2 implements AudioDevice, AudioDevice3D, AudioDevice3DL2 +{ + + // Debug boolean + private static boolean debug = false; + + private static boolean debugVersion = true; + + private static boolean debugView = false; + + private static boolean debugPrepareSound = true; + + private static boolean debugGetTotalChannels = false; + + private static boolean debugSampleDuration = false; + + private static boolean debugVelocity = false; + + private static boolean debugPosition = false; + + private static boolean debugDirection = false; + + private static boolean debugDistanceGain = false; + + private static boolean debugGain = false; + + private static boolean debugLoopCount = false; + + private static boolean debugMute = true; + + private static boolean debugUnmute = true; + + private static boolean debugStart = true; + + private static boolean debugStartTime = true; + + private static boolean debugStop = false; + + private static boolean debugClearSound = true; + + // Dictates whether or not buffers are shared between samples + private boolean shareBuffer = true; + + private HashMap<Object, Object> sharedBuffers = new HashMap<Object, Object>(); + + private boolean calculateDopplerEffect = false; + + // Determines method to call for added or setting sound into ArrayList + static final int ADD_TO_LIST = 1; + + static final int SET_INTO_LIST = 2; + + // AL for access to JOAL + static AL al; + + static ALC alc; + + // Temp arrays for passing data from Java to JOAL + private float[] singleArray = new float[1]; + + // private float[] tripleArray = new float[3]; + + // Java3D View data + // private View reference; + private Transform3D transform = new Transform3D(); + + private float[] position = new float[3]; + + private float[] lastPosition = new float[3]; + + private Vector3f positionVector = new Vector3f(); + + // private Vector3f lastPositionVector = new Vector3f(); + private Vector3f viewVector = new Vector3f(0, 0, -1); + + private Vector3f upVector = new Vector3f(0, 1, 0); + + // JOAL listener data + + private float[] velocity = + { 0.0f, 0.0f, 0.0f }; + + private float[] noVelocity = + { 0.0f, 0.0f, 0.0f }; + + private float[] orientation = + { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }; + + private long timeOfThisViewUpdate = -1; + + private long timeSinceLastViewUpdate = -1; + + private float timeSinceLastViewUpdateInSeconds = -1; + + private long timeOfLastViewUpdate = -1; + + /** Creates a new instance of JOALMixer */ + public JOALMixer(PhysicalEnvironment physicalEnvironment) + { + super(physicalEnvironment); + if (debug) + System.out.println("JOALMixer - constructor..."); + // initialize(); + // if (debug & debugVersion) System.out.println("JOAL - AL_DOPPLER_FACTOR: " + + // al.alGetFloat(AL.AL_DOPPLER_FACTOR)); + // if (debug & debugVersion) System.out.println("JOAL - AL_SPEED_OF_SOUND: " + + // al.alGetFloat(AL.AL_SPEED_OF_SOUND)); + // if (debug & debugVersion) System.out.println("JOAL - AL_DISTANCE_MODEL: " + + // al.alGetInteger(AL.AL_DISTANCE_MODEL)); + } + + /** + * Set overall gain control of all sounds playing on the audio device. + * + * @param scaleFactor + * scale factor applied to calculated amplitudes for all sounds playing on this device + */ + public void setGain(float scaleFactor) + { + singleArray[0] = scaleFactor; + al.alListenerfv(ALConstants.AL_GAIN, singleArray, 0); + } + + /** + * NOT IMPLEMENTED <br> + * Resumes audio device engine (if previously paused) without reinitializing the device. Causes all paused cached + * sounds to be resumed and all streaming sounds restarted. + */ + public void resume() + { + + } + + /** + * NOT IMPLEMENTED <br> + * Pauses audio device engine without closing the device and associated threads. Causes all cached sounds to be + * paused and all streaming sounds to be stopped. + */ + public void pause() + { + + } + + /** + * Save a reference to the current View object. + * + * @param reference + * to current view object + */ + public void setView(View reference) + { + if (debug && debugView) + System.out.println("JOALMixer - setView..."); + if (reference.getAllCanvas3Ds().hasNext()) + { + reference.getUserHeadToVworld(transform); + transform.get(positionVector); + position[0] = positionVector.x; + position[1] = positionVector.y; + position[2] = positionVector.z; + al.alListenerfv(ALConstants.AL_POSITION, position, 0); + + // Update velocity information + if (timeOfLastViewUpdate == -1) + { + timeOfLastViewUpdate = System.nanoTime(); + lastPosition[0] = positionVector.x; + lastPosition[1] = positionVector.y; + lastPosition[2] = positionVector.z; + } + else + { + timeOfThisViewUpdate = System.nanoTime(); + timeSinceLastViewUpdate = timeOfLastViewUpdate - timeOfThisViewUpdate; + timeOfLastViewUpdate = timeOfThisViewUpdate; + + if (calculateDopplerEffect) + { + timeSinceLastViewUpdateInSeconds = timeSinceLastViewUpdate / 1000000; + velocity[0] = (lastPosition[0] - position[0]) / timeSinceLastViewUpdateInSeconds; + velocity[1] = (lastPosition[1] - position[1]) / timeSinceLastViewUpdateInSeconds; + velocity[2] = (lastPosition[2] - position[2]) / timeSinceLastViewUpdateInSeconds; + al.alListenerfv(ALConstants.AL_VELOCITY, velocity, 0); + if (debug & debugVelocity) + System.out.println("JOALMixer - velocity: " + velocity[0] + ", " + velocity[1] + ", " + velocity[2]); + } + else + { + al.alListenerfv(ALConstants.AL_VELOCITY, noVelocity, 0); + } + + lastPosition[0] = positionVector.x; + lastPosition[1] = positionVector.y; + lastPosition[2] = positionVector.z; + } + // Vector3f viewVector= new Vector3f(0, 0, -1); + // Vector3f upVector = new Vector3f(0, 1, 0); + viewVector.set(0, 0, -1); + upVector.set(0, 1, 0); + // get viewVector + transform.transform(viewVector); + + // get upVector + transform.transform(upVector); + orientation[0] = viewVector.x; + orientation[1] = viewVector.y; + orientation[2] = viewVector.z; + orientation[3] = upVector.x; + orientation[4] = upVector.y; + orientation[5] = upVector.z; + + al.alListenerfv(ALConstants.AL_ORIENTATION, orientation, 0); + } + super.setView(reference); + } + + /** + * Prepare Sound in device <br> + * Makes sound assessible to device - in this case attempts to load sound Stores sound type and data. + * + * @param soundType + * denotes type of sound: Background, Point or Cone + * @param soundData + * descrition of sound source data + * @return index into sample vector of Sample object for sound + */ + + public int prepareSound(int soundType, MediaContainer soundData) + { + if (debug && debugPrepareSound) + { + if (soundData.getURLObject() != null) + { + System.out.println("JOALMixer - prepareSound - " + soundData + " - " + soundData.getURLObject()); + } + else if (soundData.getURLString() != null) + { + System.out.println("JOALMixer - prepareSound - " + soundData + " - " + soundData.getURLString()); + } + else + System.out.println("JOALMixer - prepareSound - " + soundData + " - " + soundData.getInputStream()); + } + int index = JOALSample.NULL_SAMPLE; + int methodType = ADD_TO_LIST; + + if (soundData == null) + return JOALSample.NULL_SAMPLE; + synchronized (samples) + { + int samplesSize = samples.size(); + index = samplesSize; + samples.ensureCapacity(index + 1); + + JOALSample joalSample = new JOALSample(); + boolean error = true; + + // Code added here to address bug id 500 - JOALMixer should share buffers. + // If the MediaContainer has a URLObject and has been loaded before + // then the same buffer will be used. + // As yet I am unable to determine when a buffer should be removed + // from the HashMap to release the buffer from memory + + if (shareBuffer) + { + if (soundData.getURLObject() != null) + { + if (sharedBuffers.containsKey(soundData.getURLObject())) + { + error = joalSample.load(al, (int[]) sharedBuffers.get(soundData.getURLObject()), soundType); + } + else + { + error = joalSample.load(al, soundData, soundType); + sharedBuffers.put(soundData.getURLObject(), joalSample.getBuffer()); + } + } + else if (soundData.getURLString() != null) + { + if (sharedBuffers.containsKey(soundData.getURLString())) + { + error = joalSample.load(al, (int[]) sharedBuffers.get(soundData.getURLString()), soundType); + } + else + { + error = joalSample.load(al, soundData, soundType); + sharedBuffers.put(soundData.getURLString(), joalSample.getBuffer()); + } + } + else + { + if (sharedBuffers.containsKey(soundData.getInputStream())) + { + error = joalSample.load(al, (int[]) sharedBuffers.get(soundData.getInputStream()), soundType); + } + else + { + error = joalSample.load(al, soundData, soundType); + sharedBuffers.put(soundData.getInputStream(), joalSample.getBuffer()); + } + } + + } + else + { + error = joalSample.load(al, soundData, soundType); + } + + if (error) + return JOALSample.NULL_SAMPLE; + if (methodType == SET_INTO_LIST) + samples.set(index, joalSample); + else + samples.add(index, joalSample); + if (debug) + System.out.println("JOALMixer - prepareSound - return: " + index); + return index; + } + } + + /** + * Clear Sound. Removes/clears associated sound data with this sound source node + * + * @param index + * device specific reference number to device driver sample + */ + public void clearSound(int index) + { + if (debug && debugClearSound) + System.out.println("JOALMixer - clearSound " + index); + Sample sample = null; + if ((sample = getSample(index)) == null) + return; + sample.clear(); + synchronized (samples) + { + samples.set(index, null); + } + } + + /** + * Get length of time a sample would play if allowed to play to completion. + * + * @param index + * device specific reference number to device driver sample + * @return length of sample in milliseconds + */ + public long getSampleDuration(int index) + { + if (debug && debugSampleDuration) + System.out.println("JOALMixer - getSampleDuration for " + index); + return super.getSampleDuration(index); + } + + /** + * NOT IMPLEMENTED <br> + * Get number of channels used by a particular sample on the audio device. + * + * @param index + * device specific reference number to device driver sample + * @return number of channels currently being used by this sample. + */ + public int getNumberOfChannelsUsed(int index) + { + // if (debug) System.out.println("JOALMixer - getNumberOfChannelsUsed..."); + return super.getNumberOfChannelsUsed(index); + } + + /** + * NOT IMPLEMENTED <br> + * Get number of channels that would be used by a particular sample on the audio device given the mute flag passed + * in as a parameter. + * + * @param index + * device specific reference number to device driver sample + * @param muteFlag + * denotes the mute state to assume while executing this query. This mute value does not have to match + * the current mute state of the sample. + * @return number of channels that would be used by this sample if it were playing. + */ + public int getNumberOfChannelsUsed(int index, boolean muteFlag) + { + // if (debug) System.out.println("JOALMixer - getNumberOfChannelsUsed...boolean"); + return 1; + } + + /** + * Start sample playing on audio device + * + * @param index + * device specific reference number to device driver sample + * @return status: < 0 denotes an error + */ + public int startSample(int index) + { + if (debug & debugStart) + System.out.println("JOALMixer - start..." + index); + // super.startSample(index); + JOALSample s = (JOALSample) getSample(index); + if (s == null) + return -1; + else + return s.startSample(); + } + + /** + * NOT IMPLEMENTED <br> + * Get time this sample begun playing on the audio device. + * + * @param index + * device specific reference number to device driver sample + * @return system clock time sample started + */ + public long getStartTime(int index) + { + if (debug && debugStartTime) + System.out.println("JOALMixer - getStartTime for " + index); + return -1; + } + + /** + * Stop sample playing on audio device + * + * @param index + * device specific reference number to device driver sample + * @return status: < 0 denotes an error + */ + public int stopSample(int index) + { + if (debug & debugStop) + System.out.println("JOALMixer - stopSample " + index); + JOALSample s = (JOALSample) getSample(index); + if (s == null) + return -1; + else + return s.stopSample(); + } + + /** + * Set gain scale factor applied to sample. + * + * @param index + * device specific reference number to device driver sample + * @param scaleFactor + * floating point multiplier applied to sample amplitude + */ + public void setSampleGain(int index, float scaleFactor) + { + if (debug & debugGain) + System.out.println("JOALMixer - setSampleGain for " + index + " to " + scaleFactor); + JOALSample s = (JOALSample) getSample(index); + if (s == null) + return; + else + s.setGain(scaleFactor); + } + + /** + * NOT IMPLEMENTED COMPLETELY <br> + * Set number of times sample is looped <br> + * Works for values 0 (no loop) and (-1) infinite loop but not for a fix number of loops (>1) <br> + * + * @param index + * device specific reference number to device driver sample + * @param count + * number of times sample is repeated + */ + public void setLoop(int index, int count) + { + if (debug & debugLoopCount) + System.out.println("JOALMixer - setLoop for " + index + " to " + count); + super.setLoop(index, count); + } + + /** + * DOES NOTHING - does not need to do anything special for JOAL<br> + * Set the transform for local to virtual world coordinate space + * + * @param index + * device specific reference number to device driver sample + * @param trans + * is a reference to virtual world composite transform + */ + public void setVworldXfrm(int index, Transform3D trans) + { + if (debug) + System.out.println("JOALMixer - setVworldXfrm..."); + super.setVworldXfrm(index, trans); + } + + /** + * Set location of sample. + * + * @param index + * device specific reference number to device driver sample + * @param position + * point location in virtual world coordinate of sample + */ + public void setPosition(int index, Point3d position) + { + if (debug & debugPosition) + System.out.println("JOALMixer - setPosition for " + index + " to " + position); + super.setPosition(index, position); + } + + /** + * Set elliptical distance attenuation arrays applied to sample amplitude<br> + * OPENAL/JOAL calculates the distance attenuation using its default model which is the inverse distance clamped + * model (equivalent to the IASIG I3DL2 distance model) <br> + * This is: <br> + * distance = max(distance, AL_REFERENCE_DISTANCE); <br> + * distance = max(distance, AL_MAX_DISTANCE); <br> + * gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE)); + * <br> + * This function calculates the AL_REFERENCE_DISTANCE, AL_MAX_DISTANCE and AL_ROLLOFF_FACTOR from the given distance + * attenuation data from Java3D. <br> + * + * @param index + * device specific reference number to device driver sample + * @param frontDistance + * defines an array of distance along the position axis thru which ellipses pass + * @param frontAttenuationScaleFactor + * gain scale factors + * @param backDistance + * defines an array of distance along the negative axis thru which ellipses pass + * @param backAttenuationScaleFactor + * gain scale factors + */ + public void setDistanceGain(int index, double[] frontDistance, float[] frontAttenuationScaleFactor, double[] backDistance, + float[] backAttenuationScaleFactor) + { + if (debug & debugDistanceGain) + System.out.println("JOALMixer - setDistanceGain for " + index + " with " + frontDistance + ", " + frontAttenuationScaleFactor + + ", " + backDistance + ", " + backAttenuationScaleFactor); + super.setDistanceGain(index, frontDistance, frontAttenuationScaleFactor, backDistance, backAttenuationScaleFactor); + } + + /** + * Set direction vector of sample. + * + * @param index + * device specific reference number to device driver sample + * @param direction + * vector in virtual world coordinate. + */ + public void setDirection(int index, Vector3d direction) + { + if (debug & debugDirection) + System.out.println("JOALMixer - setDirection for " + index + " to " + direction); + super.setDirection(index, direction); + } + + /** + * Set angular attenuation arrays affecting angular amplitude attenuation and angular distance filtering <br> + * Angular attenuation in OpenAL/JOAL is determined by AL_CONE_INNER_ANGLE, AL_CONE_OUTER_ANGLE and + * AL_CONE_OUTER_GAIN <br> + * The region inside and AL_CONE_OUTER_ANGLE has its gain unchanged (AL_GAIN), the region inside AL_CONE_INNER_ANGLE + * and AL_CONE_OUTER_ANGLE has its gain between AL_GAIN and AL_CONE_OUTER_GAIN, and the region outside + * AL_CONE_OUTER_ANGLE has gain of AL_CONE_OUTER_GAIN <br> + * This function calculates the AL_CONE_INNER_ANGLE, AL_CONE_OUTER_ANGLE and AL_CONE_OUTER_GAIN from the given + * angular attenuation data from Java3D. <br> + * + * @param index + * device specific reference number to device driver sample + * @param filterType + * denotes type of filtering (on no filtering) applied to sample. + * @param angle + * array containing angular distances from sound axis + * @param attenuationScaleFactor + * array containing gain scale factor + * @param filterCutoff + * array containing filter cutoff frequencies. The filter values for each tuples can be set to + * Sound.NO_FILTER. + */ + public void setAngularAttenuation(int index, int filterType, double[] angle, float[] attenuationScaleFactor, float[] filterCutoff) + { + if (debug) + System.out.println("JOALMixer - setAngularAttenuation..."); + super.setAngularAttenuation(index, filterType, angle, attenuationScaleFactor, filterCutoff); + } + + /** + * NOT IMPLEMENTED <br> + * Set rolloff value for current aural attribute applied to all samples. + * + * @param rolloff + * scale factor applied to standard speed of sound. + */ + public void setRolloff(float rolloff) + { + if (debug) + System.out.println("JOALMixer - setRolloff..."); + } + + /** + * NOT IMPLEMENTED <br> + * Set reverberation surface reflection coefficient value for current aural attribute applied to all samples. + * + * @param coefficient + * applied to amplitude of reverbation added at each iteration of reverb processing. + */ + public void setReflectionCoefficient(float coefficient) + { + if (debug) + System.out.println("JOALMixer - setReflectionCoefficient..."); + } + + /** + * NOT IMPLEMENTED <br> + * Set reverberation delay time for current aural attribute applied to all samples. + * + * @param reverbDelay + * amount of time in millisecond between each iteration of reverb processing. + */ + public void setReverbDelay(float reverbDelay) + { + if (debug) + System.out.println("JOALMixer - setReverbDelay..."); + } + + /** + * NOT IMPLEMENTED <br> + * Set reverberation order for current aural attribute applied to all samples. + * + * @param reverbOrder + * number of times reverb process loop is iterated. + */ + public void setReverbOrder(int reverbOrder) + { + if (debug) + System.out.println("JOALMixer - setReverbOrder..."); + } + + /** + * NOT IMPLEMENTED <br> + * Set distance filter for current aural attribute applied to all samples. + * + * @param filterType + * denotes type of filtering (on no filtering) applied to all sample based on distance between listener + * and sound. + * @param dist + * is an attenuation array of distance and low-pass filter values. + */ + public void setDistanceFilter(int filterType, double[] distance, float[] dist) + { + if (debug) + System.out.println("JOALMixer - setDistanceFilter..."); + } + + /** + * NOT IMPLEMENTED <br> + * Set frequency scale factor for current aural attribute applied to all samples. + * + * @param frequencyScaleFactor + * frequency scale factor applied to samples normal playback rate. + */ + public void setFrequencyScaleFactor(float frequencyScaleFactor) + { + if (debug) + System.out.println("JOALMixer - setFrequencyScaleFactor to " + frequencyScaleFactor); + } + + /** + * NOT IMPLEMENTED <br> + * Set velocity scale factor for current aural attribute applied to all samples when Doppler is calculated. + * + * @param velocityScaleFactor + * scale factor applied to postional samples' listener-to-soundSource velocity. playback rate. + */ + public void setVelocityScaleFactor(float velocityScaleFactor) + { + if (debug) + System.out.println("JOALMixer - setVelocityScaleFactor to " + velocityScaleFactor); + super.setVelocityScaleFactor(velocityScaleFactor); + } + + /** + * Mute sample. + * + * @param index + * device specific reference number to device driver sample + */ + public void muteSample(int index) + { + if (debug && debugMute) + System.out.println("JOALMixer - muteSample " + index); + Sample sample = getSample(index); + if (sample != null) + sample.setMuteFlag(true); + } + + /** + * Unmute sample. + * + * @param index + * device specific reference number to device driver sample + */ + public void unmuteSample(int index) + { + if (debug && debugUnmute) + System.out.println("JOALMixer - unmuteSample for " + index); + Sample sample = getSample(index); + if (sample != null) + sample.setMuteFlag(false); + } + + /** + * Pause sample. + * + * @param index + * device specific reference number to device driver sample + */ + public void pauseSample(int index) + { + if (debug) + System.out.println("JOALMixer - pauseSample for " + index); + al.alSourcePause(index); + } + + /** + * Unpause sample. + * + * @param index + * device specific reference number to device driver sample + */ + public void unpauseSample(int index) + { + if (debug) + System.out.println("JOALMixer - unpauseSample for " + index); + al.alSourcePlay(index); + } + + /** + * DOES NOTHING - not really needed when using JOAL <br> + * Update sample Implies that some parameters affecting rendering have been modified. + * + * @param index + * device specific reference number to device driver sample + */ + public void updateSample(int index) + { + // if (debug) System.out.println("JOALMixer - updateSample " + index); + } + + /** + * Code to initialize the device + * + * @return flag: true is initialized sucessfully, false if error + */ + public boolean initialize() + { + if (debug) + System.out.println("JOALMixer - initialize..."); + + try + { + ALut.alutInit(); + al = ALFactory.getAL(); + al.alDistanceModel(ALConstants.AL_INVERSE_DISTANCE); + // if (debug & debugVersion) System.out.println("JOALMixer - JOAL version: " + Version.getVersion()); + if (debug & debugVersion) + System.out.println("JOALMixer - JOAL renderer: " + al.alGetString(ALConstants.AL_RENDERER)); + if (debug & debugVersion) + System.out.println("JOALMixer - JOAL vendor: " + al.alGetString(ALConstants.AL_VENDOR)); + if (debug & debugVersion) + System.out.println("JOALMixer - JOAL extension: " + al.alGetString(ALConstants.AL_EXTENSIONS)); + + } + catch (ALException e) + { + e.printStackTrace(); + if (debug) + System.out.println("JOALMixer - initialize failed - Error initializing JOALMixer, code: " + al.alGetError()); + return false; + } + if (debug) + System.out.println("JOALMixer - initialize success."); + return true; + } + + /** + * NOT IMPLEMENTED <br> + * Code to close the device + * + * @return flag: true is closed sucessfully, false if error + */ + public boolean close() + { + + if (debug) + System.out.println("JOALMixer - close..."); + // Not sure if all the samples should be clear(ed), if yes then uncomment + /* + * int index = 0; synchronized(samples) { for (Object s:samples){ Sample sample = (Sample) s; if ( (sample = + * getSample(index)) == null){ } else { sample.clear(); samples.set(index, null); } } } return true; + */ + + return false; + } + + /** + * Set Type of Audio Playback physical transducer(s) sound is output to <br> + * Valid types are HEADPHONE, MONO_SPEAKER, STEREO_SPEAKERS + * + * @param type + * of audio output device + */ + public void setAudioPlaybackType(int type) + { + if (debug) + System.out.println("JOALMixer - setAudioPlaybackType to " + type); + super.setAudioPlaybackType(type); + } + + /** + * Get Type of Audio Playback Output Device returns audio playback type to which sound is currently output + */ + public int getAudioPlaybackType() + { + if (debug) + System.out.println("JOALMixer - getAudioPlaybackType..."); + return super.getAudioPlaybackType(); + } + + /** + * Set Distance from the Center Ear to a Speaker + * + * @param distance + * from the center ear and to the speaker + */ + public void setCenterEarToSpeaker(float distance) + { + if (debug) + System.out.println("JOALMixer - setCenterEarToSpeaker to " + distance); + super.setCenterEarToSpeaker(distance); + } + + /** + * Get Distance from Ear to Speaker returns value set as distance from listener's ear to speaker + */ + public float getCenterEarToSpeaker() + { + if (debug) + System.out.println("JOALMixer - getCenterEarToSpeaker..."); + return super.getCenterEarToSpeaker(); + } + + /** + * Set Angle Offset To Speaker + * + * @param angle + * in radian between head coordinate Z axis and vector to speaker + */ + public void setAngleOffsetToSpeaker(float angle) + { + if (debug) + System.out.println("JOALMixer - setAngleOffsetToSpeaker " + angle); + super.setAngleOffsetToSpeaker(angle); + } + + /** + * Get Angle Offset To Speaker returns value set as angle between vector to speaker and Z head axis + */ + public float getAngleOffsetToSpeaker() + { + if (debug) + System.out.println("JOALMixer - getAngleOffsetToSpeaker..."); + return super.getAngleOffsetToSpeaker(); + } + + /** + * NOT IMPLEMENTED <br> + * Query total number of channels available for sound rendering for this audio device. returns number of maximum + * sound channels you can run with this library/device-driver. + */ + public int getTotalChannels() + { + if (debug && debugGetTotalChannels) + System.out.println("JOALMixer - getTotalChannels..."); + return 64; + } + + /** + * NOT IMPLEMENTED <br> + * Query number of channels currently available for use by the returns number of sound channels currently available + * (number not being used by active sounds). + */ + public int getChannelsAvailable() + { + if (debug) + System.out.println("JOALMixer - getChannelsAvailable..."); + return 8; + } + + /** + * Query number of channels that would be used to render a particular sound node. + * + * @param sound + * refenence to sound node that query to be performed on returns number of sound channels used by a + * specific Sound node + * @deprecated This method is now part of the Sound class + */ + public int getChannelsUsedForSound(Sound sound) + { + if (debug) + System.out.println("JOALMixer - getChannelsUsedForSound..."); + return super.getChannelsUsedForSound(sound); + } + + /** + * Set scale factor applied to sample playback rate for a particular sound associated with the audio device <br> + * Changing the device sample rate affects both the pitch and speed. This scale factor is applied to ALL sound types + * <br> + * Changes (scales) the playback rate of a sound independent of Doppler rate changes. + * + * @param index + * device specific reference to device driver sample + * @param scaleFactor + * non-negative factor applied to calculated amplitudes for all sounds playing on this device + * @see Sound#setRateScaleFactor + */ + public void setRateScaleFactor(int index, float scaleFactor) + { + JOALSample s = (JOALSample) getSample(index); + if (s == null) + return; + else + s.setRateScaleFactor(scaleFactor); + } + + /** + * NOT IMPLEMENTED <br> + * Set late reflection (referred to as 'reverb') attenuation. This scale factor is applied to iterative, + * indistinguishable late reflections that constitute the tail of reverberated sound in the aural environment <br> + * This parameter, along with the early reflection coefficient, defines the reflective/absorptive characteristic of + * the surfaces in the current listening region. + * + * @param coefficient + * late reflection attenuation factor + * @see AuralAttributes#setReverbCoefficient + */ + public void setReverbCoefficient(float coefficient) + { + } + + /** + * NOT IMPLEMENTED Sets the early reflection delay time <br> + * In this form, the parameter specifies the delay time between each order of reflection (while reverberation is + * being rendered) explicitly given in milliseconds. + * + * @param reflectionDelay + * time between each order of early reflection + * @see AuralAttributes#setReflectionDelay + */ + public void setReflectionDelay(float reflectionDelay) + { + } + + /** + * NOT IMPLEMENTED <br> + * Set reverb decay time <br> + * Defines the reverberation decay curve. + * + * @param time + * decay time in milliseconds + * @see AuralAttributes#setDecayTime + */ + public void setDecayTime(float time) + { + } + + /** + * NOT IMPLEMENTED <br> + * Set reverb decay filter <br> + * This provides for frequencies above the given cutoff frequency to be attenuated during reverb decay at a + * different rate than frequencies below this value <br> + * Thus, defining a different reverb decay curve for frequencies above the cutoff value. + * + * @param frequencyCutoff + * value of frequencies in Hertz above which a low-pass filter is applied. + * @see AuralAttributes#setDecayFilter + */ + public void setDecayFilter(float frequencyCutoff) + { + } + + /** + * NOT IMPLEMENTED <br> + * Set reverb diffusion <br> + * This defines the echo dispersement (also referred to as 'echo density') <br> + * The value of this reverb parameter is expressed as a percent of the audio device's minimum-to-maximum values. + * + * @param diffusion + * percentage expressed within the range of 0.0 and 1.0 + * @see AuralAttributes#setDiffusion + */ + public void setDiffusion(float diffusion) + { + } + + /** + * NOT IMPLEMENTED <br> + * Set reverb density <br> + * This defines the modal density (also referred to as 'spectral coloration') <br> + * The value of this parameter is expressed as a percent of the audio device's minimum-to-maximum values for this + * reverb parameter. + * + * @param density + * reverb density expressed as a percentage, within the range of 0.0 and 1.0 + * @see AuralAttributes#setDensity + */ + public void setDensity(float density) + { + } + + /** + * NOT IMPLEMENTED <br> + * Set the obstruction gain control <br> + * This method allows for attenuating sound waves traveling between the sound source and the listener obstructed by + * objects <br> + * Direct sound signals/waves for obstructed sound source are attenuated but not indirect (reflected) waves. There + * is no corresponding Core AuralAttributes method at this time. + * + * @param index + * device specific reference to device driver sample + * @param scaleFactor + * non-negative factor applied to direct sound gain + */ + public void setObstructionGain(int index, float scaleFactor) + { + } + + /** + * NOT IMPLEMENTED <br> + * Set the obstruction filter control <br> + * This provides for frequencies above the given cutoff frequency to be attenuated, during while the gain of an + * obstruction signal is being calculated, at a different rate than frequencies below this value. There is no + * corresponding Core AuralAttributes method at this time. + * + * @param index + * device specific reference to device driver sample + * @param frequencyCutoff + * value of frequencies in Hertz above which a low-pass filter is applied. + */ + public void setObstructionFilter(int index, float frequencyCutoff) + { + } + + /** + * NOT IMPLEMENTED <br> + * Set the occlusion gain control <br> + * This method allows for attenuating sound waves traveling between the sound source and the listener occluded by + * objects <br> + * Both direct and indirect sound signals/waves for occluded sound sources are attenuated <br> + * There is no corresponding Core AuralAttributes method at this time. + * + * @param index + * device specific reference to device driver sample + * @param scaleFactor + * non-negative factor applied to direct sound gain + */ + public void setOcclusionGain(int index, float scaleFactor) + { + } + + /** + * NOT IMPLEMENTED <br> + * Set the occlusion filter control <br> + * This provides for frequencies above the given cutoff frequency to be attenuated, during while the gain of an + * occluded signal is being calculated, at a different rate than frequencies below this value <br> + * There is no corresponding Core AuralAttributes method at this time. + * + * @param index + * device specific reference to device driver sample + * @param frequencyCutoff + * value of frequencies in Hertz above which a low-pass filter is applied. + */ + public void setOcclusionFilter(int index, float frequencyCutoff) + { + } + + /** + * Unused code for more specific initialization of a sound device. Maybe useful later if the option of selecting + * which device on the machine is used. At the moment it is the 'default' device returned by OpenAL/JOAL that is + * used. + */ + static int initOpenAL() + { + + ALCdevice device; + ALCcontext context; + String deviceSpecifier; + String deviceName = "DirectSound3D"; + + // Get handle to device. + device = alc.alcOpenDevice(deviceName); + + // Get the device specifier. + deviceSpecifier = alc.alcGetString(device, ALCConstants.ALC_DEVICE_SPECIFIER); + + System.out.println("Using device " + deviceSpecifier); + + // Create audio context. + context = alc.alcCreateContext(device, null); + + // Set active context. + alc.alcMakeContextCurrent(context); + + // Check for an error. + if (alc.alcGetError(device) != ALCConstants.ALC_NO_ERROR) + return ALConstants.AL_FALSE; + + return ALConstants.AL_TRUE; + } + + /** + * Unused code for to release resources created with the function above. Maybe useful later if the option of + * selecting which device on the machine is used. This would be used to close device. + */ + public static void exitOpenAL() + { + try + { + if (alc != null) + { + ALCcontext curContext; + ALCdevice curDevice; + + // Get the current context. + curContext = alc.alcGetCurrentContext(); + + // Get the device used by that context. + curDevice = alc.alcGetContextsDevice(curContext); + + // Reset the current context to NULL. + alc.alcMakeContextCurrent(null); + + // Release the context and the device. + alc.alcDestroyContext(curContext); + alc.alcCloseDevice(curDevice); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private void calculateOrientation(View view) + { + Vector3f viewPosition = new Vector3f(); + + Vector3f viewVector = new Vector3f(0, 0, -1); + Vector3f upVector = new Vector3f(0, 1, 0); + + Transform3D viewTransform = new Transform3D(); + view.getUserHeadToVworld(viewTransform); + + // get position + viewTransform.get(viewPosition); + + // get viewVector + viewTransform.transform(viewVector); + + // get upVector + viewTransform.transform(upVector); + + System.out.println("Position: " + viewPosition); + System.out.println("viewVector: " + viewVector); + System.out.println("upVector: " + upVector); + } + +} diff --git a/src/main/java/org/jdesktop/j3d/examples/sound/audio/JOALSample.java b/src/main/java/org/jdesktop/j3d/examples/sound/audio/JOALSample.java new file mode 100644 index 0000000..be9e2f4 --- /dev/null +++ b/src/main/java/org/jdesktop/j3d/examples/sound/audio/JOALSample.java @@ -0,0 +1,936 @@ +/* + * Taken from j3d-optional-utils + * BSD License + * https://java.net/projects/j3d-optional-utils + */ +package org.jdesktop.j3d.examples.sound.audio; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.ByteBuffer; + +import org.jogamp.java3d.AudioDevice3D; +import org.jogamp.java3d.MediaContainer; +import org.jogamp.java3d.Sound; +import org.jogamp.java3d.Transform3D; +import org.jogamp.java3d.View; +import org.jogamp.java3d.audioengines.AuralParameters; +import org.jogamp.java3d.audioengines.Sample; +import org.jogamp.vecmath.Point3d; +import org.jogamp.vecmath.Point3f; +import org.jogamp.vecmath.Vector3d; +import org.jogamp.vecmath.Vector3f; + +import com.jogamp.openal.AL; +import com.jogamp.openal.ALConstants; +import com.jogamp.openal.util.ALut; + +/** + * This is the JOAL Sample object which encapsulates all the functionality and holds all the data associated with a + * Sample. + * + * @author David Grace ([email protected]) + */ +@SuppressWarnings( +{ "hiding", "unused" }) +public class JOALSample extends Sample +{ + + private static boolean debug = false; + + private static boolean debugPosition = false; + + private static boolean debugDirection = false; + + private static boolean debugDistanceGain = false; + + private static boolean debugGain = false; + + private static boolean debugLoopCount = false; + + private static boolean debugMute = false; + + private static boolean debugLoad = true; + + private static boolean debugDuration = false; + + private static boolean debugClear = true; + + private int index; + + private int[] buffer; + + private int[] source; + + private AL al; + + private static double pi = Math.PI; + + private int sampleSize; + + private int sampleBits; + + private int sampleFrequency; + + private int sampleChannels = 1; + + /** Creates a new instance of JOALSample */ + public JOALSample() + { + } + + /** + * Null Sound identifier denotes sound is not created or initialized + */ + public static final int NULL_SAMPLE = -1; + + /** + * sound data associated with sound source + */ + protected MediaContainer soundData = null; + + /** + * sound data associated with sound source + */ + protected int soundType = -1; + + /** + * Overall Scale Factor applied to sound gain. + */ + protected float gain = 1.0f; // Valid values are >= 0.0. + + /** + * Overall Scale Factor applied to sound. + * + * @since Java 3D 1.3 + */ + protected float rateScaleFactor = 1.0f; // Valid values are >= 0.0. + + /** + * Number of times sound is looped/repeated during play + */ + protected int loopCount = 0; // Range from 0 to POSITIVE_INFINITY(-1) + + /* + * Duration of sample This should match the Sound node constant of same name + */ + public static final int DURATION_UNKNOWN = -1; + + protected long duration = DURATION_UNKNOWN; + + protected int numberOfChannels = 0; + + protected boolean mute = false; // denotes if sample is muted + + // (playing with zero gain) + + /* + * + * Fields associated with positional sound samples + * + */ + /* + * Local to Vworld transform + */ + protected Transform3D vworldXfrm = new Transform3D(); + + protected boolean vwXfrmFlag = false; + + /* + * Origin of Sound source in Listener's space. + */ + protected Point3f position = new Point3f(0.0f, 0.0f, 0.0f); + + protected float[] positionArray = new float[] + { 0, 0, 0 }; + + /* + * Pairs of distances and gain scale factors that define piecewise linear gain attenuation between each pair. + */ + protected double[] attenuationDistance = null; + + protected float[] attenuationGain = null;; + + /** + * dirty flags denoting what has changed since last rendering + */ + protected int dirtyFlags = 0xFFFF; + + /* + * + * Direction sample fields + * + */ + /** + * The Cone Sound's direction vector. This is the cone axis. + */ + protected Vector3f direction = new Vector3f(0.0f, 0.0f, 1.0f); + + protected float[] directionArray = new float[] + { 0, 0, 0 }; + + /** + * Pairs of distances and gain scale factors that define piecewise linear gain BACK attenuation between each pair. + * These are used for defining elliptical attenuation regions. + */ + protected double[] backAttenuationDistance = null; + + protected float[] backAttenuationGain = null; + + /** + * Directional Sound's gain can be attenuated based on the listener's location off-angle from the source source + * direction. This can be set by three parameters: angular distance in radians gain scale factor filtering + * (currently the only filtering supported is lowpass) + */ + protected double[] angularDistance = + { 0.0, (Math.PI * 0.5) }; + + protected float[] angularGain = + { 1.0f, 0.0f }; + + /** + * Distance Filter Each sound source is attenuated by a filter based on it's distance from the listener. For now the + * only supported filterType will be LOW_PASS frequency cutoff. At some time full FIR filtering will be supported. + */ + public static final int NO_FILTERING = -1; + + public static final int LOW_PASS = 1; + + protected int angularFilterType = NO_FILTERING; + + protected float[] angularFilterCutoff = + { Sound.NO_FILTER, Sound.NO_FILTER }; + + /* + * Obstruction and Occlusion parameters For now the only type of filtering supported is a low-pass filter defined by + * a frequency cutoff value. + * + * @since Java 3D 1.3 + */ + protected float obstructionGain = 1.0f; // scale factor + + protected int obstructionFilterType = NO_FILTERING; + + protected float obstructionFilterCutoff = Sound.NO_FILTER; + + protected float occlusionGain = 1.0f; // scale factor + + protected int occlusionFilterType = NO_FILTERING; + + protected float occlusionFilterCutoff = Sound.NO_FILTER; + + public long getDuration() + { + long duration = (long) ((double) sampleSize / sampleFrequency * 1000); + if (debug && debugDuration) + System.out.println("JOALSample - getDuration of " + index + " is " + duration); + return duration; + } + + public long getStartTime() + { + return 0; + } + + public int getNumberOfChannelsUsed() + { + int[] i = new int[1]; + al.alGetBufferiv(index, ALConstants.AL_CHANNELS, i, 0); + sampleChannels = i[0]; + // System.out.println("JOALSample - getNumberOfChannelsUsed of " + index + " is " + sampleChannels); + return sampleChannels; + } + + public void setDirtyFlags(int flags) + { + dirtyFlags = flags; + } + + public int getDirtyFlags() + { + return dirtyFlags; + } + + public void setSoundType(int type) + { + soundType = type; + } + + public int getSoundType() + { + return soundType; + } + + public void setSoundData(MediaContainer ref) + { + soundData = ref; + } + + public MediaContainer getSoundData() + { + return soundData; + } + + public void setMuteFlag(boolean flag) + { + if (debug & debugMute) + System.out.println("JOALSample - setMuteFlag " + flag); + mute = flag; + if (mute) + { + al.alSourcef(index, ALConstants.AL_GAIN, 0); + } + else + { + al.alSourcef(index, ALConstants.AL_GAIN, gain); + } + } + + public boolean getMuteFlag() + { + return mute; + } + + public void setVWrldXfrmFlag(boolean flag) + { + // this flag is ONLY true if the VirtualWorld Transform is ever set + vwXfrmFlag = flag; + } + + public boolean getVWrldXfrmFlag() + { + return vwXfrmFlag; + } + + public void setGain(float scaleFactor) + { + if (debug & debugGain) + System.out.println("JOALSample - setGain " + scaleFactor); + gain = scaleFactor; + al.alSourcef(index, ALConstants.AL_GAIN, scaleFactor); + } + + public float getGain() + { + return gain; + } + + public void setLoopCount(int count) + { + if (debug & debugLoopCount) + System.out.println("JOALSample - setLoopCount " + count); + loopCount = count; + if (count == 0) + al.alSourcei(index, ALConstants.AL_LOOPING, ALConstants.AL_FALSE); + else if (count > 0) + al.alSourcei(index, ALConstants.AL_LOOPING, ALConstants.AL_TRUE); + else + al.alSourcei(index, ALConstants.AL_LOOPING, ALConstants.AL_TRUE); + } + + public int getLoopCount() + { + return loopCount; + } + + public void setPosition(Point3d position) + { + if (debug & debugPosition) + System.out.println("JOALSample - setPosition " + position); + this.position.set(position); + // this.position = position; + positionArray[0] = (float) position.x; + positionArray[1] = (float) position.y; + positionArray[2] = (float) position.z; + al.alSourcefv(index, ALConstants.AL_POSITION, positionArray, 0); + return; + } + + // TODO: no get method for Position + + public void setDistanceGain(double[] frontDistance, float[] frontAttenuationScaleFactor, double[] backDistance, float[] backAttenuationScaleFactor) + { + if (debug & debugDistanceGain) + System.out.println("JOALSample - setDistanceGain " + frontDistance + ", " + frontAttenuationScaleFactor); + + if (frontDistance == null) + { + // al.alSourcef(index, AL.AL_ROLLOFF_FACTOR, 0); + } + else if (frontDistance.length == 1) + { + double d = frontDistance[0]; + // float f = frontAttenuationScaleFactor[0]; + al.alSourcefv(index, ALConstants.AL_REFERENCE_DISTANCE, new float[] + { (float) d }, 0); + al.alSourcef(index, ALConstants.AL_ROLLOFF_FACTOR, 1); + } + else if (frontDistance.length > 1) + { + // double d = frontDistance[0]; + double dmax = frontDistance[frontDistance.length - 1]; + // float f = frontAttenuationScaleFactor[0]; + // float fmax = frontAttenuationScaleFactor[frontAttenuationScaleFactor.length - 1]; + // al.alSourcefv(index, AL.AL_REFERENCE_DISTANCE, new float[]{(float) (dmax / 2)}, 0); + al.alSourcefv(index, ALConstants.AL_MAX_DISTANCE, new float[] + { (float) (dmax) }, 0); + al.alSourcef(index, ALConstants.AL_ROLLOFF_FACTOR, 1); + } + + if (frontDistance != null) + { + int size = frontDistance.length; + attenuationDistance = new double[size]; + attenuationGain = new float[size]; + for (int i = 0; i < size; i++) + { + attenuationDistance[i] = frontDistance[i]; + attenuationGain[i] = frontAttenuationScaleFactor[i]; + } + } + else + { + attenuationDistance = null; + attenuationGain = null; + } + if (backDistance != null && frontDistance != null) + { + int size = backDistance.length; + backAttenuationDistance = new double[size]; + backAttenuationGain = new float[size]; + for (int i = 0; i < size; i++) + { + backAttenuationDistance[i] = backDistance[i]; + backAttenuationGain[i] = backAttenuationScaleFactor[i]; + } + } + else + { + backAttenuationDistance = null; + backAttenuationGain = null; + } + return; + } + + // TODO: no get method for Back Attenuation + + public void setDirection(Vector3d direction) + { + if (debug && debugDirection) + System.out.println("JOALSample - setDirection " + direction); + this.direction.set(direction); + directionArray[0] = (float) direction.x; + directionArray[1] = (float) direction.y; + directionArray[2] = (float) direction.z; + al.alSourcefv(index, ALConstants.AL_DIRECTION, directionArray, 0); + return; + } + + // TODO: no get method for Direction + + public void setAngularAttenuation(int filterType, double[] angle, float[] attenuationScaleFactor, float[] filterCutoff) + { + if (angle != null) + { + if (angle.length == 1) + { + float f = radiansToDegrees(angle[0]); + // al.alSourcef(index, AL.AL_CONE_OUTER_ANGLE, (float) angle[0]); + // al.alSourcef(index, AL.AL_CONE_OUTER_ANGLE, 30); + // al.alSourcef(index, AL.AL_CONE_INNER_ANGLE, 45); + al.alSourcef(index, ALConstants.AL_CONE_INNER_ANGLE, f / 2); + al.alSourcef(index, ALConstants.AL_CONE_OUTER_ANGLE, f); + + } + else if (angle.length == 2) + { + float f1 = radiansToDegrees(angle[0]); + float f2 = radiansToDegrees(angle[1]); + al.alSourcef(index, ALConstants.AL_CONE_INNER_ANGLE, f1); + al.alSourcef(index, ALConstants.AL_CONE_OUTER_ANGLE, f2); + } + else + { + float f1 = radiansToDegrees(angle[0]); + float f2 = radiansToDegrees(angle[angle.length - 1]); + al.alSourcef(index, ALConstants.AL_CONE_INNER_ANGLE, f1); + al.alSourcef(index, ALConstants.AL_CONE_OUTER_ANGLE, f2); + } + } + + if (angle != null) + { + int size = angle.length; + angularDistance = new double[size]; + angularGain = new float[size]; + if (filterType != NO_FILTERING && filterCutoff != null) + angularFilterCutoff = new float[size]; + else + angularFilterCutoff = null; + for (int i = 0; i < size; i++) + { + angularDistance[i] = angle[i]; + angularGain[i] = attenuationScaleFactor[i]; + if (filterType != NO_FILTERING) + angularFilterCutoff[i] = filterCutoff[i]; + } + angularFilterType = filterType; + } + else + { + angularDistance = null; + angularGain = null; + angularFilterCutoff = null; + angularFilterType = NO_FILTERING; + } + } + + // TODO: no get method for Angular Attenuation + + /* + * Set Rate ScaleFactor + * + * @since Java 3D 1.3 + */ + public void setRateScaleFactor(float scaleFactor) + { + rateScaleFactor = scaleFactor; + al.alSourcef(index, ALConstants.AL_PITCH, scaleFactor); + } + + /* + * Get Rate ScaleFactor + * + * @since Java 3D 1.3 + */ + public float getRateScaleFactor() + { + return rateScaleFactor; + } + + /* + * Set Obstruction Gain + * + * @since Java 3D 1.3 + */ + public void setObstructionGain(float scaleFactor) + { + obstructionGain = scaleFactor; + } + + /* + * Get Obstruction Gain + * + * @since Java 3D 1.3 + */ + public float getObstructionGain() + { + return obstructionGain; + } + + /* + * Set Obstruction Filter Cutoff Frequency + * + * @since Java 3D 1.3 + */ + public void setObstructionFilter(float cutoffFrequency) + { + obstructionFilterType = LOW_PASS; + obstructionFilterCutoff = cutoffFrequency; + } + + // TODO: no get method for Obstruction Filtering + + /* + * Set Occlusion Gain + * + * @since Java 3D 1.3 + */ + public void setOcclusionGain(float scaleFactor) + { + occlusionGain = scaleFactor; + } + + /* + * Get Occlusion Gain + * + * @since Java 3D 1.3 + */ + public float getOcclusionGain() + { + return occlusionGain; + } + + /* + * Set Occlusion Filter Cutoff Frequency + * + * @since Java 3D 1.3 + */ + public void setOcclusionFilter(float cutoffFrequency) + { + occlusionFilterType = LOW_PASS; + occlusionFilterCutoff = cutoffFrequency; + } + + // TODO: no get method for Occlusion Filtering + + /** + * Clears/re-initialize fields associated with sample data for this sound, and frees any device specific data + * associated with this sample. + */ + public void clear() + { + if (debug && debugClear) + System.out.println("JOALSample - clear"); + // Added to clear function to clear resources held by JOAL/OpenAL + // clearOpenALBuffer(); + soundData = null; + soundType = NULL_SAMPLE; + gain = 1.0f; + loopCount = 0; + duration = DURATION_UNKNOWN; + numberOfChannels = 0; + vworldXfrm.setIdentity(); + vwXfrmFlag = false; + position.set(0, 0, 0); + positionArray[0] = 0; + positionArray[1] = 0; + positionArray[2] = 0; + attenuationDistance = null; + attenuationGain = null; + direction.set(0.0f, 0.0f, 1.0f); + directionArray[0] = 0; + directionArray[1] = 0; + directionArray[2] = 0; + backAttenuationDistance = null; + backAttenuationGain = null; + if (angularDistance != null) + { + angularDistance[0] = 0.0f; + angularDistance[1] = (float) (Math.PI) * 0.5f; + } + if (angularGain != null) + { + angularGain[0] = 1.0f; + angularGain[1] = 0.0f; + } + angularFilterType = NO_FILTERING; + if (angularFilterCutoff != null) + { + angularFilterCutoff[0] = Sound.NO_FILTER; + angularFilterCutoff[1] = Sound.NO_FILTER; + } + obstructionGain = 1.0f; + obstructionFilterType = NO_FILTERING; + obstructionFilterCutoff = Sound.NO_FILTER; + occlusionGain = 1.0f; + occlusionFilterType = NO_FILTERING; + occlusionFilterCutoff = Sound.NO_FILTER; + } + + /* + * Render + */ + public void render(int dirtyFlags, View view, AuralParameters attribs) + { + // meant to be overridden + } + + /** + * Load the sound ready to by played. + * + * @return error true if error occurred + */ + public boolean load(AL al, MediaContainer soundData, int soundType) + { + if (debug && debugLoad) + { + if (soundData.getURLObject() != null) + System.out.print("JOALSample - load " + soundData.getURLObject() + "..."); + else + System.out.print("JOALSample - load " + soundData + "..."); + } + this.al = al; + this.soundType = soundType; + + InputStream is = soundData.getInputStream(); + if (is == null) + { + URL url = soundData.getURLObject(); + // Issue 481: JOALSample: cannot load if only URLString is given in MediaContainer + if (null == url) + { + try + { + url = new URL(soundData.getURLString()); + } + catch (MalformedURLException ex) + { + ex.printStackTrace(); + return true; + } + } + try + { + is = url.openStream(); + } + catch (IOException ex) + { + ex.printStackTrace(); + return true; + } + } + // if (debug && debugLoad) System.out.println("JOALSample - load - is: " + is); + buffer = new int[1]; + + // Sources are points emitting sound. + source = new int[1]; + + int[] format = new int[1]; + int[] size = new int[1]; + ByteBuffer[] data = new ByteBuffer[1]; + int[] freq = new int[1]; + int[] loop = new int[1]; + + // load wav data into buffers + al.alGenBuffers(1, buffer, 0); + int errorCode = al.alGetError(); + + // Note: This function should really return true when an error is generated by JOAL + // but an error code of 40961 is given with some samples that are still played + // by JOAL (bug 490). Thus the checking of this error is disabled as a fix to + // this bug. + + if (errorCode != ALConstants.AL_NO_ERROR) + { + System.out.print(" error generating buffer - JOAL error code: " + errorCode + " - "); + // return true; + } + + ALut.alutLoadWAVFile(is, format, data, size, freq, loop); + al.alBufferData(buffer[0], format[0], data[0], size[0], freq[0]); + + // bind buffers into audio sources + al.alGenSources(1, source, 0); + sampleSize = size[0]; + sampleFrequency = freq[0]; + sampleBits = format[0]; + + if (soundType == AudioDevice3D.BACKGROUND_SOUND) + { + if (debug && debugLoad) + System.out.print(" BackgroundSound..."); + al.alSourcei(source[0], ALConstants.AL_BUFFER, buffer[0]); + al.alSourcef(source[0], ALConstants.AL_PITCH, 1.0f); + al.alSourcef(source[0], ALConstants.AL_GAIN, 1.0f); + // al.alSourcefv(source[0], AL.AL_POSITION, position, 0); + // al.alSourcefv(source[0], AL.AL_POSITION, sourceVel, 0); + al.alSourcei(source[0], ALConstants.AL_LOOPING, ALConstants.AL_TRUE); + al.alSourcei(source[0], ALConstants.AL_ROLLOFF_FACTOR, 0); + al.alSourcei(source[0], ALConstants.AL_SOURCE_RELATIVE, ALConstants.AL_TRUE); + if (debug && debugLoad) + System.out.println(" success, sourceID: " + source[0]); + } + else if (soundType == AudioDevice3D.POINT_SOUND) + { + if (debug && debugLoad) + System.out.print(" PointSound..."); + al.alGenSources(1, source, 0); + + al.alSourcei(source[0], ALConstants.AL_BUFFER, buffer[0]); + al.alSourcef(source[0], ALConstants.AL_PITCH, 1.0f); + al.alSourcef(source[0], ALConstants.AL_GAIN, 1.0f); + // al.alSourcefv(source[0], AL.AL_POSITION, position, 0); + // al.alSourcefv(source[0], AL.AL_POSITION, sourceVel, 0); + al.alSourcei(source[0], ALConstants.AL_LOOPING, ALConstants.AL_TRUE); + if (debug && debugLoad) + System.out.println(" success, sourceID: " + source[0]); + } + else if (soundType == AudioDevice3D.CONE_SOUND) + { + if (debug && debugLoad) + System.out.print(" ConeSound..."); + al.alGenSources(1, source, 0); + + al.alSourcei(source[0], ALConstants.AL_BUFFER, buffer[0]); + al.alSourcef(source[0], ALConstants.AL_PITCH, 1.0f); + al.alSourcef(source[0], ALConstants.AL_GAIN, 1.0f); + // al.alSourcefv(source[0], AL.AL_POSITION, position, 0); + // al.alSourcefv(source[0], AL.AL_POSITION, sourceVel, 0); + al.alSourcei(source[0], ALConstants.AL_LOOPING, ALConstants.AL_TRUE); + if (debug && debugLoad) + System.out.println(" success, sourceID: " + source[0]); + } + + index = source[0]; + return false; + } + + /** + * Load the sound ready to by played reusing the shared buffer. + * + * @return error true if error occurred + */ + public boolean load(AL al, int[] buffer, int soundType) + { + if (debug && debugLoad) + { + System.out.print("JOALSample - load using shared buffer" + "..."); + } + this.al = al; + this.soundType = soundType; + + this.buffer = buffer; + + // Sources are points emitting sound. + source = new int[1]; + + int[] format = new int[1]; + int[] size = new int[1]; + ByteBuffer[] data = new ByteBuffer[1]; + int[] freq = new int[1]; + // int[] loop = new int[1]; + + al.alBufferData(buffer[0], format[0], data[0], size[0], freq[0]); + + // bind buffers into audio sources + al.alGenSources(1, source, 0); + sampleSize = size[0]; + sampleFrequency = freq[0]; + sampleBits = format[0]; + + if (soundType == AudioDevice3D.BACKGROUND_SOUND) + { + if (debug && debugLoad) + System.out.print(" BackgroundSound..."); + al.alSourcei(source[0], ALConstants.AL_BUFFER, buffer[0]); + al.alSourcef(source[0], ALConstants.AL_PITCH, 1.0f); + al.alSourcef(source[0], ALConstants.AL_GAIN, 1.0f); + // al.alSourcefv(source[0], AL.AL_POSITION, position, 0); + // al.alSourcefv(source[0], AL.AL_POSITION, sourceVel, 0); + al.alSourcei(source[0], ALConstants.AL_LOOPING, ALConstants.AL_TRUE); + al.alSourcei(source[0], ALConstants.AL_ROLLOFF_FACTOR, 0); + al.alSourcei(source[0], ALConstants.AL_SOURCE_RELATIVE, ALConstants.AL_TRUE); + if (debug && debugLoad) + System.out.println(" success, sourceID: " + source[0]); + } + else if (soundType == AudioDevice3D.POINT_SOUND) + { + if (debug && debugLoad) + System.out.print(" PointSound..."); + al.alGenSources(1, source, 0); + + al.alSourcei(source[0], ALConstants.AL_BUFFER, buffer[0]); + al.alSourcef(source[0], ALConstants.AL_PITCH, 1.0f); + al.alSourcef(source[0], ALConstants.AL_GAIN, 1.0f); + // al.alSourcefv(source[0], AL.AL_POSITION, position, 0); + // al.alSourcefv(source[0], AL.AL_POSITION, sourceVel, 0); + al.alSourcei(source[0], ALConstants.AL_LOOPING, ALConstants.AL_TRUE); + if (debug && debugLoad) + System.out.println(" success, sourceID: " + source[0]); + } + else if (soundType == AudioDevice3D.CONE_SOUND) + { + if (debug && debugLoad) + System.out.print(" ConeSound..."); + al.alGenSources(1, source, 0); + + al.alSourcei(source[0], ALConstants.AL_BUFFER, buffer[0]); + al.alSourcef(source[0], ALConstants.AL_PITCH, 1.0f); + al.alSourcef(source[0], ALConstants.AL_GAIN, 1.0f); + // al.alSourcefv(source[0], AL.AL_POSITION, position, 0); + // al.alSourcefv(source[0], AL.AL_POSITION, sourceVel, 0); + al.alSourcei(source[0], ALConstants.AL_LOOPING, ALConstants.AL_TRUE); + if (debug && debugLoad) + System.out.println(" success, sourceID: " + source[0]); + } + + index = source[0]; + return false; + } + + public int startSample() + { + al.alGetError(); + al.alSourcePlay(index); + int errorCode = al.alGetError(); + if (errorCode == ALConstants.AL_NO_ERROR) + return 1; + else + return -1; + } + + public int stopSample() + { + al.alGetError(); + al.alSourceStop(index); + int errorCode = al.alGetError(); + if (errorCode == ALConstants.AL_NO_ERROR) + return 1; + else + { + System.out.println("JOALAudioDevice3D - stopSample...error stopping sample " + index); + return -1; + } + } + + // Debug print flags and methods + + static final protected boolean debugFlag = false; + + static final protected boolean internalErrors = false; + + protected void debugPrint(String message) + { + if (debugFlag) + System.out.println(message); + } + + protected void debugPrintln(String message) + { + if (debugFlag) + System.out.println(message); + } + + private float radiansToDegrees(double radians) + { + return (float) (radians * 180 / pi); + } + + public AL getAl() + { + return al; + } + + public int[] getBuffer() + { + return buffer; + } + + /** + * Clears all resources held by JOAL/OpenAL + */ + private void clearOpenALBuffer() + { + al.alDeleteBuffers(1, buffer, 0); + al.alDeleteSources(1, source, 0); + } + + /** + * Check for OpenAL errors and report error code + */ + private boolean checkForErrors() + { + int i = al.alGetError(); + if (i != ALConstants.AL_NO_ERROR) + { + if (debug) + System.out.println("JOALSample - openAL error - " + i); + return true; + } + else + return false; + } +} |