Behold, Nenya, Ring of Water and repository for our media and animation related
goodies, both Java 2D and LWJGL/JME 3D. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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 java.io.IOException;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
/**
|
||||
* Represents a sound that has been loaded into the OpenAL system.
|
||||
*/
|
||||
public class ClipBuffer
|
||||
{
|
||||
/** Used to notify parties interested in when a clip is loaded. */
|
||||
public static interface Observer
|
||||
{
|
||||
/** Called when a clip has completed loading and is ready to be
|
||||
* played. */
|
||||
public void clipLoaded (ClipBuffer buffer);
|
||||
|
||||
/** Called when a clip has failed to prepare itself for one reason
|
||||
* or other. */
|
||||
public void clipFailed (ClipBuffer buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a key that uniquely identifies this combination of clip
|
||||
* provider and path.
|
||||
*/
|
||||
public static Comparable makeKey (ClipProvider provider, String path)
|
||||
{
|
||||
// we'll just use a string, amazing!
|
||||
return provider + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new clip buffer with the specified path that will obtain
|
||||
* its clip data from the specified source. The clip will
|
||||
* automatically queue itself up to be loaded into memory.
|
||||
*/
|
||||
public ClipBuffer (SoundManager manager, ClipProvider provider, String path)
|
||||
{
|
||||
_manager = manager;
|
||||
_provider = provider;
|
||||
_path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique key for this clip buffer.
|
||||
*/
|
||||
public Comparable getKey ()
|
||||
{
|
||||
return makeKey(_provider, _path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provider used to load this clip.
|
||||
*/
|
||||
public ClipProvider getClipProvider ()
|
||||
{
|
||||
return _provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path that identifies this sound clip.
|
||||
*/
|
||||
public String getPath ()
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this buffer is loaded and ready to go.
|
||||
*/
|
||||
public boolean isPlayable ()
|
||||
{
|
||||
return (_state == LOADED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier for this clip's buffer or -1 if it is not
|
||||
* loaded.
|
||||
*/
|
||||
public int getBufferId ()
|
||||
{
|
||||
return (_bufferId == null) ? -1 : _bufferId.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size (in bytes) of this clip as reported by OpenAL.
|
||||
* This value will not be valid until the clip is bound.
|
||||
*/
|
||||
public int getSize ()
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs this buffer to resolve its underlying clip and be ready
|
||||
* to be played ASAP.
|
||||
*/
|
||||
public void resolve (Observer observer)
|
||||
{
|
||||
// if we were waiting to unload, cancel that
|
||||
if (_state == UNLOADING) {
|
||||
_state = LOADED;
|
||||
_manager.restoreClip(this);
|
||||
}
|
||||
|
||||
// if we're already loaded, this is easy
|
||||
if (_state == LOADED) {
|
||||
if (observer != null) {
|
||||
observer.clipLoaded(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// queue up the observer
|
||||
if (observer != null) {
|
||||
_observers.add(observer);
|
||||
}
|
||||
|
||||
// if we're already loading, we can stop here
|
||||
if (_state == LOADING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create our OpenAL buffer and then queue ourselves up to have
|
||||
// our clip data loaded
|
||||
_bufferId = BufferUtils.createIntBuffer(1);
|
||||
AL10.alGenBuffers(_bufferId);
|
||||
int errno = AL10.alGetError();
|
||||
if (errno != AL10.AL_NO_ERROR) {
|
||||
Log.warning("Failed to create buffer [key=" + getKey() +
|
||||
", errno=" + errno + "].");
|
||||
_bufferId = 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
|
||||
_manager.queueClipFailure(this);
|
||||
|
||||
} else {
|
||||
_state = LOADING;
|
||||
_manager.queueClipLoad(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees up the internal audio buffers associated with this clip.
|
||||
*/
|
||||
public void dispose ()
|
||||
{
|
||||
if (_bufferId != null) {
|
||||
// if there are sources bound to this buffer, we must wait
|
||||
// for them to be unbound
|
||||
if (_bound > 0) {
|
||||
_state = UNLOADING;
|
||||
return;
|
||||
}
|
||||
|
||||
// free up our buffer
|
||||
AL10.alDeleteBuffers(_bufferId);
|
||||
_bufferId = null;
|
||||
_state = UNLOADED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by the background sound loading thread and
|
||||
* actually loads the sound data from wherever it cometh.
|
||||
*/
|
||||
protected Clip load ()
|
||||
throws IOException
|
||||
{
|
||||
return _provider.loadClip(_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called back on the main thread and instructs this
|
||||
* buffer to bind the clip data to this buffer's OpenAL buffer.
|
||||
*
|
||||
* @return true if the binding succeeded, false if we were unable to
|
||||
* load the sound data into OpenAL.
|
||||
*/
|
||||
protected boolean bind (Clip clip)
|
||||
{
|
||||
AL10.alBufferData(
|
||||
_bufferId.get(0), 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 + "].");
|
||||
failed();
|
||||
return false;
|
||||
}
|
||||
|
||||
_state = LOADED;
|
||||
_size = AL10.alGetBufferi(_bufferId.get(0), AL10.AL_SIZE);
|
||||
_observers.apply(new ObserverList.ObserverOp() {
|
||||
public boolean apply (Object observer) {
|
||||
((Observer)observer).clipLoaded(ClipBuffer.this);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_observers.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we fail in some part of the process in resolving our
|
||||
* clip data. Notifies our observers and resets the clip to the
|
||||
* UNLOADED state.
|
||||
*/
|
||||
protected void failed ()
|
||||
{
|
||||
if (_bufferId != null) {
|
||||
AL10.alDeleteBuffers(_bufferId);
|
||||
_bufferId = null;
|
||||
}
|
||||
_state = UNLOADED;
|
||||
|
||||
_observers.apply(new ObserverList.ObserverOp() {
|
||||
public boolean apply (Object observer) {
|
||||
((Observer)observer).clipFailed(ClipBuffer.this);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_observers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the buffer that a source has been bound to it.
|
||||
*/
|
||||
protected void sourceBound ()
|
||||
{
|
||||
_bound++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the buffer that a source has been unbound from it.
|
||||
*/
|
||||
protected void sourceUnbound ()
|
||||
{
|
||||
// dispose of the buffer when the last source is unbound
|
||||
if (--_bound == 0 && _state == UNLOADING) {
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundManager _manager;
|
||||
protected ClipProvider _provider;
|
||||
protected String _path;
|
||||
protected int _state;
|
||||
protected IntBuffer _bufferId;
|
||||
protected int _size;
|
||||
protected ObserverList _observers =
|
||||
new ObserverList(ObserverList.FAST_UNSAFE_NOTIFY);
|
||||
protected int _bound;
|
||||
|
||||
protected static final int UNLOADED = 0;
|
||||
protected static final int LOADING = 1;
|
||||
protected static final int LOADED = 2;
|
||||
protected static final int UNLOADING = 3;
|
||||
}
|
||||
Reference in New Issue
Block a user