
com.threerings.openal.ClipBuffer 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 java.io.IOException;
import org.lwjgl.openal.AL10;
import com.samskivert.util.ObserverList;
import static com.threerings.openal.Log.log;
/**
* 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 String 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 String 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 a reference to this clip's buffer or null
if it is not loaded.
*/
public Buffer getBuffer ()
{
return _buffer;
}
/**
* 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
AL10.alGetError(); // throw away any unchecked error prior to an op we want to check
_buffer = new Buffer(_manager);
int errno = AL10.alGetError();
if (errno != AL10.AL_NO_ERROR) {
log.warning("Failed to create buffer [key=" + getKey() +
", errno=" + errno + "].");
_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
_manager.queueClipFailure(this);
} else {
_state = LOADING;
_manager.queueClipLoad(this);
}
}
/**
* Frees up the internal audio buffers associated with this clip.
*/
public void dispose ()
{
if (_buffer != 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
_buffer.delete();
_buffer = 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.alGetError(); // throw away any unchecked error prior to an op we want to check
_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);
failed();
return false;
}
_state = LOADED;
_size = _buffer.getSize();
_observers.apply(new ObserverList.ObserverOp() {
public boolean apply (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 (_buffer != null) {
_buffer.delete();
_buffer = null;
}
_state = UNLOADED;
_observers.apply(new ObserverList.ObserverOp() {
public boolean apply (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 Buffer _buffer;
protected int _size;
protected ObserverList _observers = ObserverList.newFastUnsafe();
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;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy