diff options
author | Sven Gothel <[email protected]> | 2023-05-23 02:05:11 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-05-23 02:05:11 +0200 |
commit | afb386e13fd00fde1401d4551ee4790b1f6d5e09 (patch) | |
tree | c9b389c45fe0711a5a89bd37a0b763e217b46c21 | |
parent | 66e79a41f38f5694f953816f1a85a02cee71eb16 (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.java | 137 | ||||
-rw-r--r-- | src/java/com/jogamp/openal/sound3d/Buffer.java | 9 | ||||
-rw-r--r-- | src/java/com/jogamp/openal/sound3d/Context.java | 240 | ||||
-rw-r--r-- | src/java/com/jogamp/openal/sound3d/Device.java | 60 | ||||
-rw-r--r-- | src/java/com/jogamp/openal/sound3d/Listener.java | 17 | ||||
-rw-r--r-- | src/java/com/jogamp/openal/sound3d/Source.java | 78 | ||||
-rw-r--r-- | src/test/com/jogamp/openal/test/manual/Sound3DTest.java | 4 |
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; } |