/*
* Copyright (c) 2003-onwards Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.shavenpuppy.jglib.openal;
import java.util.ArrayList;
import org.lwjgl.openal.OpenALException;
import org.lwjgl.util.vector.Vector3f;
import com.shavenpuppy.jglib.Resource;
import static org.lwjgl.openal.AL10.*;
/**
* A sound source
*/
public class ALSource extends Resource {
private static final long serialVersionUID = 1L;
/** All the known created sources */
private static ArrayList<ALSource> createdSources = new ArrayList<ALSource>(8);
/** Source id */
private transient int source;
/** The currently attached buffer to this source */
private transient ALBufferID attachedBuffer;
/**
* Constructor for ALSource.
*/
public ALSource() {
super();
}
/**
* Unattach a buffer from all known sources
* @param buffer The buffer to remove from any sources
*/
static synchronized void unattach(ALBuffer buffer) {
for (int i = 0; i < createdSources.size(); i ++) {
ALSource s = createdSources.get(i);
if (s.attachedBuffer == buffer) {
s.stop();
s.unattach();
}
}
}
/**
* @see com.shavenpuppy.jglib.openAL_ALPlayable#play()
*/
public void play() {
assert isCreated() : this + " is not created yet";
alSourcePlay(source);
}
/*
* Other source commands...
*/
/**
* Pause the source
*/
public void pause() {
assert isCreated() : this + " is not created yet";
alSourcePause(source);
}
/**
* Stop the source
*/
public void stop() {
assert isCreated() : this + " is not created yet";
alSourceStop(source);
}
/**
* Rewind the source
*/
public void rewind() {
assert isCreated() : this + " is not created yet";
alSourceRewind(source);
}
/**
* Set a source property.
* @param property One of
* <ul>
* <li>AL_PITCH</li>
* <li>AL_GAIN</li>
* <li>AL_MAX_DISTANCE</li>
* <li>AL_ROLLOFF_FACTOR</li>
* <li>AL_REFERENCE_DISTANCE</li>
* <li>AL_MIN_GAIN</li>
* <li>AL_MAX_GAIN</li>
* <li>AL_CONE_OUTER_GAIN</li>
* </ul>
* @param value The value to set the property to
*/
public void set(int property, float value) {
assert isCreated() : this + " is not created yet";
alSourcef(source, property, value);
}
/**
* Set a source property
* @param property One of
* <ul>
* <li>AL_POSITION</li>
* <li>AL_VELOCITY</li>
* <li>AL_DIRECTION</li>
* </ul>
* @param x
* @param y
* @param z
*/
public void set(int property, float x, float y, float z) {
assert isCreated() : this + " is not created yet";
alSource3f(source, property, x, y, z);
}
/**
* Set a source property
* @param property One of
* <ul>
* <li>
* <ul>AL_SOURCE_RELATIVE</ul>
* <ul>AL_CONE_INNER_ANGLE</ul>
* <ul>AL_CONE_OUTER_ANGLE</ul>
* <ul>AL_LOOPING</ul>
* <ul>AL_BUFFER</ul>
* <ul>AL_SOURCE_STATE</ul>
* </ul>
*/
public void set(int property, int value) {
assert isCreated() : this + " is not created yet";
alSourcei(source, property, value);
}
/**
* @see com.shavenpuppy.jglib.Resource#doCreate()
*/
@Override
protected void doCreate() {
if (!org.lwjgl.openal.AL.isCreated()) {
return;
}
// Generate a buffer
source = alGenSources();
createdSources.add(this);
}
/**
* @see com.shavenpuppy.jglib.Resource#doDestroy()
*/
@Override
protected void doDestroy() {
if (source != 0) {
unattach();
alDeleteSources(source);
source = 0;
createdSources.remove(this);
}
}
/**
* @return the source ID
*/
public int getSourceID() {
assert isCreated() : this + " is not created yet";
return source;
}
/**
* Attach a buffer to this source.
* @param buffer The buffer to attach
*/
public void attach(ALBufferID buffer) {
assert isCreated() : this + " is not created yet";
try {
unattach();
set(AL_BUFFER, buffer.getBufferID());
attachedBuffer = buffer;
} catch (OpenALException e) {
System.err.println("Failed: "+buffer.getBufferID()+" source: "+source);
throw e;
}
if (buffer.isLooped()) {
set(AL_LOOPING, AL_TRUE);
} else {
set(AL_LOOPING, AL_FALSE);
}
}
/**
* Queue a buffer on this source. Use this to stream buffers to a source.
* @param buffer The buffer to queue
*/
public void queue(ALBufferID buffer) {
assert isCreated() : this + " is not created yet";
if (attachedBuffer != null) {
attachedBuffer = null;
stop();
}
try {
alSourceQueueBuffers(source, buffer.getBufferID());
} catch (OpenALException e) {
System.out.println("Failed to queue "+buffer+" on "+this);
throw e;
}
set(AL_LOOPING, AL_FALSE);
}
/**
* Deueue a buffer on this source. Use this to stream buffers to a source.
*/
public void dequeue(int num) {
assert isCreated() : this + " is not created yet";
if (num <= 0 || num > AL.scratch.ints.capacity()) {
throw new OpenALException("Can't dequeue "+num+" buffers");
}
AL.scratch.ints.clear();
AL.scratch.ints.limit(num);
alSourceUnqueueBuffers(source, AL.scratch.ints);
}
/**
* Unattach any attached buffer(s)
*/
public void unattach() {
stop();
if (attachedBuffer != null) {
set(AL_BUFFER, 0);
attachedBuffer = null;
}
}
/**
* Make this sound source looped or not
* @param looped Whether to loop or not
*/
public void setLooped(boolean looped) {
set(AL_LOOPING, looped ? AL_TRUE : AL_FALSE);
}
@Override
public String toString() {
return "ALSource["+source+"]";
}
/**
* Get a source integer parameter (from alGetSourcei)
* @param param The parameter
* @return the source integer parameter
*/
public int getInt(int param) {
assert isCreated() : this + " is not created yet";
return alGetSourcei(source, param);
}
/**
* Get a source float parameter (from alGetSourcef)
* @param param The parameter
* @return the source integer parameter
*/
public float getFloat(int param) {
assert isCreated() : this + " is not created yet";
return alGetSourcef(source, param);
}
/**
* Get a source vector parameter (from alGetSourcefv)
* @param param The parameter
* @param vec The destination vector, or null if you want a new one
* @return vec, or a new vector3f.
*/
public Vector3f getFloats(int param, Vector3f ret) {
assert isCreated() : this + " is not created yet";
AL.scratch.floats.clear();
alGetSource(source, param, AL.scratch.floats);
if (ret == null) {
ret = new Vector3f();
}
ret.x = AL.scratch.floats.get(0);
ret.y = AL.scratch.floats.get(1);
ret.z = AL.scratch.floats.get(2);
return ret;
}
}