
de.lessvoid.nifty.sound.openal.slick.OpenALStreamPlayer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nifty-openal-soundsystem Show documentation
Show all versions of nifty-openal-soundsystem Show documentation
This is an OpenAL/lwjgl based SoundDevice for Nifty GUI. It was extracted out of Slick2D to be
independent of the whole Slick2D library and to be used with none Slick2D based renderes. All credits for the
original code go to Kevin Glass and all Slick2D contributers.
The newest version!
package de.lessvoid.nifty.sound.openal.slick;
import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
import org.lwjgl.BufferUtils;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.AL11;
import org.lwjgl.openal.OpenALException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.logging.Logger;
/**
* A generic tool to work on a supplied stream, pulling out PCM data and buffered it to OpenAL
* as required.
*
* @author Kevin Glass
* @author Nathan Sweet
* @author Rockstar play and setPosition cleanup
*/
public class OpenALStreamPlayer {
private final Logger log = Logger.getLogger(OpenALStreamPlayer.class.getName());
/**
* The number of buffers to maintain
*/
public static final int BUFFER_COUNT = 3;
/**
* The size of the sections to stream from the stream
*/
private static final int sectionSize = 4096 * 20;
/**
* The buffer read from the data stream
*/
@Nonnull
private final byte[] buffer = new byte[sectionSize];
/**
* Holds the OpenAL buffer names
*/
private final IntBuffer bufferNames;
/**
* The byte buffer passed to OpenAL containing the section
*/
private final ByteBuffer bufferData = BufferUtils.createByteBuffer(sectionSize);
/**
* The buffer holding the names of the OpenAL buffer thats been fully played back
*/
private final IntBuffer unqueued = BufferUtils.createIntBuffer(1);
/**
* The source we're playing back on
*/
private final int source;
/**
* The number of buffers remaining
*/
private int remainingBufferCount;
/**
* True if we should loop the track
*/
private boolean loop;
/**
* True if we've completed play back
*/
private boolean done = true;
/**
* The stream we're currently reading from
*/
@Nullable
private AudioInputStream audio;
/**
* The source of the data
*/
private String ref;
/**
* The source of the data
*/
private URL url;
/**
* The pitch of the music
*/
private float pitch;
/**
* Position in seconds of the previously played buffers
*/
private float positionOffset;
private final NiftyResourceLoader resourceLoader;
/**
* Create a new player to work on an audio stream
*
* @param source The source on which we'll play the audio
* @param ref A reference to the audio file to stream
*/
public OpenALStreamPlayer(int source, String ref, NiftyResourceLoader resourceLoader) {
this.source = source;
this.ref = ref;
this.resourceLoader = resourceLoader;
bufferNames = BufferUtils.createIntBuffer(BUFFER_COUNT);
AL10.alGenBuffers(bufferNames);
}
/**
* Create a new player to work on an audio stream
*
* @param source The source on which we'll play the audio
* @param url A reference to the audio file to stream
*/
public OpenALStreamPlayer(int source, URL url, NiftyResourceLoader resourceLoader) {
this.source = source;
this.url = url;
this.resourceLoader = resourceLoader;
bufferNames = BufferUtils.createIntBuffer(BUFFER_COUNT);
AL10.alGenBuffers(bufferNames);
}
/**
* Initialise our connection to the underlying resource
*
* @throws IOException Indicates a failure to open the underling resource
*/
private void initStreams() throws IOException {
if (audio != null) {
audio.close();
}
OggInputStream audio;
InputStream in;
if (url != null) {
in = url.openStream();
} else {
in = resourceLoader.getResourceAsStream(ref);
}
if (in != null) {
audio = new OggInputStream(in);
this.audio = audio;
positionOffset = 0;
} else {
throw new IOException("Input not found.");
}
}
/**
* Get the source of this stream
*
* @return The name of the source of string
*/
public String getSource() {
return (url == null) ? ref : url.toString();
}
/**
* Clean up the buffers applied to the sound source
*/
private void removeBuffers() {
IntBuffer buffer = BufferUtils.createIntBuffer(1);
int queued = AL10.alGetSourcei(source, AL10.AL_BUFFERS_QUEUED);
while (queued > 0) {
AL10.alSourceUnqueueBuffers(source, buffer);
queued--;
}
}
/**
* Start this stream playing
*
* @param loop True if the stream should loop
* @throws IOException Indicates a failure to read from the stream
*/
public void play(boolean loop) throws IOException {
this.loop = loop;
initStreams();
done = false;
AL10.alSourceStop(source);
removeBuffers();
startPlayback();
}
/**
* Setup the playback properties
*
* @param pitch The pitch to play back at
*/
public void setup(float pitch) {
this.pitch = pitch;
}
/**
* Check if the playback is complete. Note this will never
* return true if we're looping
*
* @return True if we're looping
*/
public boolean done() {
return done;
}
/**
* Poll the bufferNames - check if we need to fill the bufferNames with another
* section.
*
* Most of the time this should be reasonably quick
*/
public void update() {
if (done || audio == null) {
return;
}
float sampleRate = audio.getRate();
float sampleSize;
if (audio.getChannels() > 1) {
sampleSize = 4; // AL10.AL_FORMAT_STEREO16
} else {
sampleSize = 2; // AL10.AL_FORMAT_MONO16
}
int processed = AL10.alGetSourcei(source, AL10.AL_BUFFERS_PROCESSED);
while (processed > 0) {
unqueued.clear();
AL10.alSourceUnqueueBuffers(source, unqueued);
int bufferIndex = unqueued.get(0);
float bufferLength = (AL10.alGetBufferi(bufferIndex, AL10.AL_SIZE) / sampleSize) / sampleRate;
positionOffset += bufferLength;
if (stream(bufferIndex)) {
AL10.alSourceQueueBuffers(source, unqueued);
} else {
remainingBufferCount--;
if (remainingBufferCount == 0) {
done = true;
}
}
processed--;
}
int state = AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE);
if (state != AL10.AL_PLAYING) {
AL10.alSourcePlay(source);
}
}
/**
* Stream some data from the audio stream to the buffer indicates by the ID
*
* @param bufferId The ID of the buffer to fill
* @return True if another section was available
*/
public boolean stream(int bufferId) {
if (audio == null) {
return false;
}
try {
int count = audio.read(buffer);
if (count != -1) {
bufferData.clear();
bufferData.put(buffer, 0, count);
bufferData.flip();
int format = audio.getChannels() > 1 ? AL10.AL_FORMAT_STEREO16 : AL10.AL_FORMAT_MONO16;
try {
AL10.alBufferData(bufferId, format, bufferData, audio.getRate());
} catch (OpenALException e) {
log.warning("Failed to loop buffer: " + bufferId + " " + format + " " + count + " " + audio.getRate() + e
.toString());
return false;
}
} else {
if (loop) {
initStreams();
stream(bufferId);
} else {
done = true;
return false;
}
}
return true;
} catch (IOException e) {
log.warning(e.toString());
return false;
}
}
/**
* Seeks to a position in the music.
*
* @param position Position in seconds.
* @return True if the setting of the position was successful
*/
public boolean setPosition(float position) {
if (audio == null) {
return false;
}
try {
if (getPosition() > position) {
initStreams();
}
float sampleRate = audio.getRate();
float sampleSize;
if (audio.getChannels() > 1) {
sampleSize = 4; // AL10.AL_FORMAT_STEREO16
} else {
sampleSize = 2; // AL10.AL_FORMAT_MONO16
}
while (positionOffset < position) {
int count = audio.read(buffer);
if (count != -1) {
float bufferLength = (count / sampleSize) / sampleRate;
positionOffset += bufferLength;
} else {
if (loop) {
initStreams();
} else {
done = true;
}
return false;
}
}
startPlayback();
return true;
} catch (IOException e) {
log.warning(e.toString());
return false;
}
}
/**
* Starts the streaming.
*/
private void startPlayback() {
AL10.alSourcei(source, AL10.AL_LOOPING, AL10.AL_FALSE);
AL10.alSourcef(source, AL10.AL_PITCH, pitch);
remainingBufferCount = BUFFER_COUNT;
for (int i = 0; i < BUFFER_COUNT; i++) {
stream(bufferNames.get(i));
}
AL10.alSourceQueueBuffers(source, bufferNames);
AL10.alSourcePlay(source);
}
/**
* Return the current playing position in the sound
*
* @return The current position in seconds.
*/
public float getPosition() {
return positionOffset + AL10.alGetSourcef(source, AL11.AL_SEC_OFFSET);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy