aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-05-23 02:05:11 +0200
committerSven Gothel <[email protected]>2023-05-23 02:05:11 +0200
commitafb386e13fd00fde1401d4551ee4790b1f6d5e09 (patch)
treec9b389c45fe0711a5a89bd37a0b763e217b46c21
parent66e79a41f38f5694f953816f1a85a02cee71eb16 (diff)
Sound3D: Further OO wrapper to be used in ALAudioSink: Context locking, ALCcontext recreation, ..
Context - Recursive context locking (only 1st shall do native makeCurrent, only last shall do native release) - Access to the current Context instance (thread local storage) - Obey "One context can only be current on one thread, and one thread can only have one context current!" - ALCcontext recreation within lock, allowing to change native OpenAL specifics via attr list - ALCcontext creation (initial) w/ attr list Device - Retrieve name if default name null has been given - Expose device name - Allow to open() again Source - Allow lazy creation w/ invalid ID - Allow create() post instantiation (for a single source) - Throw ALException in all queued buffer methods as they are crucial in multithreading streaming. - Add queue buffer with OpenAL buffer-id int[] arrays variant to be used w/o Buffer Listener - Fix (get|set)Orientation() API doc: It's 'at' vector, then 'up' vector. General: - Have toString() - Added versatile AudioSystem3D.check*Error(..) Earlier Sound3D changes - 7f73d50c90d05cf7388f23977ca956a4933019ad - 64b40bd4359cad46ebf62751ea342d80205bd98b
-rw-r--r--src/java/com/jogamp/openal/sound3d/AudioSystem3D.java137
-rw-r--r--src/java/com/jogamp/openal/sound3d/Buffer.java9
-rw-r--r--src/java/com/jogamp/openal/sound3d/Context.java240
-rw-r--r--src/java/com/jogamp/openal/sound3d/Device.java60
-rw-r--r--src/java/com/jogamp/openal/sound3d/Listener.java17
-rw-r--r--src/java/com/jogamp/openal/sound3d/Source.java78
-rw-r--r--src/test/com/jogamp/openal/test/manual/Sound3DTest.java4
7 files changed, 452 insertions, 93 deletions
diff --git a/src/java/com/jogamp/openal/sound3d/AudioSystem3D.java b/src/java/com/jogamp/openal/sound3d/AudioSystem3D.java
index 72b777c..0d35da6 100644
--- a/src/java/com/jogamp/openal/sound3d/AudioSystem3D.java
+++ b/src/java/com/jogamp/openal/sound3d/AudioSystem3D.java
@@ -40,6 +40,10 @@ import java.io.InputStream;
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.ALExt;
import com.jogamp.openal.ALFactory;
@@ -53,7 +57,7 @@ import jogamp.openal.Debug;
* The AudioSystem3D class provides a set of methods for creating and
* manipulating a 3D audio environment.
*
- * @author Athomas Goldberg
+ * @author Athomas Goldberg, Sven Gothel, et al.
*/
public class AudioSystem3D {
static boolean DEBUG = Debug.debug("AudioSystem3D");
@@ -99,37 +103,144 @@ public class AudioSystem3D {
*/
public static boolean isAvailable() { return staticAvailable; }
- public int getALError() {
+ /** Return OpenAL global {@link AL}. */
+ public static final AL getAL() { return al; }
+ /** Return OpenAL global {@link ALC}. */
+ public static final ALC getALC() { return alc; }
+ /** Return OpenAL global {@link ALExt}. */
+ public static final ALExt getALExt() { return alExt; }
+
+ public static int getALError() {
return al.alGetError();
}
/**
- * Creates a new Sound3D Context for a specified device.
- *
- * @param device The device the Context is being created for.
+ * Returns true if an OpenAL ALC or AL error occurred, otherwise false
+ * @param device referencing an {@link ALCdevice}, may be null
+ * @param prefix prefix to print on error and if `verbose`
+ * @param verbose pass true to show errors
+ * @param throwException true to throw an ALException on error
+ * @return true if an error occurred, otherwise false
+ */
+ public static boolean checkError(final Device device, final String prefix, final boolean verbose, final boolean throwException) {
+ if( !checkALCError(device, prefix, verbose, throwException) ) {
+ return checkALError(prefix, verbose, throwException);
+ }
+ return false; // no error
+ }
+
+ /**
+ * Returns true if an OpenAL AL error occurred, otherwise false
+ * @param prefix prefix to print on error and if `verbose`
+ * @param verbose pass true to show errors
+ * @param throwException true to throw an ALException on error
+ * @return true if an error occurred, otherwise false
+ */
+ public static boolean checkALError(final String prefix, final boolean verbose, final boolean throwException) {
+ final int alErr = al.alGetError();
+ if( ALConstants.AL_NO_ERROR != alErr ) {
+ final String msg = prefix+": AL error 0x"+Integer.toHexString(alErr)+", '"+al.alGetString(alErr);
+ if( verbose ) {
+ System.err.println(msg);
+ }
+ if( throwException ) {
+ throw new ALException(msg);
+ }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Returns true if an OpenAL ALC error occurred, otherwise false
+ * @param device referencing an {@link ALCdevice}, may be null
+ * @param prefix prefix to print on error and if `verbose`
+ * @param verbose pass true to show errors
+ * @param throwException true to throw an ALException on error
+ * @return true if an error occurred, otherwise false
+ */
+ public static boolean checkALCError(final Device device, final String prefix, final boolean verbose, final boolean throwException) {
+ final ALCdevice alcDevice = null != device ? device.getALDevice() : null;
+ final int alcErr = alc.alcGetError( alcDevice );
+ if( ALCConstants.ALC_NO_ERROR != alcErr ) {
+ final String msg = prefix+": ALC error 0x"+Integer.toHexString(alcErr)+", "+alc.alcGetString(alcDevice, alcErr);
+ if( verbose ) {
+ System.err.println(msg);
+ }
+ if( throwException ) {
+ throw new ALException(msg);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Creates a new Sound3D Context for a specified device including native {@link ALCcontext} creation.
*
+ * @param device The device the Context is being created for, must be valid
* @return The new Sound3D context.
*/
public static Context createContext(final Device device) {
- return new Context(device);
+ return new Context(device, null);
}
/**
- * Makes the specified context the current context.
+ * Creates a new Sound3D Context for a specified device including native {@link ALCcontext} creation.
*
- * @param context the context to make current.
+ * @param device The device the Context is being created for, must be valid.
+ * @param attributes list of {@link ALCcontext} attributes for context creation, maybe empty or null
+ * @return The new Sound3D context.
*/
- public static boolean makeContextCurrent(final Context context) {
- return context.makeCurrent();
+ public static Context createContext(final Device device, final int[] attributes) {
+ return new Context(device, attributes);
}
/**
- * Release the specified context.
+ * Returns this thread current context.
+ * If no context is current, returns null.
*
+ * @return the context current on this thread, or null if no context is current.
+ * @see Context#getCurrentContext()
+ * @see #makeContextCurrent(Context)
+ * @see #releaseContext(Context)
+ */
+ public static Context getCurrentContext() {
+ return Context.getCurrentContext();
+ }
+
+ /**
+ * Makes the audio context current on the calling thread.
+ * <p>
+ * Recursive calls are supported.
+ * </p>
+ * <p>
+ * At any point in time one context can only be current by one thread,
+ * and one thread can only have one context current.
+ * </p>
+ * @param context the context to make current.
+ * @param throwException if true, throws ALException if {@link #getALContext()} is null, current thread holds another context or failed to natively make current
+ * @return true if current thread holds no other context and context successfully made current, otherwise false
+ * @see Context#makeCurrent()
+ * @see #releaseContext(Context)
+ */
+ public static boolean makeContextCurrent(final Context context, final boolean throwException) {
+ return context.makeCurrent(throwException);
+ }
+
+ /**
+ * Releases control of this audio context from the current thread, if implementation utilizes context locking.
+ * <p>
+ * Recursive calls are supported.
+ * </p>
* @param context the context to release.
+ * @param throwException if true, throws ALException if context has not been previously made current on current thread
+ * or native release failed.
+ * @return true if context has previously been made current on the current thread and successfully released, otherwise false
+ * @see Context#release()
+ * @see #makeContextCurrent(Context)
*/
- public static boolean releaseContext(final Context context) {
- return context.release();
+ public static boolean releaseContext(final Context context, final boolean throwException) {
+ return context.release(throwException);
}
/**
diff --git a/src/java/com/jogamp/openal/sound3d/Buffer.java b/src/java/com/jogamp/openal/sound3d/Buffer.java
index 52a17bd..0b6db38 100644
--- a/src/java/com/jogamp/openal/sound3d/Buffer.java
+++ b/src/java/com/jogamp/openal/sound3d/Buffer.java
@@ -43,9 +43,9 @@ import java.nio.ByteBuffer;
* The Sound3D Buffer is a container for audio data used in the Sound3D
* environment.
*
- * @author Athomas Goldberg
+ * @author Athomas Goldberg, Sven Gothel, et al.
*/
-public class Buffer {
+public final class Buffer {
public final static int FORMAT_MONO8 = AL.AL_FORMAT_MONO8;
public final static int FORMAT_MONO16 = AL.AL_FORMAT_MONO16;
@@ -149,4 +149,9 @@ public class Buffer {
return i[0];
}
+
+ @Override
+ public String toString() {
+ return "ALBuffer[id "+alBufferID+"]";
+ }
}
diff --git a/src/java/com/jogamp/openal/sound3d/Context.java b/src/java/com/jogamp/openal/sound3d/Context.java
index 28e033e..2884f0c 100644
--- a/src/java/com/jogamp/openal/sound3d/Context.java
+++ b/src/java/com/jogamp/openal/sound3d/Context.java
@@ -34,6 +34,7 @@
package com.jogamp.openal.sound3d;
+import com.jogamp.common.ExceptionUtils;
import com.jogamp.common.util.locks.LockFactory;
import com.jogamp.common.util.locks.RecursiveLock;
import com.jogamp.openal.*;
@@ -43,25 +44,32 @@ import com.jogamp.openal.util.ALHelpers;
/**
* This class provides a Sound3D Context associated with a specified device.
*
- * @author Athomas Goldberg
+ * @author Athomas Goldberg, Sven Gothel, et al.
*/
-public class Context {
+public final class Context {
private final RecursiveLock lock = LockFactory.createRecursiveLock();
- private Device device;
- private ALCcontext alCtx;
+ private final Device device;
+ private volatile ALCcontext alCtx;
private boolean threadContextLocked;
private boolean hasALC_thread_local_context;
+ private static final ThreadLocal<Context> currentContext = new ThreadLocal<Context>();
+ /**
+ * Creates a new Context for a given {@link ALCcontext} for the specified device.
+ *
+ * @param realContext {@link ALCcontext} instance, maybe null
+ * @param device The device the Context belongs to, must be valid
+ */
public Context(final ALCcontext realContext, final Device device) {
this.device = device;
this.alCtx = realContext;
{
hasALC_thread_local_context = false;
final boolean v;
- if( makeCurrent() ) {
+ if( makeCurrent(false) ) {
v = AudioSystem3D.alc.alcIsExtensionPresent(null, ALHelpers.ALC_EXT_thread_local_context) ||
AudioSystem3D.alc.alcIsExtensionPresent(device.getALDevice(), ALHelpers.ALC_EXT_thread_local_context);
- release();
+ release(false);
} else {
v = false;
}
@@ -70,26 +78,84 @@ public class Context {
}
/**
- * Creates a new Context for a specified device.
+ * Creates a new Context for a specified device including native {@link ALCcontext} creation.
+ *
+ * @param device The device the Context is being created for, must be valid.
+ * @param attributes list of {@link ALCcontext} attributes for context creation, maybe empty or null
+ */
+ public Context(final Device device, final int[] attributes) {
+ this( createImpl(device.getALDevice(), attributes), device);
+ }
+
+ /**
+ * Creates a new {@link ALCcontext}.
*
- * @param device The device the Context is being created for.
+ * @param alDevice a valid {@link ALCdevice}
+ * @param attributes lost of {@link ALCcontext} attributes for context creation
*/
- public Context(final Device device) {
- this(AudioSystem3D.alc.alcCreateContext(device.getALDevice(), null), device);
+ private static ALCcontext createImpl(final ALCdevice alDevice, final int[] attributes) {
+ if( null != attributes && attributes.length > 0 ) {
+ return AudioSystem3D.alc.alcCreateContext(alDevice, attributes, 0);
+ } else {
+ return AudioSystem3D.alc.alcCreateContext(alDevice, null);
+ }
+ }
+
+ /**
+ * Creates the internal {@link ALCcontext} instance if {@link #getALContext()} is null
+ * @param attributes lost of {@link ALCcontext} attributes for context creation
+ * @return true if the internal context has been successfully created, otherwise false
+ */
+ public boolean create(final int[] attributes) {
+ lock.lock();
+ try {
+ if( null == alCtx ) {
+ alCtx = createImpl(device.getALDevice(), attributes);
+ return null != alCtx;
+ }
+ return false;
+ } finally {
+ lock.unlock();
+ }
}
/**
- * Returns the OpenAL context.
+ * Recreates the internal {@link ALCcontext} instance, i.e. destroys it first if {@link #getALContext()} not null.
+ * <p>
+ * Context is made current again if it was current before.
+ * </p>
+ * @param attributes lost of {@link ALCcontext} attributes for context creation
+ * @return true if the internal context has been successfully recreated and made current again if was current before, otherwise false
*/
- public ALCcontext getALContext() {
- return alCtx;
+ public boolean recreate(final int[] attributes) {
+ lock.lock();
+ try {
+ final boolean wasCurrent = this == getCurrentContext();
+ destroyImpl();
+ alCtx = createImpl(device.getALDevice(), attributes);
+ if( null != alCtx ) {
+ if( wasCurrent ) {
+ return makeCurrentImpl();
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } finally {
+ lock.unlock();
+ }
}
+ /** Returns the OpenAL {@link ALCcontext}. */
+ public ALCcontext getALContext() { return alCtx; }
+
/** Returns whether {@link #getALContext()} is valid, i.e. not null, e.g. not {@link #destroy()}'ed. */
public boolean isValid() { return null != alCtx; }
+ /** Return {@link ALC#alcGetError(ALCdevice)} using {@link #getDevice()}. */
public int getALCError() {
- return AudioSystem3D.alc.alcGetError(device.getALDevice());
+ return device.getALCError();
}
/**
@@ -98,16 +164,8 @@ public class Context {
public void destroy() {
lock.lock();
try {
- if( null != alCtx ) {
- if( threadContextLocked ) {
- AudioSystem3D.alExt.alcSetThreadContext(null);
- } else {
- AudioSystem3D.alc.alcMakeContextCurrent(null);
- }
- AudioSystem3D.alc.alcDestroyContext(alCtx);
- alCtx = null;
- }
- device = null;
+ destroyImpl();
+ currentContext.set(null);
// unroll lock !
while(lock.getHoldCount() > 1) {
lock.unlock();
@@ -116,38 +174,132 @@ public class Context {
lock.unlock();
}
}
+ private void destroyImpl() {
+ if( null != alCtx ) {
+ if( threadContextLocked ) {
+ AudioSystem3D.alExt.alcSetThreadContext(null);
+ } else {
+ AudioSystem3D.alc.alcMakeContextCurrent(null);
+ }
+ AudioSystem3D.alc.alcDestroyContext(alCtx);
+ alCtx = null;
+ }
+ }
+
+ /**
+ * Returns this thread current context.
+ * If no context is current, returns null.
+ *
+ * @return the context current on this thread, or null if no context is current.
+ * @see #makeCurrent()
+ * @see #release()
+ */
+ public static Context getCurrentContext() {
+ return currentContext.get();
+ }
+
+ /** Return the lock count of this context, i.e. 0 if not locked, 1 if locked once, >1 for recursive locks. */
+ public int getLockCount() {
+ return lock.getHoldCount();
+ }
/**
- * Makes this context current.
+ * Makes the audio context current on the calling thread.
+ * <p>
+ * Recursive call to {@link #makeCurrent()} and hence {@link #release()} are supported.
+ * </p>
+ * <p>
+ * At any point in time one context can only be current on one thread,
+ * and one thread can only have one context current.
+ * </p>
+ * @param throwException if true, throws ALException if {@link #getALContext()} is null, current thread holds another context or failed to natively make current
+ * @return true if {@link #getALContext()} is valid, current thread holds no other context and context successfully made current, otherwise false
+ * @see #release()
*/
- public boolean makeCurrent() {
- final boolean r;
+ public boolean makeCurrent(final boolean throwException) throws ALException {
lock.lock();
+
+ if( null == alCtx ) {
+ lock.unlock();
+ if( throwException ) {
+ throw new ALException("Invalid "+this);
+ }
+ return false;
+ }
+
+ // One context can only be current on one thread,
+ // and one thread can only have one context current!
+ final Context current = getCurrentContext();
+ if (current != null) {
+ if (current == this) { // implicit recursive locking, lock.getHoldCount() > 1
+ return true;
+ } else {
+ lock.unlock();
+ if( throwException ) {
+ throw new ALException("Current thread "+Thread.currentThread()+" holds another "+current+" while claiming this "+this);
+ }
+ return false;
+ }
+ }
+ final boolean r = makeCurrentImpl();
+ if( r ) {
+ currentContext.set(this);
+ } else {
+ lock.unlock();
+ if( throwException ) {
+ throw new ALException("Context make current failed "+this);
+ }
+ }
+ return r;
+ }
+ private boolean makeCurrentImpl() {
if( hasALC_thread_local_context ) {
threadContextLocked = true;
- r = AudioSystem3D.alExt.alcSetThreadContext(alCtx);
+ return AudioSystem3D.alExt.alcSetThreadContext(alCtx);
} else {
threadContextLocked = false;
- r = AudioSystem3D.alc.alcMakeContextCurrent(alCtx);
+ return AudioSystem3D.alc.alcMakeContextCurrent(alCtx);
}
- if( !r ) {
- lock.unlock();
- }
- return r;
}
/**
- * Release this context.
+ * Releases control of this audio context from the current thread, if implementation utilizes context locking.
+ * <p>
+ * Recursive call to {@link #makeCurrent()} and hence {@link #release()} are supported.
+ * </p>
+ * <p>
+ * If native release fails, internal lock is not released.
+ * </p>
+ * @param throwException if true, throws ALException if context has not been previously made current on current thread
+ * or native release failed.
+ * @return true if context has previously been made current on the current thread and successfully released, otherwise false
+ * @see #makeCurrent()
*/
- public boolean release() {
- final boolean r;
- if( threadContextLocked ) {
- r = AudioSystem3D.alExt.alcSetThreadContext(null);
- } else {
- r = AudioSystem3D.alc.alcMakeContextCurrent(null);
+ public boolean release(final boolean throwException) throws ALException {
+ if( !lock.isOwner( Thread.currentThread() ) ) {
+ if( throwException ) {
+ throw new ALException("Context not held on current thread "+Thread.currentThread()+", "+this);
+ }
+ return false;
+ }
+ if( lock.getHoldCount() == 1 ) {
+ final boolean r;
+ if( threadContextLocked ) {
+ r = AudioSystem3D.alExt.alcSetThreadContext(null);
+ } else {
+ r = AudioSystem3D.alc.alcMakeContextCurrent(null);
+ }
+ if( r ) {
+ currentContext.set(null);
+ } else {
+ if( throwException ) {
+ throw new ALException("Context release failed "+this);
+ }
+ return false; // skip unlock!
+ }
}
lock.unlock();
- return r;
+ return true;
}
/**
@@ -165,4 +317,10 @@ public class Context {
public Device getDevice() {
return device;
}
+
+ @Override
+ public String toString() {
+ final String alCtxStr = null != alCtx ? "0x"+Integer.toHexString(alCtx.hashCode()) : "null";
+ return "ALContext[this 0x"+Integer.toHexString(hashCode())+", alCtx "+alCtxStr+" lockCount "+lock.getHoldCount()+", on "+device+"]";
+ }
}
diff --git a/src/java/com/jogamp/openal/sound3d/Device.java b/src/java/com/jogamp/openal/sound3d/Device.java
index 749d606..052d6a2 100644
--- a/src/java/com/jogamp/openal/sound3d/Device.java
+++ b/src/java/com/jogamp/openal/sound3d/Device.java
@@ -40,39 +40,67 @@ import com.jogamp.openal.*;
/**
* This class provides a handle to a specific audio device.
*
- * @author Athomas Goldberg
+ * @author Athomas Goldberg, Sven Gothel, et al.
*/
-public class Device {
+public final class Device {
+ private String name;
private ALCdevice alDev;
- public Device(final ALCdevice realDevice) {
- this.alDev = realDevice;
- }
-
/**
- * Create a new device by opening the named audio device.
+ * Create a new device by {@link #open()}'ing the named audio device.
*
* @param deviceName The specified device name, null for default.
*/
public Device(final String deviceName) {
- this.alDev = AudioSystem3D.alc.alcOpenDevice(deviceName);
+ this.name = deviceName;
+ this.alDev = null;
+ open();
}
- /**
- * Returns the OpenAL context.
- */
- public ALCdevice getALDevice() {
- return alDev;
+ /** Returns the device name. */
+ public String getName() { return name; }
+
+ /** Returns the OpenAL {@link ALCdevice}. */
+ public ALCdevice getALDevice() { return alDev; }
+
+ /** Return {@link ALC#alcGetError(ALCdevice)} */
+ public int getALCError() {
+ return AudioSystem3D.alc.alcGetError(alDev);
}
- /** Returns whether {@link #getALDevice()} is valid, i.e. not null, e.g. not {@link #close()}. */
+ /** Returns whether {@link #getALDevice()} is open and valid, i.e. not null, e.g. not {@link #close()}. */
public boolean isValid() { return null != alDev; }
/**
+ * Opens the device if not yet opened
+ * @return true if already open or newly opened
+ * @see #isValid()
+ * @see #clone()
+ */
+ public boolean open() {
+ if( null == alDev ) {
+ alDev = AudioSystem3D.alc.alcOpenDevice(name);
+ if( null != alDev && null == name ) {
+ name = AudioSystem3D.alc.alcGetString(alDev, ALCConstants.ALC_DEVICE_SPECIFIER);
+ }
+ }
+ return isValid();
+ }
+
+ /**
* closes the device, freeing its resources.
*/
public void close() {
- AudioSystem3D.alc.alcCloseDevice(alDev);
- alDev = null;
+ if( null != alDev ) {
+ AudioSystem3D.alc.alcCloseDevice(alDev);
+ alDev = null;
+ }
}
+
+ @Override
+ public String toString() {
+ final String alStr = null != alDev ? "0x"+Integer.toHexString(alDev.hashCode()) : "null";
+ return "ALDevice[this 0x"+Integer.toHexString(hashCode())+", name '"+name+"', alDev "+alStr+"]";
+ }
+
}
diff --git a/src/java/com/jogamp/openal/sound3d/Listener.java b/src/java/com/jogamp/openal/sound3d/Listener.java
index 86a9307..5fc9e43 100644
--- a/src/java/com/jogamp/openal/sound3d/Listener.java
+++ b/src/java/com/jogamp/openal/sound3d/Listener.java
@@ -41,9 +41,9 @@ import com.jogamp.openal.ALConstants;
* provides methods for controlling the position, orientation as well as other
* properties associated with the listener.
*
- * @author Athomas Goldberg
+ * @author Athomas Goldberg, Sven Gothel, et al.
*/
-public class Listener {
+public final class Listener {
public Listener() {
}
@@ -141,11 +141,10 @@ public class Listener {
/**
* Sets the orientation of the Listener in the Sound3D environment.
- * Orientation is expressed as "up" and "at" vectors.
+ * Orientation is expressed as `at` and `up` vectors.
*
* @param orientation The first 3 elements of the array should contain
- * the x,y,z up-vector, the second 3 elements should contain the x,z,z
- * look-at vector.
+ * the `at` vector, the second 3 elements should contain the `up` vector.
*/
public void setOrientation(final float[] orientation) {
AudioSystem3D.al.alListenerfv(ALConstants.AL_ORIENTATION, orientation, 0);
@@ -153,12 +152,10 @@ public class Listener {
/**
* Gets the orientation of the Listener in the Sound3D environment.
- * Orientation is expressed as "up" and "at" vectors.
+ * Orientation is expressed as `at` and `up` vectors.
*
- * @return an array containing the orientation of the listener.
- * The first 3 elements of the array contain
- * the x,y,z up-vector, the second 3 elements contain the x,z,z
- * look-at vector.
+ * @return an array containing the orientation of the listener, a pair of 3-float vectors for x,y,z.
+ * The first 3 elements of the array contain the `at` vector, the second 3 elements contain the `up` vector.
*/
public float[] getOrientation() {
final float[] tmp = new float[6];
diff --git a/src/java/com/jogamp/openal/sound3d/Source.java b/src/java/com/jogamp/openal/sound3d/Source.java
index b3b0da9..09b255a 100644
--- a/src/java/com/jogamp/openal/sound3d/Source.java
+++ b/src/java/com/jogamp/openal/sound3d/Source.java
@@ -35,6 +35,7 @@
package com.jogamp.openal.sound3d;
import com.jogamp.openal.ALConstants;
+import com.jogamp.openal.ALException;
/**
* This class is used to represent sound-producing objects in the Sound3D
@@ -42,16 +43,44 @@ import com.jogamp.openal.ALConstants;
* gain and other properties along with methods for starting, pausing, rewinding
* and stopping sudio projecting from a source.
*
- * @author Athomas Goldberg
+ * @author Athomas Goldberg, Sven Gothel, et al.
*/
public final class Source {
private int sourceID;
private Buffer buffer;
+ /** Create a new instance with an invalid OpenAL source ID */
+ public Source() {
+ sourceID = -1;
+ }
+
+ /**
+ * Create a new instance with a given OpenAL source ID
+ * @param sourceID an OpenAL source ID, pass -1 for an invalid value for lazy creation
+ */
public Source(final int sourceID) {
this.sourceID = sourceID;
}
+ /**
+ * Creates a new OpenAL source ID if {@link #isValid()} == false.
+ * @return true if a new ID has been successfully created, otherwise false
+ */
+ public boolean create() {
+ if( isValid() ) {
+ return false;
+ }
+ final int[] val = { -1 };
+ AudioSystem3D.al.alGenSources(1, val, 0);
+ if( 0 <= val[0] && AudioSystem3D.al.alIsSource(val[0]) ) {
+ sourceID = val[0];
+ return true;
+ } else {
+ sourceID = -1;
+ return false;
+ }
+ }
+
/** Return the OpenAL source ID, -1 if invalid. */
public int getID() { return sourceID; }
@@ -466,22 +495,24 @@ public final class Source {
/**
* Gets the number of buffers currently queued on this source.
* @return the number of buffers currently queued on this source.
+ * @throws ALException on AL error
*/
- public int getBuffersQueued() {
+ public int getBuffersQueued() throws ALException {
final int[] result = new int[1];
AudioSystem3D.al.alGetSourcei(sourceID, ALConstants.AL_BUFFERS_QUEUED, result, 0);
-
+ AudioSystem3D.checkALError("Query AL_BUFFERS_QUEUED", true, true);
return result[0];
}
/**
* Gets the number of buffers already processed on this source.
* @return the number of buffers already processed on this source.
+ * @throws ALException on AL error
*/
- public int getBuffersProcessed() {
+ public int getBuffersProcessed() throws ALException {
final int[] result = new int[1];
AudioSystem3D.al.alGetSourcei(sourceID, ALConstants.AL_BUFFERS_PROCESSED, result, 0);
-
+ AudioSystem3D.checkALError("Query AL_BUFFERS_PROCESSED", true, true);
return result[0];
}
@@ -515,31 +546,60 @@ public final class Source {
* buffers will be played in the order they are queued.
*
* @param buffers a set of initialized (loaded) buffers.
+ * @throws ALException on AL error
*/
- public void queueBuffers(final Buffer[] buffers) {
+ public void queueBuffers(final Buffer[] buffers) throws ALException {
final int numBuffers = buffers.length;
final int[] arr = new int[numBuffers];
for (int i = 0; i < numBuffers; i++) {
arr[i] = buffers[i].getID();
}
-
AudioSystem3D.al.alSourceQueueBuffers(sourceID, numBuffers, arr, 0);
+ AudioSystem3D.checkALError("alSourceQueueBuffers", true, true);
+ }
+
+ /**
+ * Queues `bufferIDs.length` OpenAL buffers on a source.
+ *
+ * @param bufferIDs array of to be queued OpenAL buffer IDs
+ * @throws ALException on AL error
+ */
+ public void queueBuffers(final int[] bufferIDs) throws ALException {
+ AudioSystem3D.al.alSourceQueueBuffers(sourceID, bufferIDs.length, bufferIDs, 0);
+ AudioSystem3D.checkALError("alSourceQueueBuffers", true, true);
}
/**
* Unqueues one or more buffers on a source.
*
* @param buffers a set of previously queued buffers.
+ * @throws ALException on AL error
*/
- public void unqueueBuffers(final Buffer[] buffers) {
+ public void unqueueBuffers(final Buffer[] buffers) throws ALException {
final int numBuffers = buffers.length;
final int[] arr = new int[numBuffers];
for (int i = 0; i < numBuffers; i++) {
arr[i] = buffers[i].getID();
}
-
AudioSystem3D.al.alSourceUnqueueBuffers(sourceID, numBuffers, arr, 0);
+ AudioSystem3D.checkALError("alSourceUnqueueBuffers", true, true);
+ }
+
+ /**
+ * Unqueues `bufferIDs.length` OpenAL buffers on a source.
+ *
+ * @param bufferIDs array of resulting unqueued OpenAL buffer IDs of previously queued buffers.
+ * @throws ALException on AL error
+ */
+ public void unqueueBuffers(final int[] bufferIDs) throws ALException {
+ AudioSystem3D.al.alSourceUnqueueBuffers(sourceID, bufferIDs.length, bufferIDs, 0);
+ AudioSystem3D.checkALError("alSourceUnqueueBuffers", true, true);
+ }
+
+ @Override
+ public String toString() {
+ return "ALSource[id "+sourceID+", buffer "+buffer+"]";
}
}
diff --git a/src/test/com/jogamp/openal/test/manual/Sound3DTest.java b/src/test/com/jogamp/openal/test/manual/Sound3DTest.java
index 62a758a..39f96bd 100644
--- a/src/test/com/jogamp/openal/test/manual/Sound3DTest.java
+++ b/src/test/com/jogamp/openal/test/manual/Sound3DTest.java
@@ -64,8 +64,8 @@ public class Sound3DTest {
// create the initial context - this can be collapsed into the init.
final Device device = AudioSystem3D.openDevice(null);
- final Context context = new Context(device);
- if( !context.makeCurrent() ) {
+ final Context context = new Context(device, null);
+ if( !context.makeCurrent(false) ) {
System.err.println("Context.makeCurrent() failed");
return;
}