From 2d57c25287542dcbad59c6b4782e87f86bd0fbc6 Mon Sep 17 00:00:00 2001
From: Michael Bien <mbien@fh-landshut.de>
Date: Mon, 29 Mar 2010 04:40:53 +0200
Subject: moved com.jogamp.javafx.* to com.jogamp.*.

---
 .../com/jogamp/audio/windows/waveout/Audio.java    |  66 ++++
 .../com/jogamp/audio/windows/waveout/Mixer.java    | 362 +++++++++++++++++++++
 .../jogamp/audio/windows/waveout/SoundBuffer.java  | 124 +++++++
 .../audio/windows/waveout/TestSpatialization.java  |  89 +++++
 .../com/jogamp/audio/windows/waveout/Track.java    | 206 ++++++++++++
 .../com/jogamp/audio/windows/waveout/Vec3f.java    | 212 ++++++++++++
 6 files changed, 1059 insertions(+)
 create mode 100755 src/jogl/classes/com/jogamp/audio/windows/waveout/Audio.java
 create mode 100755 src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java
 create mode 100755 src/jogl/classes/com/jogamp/audio/windows/waveout/SoundBuffer.java
 create mode 100755 src/jogl/classes/com/jogamp/audio/windows/waveout/TestSpatialization.java
 create mode 100755 src/jogl/classes/com/jogamp/audio/windows/waveout/Track.java
 create mode 100755 src/jogl/classes/com/jogamp/audio/windows/waveout/Vec3f.java

(limited to 'src/jogl/classes/com/jogamp/audio/windows')

diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Audio.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Audio.java
new file mode 100755
index 000000000..2b51be164
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Audio.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+package com.jogamp.audio.windows.waveout;
+
+import java.io.*;
+
+public class Audio {
+    private static Audio instance = null;
+    private Mixer mixer;
+
+    public synchronized static Audio getInstance() {
+        if (instance == null) {
+            instance = new Audio();
+        }
+        return instance;
+    }
+
+    private Audio() {
+        mixer = Mixer.getMixer();
+    }
+
+    public Mixer getMixer() {
+        return mixer;
+    }
+
+    public Track newTrack(File file) throws IOException
+    {
+        Track res = new Track(file);
+        mixer.add(res);
+        return res;
+    }
+
+    public void shutdown() {
+        mixer.shutdown();
+    }
+}
diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java
new file mode 100755
index 000000000..60972873e
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+package com.jogamp.audio.windows.waveout;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+
+// Needed only for NIO workarounds on CVM
+import java.lang.reflect.*;
+
+public class Mixer {
+    // This class is a singleton
+    private static Mixer mixer;
+
+    private volatile boolean shutdown;
+    private volatile Object shutdownLock = new Object();
+    private volatile boolean shutdownDone;
+
+    // Windows Event object
+    private long event;
+
+    private volatile ArrayList/*<Track>*/ tracks = new ArrayList();
+
+    private Vec3f leftSpeakerPosition  = new Vec3f(-1, 0, 0);
+    private Vec3f rightSpeakerPosition = new Vec3f( 1, 0, 0);
+
+    private float falloffFactor = 1.0f;
+
+    static {
+        mixer = new Mixer();
+    }
+
+    private Mixer() {
+        event = CreateEvent();
+        new FillerThread().start();
+        MixerThread m = new MixerThread();
+        m.setPriority(Thread.MAX_PRIORITY - 1);
+        m.start();
+    }
+
+    public static Mixer getMixer() {
+        return mixer;
+    }
+
+    synchronized void add(Track track) {
+        ArrayList/*<Track>*/ newTracks = (ArrayList) tracks.clone();
+        newTracks.add(track);
+        tracks = newTracks;
+    }
+
+    synchronized void remove(Track track) {
+        ArrayList/*<Track>*/ newTracks = (ArrayList) tracks.clone();
+        newTracks.remove(track);
+        tracks = newTracks;
+    }
+
+    // NOTE: due to a bug on the APX device, we only have mono sounds,
+    // so we currently only pay attention to the position of the left
+    // speaker
+    public void setLeftSpeakerPosition(float x, float y, float z) {
+        leftSpeakerPosition.set(x, y, z);
+    }
+
+    // NOTE: due to a bug on the APX device, we only have mono sounds,
+    // so we currently only pay attention to the position of the left
+    // speaker
+    public void setRightSpeakerPosition(float x, float y, float z) {
+        rightSpeakerPosition.set(x, y, z);
+    }
+
+    /** This defines a scale factor of sorts -- the higher the number,
+        the larger an area the sound will affect. Default value is
+        1.0f. Valid values are [1.0f, ...]. The formula for the gain
+        for each channel is
+<PRE>
+     falloffFactor
+  -------------------
+  falloffFactor + r^2
+</PRE>
+*/
+    public void setFalloffFactor(float factor) {
+        falloffFactor = factor;
+    }
+
+    public void shutdown() {
+        synchronized(shutdownLock) {
+            shutdown = true;
+            SetEvent(event);
+            try {
+                shutdownLock.wait();
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    class FillerThread extends Thread {
+        FillerThread() {
+            super("Mixer Thread");
+        }
+
+        public void run() {
+            while (!shutdown) {
+                List/*<Track>*/ curTracks = tracks;
+
+                for (Iterator iter = curTracks.iterator(); iter.hasNext(); ) {
+                    Track track = (Track) iter.next();
+                    try {
+                        track.fill();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                        remove(track);
+                    }
+                }
+
+                try {
+                    // Run ten times per second
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    class MixerThread extends Thread {
+        // Temporary mixing buffer
+        // Interleaved left and right channels
+        float[] mixingBuffer;
+        private Vec3f temp = new Vec3f();
+
+        MixerThread() {
+            super("Mixer Thread");
+            if (!initializeWaveOut(event)) {
+                throw new InternalError("Error initializing waveout device");
+            }
+        }
+
+        public void run() {
+            while (!shutdown) {
+                // Get the next buffer
+                long mixerBuffer = getNextMixerBuffer();
+                if (mixerBuffer != 0) {
+                    ByteBuffer buf = getMixerBufferData(mixerBuffer);
+
+                    if (buf == null) {
+                        // This is happening on CVM because
+                        // JNI_NewDirectByteBuffer isn't implemented
+                        // by default and isn't compatible with the
+                        // JSR-239 NIO implementation (apparently)
+                        buf = newDirectByteBuffer(getMixerBufferDataAddress(mixerBuffer),
+                                                  getMixerBufferDataCapacity(mixerBuffer));
+                    }
+
+                    if (buf == null) {
+                        throw new InternalError("Couldn't wrap the native address with a direct byte buffer");
+                    }
+
+                    // System.out.println("Mixing buffer");
+
+                    // If we don't have enough samples in our mixing buffer, expand it
+                    // FIXME: knowledge of native output rendering format
+                    if ((mixingBuffer == null) || (mixingBuffer.length < (buf.capacity() / 2 /* bytes / sample */))) {
+                        mixingBuffer = new float[buf.capacity() / 2];
+                    } else {
+                        // Zap it
+                        for (int i = 0; i < mixingBuffer.length; i++) {
+                            mixingBuffer[i] = 0.0f;
+                        }
+                    }
+
+                    // This assertion should be in place if we have stereo
+                    if ((mixingBuffer.length % 2) != 0) {
+                        String msg = "FATAL ERROR: odd number of samples in the mixing buffer";
+                        System.out.println(msg);
+                        throw new InternalError(msg);
+                    }
+
+                    // Run down all of the registered tracks mixing them in
+                    List/*<Track>*/ curTracks = tracks;
+
+                    for (Iterator iter = curTracks.iterator(); iter.hasNext(); ) {
+                        Track track = (Track) iter.next();
+                        // Consider only playing tracks
+                        if (track.isPlaying()) {
+                            // First recompute its gain
+                            Vec3f pos = track.getPosition();
+                            float leftGain  = gain(pos, leftSpeakerPosition);
+                            float rightGain = gain(pos, rightSpeakerPosition);
+                            // Now mix it in
+                            int i = 0;
+                            while (i < mixingBuffer.length) {
+                                if (track.hasNextSample()) {
+                                    float sample = track.nextSample();
+                                    mixingBuffer[i++] = sample * leftGain;
+                                    mixingBuffer[i++] = sample * rightGain;
+                                } else {
+                                    // This allows tracks to stall without being abruptly cancelled
+                                    if (track.done()) {
+                                        remove(track);
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                    }
+
+                    // Now that we have our data, send it down to the card
+                    int outPos = 0;
+                    for (int i = 0; i < mixingBuffer.length; i++) {
+                        short val = (short) mixingBuffer[i];
+                        buf.put(outPos++, (byte)  val);
+                        buf.put(outPos++, (byte) (val >> 8));
+                    }
+                    if (!prepareMixerBuffer(mixerBuffer)) {
+                        throw new RuntimeException("Error preparing mixer buffer");
+                    }
+                    if (!writeMixerBuffer(mixerBuffer)) {
+                        throw new RuntimeException("Error writing mixer buffer to device");
+                    }
+                } else {
+                    // System.out.println("No mixer buffer available");
+
+                    // Wait for a buffer to become available
+                    if (!WaitForSingleObject(event)) {
+                        throw new RuntimeException("Error while waiting for event object");
+                    }
+
+                    /*
+                    try {
+                        Thread.sleep(10);
+                    } catch (InterruptedException e) {
+                    }
+                    */
+                }
+            }
+
+            // Need to shut down
+            shutdownWaveOut();
+            synchronized(shutdownLock) {
+                shutdownLock.notifyAll();
+            }
+        }
+
+        // This defines the 3D spatialization gain function.
+        // The function is defined as:
+        //    falloffFactor
+        // -------------------
+        // falloffFactor + r^2
+        private float gain(Vec3f pos, Vec3f speakerPos) {
+            temp.sub(pos, speakerPos);
+            float dotp = temp.dot(temp);
+            return (falloffFactor / (falloffFactor + dotp));
+        }
+    }
+
+    // Initializes waveout device
+    private static native boolean initializeWaveOut(long eventObject);
+    // Shuts down waveout device
+    private static native void shutdownWaveOut();
+
+    // Gets the next (opaque) buffer of data to fill from the native
+    // code, or 0 if none was available yet (it should not happen that
+    // none is available the way the code is written).
+    private static native long getNextMixerBuffer();
+    // Gets the next ByteBuffer to fill out of the mixer buffer. It
+    // requires interleaved left and right channel samples, 16 signed
+    // bits per sample, little endian. Implicit 44.1 kHz sample rate.
+    private static native ByteBuffer getMixerBufferData(long mixerBuffer);
+    // We need these to work around the lack of
+    // JNI_NewDirectByteBuffer in CVM + the JSR 239 NIO classes
+    private static native long getMixerBufferDataAddress(long mixerBuffer);
+    private static native int  getMixerBufferDataCapacity(long mixerBuffer);
+    // Prepares this mixer buffer for writing to the device.
+    private static native boolean prepareMixerBuffer(long mixerBuffer);
+    // Writes this mixer buffer to the device.
+    private static native boolean writeMixerBuffer(long mixerBuffer);
+
+    // Helpers to prevent mixer thread from busy waiting
+    private static native long CreateEvent();
+    private static native boolean WaitForSingleObject(long event);
+    private static native void SetEvent(long event);
+    private static native void CloseHandle(long handle);
+
+    // We need a reflective hack to wrap a direct ByteBuffer around
+    // the native memory because JNI_NewDirectByteBuffer doesn't work
+    // in CVM + JSR-239 NIO
+    private static Class directByteBufferClass;
+    private static Constructor directByteBufferConstructor;
+    private static Map createdBuffers = new HashMap(); // Map Long, ByteBuffer
+
+    private static ByteBuffer newDirectByteBuffer(long address, long capacity) {
+        Long key = new Long(address);
+        ByteBuffer buf = (ByteBuffer) createdBuffers.get(key);
+        if (buf == null) {
+            buf = newDirectByteBufferImpl(address, capacity);
+            if (buf != null) {
+                createdBuffers.put(key, buf);
+            }
+        }
+        return buf;
+    }
+    private static ByteBuffer newDirectByteBufferImpl(long address, long capacity) {
+        if (directByteBufferClass == null) {
+            try {
+                directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
+                byte[] tmp = new byte[0];
+                directByteBufferConstructor =
+                    directByteBufferClass.getDeclaredConstructor(new Class[] { Integer.TYPE,
+                                                                               tmp.getClass(),
+                                                                               Integer.TYPE });
+                directByteBufferConstructor.setAccessible(true);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        
+        if (directByteBufferConstructor != null) {
+            try {
+                return (ByteBuffer)
+                    directByteBufferConstructor.newInstance(new Object[] {
+                            new Integer((int) capacity),
+                            null,
+                            new Integer((int) address)
+                        });
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/SoundBuffer.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/SoundBuffer.java
new file mode 100755
index 000000000..c45430d23
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/SoundBuffer.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+package com.jogamp.audio.windows.waveout;
+
+import java.io.*;
+
+class SoundBuffer {
+    private byte[] data;
+    private boolean needsByteSwap;
+    private int numBytes;
+    private int bytesPerSample;
+    private int numSamples;
+    private boolean playing;
+    private boolean empty;
+
+    // Note: needsByteSwap argument makes assumptions about the format
+    SoundBuffer(int size, int bytesPerSample, boolean needsByteSwap) {
+        this.bytesPerSample = bytesPerSample;
+        this.needsByteSwap = needsByteSwap;
+        data = new byte[size * bytesPerSample];
+        empty = true;
+    }
+
+    boolean playing() {
+        return playing;
+    }
+
+    void playing(boolean playing) {
+        this.playing = playing;
+    }
+
+    boolean empty() {
+        return empty;
+    }
+
+    void empty(boolean empty) {
+        this.empty = empty;
+    }
+
+    void fill(InputStream input) throws IOException {
+        synchronized(this) {
+            if (playing) {
+                throw new IllegalStateException("Can not fill a buffer that is playing");
+            }
+        }
+
+        empty(true);
+        int num = input.read(data);
+        if (num > 0) {
+            numBytes = num;
+            numSamples = numBytes / bytesPerSample;
+            empty(false);
+            if ((numBytes % bytesPerSample) != 0) {
+                System.out.println("WARNING: needed integral multiple of " + bytesPerSample +
+                                   " bytes, but read " + numBytes + " bytes");
+            }
+        } else {
+            numBytes = 0;
+        }
+    }
+
+    int numSamples() {
+        return numSamples;
+    }
+
+    // This is called by the mixer and must be extremely fast
+    // FIXME: may want to reconsider use of floating point at this point
+    // FIXME: assumes all sounds are of the same format to avoid normalization
+    float getSample(int sample) {
+        int startByte = sample * bytesPerSample;
+        // FIXME: assumes no more than 4 bytes per sample
+        int res = 0;
+        if (needsByteSwap) {
+            for (int i = startByte + bytesPerSample - 1; i >= startByte; i--) {
+                res <<= 8;
+                res |= (data[i] & 0xff);
+            }
+        } else {
+            int endByte = startByte + bytesPerSample - 1;
+            for (int i = startByte; i <= endByte; i++) {
+                res <<= 8;
+                res |= (data[i] & 0xff);
+            }
+        }
+        // Sign extend
+        if (bytesPerSample == 2) {
+            res = (short) res;
+        } else if (bytesPerSample == 1) {
+            res = (byte) res;
+        }
+
+        return (float) res;
+    }
+}
diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/TestSpatialization.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/TestSpatialization.java
new file mode 100755
index 000000000..78fb3493f
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/TestSpatialization.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+package com.jogamp.audio.windows.waveout;
+
+import java.io.*;
+import javax.media.nativewindow.NativeWindow;
+import javax.media.opengl.GLDrawableFactory;
+
+public class TestSpatialization {
+    public static void main(String[] args) {
+        if (args.length != 1) {
+            System.out.println("Usage: TestSpatialization [file name]");
+            System.exit(1);
+        }
+
+        try {
+            // FIXME: this is a hack to get the native library loaded
+            try {
+                GLDrawableFactory.getFactory(NativeWindow.class);
+            } catch (Exception e) {}
+            // Initialize the audio subsystem
+            Audio audio = Audio.getInstance();
+            // Create a track
+            Track track = audio.newTrack(new File(args[0]));
+            track.setPosition(1, 0, 0);
+            // Run for ten seconds
+            long startTime = System.currentTimeMillis();
+            long duration = 10000;
+            long curTime = 0;
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+            }
+            System.out.println("Playing...");
+            track.setLooping(true);
+            track.play();
+            while ((curTime = System.currentTimeMillis()) < startTime + duration) {
+                // Make one revolution every two seconds
+                float rads = (float) (((2 * Math.PI) * (((float) (curTime - startTime)) / 1000.0f)) / 2);
+                track.setPosition((float) Math.cos(rads), 0, (float) Math.sin(rads));
+                // Would like to make it go in a circle, but since
+                // stereo doesn't work now, make it move along a line
+                // track.setPosition(-1.0f, 0, 2.0f * (float) Math.sin(rads));
+                // Sleep a little between updates
+                try {
+                    Thread.sleep(10);
+                } catch (InterruptedException e) {
+                }
+            }
+            System.out.println("Shutting down audio subsystem");
+            audio.shutdown();
+            System.out.println("Exiting.");
+            System.exit(0);
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+}
diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Track.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Track.java
new file mode 100755
index 000000000..b57bf1dc6
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Track.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+package com.jogamp.audio.windows.waveout;
+
+import java.io.*;
+import java.nio.*;
+
+public class Track {
+    // Default number of samples per buffer
+    private static final int BUFFER_SIZE = 32768;
+    // Number of bytes per sample (FIXME: dependence on audio format)
+    static final int BYTES_PER_SAMPLE = 2;
+    // Whether we need byte swapping (FIXME: dependence on audio format)
+    static final boolean NEEDS_BYTE_SWAP = true;
+
+    // This is the buffer this track is currently playing from
+    private SoundBuffer activeBuffer;
+    // This is the sample position in the active buffer
+    private int samplePosition;
+    // This is the total number of samples in the file
+    private int totalSamples;
+    // This is the total number of samples we have read
+    private int samplesRead;
+    // This is the buffer that the background filler thread may be filling
+    private SoundBuffer fillingBuffer;
+    // If we're playing the file, this is its input stream
+    private InputStream input;
+    // Keep around the file name
+    private File file;
+    // Whether we're playing this sound
+    private boolean playing;
+    // Whether we're looping this sound
+    private boolean looping;
+    // The position of this sound; defaults to being at the origin
+    private volatile Vec3f position = new Vec3f();
+
+    Track(File file) throws IOException {
+        if (!file.getName().endsWith(".rawsound")) {
+            throw new IOException("Unsupported file format (currently supports only raw sounds)");
+        }
+
+        this.file = file;
+        openInput();
+
+        // Allocate the buffers
+        activeBuffer  = new SoundBuffer(BUFFER_SIZE, BYTES_PER_SAMPLE, NEEDS_BYTE_SWAP);
+        fillingBuffer = new SoundBuffer(BUFFER_SIZE, BYTES_PER_SAMPLE, NEEDS_BYTE_SWAP);
+
+        // Fill the first buffer immediately
+        fill();
+        swapBuffers();
+    }
+
+    private void openInput() throws IOException {
+        input = new BufferedInputStream(new FileInputStream(file));
+        totalSamples = (int) file.length() / BYTES_PER_SAMPLE;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public synchronized void play() {
+        if (input == null) {
+            try {
+                openInput();
+                // Fill it immediately
+                fill();
+            } catch (IOException e) {
+                e.printStackTrace();
+                return;
+            }
+        }
+
+        playing = true;
+    }
+
+    public synchronized boolean isPlaying() {
+        return playing;
+    }
+
+    public synchronized void setLooping(boolean looping) {
+        this.looping = looping;
+    }
+
+    public synchronized boolean isLooping() {
+        return looping;
+    }
+
+    public void setPosition(float x, float y, float z) {
+        position = new Vec3f(x, y, z);
+    }
+
+    synchronized void fill() throws IOException {
+        if (input == null) {
+            return;
+        }
+        SoundBuffer curBuffer = fillingBuffer;
+        if (!curBuffer.empty()) {
+            return;
+        }
+        curBuffer.fill(input);
+        if (curBuffer.empty()) {
+            // End of file
+            InputStream tmp = null;
+            synchronized(this) {
+                tmp = input;
+                input = null;
+            }
+            tmp.close();
+
+            // If looping, re-open
+            if (isLooping()) {
+                openInput();
+                // and fill
+                fill();
+            }
+        }
+    }
+
+    // These are only for use by the Mixer
+    private float leftGain;
+    private float rightGain;
+    
+    void setLeftGain(float leftGain) {
+        this.leftGain = leftGain;
+    }
+
+    float getLeftGain() {
+        return leftGain;
+    }
+
+    void setRightGain(float rightGain) {
+        this.rightGain = rightGain;
+    }
+
+    float getRightGain() {
+        return rightGain;
+    }
+
+    Vec3f getPosition() {
+        return position;
+    }
+
+    // This is called by the mixer and must be extremely fast
+    // Note this assumes mono sounds (FIXME)
+    boolean hasNextSample() {
+        return (!activeBuffer.empty() && samplePosition < activeBuffer.numSamples());
+    }
+
+    // This is called by the mixer and must be extremely fast
+    float nextSample() {
+        float res = activeBuffer.getSample(samplePosition++);
+        ++samplesRead;
+        if (!hasNextSample()) {
+            swapBuffers();
+            samplePosition = 0;
+            if (done()) {
+                playing = false;
+            }
+        }
+        return res;
+    }
+
+    synchronized void swapBuffers() {
+        SoundBuffer tmp = activeBuffer;
+        activeBuffer = fillingBuffer;
+        fillingBuffer = tmp;
+        fillingBuffer.empty(true);
+    }
+
+    // This provides a more robust termination condition
+    boolean done() {
+        return (samplesRead == totalSamples) && !looping;
+    }
+}
diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Vec3f.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Vec3f.java
new file mode 100755
index 000000000..1afdaf081
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Vec3f.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+package com.jogamp.audio.windows.waveout;
+
+/** 3-element single-precision vector */
+
+class Vec3f {
+  public static final Vec3f X_AXIS     = new Vec3f( 1,  0,  0);
+  public static final Vec3f Y_AXIS     = new Vec3f( 0,  1,  0);
+  public static final Vec3f Z_AXIS     = new Vec3f( 0,  0,  1);
+  public static final Vec3f NEG_X_AXIS = new Vec3f(-1,  0,  0);
+  public static final Vec3f NEG_Y_AXIS = new Vec3f( 0, -1,  0);
+  public static final Vec3f NEG_Z_AXIS = new Vec3f( 0,  0, -1);
+
+  private float x;
+  private float y;
+  private float z;
+
+  public Vec3f() {}
+
+  public Vec3f(Vec3f arg) {
+    set(arg);
+  }
+
+  public Vec3f(float x, float y, float z) {
+    set(x, y, z);
+  }
+
+  public Vec3f copy() {
+    return new Vec3f(this);
+  }
+
+  public void set(Vec3f arg) {
+    set(arg.x, arg.y, arg.z);
+  }
+
+  public void set(float x, float y, float z) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+  }
+
+  /** Sets the ith component, 0 <= i < 3 */
+  public void set(int i, float val) {
+    switch (i) {
+    case 0: x = val; break;
+    case 1: y = val; break;
+    case 2: z = val; break;
+    default: throw new IndexOutOfBoundsException();
+    }
+  }
+
+  /** Gets the ith component, 0 <= i < 3 */
+  public float get(int i) {
+    switch (i) {
+    case 0: return x;
+    case 1: return y;
+    case 2: return z;
+    default: throw new IndexOutOfBoundsException();
+    }
+  }
+
+  public float x() { return x; }
+  public float y() { return y; }
+  public float z() { return z; }
+
+  public void setX(float x) { this.x = x; }
+  public void setY(float y) { this.y = y; }
+  public void setZ(float z) { this.z = z; }
+
+  public float dot(Vec3f arg) {
+    return x * arg.x + y * arg.y + z * arg.z;
+  }
+
+  public float length() {
+    return (float) Math.sqrt(lengthSquared());
+  }
+
+  public float lengthSquared() {
+    return this.dot(this);
+  }
+
+  public void normalize() {
+    float len = length();
+    if (len == 0.0f) return;
+    scale(1.0f / len);
+  }
+
+  /** Returns this * val; creates new vector */
+  public Vec3f times(float val) {
+    Vec3f tmp = new Vec3f(this);
+    tmp.scale(val);
+    return tmp;
+  }
+
+  /** this = this * val */
+  public void scale(float val) {
+    x *= val;
+    y *= val;
+    z *= val;
+  }
+
+  /** Returns this + arg; creates new vector */
+  public Vec3f plus(Vec3f arg) {
+    Vec3f tmp = new Vec3f();
+    tmp.add(this, arg);
+    return tmp;
+  }
+
+  /** this = this + b */
+  public void add(Vec3f b) {
+    add(this, b);
+  }
+
+  /** this = a + b */
+  public void add(Vec3f a, Vec3f b) {
+    x = a.x + b.x;
+    y = a.y + b.y;
+    z = a.z + b.z;
+  }
+
+  /** Returns this + s * arg; creates new vector */
+  public Vec3f addScaled(float s, Vec3f arg) {
+    Vec3f tmp = new Vec3f();
+    tmp.addScaled(this, s, arg);
+    return tmp;
+  }
+
+  /** this = a + s * b */
+  public void addScaled(Vec3f a, float s, Vec3f b) {
+    x = a.x + s * b.x;
+    y = a.y + s * b.y;
+    z = a.z + s * b.z;
+  }
+
+  /** Returns this - arg; creates new vector */
+  public Vec3f minus(Vec3f arg) {
+    Vec3f tmp = new Vec3f();
+    tmp.sub(this, arg);
+    return tmp;
+  }
+
+  /** this = this - b */
+  public void sub(Vec3f b) {
+    sub(this, b);
+  }
+
+  /** this = a - b */
+  public void sub(Vec3f a, Vec3f b) {
+    x = a.x - b.x;
+    y = a.y - b.y;
+    z = a.z - b.z;
+  }
+
+  /** Returns this cross arg; creates new vector */
+  public Vec3f cross(Vec3f arg) {
+    Vec3f tmp = new Vec3f();
+    tmp.cross(this, arg);
+    return tmp;
+  }
+
+  /** this = a cross b. NOTE: "this" must be a different vector than
+      both a and b. */
+  public void cross(Vec3f a, Vec3f b) {
+    x = a.y * b.z - a.z * b.y;
+    y = a.z * b.x - a.x * b.z;
+    z = a.x * b.y - a.y * b.x;
+  }
+
+  /** Sets each component of this vector to the product of the
+      component with the corresponding component of the argument
+      vector. */
+  public void componentMul(Vec3f arg) {
+    x *= arg.x;
+    y *= arg.y;
+    z *= arg.z;
+  }
+
+  public String toString() {
+    return "(" + x + ", " + y + ", " + z + ")";
+  }
+}
-- 
cgit v1.2.3