
com.threerings.openal.Sound Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nenya Show documentation
Show all versions of nenya Show documentation
Facilities for making networked multiplayer games.
The newest version!
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.openal;
import org.lwjgl.openal.AL10;
/**
* Represents an instance of a sound clip which can be positioned in 3D space, gain and pitch
* adjusted and played or looped.
*/
public class Sound
{
/**
* Used to await notification of the starting of a sound which may be delayed in loading.
*/
public interface StartObserver
{
/**
* Called when the specified sound has started playing. If sound is null then the sound
* failed to play but soundStarted was called anyway to perform whatever actions were
* waiting on the sound.
*/
public void soundStarted (Sound sound);
}
/**
* Returns a reference to the group to which the sound belongs.
*/
public SoundGroup getGroup ()
{
return _group;
}
/**
* Returns the buffer of audio data associated with this sound.
*/
public ClipBuffer getBuffer ()
{
return _buffer;
}
/**
* Sets the position of the sound.
*/
public void setPosition (float x, float y, float z)
{
if (_source != null) {
_source.setPosition(x, y, z);
}
_px = x;
_py = y;
_pz = z;
}
/**
* Sets the velocity of the sound.
*/
public void setVelocity (float x, float y, float z)
{
if (_source != null) {
_source.setVelocity(x, y, z);
}
_vx = x;
_vy = y;
_vz = z;
}
/**
* Sets the gain of the sound (which will be multiplied by the base gain).
*/
public void setGain (float gain)
{
_gain = gain;
updateSourceGain();
}
/**
* Sets whether or not the position, velocity, etc., of the sound are relative to the
* listener.
*/
public void setSourceRelative (boolean relative)
{
if (_source != null) {
_source.setSourceRelative(relative);
}
_sourceRelative = relative;
}
/**
* Sets the minimum gain.
*/
public void setMinGain (float gain)
{
if (_source != null) {
_source.setMinGain(gain);
}
_minGain = gain;
}
/**
* Sets the maximum gain.
*/
public void setMaxGain (float gain)
{
if (_source != null) {
_source.setMaxGain(gain);
}
_maxGain = gain;
}
/**
* Sets the reference distance for attenuation.
*/
public void setReferenceDistance (float distance)
{
if (_source != null) {
_source.setReferenceDistance(distance);
}
_referenceDistance = distance;
}
/**
* Sets the rolloff factor for attenuation.
*/
public void setRolloffFactor (float rolloff)
{
if (_source != null) {
_source.setRolloffFactor(rolloff);
}
_rolloffFactor = rolloff;
}
/**
* Sets the maximum distance for attenuation.
*/
public void setMaxDistance (float distance)
{
if (_source != null) {
_source.setMaxDistance(distance);
}
_maxDistance = distance;
}
/**
* Sets the pitch multiplier.
*/
public void setPitch (float pitch)
{
if (_source != null) {
_source.setPitch(pitch);
}
_pitch = pitch;
}
/**
* Sets the direction of the sound.
*/
public void setDirection (float x, float y, float z)
{
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;
}
/**
* Plays this sound from the beginning. While the sound is playing, an audio channel will be
* locked and then freed when the sound completes.
*
* @param allowDefer if false, the sound will be played immediately or not at all. If true,
* the sound will be queued up for loading if it is currently flushed from the cache and
* played once loaded.
*
* @return true if the sound could be played and was started (or queued up to be loaded and
* played ASAP if it was specified as deferrable) or false if the sound could not be played
* either because it was not ready and deferral was not allowed or because too many other
* sounds were playing concurrently.
*/
public boolean play (boolean allowDefer)
{
return play(allowDefer, false, null);
}
/**
* Loops this sound, starting from the beginning of the audio data. It will continue to loop
* until {@link #pause}d or {@link #stop}ped. While the sound is playing an audio channel will
* be locked.
*
* @return true if a channel could be obtained to play the sound (and the sound was thus
* started) or false if no channels were available.
*/
public boolean loop (boolean allowDefer)
{
return play(allowDefer, true, null);
}
/**
* Plays this sound from the beginning, notifying the supplied observer when the audio starts.
*
* @param loop whether or not to loop the sampe until {@link #stop}ped.
*/
public boolean play (StartObserver obs, boolean loop)
{
return play(true, loop, obs);
}
/**
* Pauses this sound. A subsequent call to {@link #play} will resume the sound from the
* precise position that it left off. While the sound is paused, its audio channel will remain
* locked.
*/
public void pause ()
{
_stateDesired = AL10.AL_PAUSED;
if (_source != null) {
_source.pause();
}
}
/**
* Stops this sound and rewinds to its beginning. The audio channel being used to play the
* sound will be released.
*/
public void stop ()
{
_stateDesired = AL10.AL_STOPPED;
if (_source != null) {
_source.stop();
}
}
/**
* Called to check if this sound is currently playing.
*/
public boolean isPlaying ()
{
return _source != null && _source.isPlaying();
}
/**
* Called to check if this sound wants to start playing.
*/
public boolean isPending ()
{
return _stateDesired == AL10.AL_PLAYING;
}
protected Sound (SoundGroup group, ClipBuffer buffer)
{
_group = group;
_buffer = buffer;
}
protected boolean play (boolean allowDefer, final boolean loop, final StartObserver obs)
{
// if we were unable to get our buffer, fail immediately
if (_buffer == null) {
if (obs != null) {
obs.soundStarted(null);
}
_stateDesired = AL10.AL_INVALID;
return false;
}
// if we're not ready to go...
if (!_buffer.isPlayable()) {
if (allowDefer) {
// save the desired state, which may be overridden by calls to play/pause/stop
_stateDesired = AL10.AL_PLAYING;
_loopDesired = loop;
// resolve the buffer and instruct it to play once it is resolved
_buffer.resolve(new ClipBuffer.Observer() {
public void clipLoaded (ClipBuffer buffer) {
if (_stateDesired == AL10.AL_STOPPED) {
return;
}
play(false, _loopDesired, obs);
if (_stateDesired == AL10.AL_PAUSED) {
pause();
}
}
public void clipFailed (ClipBuffer buffer) {
// well, let's pretend like the sound started so that the observer isn't
// left hanging
if (obs != null && _stateDesired != AL10.AL_STOPPED) {
obs.soundStarted(Sound.this);
_stateDesired = AL10.AL_INVALID;
}
}
});
return true;
} else {
// sorry charlie...
if (obs != null) {
obs.soundStarted(null);
}
_stateDesired = AL10.AL_INVALID;
return false;
}
}
// let the observer know that (as far as they're concerned), we're started
if (obs != null) {
obs.soundStarted(this);
}
// if we do not already have a source, obtain one
if (_source == null) {
_source = _group.acquireSource(this);
if (_source == null) {
_stateDesired = AL10.AL_INVALID;
return false;
}
// bind our clip buffer to the source and notify it
_source.setBuffer(_buffer.getBuffer());
_buffer.sourceBound();
// configure the source with our ephemera
_source.setPosition(_px, _py, _pz);
_source.setVelocity(_vx, _vy, _vz);
updateSourceGain();
_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
_source.setLooping(loop);
// and start that damned thing up!
_source.play();
return true;
}
/**
* Updates the source gain according to our configured gain and the base gain.
*/
protected void updateSourceGain ()
{
if (_source != null) {
_source.setGain(_gain * _group.getInheritedBaseGain());
}
}
/**
* Called by the {@link SoundGroup} when it wants to reclaim our source.
*
* @return false if we have no source to reclaim or if we're still busy playing our sound,
* true if we gave up our source.
*/
protected boolean reclaim ()
{
if (_source != null && _source.isStopped()) {
_source.setBuffer(null);
_buffer.sourceUnbound();
_source = null;
return true;
}
return false;
}
/** The sound group with which we are associated. */
protected SoundGroup _group;
/** The OpenAL buffer from which we get our sound data. */
protected ClipBuffer _buffer;
/** The source via which we are playing our sound currently. */
protected Source _source;
/** The desired state of the sound (stopped, playing, paused) after resolution. */
protected int _stateDesired = AL10.AL_INVALID;
/** Whether or not looping is desired after resolution. */
protected boolean _loopDesired;
/** The position of the sound. */
protected float _px, _py, _pz;
/** The velocity of the sound. */
protected float _vx, _vy, _vz;
/** The gain of the sound. */
protected float _gain = 1f;
/** 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;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy