mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-13 03:54:06 +09:00
added sources for Slick
Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054 Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
This commit is contained in:
268
lib/slick-source/org/newdawn/slick/openal/AiffData.java
Normal file
268
lib/slick-source/org/newdawn/slick/openal/AiffData.java
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2004 LWJGL Project
|
||||
* 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 'LWJGL' 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 org.newdawn.slick.openal;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.AudioFormat.Encoding;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
/**
|
||||
*
|
||||
* Utitlity class for loading wavefiles.
|
||||
*
|
||||
* @author Brian Matzon <brian@matzon.dk>
|
||||
* @version $Revision: 2286 $
|
||||
*/
|
||||
public class AiffData {
|
||||
/** actual AIFF data */
|
||||
public final ByteBuffer data;
|
||||
|
||||
/** format type of data */
|
||||
public final int format;
|
||||
|
||||
/** sample rate of data */
|
||||
public final int samplerate;
|
||||
|
||||
/**
|
||||
* Creates a new AiffData
|
||||
*
|
||||
* @param data actual Aiffdata
|
||||
* @param format format of Aiff data
|
||||
* @param samplerate sample rate of data
|
||||
*/
|
||||
private AiffData(ByteBuffer data, int format, int samplerate) {
|
||||
this.data = data;
|
||||
this.format = format;
|
||||
this.samplerate = samplerate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the Aiffdata
|
||||
*/
|
||||
public void dispose() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a AiffData container from the specified url
|
||||
*
|
||||
* @param path URL to file
|
||||
* @return AiffData containing data, or null if a failure occured
|
||||
*/
|
||||
public static AiffData create(URL path) {
|
||||
try {
|
||||
return create(
|
||||
AudioSystem.getAudioInputStream(
|
||||
new BufferedInputStream(path.openStream())));
|
||||
} catch (Exception e) {
|
||||
org.lwjgl.LWJGLUtil.log("Unable to create from: " + path);
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a AiffData container from the specified in the classpath
|
||||
*
|
||||
* @param path path to file (relative, and in classpath)
|
||||
* @return AiffData containing data, or null if a failure occured
|
||||
*/
|
||||
public static AiffData create(String path) {
|
||||
return create(AiffData.class.getClassLoader().getResource(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a AiffData container from the specified inputstream
|
||||
*
|
||||
* @param is InputStream to read from
|
||||
* @return AiffData containing data, or null if a failure occured
|
||||
*/
|
||||
public static AiffData create(InputStream is) {
|
||||
try {
|
||||
return create(
|
||||
AudioSystem.getAudioInputStream(is));
|
||||
} catch (Exception e) {
|
||||
org.lwjgl.LWJGLUtil.log("Unable to create from inputstream");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a AiffData container from the specified bytes
|
||||
*
|
||||
* @param buffer array of bytes containing the complete Aiff file
|
||||
* @return AiffData containing data, or null if a failure occured
|
||||
*/
|
||||
public static AiffData create(byte[] buffer) {
|
||||
try {
|
||||
return create(
|
||||
AudioSystem.getAudioInputStream(
|
||||
new BufferedInputStream(new ByteArrayInputStream(buffer))));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a AiffData container from the specified ByetBuffer.
|
||||
* If the buffer is backed by an array, it will be used directly,
|
||||
* else the contents of the buffer will be copied using get(byte[]).
|
||||
*
|
||||
* @param buffer ByteBuffer containing sound file
|
||||
* @return AiffData containing data, or null if a failure occured
|
||||
*/
|
||||
public static AiffData create(ByteBuffer buffer) {
|
||||
try {
|
||||
byte[] bytes = null;
|
||||
|
||||
if(buffer.hasArray()) {
|
||||
bytes = buffer.array();
|
||||
} else {
|
||||
bytes = new byte[buffer.capacity()];
|
||||
buffer.get(bytes);
|
||||
}
|
||||
return create(bytes);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a AiffData container from the specified stream
|
||||
*
|
||||
* @param ais AudioInputStream to read from
|
||||
* @return AiffData containing data, or null if a failure occured
|
||||
*/
|
||||
public static AiffData create(AudioInputStream ais) {
|
||||
//get format of data
|
||||
AudioFormat audioformat = ais.getFormat();
|
||||
|
||||
// get channels
|
||||
int channels = 0;
|
||||
if (audioformat.getChannels() == 1) {
|
||||
if (audioformat.getSampleSizeInBits() == 8) {
|
||||
channels = AL10.AL_FORMAT_MONO8;
|
||||
} else if (audioformat.getSampleSizeInBits() == 16) {
|
||||
channels = AL10.AL_FORMAT_MONO16;
|
||||
} else {
|
||||
throw new RuntimeException("Illegal sample size");
|
||||
}
|
||||
} else if (audioformat.getChannels() == 2) {
|
||||
if (audioformat.getSampleSizeInBits() == 8) {
|
||||
channels = AL10.AL_FORMAT_STEREO8;
|
||||
} else if (audioformat.getSampleSizeInBits() == 16) {
|
||||
channels = AL10.AL_FORMAT_STEREO16;
|
||||
} else {
|
||||
throw new RuntimeException("Illegal sample size");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Only mono or stereo is supported");
|
||||
}
|
||||
|
||||
//read data into buffer
|
||||
byte[] buf =
|
||||
new byte[audioformat.getChannels()
|
||||
* (int) ais.getFrameLength()
|
||||
* audioformat.getSampleSizeInBits()
|
||||
/ 8];
|
||||
int read = 0, total = 0;
|
||||
try {
|
||||
while ((read = ais.read(buf, total, buf.length - total)) != -1
|
||||
&& total < buf.length) {
|
||||
total += read;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//insert data into bytebuffer
|
||||
ByteBuffer buffer = convertAudioBytes(audioformat, buf, audioformat.getSampleSizeInBits() == 16);
|
||||
|
||||
//create our result
|
||||
AiffData Aiffdata =
|
||||
new AiffData(buffer, channels, (int) audioformat.getSampleRate());
|
||||
|
||||
//close stream
|
||||
try {
|
||||
ais.close();
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
|
||||
return Aiffdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the audio bytes into the stream
|
||||
*
|
||||
* @param format The audio format being decoded
|
||||
* @param audio_bytes The audio byts
|
||||
* @param two_bytes_data True if we using double byte data
|
||||
* @return The byte bufer of data
|
||||
*/
|
||||
private static ByteBuffer convertAudioBytes(AudioFormat format, byte[] audio_bytes, boolean two_bytes_data) {
|
||||
ByteBuffer dest = ByteBuffer.allocateDirect(audio_bytes.length);
|
||||
dest.order(ByteOrder.nativeOrder());
|
||||
ByteBuffer src = ByteBuffer.wrap(audio_bytes);
|
||||
src.order(ByteOrder.BIG_ENDIAN);
|
||||
if (two_bytes_data) {
|
||||
ShortBuffer dest_short = dest.asShortBuffer();
|
||||
ShortBuffer src_short = src.asShortBuffer();
|
||||
while (src_short.hasRemaining())
|
||||
dest_short.put(src_short.get());
|
||||
} else {
|
||||
while (src.hasRemaining()) {
|
||||
byte b = src.get();
|
||||
if (format.getEncoding() == Encoding.PCM_SIGNED) {
|
||||
b = (byte) (b + 127);
|
||||
}
|
||||
dest.put(b);
|
||||
}
|
||||
}
|
||||
dest.rewind();
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
79
lib/slick-source/org/newdawn/slick/openal/Audio.java
Normal file
79
lib/slick-source/org/newdawn/slick/openal/Audio.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
/**
|
||||
* The description of of audio data loaded by the AudioLoader
|
||||
*
|
||||
* @author kevin
|
||||
* @author Nathan Sweet <misc@n4te.com>
|
||||
*/
|
||||
public interface Audio {
|
||||
|
||||
/**
|
||||
* Stop the sound effect
|
||||
*/
|
||||
public void stop();
|
||||
|
||||
/**
|
||||
* Get the ID of the OpenAL buffer holding this data (if any). This method
|
||||
* is not valid with streaming resources.
|
||||
*
|
||||
* @return The ID of the OpenAL buffer holding this data
|
||||
*/
|
||||
public int getBufferID();
|
||||
|
||||
/**
|
||||
* Check if the sound is playing as sound fx
|
||||
*
|
||||
* @return True if the sound is playing
|
||||
*/
|
||||
public boolean isPlaying();
|
||||
|
||||
/**
|
||||
* Play this sound as a sound effect
|
||||
*
|
||||
* @param pitch The pitch of the play back
|
||||
* @param gain The gain of the play back
|
||||
* @param loop True if we should loop
|
||||
* @return The ID of the source playing the sound
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop);
|
||||
|
||||
/**
|
||||
* Play this sound as a sound effect
|
||||
*
|
||||
* @param pitch The pitch of the play back
|
||||
* @param gain The gain of the play back
|
||||
* @param loop True if we should loop
|
||||
* @param x The x position of the sound
|
||||
* @param y The y position of the sound
|
||||
* @param z The z position of the sound
|
||||
* @return The ID of the source playing the sound
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop,
|
||||
float x, float y, float z);
|
||||
|
||||
/**
|
||||
* Play this sound as music
|
||||
*
|
||||
* @param pitch The pitch of the play back
|
||||
* @param gain The gain of the play back
|
||||
* @param loop True if we should loop
|
||||
* @return The ID of the source playing the sound
|
||||
*/
|
||||
public int playAsMusic(float pitch, float gain, boolean loop);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Return the current playing position in the sound
|
||||
*
|
||||
* @return The current position in seconds.
|
||||
*/
|
||||
public float getPosition();
|
||||
}
|
||||
139
lib/slick-source/org/newdawn/slick/openal/AudioImpl.java
Normal file
139
lib/slick-source/org/newdawn/slick/openal/AudioImpl.java
Normal file
@@ -0,0 +1,139 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.openal.AL11;
|
||||
|
||||
/**
|
||||
* A sound that can be played through OpenAL
|
||||
*
|
||||
* @author Kevin Glass
|
||||
* @author Nathan Sweet <misc@n4te.com>
|
||||
*/
|
||||
public class AudioImpl implements Audio {
|
||||
/** The store from which this sound was loaded */
|
||||
private SoundStore store;
|
||||
/** The buffer containing the sound */
|
||||
private int buffer;
|
||||
/** The index of the source being used to play this sound */
|
||||
private int index = -1;
|
||||
|
||||
/** The length of the audio */
|
||||
private float length;
|
||||
|
||||
/**
|
||||
* Create a new sound
|
||||
*
|
||||
* @param store The sound store from which the sound was created
|
||||
* @param buffer The buffer containing the sound data
|
||||
*/
|
||||
AudioImpl(SoundStore store, int buffer) {
|
||||
this.store = store;
|
||||
this.buffer = buffer;
|
||||
|
||||
int bytes = AL10.alGetBufferi(buffer, AL10.AL_SIZE);
|
||||
int bits = AL10.alGetBufferi(buffer, AL10.AL_BITS);
|
||||
int channels = AL10.alGetBufferi(buffer, AL10.AL_CHANNELS);
|
||||
int freq = AL10.alGetBufferi(buffer, AL10.AL_FREQUENCY);
|
||||
|
||||
int samples = bytes / (bits / 8);
|
||||
length = (samples / (float) freq) / channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of the OpenAL buffer holding this data (if any). This method
|
||||
* is not valid with streaming resources.
|
||||
*
|
||||
* @return The ID of the OpenAL buffer holding this data
|
||||
*/
|
||||
public int getBufferID() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected AudioImpl() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#stop()
|
||||
*/
|
||||
public void stop() {
|
||||
if (index != -1) {
|
||||
store.stopSource(index);
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#isPlaying()
|
||||
*/
|
||||
public boolean isPlaying() {
|
||||
if (index != -1) {
|
||||
return store.isPlaying(index);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#playAsSoundEffect(float, float, boolean)
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop) {
|
||||
index = store.playAsSound(buffer, pitch, gain, loop);
|
||||
return store.getSource(index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#playAsSoundEffect(float, float, boolean, float, float, float)
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop, float x, float y, float z) {
|
||||
index = store.playAsSoundAt(buffer, pitch, gain, loop, x, y, z);
|
||||
return store.getSource(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#playAsMusic(float, float, boolean)
|
||||
*/
|
||||
public int playAsMusic(float pitch, float gain, boolean loop) {
|
||||
store.playAsMusic(buffer, pitch, gain, loop);
|
||||
index = 0;
|
||||
return store.getSource(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the music currently being played
|
||||
*/
|
||||
public static void pauseMusic() {
|
||||
SoundStore.get().pauseLoop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the music currently being paused
|
||||
*/
|
||||
public static void restartMusic() {
|
||||
SoundStore.get().restartLoop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#setPosition(float)
|
||||
*/
|
||||
public boolean setPosition(float position) {
|
||||
position = position % length;
|
||||
|
||||
AL10.alSourcef(store.getSource(index), AL11.AL_SEC_OFFSET, position);
|
||||
if (AL10.alGetError() != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#getPosition()
|
||||
*/
|
||||
public float getPosition() {
|
||||
return AL10.alGetSourcef(store.getSource(index), AL11.AL_SEC_OFFSET);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The description of an input stream that supplied audio data suitable for
|
||||
* use in OpenAL buffers
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
interface AudioInputStream {
|
||||
/**
|
||||
* Get the number of channels used by the audio
|
||||
*
|
||||
* @return The number of channels used by the audio
|
||||
*/
|
||||
public int getChannels();
|
||||
|
||||
/**
|
||||
* The play back rate described in the underling audio file
|
||||
*
|
||||
* @return The playback rate
|
||||
*/
|
||||
public int getRate();
|
||||
|
||||
/**
|
||||
* Read a single byte from the stream
|
||||
*
|
||||
* @return The single byte read
|
||||
* @throws IOException Indicates a failure to read the underlying media
|
||||
* @see java.io.InputStream#read()
|
||||
*/
|
||||
public int read() throws IOException;
|
||||
|
||||
/**
|
||||
* Read up to data.length bytes from the stream
|
||||
*
|
||||
* @param data The array to read into
|
||||
* @return The number of bytes read or -1 to indicate no more bytes are available
|
||||
* @throws IOException Indicates a failure to read the underlying media
|
||||
* @see java.io.InputStream#read(byte[])
|
||||
*/
|
||||
public int read(byte[] data) throws IOException;
|
||||
|
||||
/**
|
||||
* Read up to len bytes from the stream
|
||||
*
|
||||
* @param data The array to read into
|
||||
* @param ofs The offset into the array at which to start writing
|
||||
* @param len The maximum number of bytes to read
|
||||
* @return The number of bytes read or -1 to indicate no more bytes are available
|
||||
* @throws IOException Indicates a failure to read the underlying media
|
||||
* @see java.io.InputStream#read(byte[], int, int)
|
||||
*/
|
||||
public int read(byte[] data, int ofs, int len) throws IOException;
|
||||
|
||||
/**
|
||||
* Check if the stream is at the end, i.e. end of file or URL
|
||||
*
|
||||
* @return True if the stream has no more data available
|
||||
*/
|
||||
public boolean atEnd();
|
||||
|
||||
/**
|
||||
* Close the stream
|
||||
*
|
||||
* @see java.io.InputStream#close()
|
||||
* @throws IOException Indicates a failure to access the resource
|
||||
*/
|
||||
public void close() throws IOException;
|
||||
}
|
||||
96
lib/slick-source/org/newdawn/slick/openal/AudioLoader.java
Normal file
96
lib/slick-source/org/newdawn/slick/openal/AudioLoader.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* A utility to provide a simple and rational interface to the
|
||||
* slick internals
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class AudioLoader {
|
||||
/** AIF Format Indicator */
|
||||
private static final String AIF = "AIF";
|
||||
/** WAV Format Indicator */
|
||||
private static final String WAV = "WAV";
|
||||
/** OGG Format Indicator */
|
||||
private static final String OGG = "OGG";
|
||||
/** MOD/XM Format Indicator */
|
||||
private static final String MOD = "MOD";
|
||||
/** MOD/XM Format Indicator */
|
||||
private static final String XM = "XM";
|
||||
|
||||
/** True if the audio loader has be initialised */
|
||||
private static boolean inited = false;
|
||||
|
||||
/**
|
||||
* Initialise the audio loader
|
||||
*/
|
||||
private static void init() {
|
||||
if (!inited) {
|
||||
SoundStore.get().init();
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get audio data in a playable state by loading the complete audio into
|
||||
* memory.
|
||||
*
|
||||
* @param format The format of the audio to be loaded (something like "XM" or "OGG")
|
||||
* @param in The input stream from which to load the audio data
|
||||
* @return An object representing the audio data
|
||||
* @throws IOException Indicates a failure to access the audio data
|
||||
*/
|
||||
public static Audio getAudio(String format, InputStream in) throws IOException {
|
||||
init();
|
||||
|
||||
if (format.equals(AIF)) {
|
||||
return SoundStore.get().getAIF(in);
|
||||
}
|
||||
if (format.equals(WAV)) {
|
||||
return SoundStore.get().getWAV(in);
|
||||
}
|
||||
if (format.equals(OGG)) {
|
||||
return SoundStore.get().getOgg(in);
|
||||
}
|
||||
|
||||
throw new IOException("Unsupported format for non-streaming Audio: "+format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get audio data in a playable state by setting up a stream that can be piped into
|
||||
* OpenAL - i.e. streaming audio
|
||||
*
|
||||
* @param format The format of the audio to be loaded (something like "XM" or "OGG")
|
||||
* @param url The location of the data that should be streamed
|
||||
* @return An object representing the audio data
|
||||
* @throws IOException Indicates a failure to access the audio data
|
||||
*/
|
||||
public static Audio getStreamingAudio(String format, URL url) throws IOException {
|
||||
init();
|
||||
|
||||
if (format.equals(OGG)) {
|
||||
return SoundStore.get().getOggStream(url);
|
||||
}
|
||||
if (format.equals(MOD)) {
|
||||
return SoundStore.get().getMOD(url.openStream());
|
||||
}
|
||||
if (format.equals(XM)) {
|
||||
return SoundStore.get().getMOD(url.openStream());
|
||||
}
|
||||
|
||||
throw new IOException("Unsupported format for streaming Audio: "+format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the streaming system to update itself
|
||||
*/
|
||||
public static void update() {
|
||||
init();
|
||||
|
||||
SoundStore.get().poll(0);
|
||||
}
|
||||
}
|
||||
164
lib/slick-source/org/newdawn/slick/openal/DeferredSound.java
Normal file
164
lib/slick-source/org/newdawn/slick/openal/DeferredSound.java
Normal file
@@ -0,0 +1,164 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.newdawn.slick.loading.DeferredResource;
|
||||
import org.newdawn.slick.loading.LoadingList;
|
||||
import org.newdawn.slick.util.Log;
|
||||
|
||||
/**
|
||||
* A sound implementation that can load the actual sound file at a later
|
||||
* point.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class DeferredSound extends AudioImpl implements DeferredResource {
|
||||
/** Indicate a OGG to be loaded */
|
||||
public static final int OGG = 1;
|
||||
/** Indicate a WAV to be loaded */
|
||||
public static final int WAV = 2;
|
||||
/** Indicate a MOD/XM to be loaded */
|
||||
public static final int MOD = 3;
|
||||
/** Indicate a AIF to be loaded */
|
||||
public static final int AIF = 4;
|
||||
|
||||
/** The type of sound to be loader */
|
||||
private int type;
|
||||
/** The location of the sound this proxy wraps */
|
||||
private String ref;
|
||||
/** The loaded sound if it's already been brought up */
|
||||
private Audio target;
|
||||
/** The input stream to load the sound this proxy wraps from (can be null) */
|
||||
private InputStream in;
|
||||
|
||||
/**
|
||||
* Create a new sound on request to load
|
||||
*
|
||||
* @param ref The location of the sound to load
|
||||
* @param type The type of sound to load
|
||||
* @param in The input stream to load from
|
||||
*/
|
||||
public DeferredSound(String ref, InputStream in, int type) {
|
||||
this.ref = ref;
|
||||
this.type = type;
|
||||
|
||||
// nasty hack to detect when we're loading from a stream
|
||||
if (ref.equals(in.toString())) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
LoadingList.get().add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the target has already been loaded
|
||||
*/
|
||||
private void checkTarget() {
|
||||
if (target == null) {
|
||||
throw new RuntimeException("Attempt to use deferred sound before loading");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.loading.DeferredResource#load()
|
||||
*/
|
||||
public void load() throws IOException {
|
||||
boolean before = SoundStore.get().isDeferredLoading();
|
||||
SoundStore.get().setDeferredLoading(false);
|
||||
if (in != null) {
|
||||
switch (type) {
|
||||
case OGG:
|
||||
target = SoundStore.get().getOgg(in);
|
||||
break;
|
||||
case WAV:
|
||||
target = SoundStore.get().getWAV(in);
|
||||
break;
|
||||
case MOD:
|
||||
target = SoundStore.get().getMOD(in);
|
||||
break;
|
||||
case AIF:
|
||||
target = SoundStore.get().getAIF(in);
|
||||
break;
|
||||
default:
|
||||
Log.error("Unrecognised sound type: "+type);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case OGG:
|
||||
target = SoundStore.get().getOgg(ref);
|
||||
break;
|
||||
case WAV:
|
||||
target = SoundStore.get().getWAV(ref);
|
||||
break;
|
||||
case MOD:
|
||||
target = SoundStore.get().getMOD(ref);
|
||||
break;
|
||||
case AIF:
|
||||
target = SoundStore.get().getAIF(ref);
|
||||
break;
|
||||
default:
|
||||
Log.error("Unrecognised sound type: "+type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SoundStore.get().setDeferredLoading(before);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#isPlaying()
|
||||
*/
|
||||
public boolean isPlaying() {
|
||||
checkTarget();
|
||||
|
||||
return target.isPlaying();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#playAsMusic(float, float, boolean)
|
||||
*/
|
||||
public int playAsMusic(float pitch, float gain, boolean loop) {
|
||||
checkTarget();
|
||||
return target.playAsMusic(pitch, gain, loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#playAsSoundEffect(float, float, boolean)
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop) {
|
||||
checkTarget();
|
||||
return target.playAsSoundEffect(pitch, gain, loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play this sound as a sound effect
|
||||
*
|
||||
* @param pitch The pitch of the play back
|
||||
* @param gain The gain of the play back
|
||||
* @param loop True if we should loop
|
||||
* @param x The x position of the sound
|
||||
* @param y The y position of the sound
|
||||
* @param z The z position of the sound
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop, float x, float y, float z) {
|
||||
checkTarget();
|
||||
return target.playAsSoundEffect(pitch, gain, loop, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#stop()
|
||||
*/
|
||||
public void stop() {
|
||||
checkTarget();
|
||||
target.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.loading.DeferredResource#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
}
|
||||
105
lib/slick-source/org/newdawn/slick/openal/MODSound.java
Normal file
105
lib/slick-source/org/newdawn/slick/openal/MODSound.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import ibxm.Module;
|
||||
import ibxm.OpenALMODPlayer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
/**
|
||||
* A sound as a MOD file - can only be played as music
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public class MODSound extends AudioImpl {
|
||||
/** The MOD play back system */
|
||||
private static OpenALMODPlayer player = new OpenALMODPlayer();
|
||||
|
||||
/** The module to play back */
|
||||
private Module module;
|
||||
/** The sound store this belongs to */
|
||||
private SoundStore store;
|
||||
|
||||
/**
|
||||
* Create a mod sound to be played back
|
||||
*
|
||||
* @param store The store this sound belongs to
|
||||
* @param in The input stream to read the data from
|
||||
* @throws IOException Indicates a failure to load a sound
|
||||
*/
|
||||
public MODSound(SoundStore store, InputStream in) throws IOException {
|
||||
this.store = store;
|
||||
module = OpenALMODPlayer.loadModule(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#playAsMusic(float, float, boolean)
|
||||
*/
|
||||
public int playAsMusic(float pitch, float gain, boolean loop) {
|
||||
cleanUpSource();
|
||||
|
||||
player.play(module, store.getSource(0), loop, SoundStore.get().isMusicOn());
|
||||
player.setup(pitch, 1.0f);
|
||||
store.setCurrentMusicVolume(gain);
|
||||
|
||||
store.setMOD(this);
|
||||
|
||||
return store.getSource(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the buffers applied to the sound source
|
||||
*/
|
||||
private void cleanUpSource() {
|
||||
AL10.alSourceStop(store.getSource(0));
|
||||
IntBuffer buffer = BufferUtils.createIntBuffer(1);
|
||||
int queued = AL10.alGetSourcei(store.getSource(0), AL10.AL_BUFFERS_QUEUED);
|
||||
|
||||
while (queued > 0)
|
||||
{
|
||||
AL10.alSourceUnqueueBuffers(store.getSource(0), buffer);
|
||||
queued--;
|
||||
}
|
||||
|
||||
AL10.alSourcei(store.getSource(0), AL10.AL_BUFFER, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the streaming on the MOD
|
||||
*/
|
||||
public void poll() {
|
||||
player.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#playAsSoundEffect(float, float, boolean)
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#stop()
|
||||
*/
|
||||
public void stop() {
|
||||
store.setMOD(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#getPosition()
|
||||
*/
|
||||
public float getPosition() {
|
||||
throw new RuntimeException("Positioning on modules is not currently supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#setPosition(float)
|
||||
*/
|
||||
public boolean setPosition(float position) {
|
||||
throw new RuntimeException("Positioning on modules is not currently supported");
|
||||
}
|
||||
}
|
||||
66
lib/slick-source/org/newdawn/slick/openal/NullAudio.java
Normal file
66
lib/slick-source/org/newdawn/slick/openal/NullAudio.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
/**
|
||||
* A null implementation used to provide an object reference when sound
|
||||
* has failed.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class NullAudio implements Audio {
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#getBufferID()
|
||||
*/
|
||||
public int getBufferID() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#getPosition()
|
||||
*/
|
||||
public float getPosition() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#isPlaying()
|
||||
*/
|
||||
public boolean isPlaying() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#playAsMusic(float, float, boolean)
|
||||
*/
|
||||
public int playAsMusic(float pitch, float gain, boolean loop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#playAsSoundEffect(float, float, boolean)
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#playAsSoundEffect(float, float, boolean, float, float, float)
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop,
|
||||
float x, float y, float z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#setPosition(float)
|
||||
*/
|
||||
public boolean setPosition(float position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.Audio#stop()
|
||||
*/
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
}
|
||||
17
lib/slick-source/org/newdawn/slick/openal/OggData.java
Normal file
17
lib/slick-source/org/newdawn/slick/openal/OggData.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Data describing the sounds in a OGG file
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public class OggData {
|
||||
/** The data that has been read from the OGG file */
|
||||
public ByteBuffer data;
|
||||
/** The sampling rate */
|
||||
public int rate;
|
||||
/** The number of channels in the sound file */
|
||||
public int channels;
|
||||
}
|
||||
329
lib/slick-source/org/newdawn/slick/openal/OggDecoder.java
Normal file
329
lib/slick-source/org/newdawn/slick/openal/OggDecoder.java
Normal file
@@ -0,0 +1,329 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Decode an OGG file to PCM data
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public class OggDecoder {
|
||||
/** The conversion buffer size */
|
||||
private int convsize = 4096 * 4;
|
||||
/** The buffer used to read OGG file */
|
||||
private byte[] convbuffer = new byte[convsize]; // take 8k out of the data segment, not the stack
|
||||
|
||||
/**
|
||||
* Create a new OGG decoder
|
||||
*/
|
||||
public OggDecoder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data out of an OGG file
|
||||
*
|
||||
* @param input The input stream from which to read the OGG file
|
||||
* @return The data describing the OGG thats been read
|
||||
* @throws IOException Indicaites a failure to read the OGG file
|
||||
*/
|
||||
public OggData getData(InputStream input) throws IOException {
|
||||
if (input == null) {
|
||||
throw new IOException("Failed to read OGG, source does not exist?");
|
||||
}
|
||||
ByteArrayOutputStream dataout = new ByteArrayOutputStream();
|
||||
|
||||
// SyncState oy = new SyncState(); // sync and verify incoming physical bitstream
|
||||
// StreamState os = new StreamState(); // take physical pages, weld into a logical stream of packets
|
||||
// Page og = new Page(); // one Ogg bitstream page. Vorbis packets are inside
|
||||
// Packet op = new Packet(); // one raw packet of data for decode
|
||||
//
|
||||
// Info vi = new Info(); // struct that stores all the static vorbis bitstream settings
|
||||
// Comment vc = new Comment(); // struct that stores all the bitstream user comments
|
||||
// DspState vd = new DspState(); // central working state for the packet->PCM decoder
|
||||
// Block vb = new Block(vd); // local working space for packet->PCM decode
|
||||
//
|
||||
// byte[] buffer;
|
||||
// int bytes = 0;
|
||||
//
|
||||
// boolean bigEndian = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
|
||||
// // Decode setup
|
||||
//
|
||||
// oy.init(); // Now we can read pages
|
||||
//
|
||||
// while (true) { // we repeat if the bitstream is chained
|
||||
// int eos = 0;
|
||||
//
|
||||
// // grab some data at the head of the stream. We want the first page
|
||||
// // (which is guaranteed to be small and only contain the Vorbis
|
||||
// // stream initial header) We need the first page to get the stream
|
||||
// // serialno.
|
||||
//
|
||||
// // submit a 4k block to libvorbis' Ogg layer
|
||||
// int index = oy.buffer(4096);
|
||||
//
|
||||
// buffer = oy.data;
|
||||
// try {
|
||||
// bytes = input.read(buffer, index, 4096);
|
||||
// } catch (Exception e) {
|
||||
// Log.error("Failure reading in vorbis");
|
||||
// Log.error(e);
|
||||
// System.exit(0);
|
||||
// }
|
||||
// oy.wrote(bytes);
|
||||
//
|
||||
// // Get the first page.
|
||||
// if (oy.pageout(og) != 1) {
|
||||
// // have we simply run out of data? If so, we're done.
|
||||
// if (bytes < 4096)
|
||||
// break;
|
||||
//
|
||||
// // error case. Must not be Vorbis data
|
||||
// Log.error("Input does not appear to be an Ogg bitstream.");
|
||||
// System.exit(0);
|
||||
// }
|
||||
//
|
||||
// // Get the serial number and set up the rest of decode.
|
||||
// // serialno first; use it to set up a logical stream
|
||||
// os.init(og.serialno());
|
||||
//
|
||||
// // extract the initial header from the first page and verify that the
|
||||
// // Ogg bitstream is in fact Vorbis data
|
||||
//
|
||||
// // I handle the initial header first instead of just having the code
|
||||
// // read all three Vorbis headers at once because reading the initial
|
||||
// // header is an easy way to identify a Vorbis bitstream and it's
|
||||
// // useful to see that functionality seperated out.
|
||||
//
|
||||
// vi.init();
|
||||
// vc.init();
|
||||
// if (os.pagein(og) < 0) {
|
||||
// // error; stream version mismatch perhaps
|
||||
// Log.error("Error reading first page of Ogg bitstream data.");
|
||||
// System.exit(0);
|
||||
// }
|
||||
//
|
||||
// if (os.packetout(op) != 1) {
|
||||
// // no page? must not be vorbis
|
||||
// Log.error("Error reading initial header packet.");
|
||||
// System.exit(0);
|
||||
// }
|
||||
//
|
||||
// if (vi.synthesis_headerin(vc, op) < 0) {
|
||||
// // error case; not a vorbis header
|
||||
// Log.error("This Ogg bitstream does not contain Vorbis audio data.");
|
||||
// System.exit(0);
|
||||
// }
|
||||
//
|
||||
// // At this point, we're sure we're Vorbis. We've set up the logical
|
||||
// // (Ogg) bitstream decoder. Get the comment and codebook headers and
|
||||
// // set up the Vorbis decoder
|
||||
//
|
||||
// // The next two packets in order are the comment and codebook headers.
|
||||
// // They're likely large and may span multiple pages. Thus we reead
|
||||
// // and submit data until we get our two pacakets, watching that no
|
||||
// // pages are missing. If a page is missing, error out; losing a
|
||||
// // header page is the only place where missing data is fatal. */
|
||||
//
|
||||
// int i = 0;
|
||||
// while (i < 2) {
|
||||
// while (i < 2) {
|
||||
//
|
||||
// int result = oy.pageout(og);
|
||||
// if (result == 0)
|
||||
// break; // Need more data
|
||||
// // Don't complain about missing or corrupt data yet. We'll
|
||||
// // catch it at the packet output phase
|
||||
//
|
||||
// if (result == 1) {
|
||||
// os.pagein(og); // we can ignore any errors here
|
||||
// // as they'll also become apparent
|
||||
// // at packetout
|
||||
// while (i < 2) {
|
||||
// result = os.packetout(op);
|
||||
// if (result == 0)
|
||||
// break;
|
||||
// if (result == -1) {
|
||||
// // Uh oh; data at some point was corrupted or missing!
|
||||
// // We can't tolerate that in a header. Die.
|
||||
// Log.error("Corrupt secondary header. Exiting.");
|
||||
// System.exit(0);
|
||||
// }
|
||||
// vi.synthesis_headerin(vc, op);
|
||||
// i++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // no harm in not checking before adding more
|
||||
// index = oy.buffer(4096);
|
||||
// buffer = oy.data;
|
||||
// try {
|
||||
// bytes = input.read(buffer, index, 4096);
|
||||
// } catch (Exception e) {
|
||||
// Log.error("Failed to read Vorbis: ");
|
||||
// Log.error(e);
|
||||
// System.exit(0);
|
||||
// }
|
||||
// if (bytes == 0 && i < 2) {
|
||||
// Log.error("End of file before finding all Vorbis headers!");
|
||||
// System.exit(0);
|
||||
// }
|
||||
// oy.wrote(bytes);
|
||||
// }
|
||||
//
|
||||
// convsize = 4096 / vi.channels;
|
||||
//
|
||||
// // OK, got and parsed all three headers. Initialize the Vorbis
|
||||
// // packet->PCM decoder.
|
||||
// vd.synthesis_init(vi); // central decode state
|
||||
// vb.init(vd); // local state for most of the decode
|
||||
// // so multiple block decodes can
|
||||
// // proceed in parallel. We could init
|
||||
// // multiple vorbis_block structures
|
||||
// // for vd here
|
||||
//
|
||||
// float[][][] _pcm = new float[1][][];
|
||||
// int[] _index = new int[vi.channels];
|
||||
// // The rest is just a straight decode loop until end of stream
|
||||
// while (eos == 0) {
|
||||
// while (eos == 0) {
|
||||
//
|
||||
// int result = oy.pageout(og);
|
||||
// if (result == 0)
|
||||
// break; // need more data
|
||||
// if (result == -1) { // missing or corrupt data at this page position
|
||||
// Log.error("Corrupt or missing data in bitstream; continuing...");
|
||||
// } else {
|
||||
// os.pagein(og); // can safely ignore errors at
|
||||
// // this point
|
||||
// while (true) {
|
||||
// result = os.packetout(op);
|
||||
//
|
||||
// if (result == 0)
|
||||
// break; // need more data
|
||||
// if (result == -1) { // missing or corrupt data at this page position
|
||||
// // no reason to complain; already complained above
|
||||
// } else {
|
||||
// // we have a packet. Decode it
|
||||
// int samples;
|
||||
// if (vb.synthesis(op) == 0) { // test for success!
|
||||
// vd.synthesis_blockin(vb);
|
||||
// }
|
||||
//
|
||||
// // **pcm is a multichannel float vector. In stereo, for
|
||||
// // example, pcm[0] is left, and pcm[1] is right. samples is
|
||||
// // the size of each channel. Convert the float values
|
||||
// // (-1.<=range<=1.) to whatever PCM format and write it out
|
||||
//
|
||||
// while ((samples = vd.synthesis_pcmout(_pcm,
|
||||
// _index)) > 0) {
|
||||
// float[][] pcm = _pcm[0];
|
||||
// //boolean clipflag = false;
|
||||
// int bout = (samples < convsize ? samples
|
||||
// : convsize);
|
||||
//
|
||||
// // convert floats to 16 bit signed ints (host order) and
|
||||
// // interleave
|
||||
// for (i = 0; i < vi.channels; i++) {
|
||||
// int ptr = i * 2;
|
||||
// //int ptr=i;
|
||||
// int mono = _index[i];
|
||||
// for (int j = 0; j < bout; j++) {
|
||||
// int val = (int) (pcm[i][mono + j] * 32767.);
|
||||
// // short val=(short)(pcm[i][mono+j]*32767.);
|
||||
// // int val=(int)Math.round(pcm[i][mono+j]*32767.);
|
||||
// // might as well guard against clipping
|
||||
// if (val > 32767) {
|
||||
// val = 32767;
|
||||
// //clipflag = true;
|
||||
// }
|
||||
// if (val < -32768) {
|
||||
// val = -32768;
|
||||
// //clipflag = true;
|
||||
// }
|
||||
// if (val < 0)
|
||||
// val = val | 0x8000;
|
||||
//
|
||||
// if (bigEndian) {
|
||||
// convbuffer[ptr] = (byte) (val >>> 8);
|
||||
// convbuffer[ptr + 1] = (byte) (val);
|
||||
// } else {
|
||||
// convbuffer[ptr] = (byte) (val);
|
||||
// convbuffer[ptr + 1] = (byte) (val >>> 8);
|
||||
// }
|
||||
// ptr += 2 * (vi.channels);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// dataout.write(convbuffer, 0, 2 * vi.channels * bout);
|
||||
//
|
||||
// vd.synthesis_read(bout); // tell libvorbis how
|
||||
// // many samples we
|
||||
// // actually consumed
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (og.eos() != 0)
|
||||
// eos = 1;
|
||||
// }
|
||||
// }
|
||||
// if (eos == 0) {
|
||||
// index = oy.buffer(4096);
|
||||
// if (index >= 0) {
|
||||
// buffer = oy.data;
|
||||
// try {
|
||||
// bytes = input.read(buffer, index, 4096);
|
||||
// } catch (Exception e) {
|
||||
// Log.error("Failure during vorbis decoding");
|
||||
// Log.error(e);
|
||||
// return null;
|
||||
// }
|
||||
// } else {
|
||||
// bytes = 0;
|
||||
// }
|
||||
// oy.wrote(bytes);
|
||||
// if (bytes == 0)
|
||||
// eos = 1;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // clean up this logical bitstream; before exit we see if we're
|
||||
// // followed by another [chained]
|
||||
//
|
||||
// os.clear();
|
||||
//
|
||||
// // ogg_page and ogg_packet structs always point to storage in
|
||||
// // libvorbis. They're never freed or manipulated directly
|
||||
//
|
||||
// vb.clear();
|
||||
// vd.clear();
|
||||
// vi.clear(); // must be called last
|
||||
// }
|
||||
//
|
||||
// // OK, clean up the framer
|
||||
// oy.clear();
|
||||
// OggData ogg = new OggData();
|
||||
// ogg.channels = vi.channels;
|
||||
// ogg.rate = vi.rate;
|
||||
|
||||
OggInputStream oggInput = new OggInputStream(input);
|
||||
|
||||
boolean done = false;
|
||||
while (!oggInput.atEnd()) {
|
||||
dataout.write(oggInput.read());
|
||||
}
|
||||
|
||||
OggData ogg = new OggData();
|
||||
ogg.channels = oggInput.getChannels();
|
||||
ogg.rate = oggInput.getRate();
|
||||
|
||||
byte[] data = dataout.toByteArray();
|
||||
ogg.data = ByteBuffer.allocateDirect(data.length);
|
||||
ogg.data.put(data);
|
||||
ogg.data.rewind();
|
||||
|
||||
return ogg;
|
||||
}
|
||||
}
|
||||
506
lib/slick-source/org/newdawn/slick/openal/OggInputStream.java
Normal file
506
lib/slick-source/org/newdawn/slick/openal/OggInputStream.java
Normal file
@@ -0,0 +1,506 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.newdawn.slick.util.Log;
|
||||
|
||||
import com.jcraft.jogg.Packet;
|
||||
import com.jcraft.jogg.Page;
|
||||
import com.jcraft.jogg.StreamState;
|
||||
import com.jcraft.jogg.SyncState;
|
||||
import com.jcraft.jorbis.Block;
|
||||
import com.jcraft.jorbis.Comment;
|
||||
import com.jcraft.jorbis.DspState;
|
||||
import com.jcraft.jorbis.Info;
|
||||
|
||||
/**
|
||||
* An input stream that can extract ogg data. This class is a bit of an experiment with continuations
|
||||
* so uses thread where possibly not required. It's just a test to see if continuations make sense in
|
||||
* some cases.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class OggInputStream extends InputStream implements AudioInputStream {
|
||||
/** The conversion buffer size */
|
||||
private int convsize = 4096 * 4;
|
||||
/** The buffer used to read OGG file */
|
||||
private byte[] convbuffer = new byte[convsize]; // take 8k out of the data segment, not the stack
|
||||
/** The stream we're reading the OGG file from */
|
||||
private InputStream input;
|
||||
/** The audio information from the OGG header */
|
||||
private Info oggInfo = new Info(); // struct that stores all the static vorbis bitstream settings
|
||||
/** True if we're at the end of the available data */
|
||||
private boolean endOfStream;
|
||||
|
||||
/** The Vorbis SyncState used to decode the OGG */
|
||||
private SyncState syncState = new SyncState(); // sync and verify incoming physical bitstream
|
||||
/** The Vorbis Stream State used to decode the OGG */
|
||||
private StreamState streamState = new StreamState(); // take physical pages, weld into a logical stream of packets
|
||||
/** The current OGG page */
|
||||
private Page page = new Page(); // one Ogg bitstream page. Vorbis packets are inside
|
||||
/** The current packet page */
|
||||
private Packet packet = new Packet(); // one raw packet of data for decode
|
||||
|
||||
/** The comment read from the OGG file */
|
||||
private Comment comment = new Comment(); // struct that stores all the bitstream user comments
|
||||
/** The Vorbis DSP stat eused to decode the OGG */
|
||||
private DspState dspState = new DspState(); // central working state for the packet->PCM decoder
|
||||
/** The OGG block we're currently working with to convert PCM */
|
||||
private Block vorbisBlock = new Block(dspState); // local working space for packet->PCM decode
|
||||
|
||||
/** Temporary scratch buffer */
|
||||
byte[] buffer;
|
||||
/** The number of bytes read */
|
||||
int bytes = 0;
|
||||
/** The true if we should be reading big endian */
|
||||
boolean bigEndian = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
|
||||
/** True if we're reached the end of the current bit stream */
|
||||
boolean endOfBitStream = true;
|
||||
/** True if we're initialise the OGG info block */
|
||||
boolean inited = false;
|
||||
|
||||
/** The index into the byte array we currently read from */
|
||||
private int readIndex;
|
||||
/** The byte array store used to hold the data read from the ogg */
|
||||
private ByteBuffer pcmBuffer = BufferUtils.createByteBuffer(4096 * 500);
|
||||
/** The total number of bytes */
|
||||
private int total;
|
||||
|
||||
/**
|
||||
* Create a new stream to decode OGG data
|
||||
*
|
||||
* @param input The input stream from which to read the OGG file
|
||||
* @throws IOException Indicates a failure to read from the supplied stream
|
||||
*/
|
||||
public OggInputStream(InputStream input) throws IOException {
|
||||
this.input = input;
|
||||
total = input.available();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes on the stream
|
||||
*
|
||||
* @return The number of the bytes on the stream
|
||||
*/
|
||||
public int getLength() {
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioInputStream#getChannels()
|
||||
*/
|
||||
public int getChannels() {
|
||||
return oggInfo.channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioInputStream#getRate()
|
||||
*/
|
||||
public int getRate() {
|
||||
return oggInfo.rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the streams and thread involved in the streaming of OGG data
|
||||
*
|
||||
* @throws IOException Indicates a failure to link up the streams
|
||||
*/
|
||||
private void init() throws IOException {
|
||||
initVorbis();
|
||||
readPCM();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#available()
|
||||
*/
|
||||
public int available() {
|
||||
return endOfStream ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the vorbis decoding
|
||||
*/
|
||||
private void initVorbis() {
|
||||
syncState.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a page and packet from that page
|
||||
*
|
||||
* @return True if there was a page available
|
||||
*/
|
||||
private boolean getPageAndPacket() {
|
||||
// grab some data at the head of the stream. We want the first page
|
||||
// (which is guaranteed to be small and only contain the Vorbis
|
||||
// stream initial header) We need the first page to get the stream
|
||||
// serialno.
|
||||
|
||||
// submit a 4k block to libvorbis' Ogg layer
|
||||
int index = syncState.buffer(4096);
|
||||
|
||||
buffer = syncState.data;
|
||||
if (buffer == null) {
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
bytes = input.read(buffer, index, 4096);
|
||||
} catch (Exception e) {
|
||||
Log.error("Failure reading in vorbis");
|
||||
Log.error(e);
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
syncState.wrote(bytes);
|
||||
|
||||
// Get the first page.
|
||||
if (syncState.pageout(page) != 1) {
|
||||
// have we simply run out of data? If so, we're done.
|
||||
if (bytes < 4096)
|
||||
return false;
|
||||
|
||||
// error case. Must not be Vorbis data
|
||||
Log.error("Input does not appear to be an Ogg bitstream.");
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the serial number and set up the rest of decode.
|
||||
// serialno first; use it to set up a logical stream
|
||||
streamState.init(page.serialno());
|
||||
|
||||
// extract the initial header from the first page and verify that the
|
||||
// Ogg bitstream is in fact Vorbis data
|
||||
|
||||
// I handle the initial header first instead of just having the code
|
||||
// read all three Vorbis headers at once because reading the initial
|
||||
// header is an easy way to identify a Vorbis bitstream and it's
|
||||
// useful to see that functionality seperated out.
|
||||
|
||||
oggInfo.init();
|
||||
comment.init();
|
||||
if (streamState.pagein(page) < 0) {
|
||||
// error; stream version mismatch perhaps
|
||||
Log.error("Error reading first page of Ogg bitstream data.");
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (streamState.packetout(packet) != 1) {
|
||||
// no page? must not be vorbis
|
||||
Log.error("Error reading initial header packet.");
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oggInfo.synthesis_headerin(comment, packet) < 0) {
|
||||
// error case; not a vorbis header
|
||||
Log.error("This Ogg bitstream does not contain Vorbis audio data.");
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point, we're sure we're Vorbis. We've set up the logical
|
||||
// (Ogg) bitstream decoder. Get the comment and codebook headers and
|
||||
// set up the Vorbis decoder
|
||||
|
||||
// The next two packets in order are the comment and codebook headers.
|
||||
// They're likely large and may span multiple pages. Thus we reead
|
||||
// and submit data until we get our two pacakets, watching that no
|
||||
// pages are missing. If a page is missing, error out; losing a
|
||||
// header page is the only place where missing data is fatal. */
|
||||
|
||||
int i = 0;
|
||||
while (i < 2) {
|
||||
while (i < 2) {
|
||||
|
||||
int result = syncState.pageout(page);
|
||||
if (result == 0)
|
||||
break; // Need more data
|
||||
// Don't complain about missing or corrupt data yet. We'll
|
||||
// catch it at the packet output phase
|
||||
|
||||
if (result == 1) {
|
||||
streamState.pagein(page); // we can ignore any errors here
|
||||
// as they'll also become apparent
|
||||
// at packetout
|
||||
while (i < 2) {
|
||||
result = streamState.packetout(packet);
|
||||
if (result == 0)
|
||||
break;
|
||||
if (result == -1) {
|
||||
// Uh oh; data at some point was corrupted or missing!
|
||||
// We can't tolerate that in a header. Die.
|
||||
Log.error("Corrupt secondary header. Exiting.");
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
oggInfo.synthesis_headerin(comment, packet);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// no harm in not checking before adding more
|
||||
index = syncState.buffer(4096);
|
||||
buffer = syncState.data;
|
||||
try {
|
||||
bytes = input.read(buffer, index, 4096);
|
||||
} catch (Exception e) {
|
||||
Log.error("Failed to read Vorbis: ");
|
||||
Log.error(e);
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
if (bytes == 0 && i < 2) {
|
||||
Log.error("End of file before finding all Vorbis headers!");
|
||||
endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
syncState.wrote(bytes);
|
||||
}
|
||||
|
||||
convsize = 4096 / oggInfo.channels;
|
||||
|
||||
// OK, got and parsed all three headers. Initialize the Vorbis
|
||||
// packet->PCM decoder.
|
||||
dspState.synthesis_init(oggInfo); // central decode state
|
||||
vorbisBlock.init(dspState); // local state for most of the decode
|
||||
// so multiple block decodes can
|
||||
// proceed in parallel. We could init
|
||||
// multiple vorbis_block structures
|
||||
// for vd here
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the OGG file as shown in the jogg/jorbis examples
|
||||
*
|
||||
* @throws IOException Indicates a failure to read from the supplied stream
|
||||
*/
|
||||
private void readPCM() throws IOException {
|
||||
boolean wrote = false;
|
||||
|
||||
while (true) { // we repeat if the bitstream is chained
|
||||
if (endOfBitStream) {
|
||||
if (!getPageAndPacket()) {
|
||||
break;
|
||||
}
|
||||
endOfBitStream = false;
|
||||
}
|
||||
|
||||
if (!inited) {
|
||||
inited = true;
|
||||
return;
|
||||
}
|
||||
|
||||
float[][][] _pcm = new float[1][][];
|
||||
int[] _index = new int[oggInfo.channels];
|
||||
// The rest is just a straight decode loop until end of stream
|
||||
while (!endOfBitStream) {
|
||||
while (!endOfBitStream) {
|
||||
int result = syncState.pageout(page);
|
||||
|
||||
if (result == 0) {
|
||||
break; // need more data
|
||||
}
|
||||
|
||||
if (result == -1) { // missing or corrupt data at this page position
|
||||
Log.error("Corrupt or missing data in bitstream; continuing...");
|
||||
} else {
|
||||
streamState.pagein(page); // can safely ignore errors at
|
||||
// this point
|
||||
while (true) {
|
||||
result = streamState.packetout(packet);
|
||||
|
||||
if (result == 0)
|
||||
break; // need more data
|
||||
if (result == -1) { // missing or corrupt data at this page position
|
||||
// no reason to complain; already complained above
|
||||
} else {
|
||||
// we have a packet. Decode it
|
||||
int samples;
|
||||
if (vorbisBlock.synthesis(packet) == 0) { // test for success!
|
||||
dspState.synthesis_blockin(vorbisBlock);
|
||||
}
|
||||
|
||||
// **pcm is a multichannel float vector. In stereo, for
|
||||
// example, pcm[0] is left, and pcm[1] is right. samples is
|
||||
// the size of each channel. Convert the float values
|
||||
// (-1.<=range<=1.) to whatever PCM format and write it out
|
||||
|
||||
while ((samples = dspState.synthesis_pcmout(_pcm,
|
||||
_index)) > 0) {
|
||||
float[][] pcm = _pcm[0];
|
||||
//boolean clipflag = false;
|
||||
int bout = (samples < convsize ? samples
|
||||
: convsize);
|
||||
|
||||
// convert floats to 16 bit signed ints (host order) and
|
||||
// interleave
|
||||
for (int i = 0; i < oggInfo.channels; i++) {
|
||||
int ptr = i * 2;
|
||||
//int ptr=i;
|
||||
int mono = _index[i];
|
||||
for (int j = 0; j < bout; j++) {
|
||||
int val = (int) (pcm[i][mono + j] * 32767.);
|
||||
// might as well guard against clipping
|
||||
if (val > 32767) {
|
||||
val = 32767;
|
||||
}
|
||||
if (val < -32768) {
|
||||
val = -32768;
|
||||
}
|
||||
if (val < 0)
|
||||
val = val | 0x8000;
|
||||
|
||||
if (bigEndian) {
|
||||
convbuffer[ptr] = (byte) (val >>> 8);
|
||||
convbuffer[ptr + 1] = (byte) (val);
|
||||
} else {
|
||||
convbuffer[ptr] = (byte) (val);
|
||||
convbuffer[ptr + 1] = (byte) (val >>> 8);
|
||||
}
|
||||
ptr += 2 * (oggInfo.channels);
|
||||
}
|
||||
}
|
||||
|
||||
int bytesToWrite = 2 * oggInfo.channels * bout;
|
||||
if (bytesToWrite >= pcmBuffer.remaining()) {
|
||||
Log.warn("Read block from OGG that was too big to be buffered: " + bytesToWrite);
|
||||
} else {
|
||||
pcmBuffer.put(convbuffer, 0, bytesToWrite);
|
||||
}
|
||||
|
||||
wrote = true;
|
||||
dspState.synthesis_read(bout); // tell libvorbis how
|
||||
// many samples we
|
||||
// actually consumed
|
||||
}
|
||||
}
|
||||
}
|
||||
if (page.eos() != 0) {
|
||||
endOfBitStream = true;
|
||||
}
|
||||
|
||||
if ((!endOfBitStream) && (wrote)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!endOfBitStream) {
|
||||
bytes = 0;
|
||||
int index = syncState.buffer(4096);
|
||||
if (index >= 0) {
|
||||
buffer = syncState.data;
|
||||
try {
|
||||
bytes = input.read(buffer, index, 4096);
|
||||
} catch (Exception e) {
|
||||
Log.error("Failure during vorbis decoding");
|
||||
Log.error(e);
|
||||
endOfStream = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
bytes = 0;
|
||||
}
|
||||
syncState.wrote(bytes);
|
||||
if (bytes == 0) {
|
||||
endOfBitStream = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clean up this logical bitstream; before exit we see if we're
|
||||
// followed by another [chained]
|
||||
streamState.clear();
|
||||
|
||||
// ogg_page and ogg_packet structs always point to storage in
|
||||
// libvorbis. They're never freed or manipulated directly
|
||||
|
||||
vorbisBlock.clear();
|
||||
dspState.clear();
|
||||
oggInfo.clear(); // must be called last
|
||||
}
|
||||
|
||||
// OK, clean up the framer
|
||||
syncState.clear();
|
||||
endOfStream = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read()
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
if (readIndex >= pcmBuffer.position()) {
|
||||
pcmBuffer.clear();
|
||||
readPCM();
|
||||
readIndex = 0;
|
||||
}
|
||||
if (readIndex >= pcmBuffer.position()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int value = pcmBuffer.get(readIndex);
|
||||
if (value < 0) {
|
||||
value = 256 + value;
|
||||
}
|
||||
readIndex++;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioInputStream#atEnd()
|
||||
*/
|
||||
public boolean atEnd() {
|
||||
return endOfStream && (readIndex >= pcmBuffer.position());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read(byte[], int, int)
|
||||
*/
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
for (int i=0;i<len;i++) {
|
||||
try {
|
||||
int value = read();
|
||||
if (value >= 0) {
|
||||
b[i] = (byte) value;
|
||||
} else {
|
||||
if (i == 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.error(e);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read(byte[])
|
||||
*/
|
||||
public int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#close()
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.openal.AL11;
|
||||
import org.lwjgl.openal.OpenALException;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
/**
|
||||
* 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 <misc@n4te.com>
|
||||
* @author Rockstar play and setPosition cleanup
|
||||
*/
|
||||
public class OpenALStreamPlayer {
|
||||
/** 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 */
|
||||
private byte[] buffer = new byte[sectionSize];
|
||||
/** Holds the OpenAL buffer names */
|
||||
private IntBuffer bufferNames;
|
||||
/** The byte buffer passed to OpenAL containing the section */
|
||||
private ByteBuffer bufferData = BufferUtils.createByteBuffer(sectionSize);
|
||||
/** The buffer holding the names of the OpenAL buffer thats been fully played back */
|
||||
private IntBuffer unqueued = BufferUtils.createIntBuffer(1);
|
||||
/** The source we're playing back on */
|
||||
private 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 */
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.source = source;
|
||||
this.ref = ref;
|
||||
|
||||
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) {
|
||||
this.source = source;
|
||||
this.url = url;
|
||||
|
||||
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;
|
||||
|
||||
if (url != null) {
|
||||
audio = new OggInputStream(url.openStream());
|
||||
} else {
|
||||
audio = new OggInputStream(ResourceLoader.getResourceAsStream(ref));
|
||||
}
|
||||
|
||||
this.audio = audio;
|
||||
positionOffset = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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) {
|
||||
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.error("Failed to loop buffer: "+bufferId+" "+format+" "+count+" "+audio.getRate(), e);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (loop) {
|
||||
initStreams();
|
||||
stream(bufferId);
|
||||
} else {
|
||||
done = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.error(e);
|
||||
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) {
|
||||
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.error(e);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
974
lib/slick-source/org/newdawn/slick/openal/SoundStore.java
Normal file
974
lib/slick-source/org/newdawn/slick/openal/SoundStore.java
Normal file
@@ -0,0 +1,974 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.Sys;
|
||||
import org.lwjgl.openal.AL;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.openal.OpenALException;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
/**
|
||||
* Responsible for holding and playing the sounds used in the game.
|
||||
*
|
||||
* @author Kevin Glass
|
||||
* @author Rockstar setVolume cleanup
|
||||
*/
|
||||
public class SoundStore {
|
||||
|
||||
/** The single instance of this class */
|
||||
private static SoundStore store = new SoundStore();
|
||||
|
||||
/** True if sound effects are turned on */
|
||||
private boolean sounds;
|
||||
/** True if music is turned on */
|
||||
private boolean music;
|
||||
/** True if sound initialisation succeeded */
|
||||
private boolean soundWorks;
|
||||
/** The number of sound sources enabled - default 8 */
|
||||
private int sourceCount;
|
||||
/** The map of references to IDs of previously loaded sounds */
|
||||
private HashMap loaded = new HashMap();
|
||||
/** The ID of the buffer containing the music currently being played */
|
||||
private int currentMusic = -1;
|
||||
/** The OpenGL AL sound sources in use */
|
||||
private IntBuffer sources;
|
||||
/** The next source to be used for sound effects */
|
||||
private int nextSource;
|
||||
/** True if the sound system has been initialise */
|
||||
private boolean inited = false;
|
||||
/** The MODSound to be updated */
|
||||
private MODSound mod;
|
||||
/** The stream to be updated */
|
||||
private OpenALStreamPlayer stream;
|
||||
|
||||
/** The global music volume setting */
|
||||
private float musicVolume = 1.0f;
|
||||
/** The global sound fx volume setting */
|
||||
private float soundVolume = 1.0f;
|
||||
/** The volume given for the last current music */
|
||||
private float lastCurrentMusicVolume = 1.0f;
|
||||
|
||||
/** True if the music is paused */
|
||||
private boolean paused;
|
||||
/** True if we're returning deferred versions of resources */
|
||||
private boolean deferred;
|
||||
|
||||
/** The buffer used to set the velocity of a source */
|
||||
private FloatBuffer sourceVel = BufferUtils.createFloatBuffer(3).put(new float[] { 0.0f, 0.0f, 0.0f });
|
||||
/** The buffer used to set the position of a source */
|
||||
private FloatBuffer sourcePos = BufferUtils.createFloatBuffer(3);
|
||||
|
||||
/** The maximum number of sources */
|
||||
private int maxSources = 64;
|
||||
|
||||
/**
|
||||
* Create a new sound store
|
||||
*/
|
||||
private SoundStore() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear out the sound store contents
|
||||
*/
|
||||
public void clear() {
|
||||
store = new SoundStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable use of the Sound Store
|
||||
*/
|
||||
public void disable() {
|
||||
inited = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if we should only record the request to load in the intention
|
||||
* of loading the sound later
|
||||
*
|
||||
* @param deferred True if the we should load a token
|
||||
*/
|
||||
public void setDeferredLoading(boolean deferred) {
|
||||
this.deferred = deferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're using deferred loading
|
||||
*
|
||||
* @return True if we're loading deferred sounds
|
||||
*/
|
||||
public boolean isDeferredLoading() {
|
||||
return deferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inidicate whether music should be playing
|
||||
*
|
||||
* @param music True if music should be played
|
||||
*/
|
||||
public void setMusicOn(boolean music) {
|
||||
if (soundWorks) {
|
||||
this.music = music;
|
||||
if (music) {
|
||||
restartLoop();
|
||||
setMusicVolume(musicVolume);
|
||||
} else {
|
||||
pauseLoop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if music should currently be playing
|
||||
*
|
||||
* @return True if music is currently playing
|
||||
*/
|
||||
public boolean isMusicOn() {
|
||||
return music;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the music volume
|
||||
*
|
||||
* @param volume The volume for music
|
||||
*/
|
||||
public void setMusicVolume(float volume) {
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
}
|
||||
if (volume > 1) {
|
||||
volume = 1;
|
||||
}
|
||||
|
||||
musicVolume = volume;
|
||||
if (soundWorks) {
|
||||
AL10.alSourcef(sources.get(0), AL10.AL_GAIN, lastCurrentMusicVolume * musicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the volume scalar of the music that is currently playing.
|
||||
*
|
||||
* @return The volume of the music currently playing
|
||||
*/
|
||||
public float getCurrentMusicVolume() {
|
||||
return lastCurrentMusicVolume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the music volume of the current playing music. Does NOT affect the global volume
|
||||
*
|
||||
* @param volume The volume for the current playing music
|
||||
*/
|
||||
public void setCurrentMusicVolume(float volume) {
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
}
|
||||
if (volume > 1) {
|
||||
volume = 1;
|
||||
}
|
||||
|
||||
if (soundWorks) {
|
||||
lastCurrentMusicVolume = volume;
|
||||
AL10.alSourcef(sources.get(0), AL10.AL_GAIN, lastCurrentMusicVolume * musicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sound volume
|
||||
*
|
||||
* @param volume The volume for sound fx
|
||||
*/
|
||||
public void setSoundVolume(float volume) {
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
}
|
||||
soundVolume = volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if sound works at all
|
||||
*
|
||||
* @return True if sound works at all
|
||||
*/
|
||||
public boolean soundWorks() {
|
||||
return soundWorks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if music is currently enabled
|
||||
*
|
||||
* @return True if music is currently enabled
|
||||
*/
|
||||
public boolean musicOn() {
|
||||
return music;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the volume for sounds
|
||||
*
|
||||
* @return The volume for sounds
|
||||
*/
|
||||
public float getSoundVolume() {
|
||||
return soundVolume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the volume for music
|
||||
*
|
||||
* @return The volume for music
|
||||
*/
|
||||
public float getMusicVolume() {
|
||||
return musicVolume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of a given source
|
||||
*
|
||||
* @param index The ID of a given source
|
||||
* @return The ID of the given source
|
||||
*/
|
||||
public int getSource(int index) {
|
||||
if (!soundWorks) {
|
||||
return -1;
|
||||
}
|
||||
if (index < 0) {
|
||||
return -1;
|
||||
}
|
||||
return sources.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether sound effects should be played
|
||||
*
|
||||
* @param sounds True if sound effects should be played
|
||||
*/
|
||||
public void setSoundsOn(boolean sounds) {
|
||||
if (soundWorks) {
|
||||
this.sounds = sounds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if sound effects are currently enabled
|
||||
*
|
||||
* @return True if sound effects are currently enabled
|
||||
*/
|
||||
public boolean soundsOn() {
|
||||
return sounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of concurrent sound effects that will be
|
||||
* attempted
|
||||
*
|
||||
* @param max The maximum number of sound effects/music to mix
|
||||
*/
|
||||
public void setMaxSources(int max) {
|
||||
this.maxSources = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the sound effects stored. This must be called
|
||||
* before anything else will work
|
||||
*/
|
||||
public void init() {
|
||||
if (inited) {
|
||||
return;
|
||||
}
|
||||
Log.info("Initialising sounds..");
|
||||
inited = true;
|
||||
|
||||
AccessController.doPrivileged(new PrivilegedAction() {
|
||||
public Object run() {
|
||||
try {
|
||||
AL.create();
|
||||
soundWorks = true;
|
||||
sounds = true;
|
||||
music = true;
|
||||
Log.info("- Sound works");
|
||||
} catch (Exception e) {
|
||||
Log.error("Sound initialisation failure.");
|
||||
Log.error(e);
|
||||
soundWorks = false;
|
||||
sounds = false;
|
||||
music = false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}});
|
||||
|
||||
if (soundWorks) {
|
||||
sourceCount = 0;
|
||||
sources = BufferUtils.createIntBuffer(maxSources);
|
||||
while (AL10.alGetError() == AL10.AL_NO_ERROR) {
|
||||
IntBuffer temp = BufferUtils.createIntBuffer(1);
|
||||
|
||||
try {
|
||||
AL10.alGenSources(temp);
|
||||
|
||||
if (AL10.alGetError() == AL10.AL_NO_ERROR) {
|
||||
sourceCount++;
|
||||
sources.put(temp.get(0));
|
||||
if (sourceCount > maxSources-1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (OpenALException e) {
|
||||
// expected at the end
|
||||
break;
|
||||
}
|
||||
}
|
||||
Log.info("- "+sourceCount+" OpenAL source available");
|
||||
|
||||
if (AL10.alGetError() != AL10.AL_NO_ERROR) {
|
||||
sounds = false;
|
||||
music = false;
|
||||
soundWorks = false;
|
||||
Log.error("- AL init failed");
|
||||
} else {
|
||||
FloatBuffer listenerOri = BufferUtils.createFloatBuffer(6).put(
|
||||
new float[] { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f });
|
||||
FloatBuffer listenerVel = BufferUtils.createFloatBuffer(3).put(
|
||||
new float[] { 0.0f, 0.0f, 0.0f });
|
||||
FloatBuffer listenerPos = BufferUtils.createFloatBuffer(3).put(
|
||||
new float[] { 0.0f, 0.0f, 0.0f });
|
||||
listenerPos.flip();
|
||||
listenerVel.flip();
|
||||
listenerOri.flip();
|
||||
AL10.alListener(AL10.AL_POSITION, listenerPos);
|
||||
AL10.alListener(AL10.AL_VELOCITY, listenerVel);
|
||||
AL10.alListener(AL10.AL_ORIENTATION, listenerOri);
|
||||
|
||||
Log.info("- Sounds source generated");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a particular sound source
|
||||
*
|
||||
* @param index The index of the source to stop
|
||||
*/
|
||||
void stopSource(int index) {
|
||||
AL10.alSourceStop(sources.get(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the specified buffer as a sound effect with the specified
|
||||
* pitch and gain.
|
||||
*
|
||||
* @param buffer The ID of the buffer to play
|
||||
* @param pitch The pitch to play at
|
||||
* @param gain The gain to play at
|
||||
* @param loop True if the sound should loop
|
||||
* @return source The source that will be used
|
||||
*/
|
||||
int playAsSound(int buffer,float pitch,float gain,boolean loop) {
|
||||
return playAsSoundAt(buffer, pitch, gain, loop, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the specified buffer as a sound effect with the specified
|
||||
* pitch and gain.
|
||||
*
|
||||
* @param buffer The ID of the buffer to play
|
||||
* @param pitch The pitch to play at
|
||||
* @param gain The gain to play at
|
||||
* @param loop True if the sound should loop
|
||||
* @param x The x position to play the sound from
|
||||
* @param y The y position to play the sound from
|
||||
* @param z The z position to play the sound from
|
||||
* @return source The source that will be used
|
||||
*/
|
||||
int playAsSoundAt(int buffer,float pitch,float gain,boolean loop,float x, float y, float z) {
|
||||
gain *= soundVolume;
|
||||
if (gain == 0) {
|
||||
gain = 0.001f;
|
||||
}
|
||||
if (soundWorks) {
|
||||
if (sounds) {
|
||||
int nextSource = findFreeSource();
|
||||
if (nextSource == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
AL10.alSourceStop(sources.get(nextSource));
|
||||
|
||||
AL10.alSourcei(sources.get(nextSource), AL10.AL_BUFFER, buffer);
|
||||
AL10.alSourcef(sources.get(nextSource), AL10.AL_PITCH, pitch);
|
||||
AL10.alSourcef(sources.get(nextSource), AL10.AL_GAIN, gain);
|
||||
AL10.alSourcei(sources.get(nextSource), AL10.AL_LOOPING, loop ? AL10.AL_TRUE : AL10.AL_FALSE);
|
||||
|
||||
sourcePos.clear();
|
||||
sourceVel.clear();
|
||||
sourceVel.put(new float[] { 0, 0, 0 });
|
||||
sourcePos.put(new float[] { x, y, z });
|
||||
sourcePos.flip();
|
||||
sourceVel.flip();
|
||||
AL10.alSource(sources.get(nextSource), AL10.AL_POSITION, sourcePos);
|
||||
AL10.alSource(sources.get(nextSource), AL10.AL_VELOCITY, sourceVel);
|
||||
|
||||
AL10.alSourcePlay(sources.get(nextSource));
|
||||
|
||||
return nextSource;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
/**
|
||||
* Check if a particular source is playing
|
||||
*
|
||||
* @param index The index of the source to check
|
||||
* @return True if the source is playing
|
||||
*/
|
||||
boolean isPlaying(int index) {
|
||||
int state = AL10.alGetSourcei(sources.get(index), AL10.AL_SOURCE_STATE);
|
||||
|
||||
return (state == AL10.AL_PLAYING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a free sound source
|
||||
*
|
||||
* @return The index of the free sound source
|
||||
*/
|
||||
private int findFreeSource() {
|
||||
for (int i=1;i<sourceCount-1;i++) {
|
||||
int state = AL10.alGetSourcei(sources.get(i), AL10.AL_SOURCE_STATE);
|
||||
|
||||
if ((state != AL10.AL_PLAYING) && (state != AL10.AL_PAUSED)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the specified buffer as music (i.e. use the music channel)
|
||||
*
|
||||
* @param buffer The buffer to be played
|
||||
* @param pitch The pitch to play the music at
|
||||
* @param gain The gaing to play the music at
|
||||
* @param loop True if we should loop the music
|
||||
*/
|
||||
void playAsMusic(int buffer,float pitch,float gain, boolean loop) {
|
||||
paused = false;
|
||||
|
||||
setMOD(null);
|
||||
|
||||
if (soundWorks) {
|
||||
if (currentMusic != -1) {
|
||||
AL10.alSourceStop(sources.get(0));
|
||||
}
|
||||
|
||||
getMusicSource();
|
||||
|
||||
AL10.alSourcei(sources.get(0), AL10.AL_BUFFER, buffer);
|
||||
AL10.alSourcef(sources.get(0), AL10.AL_PITCH, pitch);
|
||||
AL10.alSourcei(sources.get(0), AL10.AL_LOOPING, loop ? AL10.AL_TRUE : AL10.AL_FALSE);
|
||||
|
||||
currentMusic = sources.get(0);
|
||||
|
||||
if (!music) {
|
||||
pauseLoop();
|
||||
} else {
|
||||
AL10.alSourcePlay(sources.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OpenAL source used for music
|
||||
*
|
||||
* @return The open al source used for music
|
||||
*/
|
||||
private int getMusicSource() {
|
||||
return sources.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pitch at which the current music is being played
|
||||
*
|
||||
* @param pitch The pitch at which the current music is being played
|
||||
*/
|
||||
public void setMusicPitch(float pitch) {
|
||||
if (soundWorks) {
|
||||
AL10.alSourcef(sources.get(0), AL10.AL_PITCH, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the music loop that is currently playing
|
||||
*/
|
||||
public void pauseLoop() {
|
||||
if ((soundWorks) && (currentMusic != -1)){
|
||||
paused = true;
|
||||
AL10.alSourcePause(currentMusic);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the music loop that is currently paused
|
||||
*/
|
||||
public void restartLoop() {
|
||||
if ((music) && (soundWorks) && (currentMusic != -1)){
|
||||
paused = false;
|
||||
AL10.alSourcePlay(currentMusic);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the supplied player is currently being polled by this
|
||||
* sound store.
|
||||
*
|
||||
* @param player The player to check
|
||||
* @return True if this player is currently in use by this sound store
|
||||
*/
|
||||
boolean isPlaying(OpenALStreamPlayer player) {
|
||||
return stream == player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a MOD sound (mod/xm etc)
|
||||
*
|
||||
* @param ref The refernece to the mod to load
|
||||
* @return The sound for play back
|
||||
* @throws IOException Indicates a failure to read the data
|
||||
*/
|
||||
public Audio getMOD(String ref) throws IOException {
|
||||
return getMOD(ref, ResourceLoader.getResourceAsStream(ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a MOD sound (mod/xm etc)
|
||||
*
|
||||
* @param in The stream to the MOD to load
|
||||
* @return The sound for play back
|
||||
* @throws IOException Indicates a failure to read the data
|
||||
*/
|
||||
public Audio getMOD(InputStream in) throws IOException {
|
||||
return getMOD(in.toString(), in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a MOD sound (mod/xm etc)
|
||||
*
|
||||
* @param ref The stream to the MOD to load
|
||||
* @param in The stream to the MOD to load
|
||||
* @return The sound for play back
|
||||
* @throws IOException Indicates a failure to read the data
|
||||
*/
|
||||
public Audio getMOD(String ref, InputStream in) throws IOException {
|
||||
if (!soundWorks) {
|
||||
return new NullAudio();
|
||||
}
|
||||
if (!inited) {
|
||||
throw new RuntimeException("Can't load sounds until SoundStore is init(). Use the container init() method.");
|
||||
}
|
||||
if (deferred) {
|
||||
return new DeferredSound(ref, in, DeferredSound.MOD);
|
||||
}
|
||||
|
||||
return new MODSound(this, in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified AIF file
|
||||
*
|
||||
* @param ref The reference to the AIF file in the classpath
|
||||
* @return The Sound read from the AIF file
|
||||
* @throws IOException Indicates a failure to load the AIF
|
||||
*/
|
||||
public Audio getAIF(String ref) throws IOException {
|
||||
return getAIF(ref, ResourceLoader.getResourceAsStream(ref));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified AIF file
|
||||
*
|
||||
* @param in The stream to the MOD to load
|
||||
* @return The Sound read from the AIF file
|
||||
* @throws IOException Indicates a failure to load the AIF
|
||||
*/
|
||||
public Audio getAIF(InputStream in) throws IOException {
|
||||
return getAIF(in.toString(), in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified AIF file
|
||||
*
|
||||
* @param ref The reference to the AIF file in the classpath
|
||||
* @param in The stream to the AIF to load
|
||||
* @return The Sound read from the AIF file
|
||||
* @throws IOException Indicates a failure to load the AIF
|
||||
*/
|
||||
public Audio getAIF(String ref, InputStream in) throws IOException {
|
||||
in = new BufferedInputStream(in);
|
||||
|
||||
if (!soundWorks) {
|
||||
return new NullAudio();
|
||||
}
|
||||
if (!inited) {
|
||||
throw new RuntimeException("Can't load sounds until SoundStore is init(). Use the container init() method.");
|
||||
}
|
||||
if (deferred) {
|
||||
return new DeferredSound(ref, in, DeferredSound.AIF);
|
||||
}
|
||||
|
||||
int buffer = -1;
|
||||
|
||||
if (loaded.get(ref) != null) {
|
||||
buffer = ((Integer) loaded.get(ref)).intValue();
|
||||
} else {
|
||||
try {
|
||||
IntBuffer buf = BufferUtils.createIntBuffer(1);
|
||||
|
||||
AiffData data = AiffData.create(in);
|
||||
AL10.alGenBuffers(buf);
|
||||
AL10.alBufferData(buf.get(0), data.format, data.data, data.samplerate);
|
||||
|
||||
loaded.put(ref,new Integer(buf.get(0)));
|
||||
buffer = buf.get(0);
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
IOException x = new IOException("Failed to load: "+ref);
|
||||
x.initCause(e);
|
||||
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer == -1) {
|
||||
throw new IOException("Unable to load: "+ref);
|
||||
}
|
||||
|
||||
return new AudioImpl(this, buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified WAV file
|
||||
*
|
||||
* @param ref The reference to the WAV file in the classpath
|
||||
* @return The Sound read from the WAV file
|
||||
* @throws IOException Indicates a failure to load the WAV
|
||||
*/
|
||||
public Audio getWAV(String ref) throws IOException {
|
||||
return getWAV(ref, ResourceLoader.getResourceAsStream(ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified WAV file
|
||||
*
|
||||
* @param in The stream to the WAV to load
|
||||
* @return The Sound read from the WAV file
|
||||
* @throws IOException Indicates a failure to load the WAV
|
||||
*/
|
||||
public Audio getWAV(InputStream in) throws IOException {
|
||||
return getWAV(in.toString(), in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified WAV file
|
||||
*
|
||||
* @param ref The reference to the WAV file in the classpath
|
||||
* @param in The stream to the WAV to load
|
||||
* @return The Sound read from the WAV file
|
||||
* @throws IOException Indicates a failure to load the WAV
|
||||
*/
|
||||
public Audio getWAV(String ref, InputStream in) throws IOException {
|
||||
if (!soundWorks) {
|
||||
return new NullAudio();
|
||||
}
|
||||
if (!inited) {
|
||||
throw new RuntimeException("Can't load sounds until SoundStore is init(). Use the container init() method.");
|
||||
}
|
||||
if (deferred) {
|
||||
return new DeferredSound(ref, in, DeferredSound.WAV);
|
||||
}
|
||||
|
||||
int buffer = -1;
|
||||
|
||||
if (loaded.get(ref) != null) {
|
||||
buffer = ((Integer) loaded.get(ref)).intValue();
|
||||
} else {
|
||||
try {
|
||||
IntBuffer buf = BufferUtils.createIntBuffer(1);
|
||||
|
||||
WaveData data = WaveData.create(in);
|
||||
AL10.alGenBuffers(buf);
|
||||
AL10.alBufferData(buf.get(0), data.format, data.data, data.samplerate);
|
||||
|
||||
loaded.put(ref,new Integer(buf.get(0)));
|
||||
buffer = buf.get(0);
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
IOException x = new IOException("Failed to load: "+ref);
|
||||
x.initCause(e);
|
||||
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer == -1) {
|
||||
throw new IOException("Unable to load: "+ref);
|
||||
}
|
||||
|
||||
return new AudioImpl(this, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified OGG file
|
||||
*
|
||||
* @param ref The reference to the OGG file in the classpath
|
||||
* @return The Sound read from the OGG file
|
||||
* @throws IOException Indicates a failure to load the OGG
|
||||
*/
|
||||
public Audio getOggStream(String ref) throws IOException {
|
||||
if (!soundWorks) {
|
||||
return new NullAudio();
|
||||
}
|
||||
|
||||
setMOD(null);
|
||||
setStream(null);
|
||||
|
||||
if (currentMusic != -1) {
|
||||
AL10.alSourceStop(sources.get(0));
|
||||
}
|
||||
|
||||
getMusicSource();
|
||||
currentMusic = sources.get(0);
|
||||
|
||||
return new StreamSound(new OpenALStreamPlayer(currentMusic, ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified OGG file
|
||||
*
|
||||
* @param ref The reference to the OGG file in the classpath
|
||||
* @return The Sound read from the OGG file
|
||||
* @throws IOException Indicates a failure to load the OGG
|
||||
*/
|
||||
public Audio getOggStream(URL ref) throws IOException {
|
||||
if (!soundWorks) {
|
||||
return new NullAudio();
|
||||
}
|
||||
|
||||
setMOD(null);
|
||||
setStream(null);
|
||||
|
||||
if (currentMusic != -1) {
|
||||
AL10.alSourceStop(sources.get(0));
|
||||
}
|
||||
|
||||
getMusicSource();
|
||||
currentMusic = sources.get(0);
|
||||
|
||||
return new StreamSound(new OpenALStreamPlayer(currentMusic, ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified OGG file
|
||||
*
|
||||
* @param ref The reference to the OGG file in the classpath
|
||||
* @return The Sound read from the OGG file
|
||||
* @throws IOException Indicates a failure to load the OGG
|
||||
*/
|
||||
public Audio getOgg(String ref) throws IOException {
|
||||
return getOgg(ref, ResourceLoader.getResourceAsStream(ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified OGG file
|
||||
*
|
||||
* @param in The stream to the OGG to load
|
||||
* @return The Sound read from the OGG file
|
||||
* @throws IOException Indicates a failure to load the OGG
|
||||
*/
|
||||
public Audio getOgg(InputStream in) throws IOException {
|
||||
return getOgg(in.toString(), in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sound based on a specified OGG file
|
||||
*
|
||||
* @param ref The reference to the OGG file in the classpath
|
||||
* @param in The stream to the OGG to load
|
||||
* @return The Sound read from the OGG file
|
||||
* @throws IOException Indicates a failure to load the OGG
|
||||
*/
|
||||
public Audio getOgg(String ref, InputStream in) throws IOException {
|
||||
if (!soundWorks) {
|
||||
return new NullAudio();
|
||||
}
|
||||
if (!inited) {
|
||||
throw new RuntimeException("Can't load sounds until SoundStore is init(). Use the container init() method.");
|
||||
}
|
||||
if (deferred) {
|
||||
return new DeferredSound(ref, in, DeferredSound.OGG);
|
||||
}
|
||||
|
||||
int buffer = -1;
|
||||
|
||||
if (loaded.get(ref) != null) {
|
||||
buffer = ((Integer) loaded.get(ref)).intValue();
|
||||
} else {
|
||||
try {
|
||||
IntBuffer buf = BufferUtils.createIntBuffer(1);
|
||||
|
||||
OggDecoder decoder = new OggDecoder();
|
||||
OggData ogg = decoder.getData(in);
|
||||
|
||||
AL10.alGenBuffers(buf);
|
||||
AL10.alBufferData(buf.get(0), ogg.channels > 1 ? AL10.AL_FORMAT_STEREO16 : AL10.AL_FORMAT_MONO16, ogg.data, ogg.rate);
|
||||
|
||||
loaded.put(ref,new Integer(buf.get(0)));
|
||||
|
||||
buffer = buf.get(0);
|
||||
} catch (Exception e) {
|
||||
Log.error(e);
|
||||
Sys.alert("Error","Failed to load: "+ref+" - "+e.getMessage());
|
||||
throw new IOException("Unable to load: "+ref);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer == -1) {
|
||||
throw new IOException("Unable to load: "+ref);
|
||||
}
|
||||
|
||||
return new AudioImpl(this, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mod thats being streamed if any
|
||||
*
|
||||
* @param sound The mod being streamed
|
||||
*/
|
||||
void setMOD(MODSound sound) {
|
||||
if (!soundWorks) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentMusic = sources.get(0);
|
||||
stopSource(0);
|
||||
|
||||
this.mod = sound;
|
||||
if (sound != null) {
|
||||
this.stream = null;
|
||||
}
|
||||
paused = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stream being played
|
||||
*
|
||||
* @param stream The stream being streamed
|
||||
*/
|
||||
void setStream(OpenALStreamPlayer stream) {
|
||||
if (!soundWorks) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentMusic = sources.get(0);
|
||||
this.stream = stream;
|
||||
if (stream != null) {
|
||||
this.mod = null;
|
||||
}
|
||||
paused = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the streaming system
|
||||
*
|
||||
* @param delta The amount of time passed since last poll (in milliseconds)
|
||||
*/
|
||||
public void poll(int delta) {
|
||||
if (!soundWorks) {
|
||||
return;
|
||||
}
|
||||
if (paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (music) {
|
||||
if (mod != null) {
|
||||
try {
|
||||
mod.poll();
|
||||
} catch (OpenALException e) {
|
||||
Log.error("Error with OpenGL MOD Player on this this platform");
|
||||
Log.error(e);
|
||||
mod = null;
|
||||
}
|
||||
}
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.update();
|
||||
} catch (OpenALException e) {
|
||||
Log.error("Error with OpenGL Streaming Player on this this platform");
|
||||
Log.error(e);
|
||||
mod = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the music is currently playing
|
||||
*
|
||||
* @return True if the music is playing
|
||||
*/
|
||||
public boolean isMusicPlaying()
|
||||
{
|
||||
if (!soundWorks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int state = AL10.alGetSourcei(sources.get(0), AL10.AL_SOURCE_STATE);
|
||||
return ((state == AL10.AL_PLAYING) || (state == AL10.AL_PAUSED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the single instance of this class
|
||||
*
|
||||
* @return The single instnace of this class
|
||||
*/
|
||||
public static SoundStore get() {
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a playing sound identified by the ID returned from playing. This utility method
|
||||
* should only be used when needing to stop sound effects that may have been played
|
||||
* more than once and need to be explicitly stopped.
|
||||
*
|
||||
* @param id The ID of the underlying OpenAL source as returned from playAsSoundEffect
|
||||
*/
|
||||
public void stopSoundEffect(int id) {
|
||||
AL10.alSourceStop(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of OpenAL sound sources that have been
|
||||
* determined at initialisation.
|
||||
*
|
||||
* @return The number of sources available
|
||||
*/
|
||||
public int getSourceCount() {
|
||||
return sourceCount;
|
||||
}
|
||||
}
|
||||
108
lib/slick-source/org/newdawn/slick/openal/StreamSound.java
Normal file
108
lib/slick-source/org/newdawn/slick/openal/StreamSound.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package org.newdawn.slick.openal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.newdawn.slick.util.Log;
|
||||
|
||||
/**
|
||||
* A sound implementation wrapped round a player which reads (and potentially) rereads
|
||||
* a stream. This supplies streaming audio
|
||||
*
|
||||
* @author kevin
|
||||
* @author Nathan Sweet <misc@n4te.com>
|
||||
* @author Rockstar playAsMusic cleanup
|
||||
*/
|
||||
public class StreamSound extends AudioImpl {
|
||||
/** The player we're going to ask to stream data */
|
||||
private OpenALStreamPlayer player;
|
||||
|
||||
/**
|
||||
* Create a new sound wrapped round a stream
|
||||
*
|
||||
* @param player The stream player we'll use to access the stream
|
||||
*/
|
||||
public StreamSound(OpenALStreamPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#isPlaying()
|
||||
*/
|
||||
public boolean isPlaying() {
|
||||
return SoundStore.get().isPlaying(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#playAsMusic(float, float, boolean)
|
||||
*/
|
||||
public int playAsMusic(float pitch, float gain, boolean loop) {
|
||||
try {
|
||||
cleanUpSource();
|
||||
|
||||
player.setup(pitch);
|
||||
player.play(loop);
|
||||
SoundStore.get().setStream(player);
|
||||
} catch (IOException e) {
|
||||
Log.error("Failed to read OGG source: "+player.getSource());
|
||||
}
|
||||
|
||||
return SoundStore.get().getSource(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the buffers applied to the sound source
|
||||
*/
|
||||
private void cleanUpSource() {
|
||||
SoundStore store = SoundStore.get();
|
||||
|
||||
AL10.alSourceStop(store.getSource(0));
|
||||
IntBuffer buffer = BufferUtils.createIntBuffer(1);
|
||||
int queued = AL10.alGetSourcei(store.getSource(0), AL10.AL_BUFFERS_QUEUED);
|
||||
|
||||
while (queued > 0)
|
||||
{
|
||||
AL10.alSourceUnqueueBuffers(store.getSource(0), buffer);
|
||||
queued--;
|
||||
}
|
||||
|
||||
AL10.alSourcei(store.getSource(0), AL10.AL_BUFFER, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#playAsSoundEffect(float, float, boolean, float, float, float)
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop, float x, float y, float z) {
|
||||
return playAsMusic(pitch, gain, loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#playAsSoundEffect(float, float, boolean)
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop) {
|
||||
return playAsMusic(pitch, gain, loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#stop()
|
||||
*/
|
||||
public void stop() {
|
||||
SoundStore.get().setStream(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#setPosition(float)
|
||||
*/
|
||||
public boolean setPosition(float position) {
|
||||
return player.setPosition(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.openal.AudioImpl#getPosition()
|
||||
*/
|
||||
public float getPosition() {
|
||||
return player.getPosition();
|
||||
}
|
||||
}
|
||||
265
lib/slick-source/org/newdawn/slick/openal/WaveData.java
Normal file
265
lib/slick-source/org/newdawn/slick/openal/WaveData.java
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2004 LWJGL Project
|
||||
* 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 'LWJGL' 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 org.newdawn.slick.openal;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
/**
|
||||
*
|
||||
* Utitlity class for loading wavefiles.
|
||||
*
|
||||
* @author Brian Matzon <brian@matzon.dk>
|
||||
* @version $Revision: 2286 $
|
||||
* $Id: WaveData.java 2286 2006-03-23 19:32:21Z matzon $
|
||||
*/
|
||||
public class WaveData {
|
||||
/** actual wave data */
|
||||
public final ByteBuffer data;
|
||||
|
||||
/** format type of data */
|
||||
public final int format;
|
||||
|
||||
/** sample rate of data */
|
||||
public final int samplerate;
|
||||
|
||||
/**
|
||||
* Creates a new WaveData
|
||||
*
|
||||
* @param data actual wavedata
|
||||
* @param format format of wave data
|
||||
* @param samplerate sample rate of data
|
||||
*/
|
||||
private WaveData(ByteBuffer data, int format, int samplerate) {
|
||||
this.data = data;
|
||||
this.format = format;
|
||||
this.samplerate = samplerate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the wavedata
|
||||
*/
|
||||
public void dispose() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified url
|
||||
*
|
||||
* @param path URL to file
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(URL path) {
|
||||
try {
|
||||
return create(
|
||||
AudioSystem.getAudioInputStream(
|
||||
new BufferedInputStream(path.openStream())));
|
||||
} catch (Exception e) {
|
||||
org.lwjgl.LWJGLUtil.log("Unable to create from: " + path);
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified in the classpath
|
||||
*
|
||||
* @param path path to file (relative, and in classpath)
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(String path) {
|
||||
return create(WaveData.class.getClassLoader().getResource(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified inputstream
|
||||
*
|
||||
* @param is InputStream to read from
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(InputStream is) {
|
||||
try {
|
||||
return create(
|
||||
AudioSystem.getAudioInputStream(is));
|
||||
} catch (Exception e) {
|
||||
org.lwjgl.LWJGLUtil.log("Unable to create from inputstream");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified bytes
|
||||
*
|
||||
* @param buffer array of bytes containing the complete wave file
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(byte[] buffer) {
|
||||
try {
|
||||
return create(
|
||||
AudioSystem.getAudioInputStream(
|
||||
new BufferedInputStream(new ByteArrayInputStream(buffer))));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified ByetBuffer.
|
||||
* If the buffer is backed by an array, it will be used directly,
|
||||
* else the contents of the buffer will be copied using get(byte[]).
|
||||
*
|
||||
* @param buffer ByteBuffer containing sound file
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(ByteBuffer buffer) {
|
||||
try {
|
||||
byte[] bytes = null;
|
||||
|
||||
if(buffer.hasArray()) {
|
||||
bytes = buffer.array();
|
||||
} else {
|
||||
bytes = new byte[buffer.capacity()];
|
||||
buffer.get(bytes);
|
||||
}
|
||||
return create(bytes);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified stream
|
||||
*
|
||||
* @param ais AudioInputStream to read from
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(AudioInputStream ais) {
|
||||
//get format of data
|
||||
AudioFormat audioformat = ais.getFormat();
|
||||
|
||||
// get channels
|
||||
int channels = 0;
|
||||
if (audioformat.getChannels() == 1) {
|
||||
if (audioformat.getSampleSizeInBits() == 8) {
|
||||
channels = AL10.AL_FORMAT_MONO8;
|
||||
} else if (audioformat.getSampleSizeInBits() == 16) {
|
||||
channels = AL10.AL_FORMAT_MONO16;
|
||||
} else {
|
||||
throw new RuntimeException("Illegal sample size");
|
||||
}
|
||||
} else if (audioformat.getChannels() == 2) {
|
||||
if (audioformat.getSampleSizeInBits() == 8) {
|
||||
channels = AL10.AL_FORMAT_STEREO8;
|
||||
} else if (audioformat.getSampleSizeInBits() == 16) {
|
||||
channels = AL10.AL_FORMAT_STEREO16;
|
||||
} else {
|
||||
throw new RuntimeException("Illegal sample size");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Only mono or stereo is supported");
|
||||
}
|
||||
|
||||
//read data into buffer
|
||||
byte[] buf =
|
||||
new byte[audioformat.getChannels()
|
||||
* (int) ais.getFrameLength()
|
||||
* audioformat.getSampleSizeInBits()
|
||||
/ 8];
|
||||
int read = 0, total = 0;
|
||||
try {
|
||||
while ((read = ais.read(buf, total, buf.length - total)) != -1
|
||||
&& total < buf.length) {
|
||||
total += read;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//insert data into bytebuffer
|
||||
ByteBuffer buffer = convertAudioBytes(buf, audioformat.getSampleSizeInBits() == 16);
|
||||
/* ByteBuffer buffer = ByteBuffer.allocateDirect(buf.length);
|
||||
buffer.put(buf);
|
||||
buffer.rewind();*/
|
||||
|
||||
//create our result
|
||||
WaveData wavedata =
|
||||
new WaveData(buffer, channels, (int) audioformat.getSampleRate());
|
||||
|
||||
//close stream
|
||||
try {
|
||||
ais.close();
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
|
||||
return wavedata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the audio bytes into the stream
|
||||
*
|
||||
* @param audio_bytes The audio byts
|
||||
* @param two_bytes_data True if we using double byte data
|
||||
* @return The byte bufer of data
|
||||
*/
|
||||
private static ByteBuffer convertAudioBytes(byte[] audio_bytes, boolean two_bytes_data) {
|
||||
ByteBuffer dest = ByteBuffer.allocateDirect(audio_bytes.length);
|
||||
dest.order(ByteOrder.nativeOrder());
|
||||
ByteBuffer src = ByteBuffer.wrap(audio_bytes);
|
||||
src.order(ByteOrder.LITTLE_ENDIAN);
|
||||
if (two_bytes_data) {
|
||||
ShortBuffer dest_short = dest.asShortBuffer();
|
||||
ShortBuffer src_short = src.asShortBuffer();
|
||||
while (src_short.hasRemaining())
|
||||
dest_short.put(src_short.get());
|
||||
} else {
|
||||
while (src.hasRemaining())
|
||||
dest.put(src.get());
|
||||
}
|
||||
dest.rewind();
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
4
lib/slick-source/org/newdawn/slick/openal/package.html
Normal file
4
lib/slick-source/org/newdawn/slick/openal/package.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<BODY>
|
||||
This package contains the nitty gritty sound manipulation code for using OpenAL with standard audio formats. As
|
||||
a user you shouldn't need to access anything here directly.
|
||||
</BODY>
|
||||
Reference in New Issue
Block a user