null if it is not loaded.
*/
- public int getBufferId ()
+ public Buffer getBuffer ()
{
- return (_bufferId == null) ? -1 : _bufferId.get(0);
+ return _buffer;
}
/**
@@ -150,13 +149,12 @@ public class ClipBuffer
// create our OpenAL buffer and then queue ourselves up to have
// our clip data loaded
- _bufferId = BufferUtils.createIntBuffer(1);
- AL10.alGenBuffers(_bufferId);
+ _buffer = new Buffer(_manager);
int errno = AL10.alGetError();
if (errno != AL10.AL_NO_ERROR) {
log.warning("Failed to create buffer [key=" + getKey() +
", errno=" + errno + "].");
- _bufferId = null;
+ _buffer = null;
// queue up a failure notification so that we properly return
// from this method and our sound has a chance to register
// itself as an observer before we jump up and declare failure
@@ -173,7 +171,7 @@ public class ClipBuffer
*/
public void dispose ()
{
- if (_bufferId != null) {
+ if (_buffer != null) {
// if there are sources bound to this buffer, we must wait
// for them to be unbound
if (_bound > 0) {
@@ -182,8 +180,8 @@ public class ClipBuffer
}
// free up our buffer
- AL10.alDeleteBuffers(_bufferId);
- _bufferId = null;
+ _buffer.delete();
+ _buffer = null;
_state = UNLOADED;
}
}
@@ -207,7 +205,7 @@ public class ClipBuffer
*/
protected boolean bind (Clip clip)
{
- AL10.alBufferData(_bufferId.get(0), clip.format, clip.data, clip.frequency);
+ _buffer.setData(clip.format, clip.data, clip.frequency);
int errno = AL10.alGetError();
if (errno != AL10.AL_NO_ERROR) {
log.warning("Failed to bind clip", "key", getKey(), "errno", errno);
@@ -216,7 +214,7 @@ public class ClipBuffer
}
_state = LOADED;
- _size = AL10.alGetBufferi(_bufferId.get(0), AL10.AL_SIZE);
+ _size = _buffer.getSize();
_observers.apply(new ObserverList.ObserverOp
- * Note: this value is multiplied by the base gain configured in the sound manager.
+ * Sets the direction of the sound.
*/
- public void setGain (float gain)
+ public void setDirection (float x, float y, float z)
{
- _gain = gain;
+ if (_source != null) {
+ _source.setDirection(x, y, z);
+ }
+ _dx = x;
+ _dy = y;
+ _dz = z;
+ }
+
+ /**
+ * Sets the inside angle of the sound cone.
+ */
+ public void setConeInnerAngle (float angle)
+ {
+ if (_source != null) {
+ _source.setConeInnerAngle(angle);
+ }
+ _coneInnerAngle = angle;
+ }
+
+ /**
+ * Sets the outside angle of the sound cone.
+ */
+ public void setConeOuterAngle (float angle)
+ {
+ if (_source != null) {
+ _source.setConeOuterAngle(angle);
+ }
+ _coneOuterAngle = angle;
+ }
+
+ /**
+ * Sets the gain outside of the sound cone.
+ */
+ public void setConeOuterGain (float gain)
+ {
+ if (_source != null) {
+ _source.setConeOuterGain(gain);
+ }
+ _coneOuterGain = gain;
}
/**
@@ -151,8 +262,8 @@ public class Sound
*/
public void pause ()
{
- if (_sourceId != -1) {
- AL10.alSourcePause(_sourceId);
+ if (_source != null) {
+ _source.pause();
}
}
@@ -162,8 +273,8 @@ public class Sound
*/
public void stop ()
{
- if (_sourceId != -1) {
- AL10.alSourceStop(_sourceId);
+ if (_source != null) {
+ _source.stop();
}
}
@@ -172,8 +283,7 @@ public class Sound
*/
public boolean isPlaying ()
{
- return (_sourceId != -1 && AL10.AL_PLAYING ==
- AL10.alGetSourcei(_sourceId, AL10.AL_SOURCE_STATE));
+ return _source != null && _source.isPlaying();
}
protected Sound (SoundGroup group, ClipBuffer buffer)
@@ -224,32 +334,38 @@ public class Sound
}
// if we do not already have a source, obtain one
- if (_sourceId == -1) {
- _sourceId = _group.acquireSource(this);
- if (_sourceId == -1) {
+ if (_source == null) {
+ _source = _group.acquireSource(this);
+ if (_source == null) {
return false;
}
// bind our clip buffer to the source and notify it
- AL10.alSourcei(_sourceId, AL10.AL_BUFFER, _buffer.getBufferId());
+ _source.setBuffer(_buffer.getBuffer());
_buffer.sourceBound();
- }
- // configure the source with our ephemera
- AL10.alSourcef(_sourceId, AL10.AL_PITCH, _pitch);
- AL10.alSourcef(_sourceId, AL10.AL_GAIN, _gain * _group.getBaseGain());
- if (_position != null) {
- AL10.alSource(_sourceId, AL10.AL_POSITION, _position);
- }
- if (_velocity != null) {
- AL10.alSource(_sourceId, AL10.AL_VELOCITY, _velocity);
+ // configure the source with our ephemera
+ _source.setPosition(_px, _py, _pz);
+ _source.setVelocity(_vx, _vy, _vz);
+ _source.setGain(_gain * _group.getBaseGain());
+ _source.setSourceRelative(_sourceRelative);
+ _source.setMinGain(_minGain);
+ _source.setMaxGain(_maxGain);
+ _source.setReferenceDistance(_referenceDistance);
+ _source.setRolloffFactor(_rolloffFactor);
+ _source.setMaxDistance(_maxDistance);
+ _source.setPitch(_pitch);
+ _source.setDirection(_dx, _dy, _dz);
+ _source.setConeInnerAngle(_coneInnerAngle);
+ _source.setConeOuterAngle(_coneOuterAngle);
+ _source.setConeOuterGain(_coneOuterGain);
}
// configure whether or not we should loop
- AL10.alSourcei(_sourceId, AL10.AL_LOOPING, loop ? AL10.AL_TRUE : AL10.AL_FALSE);
+ _source.setLooping(loop);
// and start that damned thing up!
- AL10.alSourcePlay(_sourceId);
+ _source.play();
return true;
}
@@ -262,11 +378,10 @@ public class Sound
*/
protected boolean reclaim ()
{
- if (_sourceId != -1 &&
- AL10.alGetSourcei(_sourceId, AL10.AL_SOURCE_STATE) == AL10.AL_STOPPED) {
- AL10.alSourcei(_sourceId, AL10.AL_BUFFER, 0);
+ if (_source != null && _source.isStopped()) {
+ _source.setBuffer(null);
_buffer.sourceUnbound();
- _sourceId = -1;
+ _source = null;
return true;
}
return false;
@@ -279,17 +394,47 @@ public class Sound
protected ClipBuffer _buffer;
/** The source via which we are playing our sound currently. */
- protected int _sourceId = -1;
+ protected Source _source;
- /** The pitch adjustment. */
- protected float _pitch = 1;
+ /** The position of the sound. */
+ protected float _px, _py, _pz;
- /** The gain adjustment. */
- protected float _gain = 1;
+ /** The velocity of the sound. */
+ protected float _vx, _vy, _vz;
- /** The starting position in 3D space. */
- protected FloatBuffer _position;
+ /** The gain of the sound. */
+ protected float _gain = 1f;
- /** The velocity vector. */
- protected FloatBuffer _velocity;
+ /** Whether or not the sound's position, velocity, etc. are relative to the listener. */
+ protected boolean _sourceRelative;
+
+ /** The minimum gain. */
+ protected float _minGain;
+
+ /** The maximum gain. */
+ protected float _maxGain = 1f;
+
+ /** The reference distance for attenuation. */
+ protected float _referenceDistance = 1f;
+
+ /** The attenuation rolloff factor. */
+ protected float _rolloffFactor = 1f;
+
+ /** The maximum distance for attenuation. */
+ protected float _maxDistance = Float.MAX_VALUE;
+
+ /** The pitch multiplier. */
+ protected float _pitch = 1f;
+
+ /** The direction of the sound. */
+ protected float _dx, _dy, _dz;
+
+ /** The inside angle of the sound cone. */
+ protected float _coneInnerAngle = 360f;
+
+ /** The outside angle of the sound cone. */
+ protected float _coneOuterAngle = 360f;
+
+ /** The gain outside the sound cone. */
+ protected float _coneOuterGain;
}
diff --git a/src/java/com/threerings/openal/SoundGroup.java b/src/java/com/threerings/openal/SoundGroup.java
index dc87a7af..b70978db 100644
--- a/src/java/com/threerings/openal/SoundGroup.java
+++ b/src/java/com/threerings/openal/SoundGroup.java
@@ -65,11 +65,10 @@ public class SoundGroup
public void dispose ()
{
reclaimAll();
- if (_sourceIds != null) {
- _sources.clear();
- AL10.alDeleteSources(_sourceIds);
- _sourceIds = null;
+ for (PooledSource pooled : _sources) {
+ pooled.source.delete();
}
+ _sources.clear();
}
/**
@@ -77,14 +76,12 @@ public class SoundGroup
*/
public void reclaimAll ()
{
- if (_sourceIds != null) {
- // make sure any bound sources are released
- for (Source source : _sources) {
- if (source.holder != null) {
- source.holder.stop();
- source.holder.reclaim();
- source.holder = null;
- }
+ // make sure any bound sources are released
+ for (PooledSource pooled : _sources) {
+ if (pooled.holder != null) {
+ pooled.holder.stop();
+ pooled.holder.reclaim();
+ pooled.holder = null;
}
}
}
@@ -100,45 +97,38 @@ public class SoundGroup
return;
}
- // create our sources
- _sourceIds = BufferUtils.createIntBuffer(sources);
- AL10.alGenSources(_sourceIds);
- int errno = AL10.alGetError();
- if (errno != AL10.AL_NO_ERROR) {
- log.warning("Failed to create sources [cprov=" + provider +
- ", sources=" + sources + ", errno=" + errno + "].");
- _sourceIds = null;
- // we'll have no sources which means all requests to play
- // sounds will silently fail as if all sources were in use
-
- } else {
- for (int ii = 0; ii < sources; ii++) {
- Source source = new Source();
- source.sourceId = _sourceIds.get(ii);
- _sources.add(source);
+ // create our sources (or as many of them as we can)
+ for (int ii = 0; ii < sources; ii++) {
+ PooledSource pooled = new PooledSource();
+ pooled.source = new Source(manager);
+ int errno = AL10.alGetError();
+ if (errno != AL10.AL_NO_ERROR) {
+ log.warning("Failed to create sources [cprov=" + provider +
+ ", sources=" + sources + ", errno=" + errno + "].");
+ return;
}
+ _sources.add(pooled);
}
}
/**
* Called by a {@link Sound} when it wants to obtain a source on which to play its clip.
*/
- protected int acquireSource (Sound acquirer)
+ protected Source acquireSource (Sound acquirer)
{
- // start at the beginning of the list looking for an available
- // source
+ // start at the beginning of the list looking for an available source
for (int ii = 0, ll = _sources.size(); ii < ll; ii++) {
- Source source = _sources.get(ii);
- if (source.holder == null || source.holder.reclaim()) {
+ PooledSource pooled = _sources.get(ii);
+ if (pooled.holder == null || pooled.holder.reclaim()) {
// note this source's new holder
- source.holder = acquirer;
+ pooled.holder = acquirer;
// move this source to the end of the list
_sources.remove(ii);
- _sources.add(source);
- return source.sourceId;
+ _sources.add(pooled);
+ return pooled.source;
}
}
- return -1;
+ return null;
}
/**
@@ -150,15 +140,14 @@ public class SoundGroup
}
/** Used to track which sources are in use. */
- protected static class Source
+ protected static class PooledSource
{
- public int sourceId;
+ public Source source;
public Sound holder;
}
protected SoundManager _manager;
protected ClipProvider _provider;
- protected IntBuffer _sourceIds;
- protected ArrayListnull to clear.
+ */
+ public void setBuffer (Buffer buffer)
+ {
+ _queue.clear();
+ if (buffer != null) {
+ _queue.add(buffer);
+ }
+ AL10.alSourcei(_id, AL10.AL_BUFFER, buffer == null ? AL10.AL_NONE : buffer.getId());
+ }
+
+ /**
+ * Enqueues the specified buffers.
+ */
+ public void queueBuffers (Buffer... buffers)
+ {
+ IntBuffer idbuf = BufferUtils.createIntBuffer(buffers.length);
+ for (int ii = 0; ii < buffers.length; ii++) {
+ Buffer buffer = buffers[ii];
+ _queue.add(buffer);
+ idbuf.put(ii, buffer.getId());
+ }
+ AL10.alSourceQueueBuffers(_id, idbuf);
+ }
+
+ /**
+ * Removes the specified buffers from the queue.
+ */
+ public void unqueueBuffers (Buffer... buffers)
+ {
+ IntBuffer idbuf = BufferUtils.createIntBuffer(buffers.length);
+ for (int ii = 0; ii < buffers.length; ii++) {
+ Buffer buffer = buffers[ii];
+ _queue.remove(buffer);
+ idbuf.put(ii, buffer.getId());
+ }
+ AL10.alSourceUnqueueBuffers(_id, idbuf);
+ }
+
+ /**
+ * Determines whether the source is playing.
+ */
+ public boolean isPlaying ()
+ {
+ return getSourceState() == AL10.AL_PLAYING;
+ }
+
+ /**
+ * Determines whether the source is paused.
+ */
+ public boolean isPaused ()
+ {
+ return getSourceState() == AL10.AL_PAUSED;
+ }
+
+ /**
+ * Determines whether the source is stopped.
+ */
+ public boolean isStopped ()
+ {
+ return getSourceState() == AL10.AL_STOPPED;
+ }
+
+ /**
+ * Returns the state of the source: {@link AL10#AL_INITIAL}, {@link AL10#AL_PLAYING},
+ * {@link AL10#AL_PAUSED}, or {@link AL10#AL_STOPPED}.
+ */
+ public int getSourceState ()
+ {
+ return AL10.alGetSourcei(_id, AL10.AL_SOURCE_STATE);
+ }
+
+ /**
+ * Returns the number of buffers that have been processed.
+ */
+ public int getBuffersProcessed ()
+ {
+ return AL10.alGetSourcei(_id, AL10.AL_BUFFERS_PROCESSED);
+ }
+
+ /**
+ * Starts playing the source.
+ */
+ public void play ()
+ {
+ AL10.alSourcePlay(_id);
+ }
+
+ /**
+ * Pauses the source.
+ */
+ public void pause ()
+ {
+ AL10.alSourcePause(_id);
+ }
+
+ /**
+ * Stops the source.
+ */
+ public void stop ()
+ {
+ AL10.alSourceStop(_id);
+ }
+
+ /**
+ * Rewinds the source.
+ */
+ public void rewind ()
+ {
+ AL10.alSourceRewind(_id);
+ }
+
+ /**
+ * Deletes this source, rendering it unusable.
+ */
+ public void delete ()
+ {
+ IntBuffer idbuf = BufferUtils.createIntBuffer(1);
+ idbuf.put(_id).rewind();
+ AL10.alDeleteSources(idbuf);
+ _id = 0;
+ _queue.clear();
+ }
+
+ @Override // documentation inherited
+ protected void finalize ()
+ throws Throwable
+ {
+ super.finalize();
+ if (_id > 0) {
+ _soundmgr.sourceFinalized(_id);
+ }
+ }
+
+ /** The sound manager responsible for this source. */
+ protected SoundManager _soundmgr;
+
+ /** The OpenAL identifier for this source. */
+ protected int _id;
+
+ /** The position of the source. */
+ protected float _px, _py, _pz;
+
+ /** The velocity of the source. */
+ protected float _vx, _vy, _vz;
+
+ /** The gain of the source. */
+ protected float _gain = 1f;
+
+ /** Whether or not the source's position, velocity, etc. are relative to the listener. */
+ protected boolean _sourceRelative;
+
+ /** Whether or not the source is looping. */
+ protected boolean _looping;
+
+ /** The minimum gain. */
+ protected float _minGain;
+
+ /** The maximum gain. */
+ protected float _maxGain = 1f;
+
+ /** The reference distance for attenuation. */
+ protected float _referenceDistance = 1f;
+
+ /** The attenuation rolloff factor. */
+ protected float _rolloffFactor = 1f;
+
+ /** The maximum distance for attenuation. */
+ protected float _maxDistance = Float.MAX_VALUE;
+
+ /** The pitch multiplier. */
+ protected float _pitch = 1f;
+
+ /** The direction of the source. */
+ protected float _dx, _dy, _dz;
+
+ /** The inside angle of the sound cone. */
+ protected float _coneInnerAngle = 360f;
+
+ /** The outside angle of the sound cone. */
+ protected float _coneOuterAngle = 360f;
+
+ /** The gain outside the sound cone. */
+ protected float _coneOuterGain;
+
+ /** The source's queue of buffers (storing them keeps them from being garbage-collected). */
+ protected ArrayList