import java.io.IOException;
import java.io.InputStream;
import java.nio.IntBuffer;
import java.util.HashMap;
import org.lwjgl.BufferUtils;
import org.lwjgl.Sys;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10;
/**
* Responsible for holding and playing the sounds used in the game.
*
* @author Kevin Glass
*/
public class SoundStore {
/** The single instance of this class */
private static final 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 global music volume setting */
private float musicVolume = 1.0f;
/** The global sound fx volume setting */
private float soundVolume = 1.0f;
/** The last "gain" applied to music */
private float lastMusicGain = 1.0f;
/** True if we should fade in */
private boolean fadeIn;
/** The current fade in value */
private float fade;
private SoundStore() {
}
// Inidicate whether music should be playing
public void setMusicOn(boolean music) {
if (soundWorks) {
this.music = music;
if (music) {
restartLoop();
}
else {
pauseLoop();
}
}
}
// Check if music should currently be playing
public boolean isMusicOn() {
return music;
}
// Set the music volume
public void setMusicVolume(float volume) {
if (volume < 0) {
volume = 0;
}
musicVolume = volume;
if (soundWorks) {
if (volume == 0) {
volume = 0.001f;
}
AL10.alSourcef(sources.get(0), AL10.AL_GAIN, lastMusicGain * volume);
}
}
// Set the sound volume
public void setSoundVolume(float volume) {
if (volume < 0) {
volume = 0;
}
soundVolume = volume;
}
// Check if sound works at all
public boolean soundWorks() {
return soundWorks;
}
// Check if music is currently enabled
public boolean musicOn() {
return music;
}
//Get 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) {
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;
}
// Initialise the sound effects stored. This must be called
// before anything else will work
public void init() {
System.out.println("Initialising sounds..");
inited = true;
try {
AL.create();
soundWorks = true;
sounds = true;
music = true;
System.out.println("- Sound works");
}
catch (Exception e) {
System.out.println("Sound initialisation failure.");
System.out.println(e);
soundWorks = false;
sounds = false;
music = false;
}
if (soundWorks) {
sourceCount = 8;
sources = BufferUtils.createIntBuffer(sourceCount);
AL10.alGenSources(sources);
if (AL10.alGetError() != AL10.AL_NO_ERROR) {
sounds = false;
music = false;
soundWorks = false;
System.out.println("- AL init failed");
}
else {
System.out.println("- Sounds source generated");
}
}
}
/**
* 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
*/
void playAsSound(int buffer, float pitch, float gain) {
gain *= soundVolume;
if (gain == 0) {
gain = 0.001f;
}
if (soundWorks) {
if (sounds) {
nextSource++;
if (nextSource > sourceCount - 1) {
nextSource = 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.alSourcePlay(sources.get(nextSource));
}
}
}
/**
* 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
*/
void playAsMusic(int buffer, float pitch, float gain) {
lastMusicGain = gain;
gain *= musicVolume;
if (gain == 0) {
gain = 0.001f;
}
if (soundWorks) {
if (currentMusic != -1) {
AL10.alSourceStop(sources.get(0));
}
AL10.alSourcei(sources.get(0), AL10.AL_BUFFER, buffer);
AL10.alSourcef(sources.get(0), AL10.AL_PITCH, pitch);
AL10.alSourcef(sources.get(0), AL10.AL_GAIN, gain);
AL10.alSourcei(sources.get(0), AL10.AL_LOOPING, AL10.AL_TRUE);
currentMusic = sources.get(0);
if (!music) {
pauseLoop();
}
else {
AL10.alSourcePlay(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)) {
AL10.alSourcePause(currentMusic);
}
}
/**
* Restart the music loop that is currently paused
*/
public void restartLoop() {
if ((music) && (soundWorks) && (currentMusic != -1)) {
AL10.alSourcePlay(currentMusic);
}
}
/**
* 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 Sound getOgg(String ref) throws IOException {
if (!soundWorks) {
return new Sound(this, 0);
}
if (!inited) {
throw new RuntimeException("Can't load sounds until SoundStore is init()");
}
int buffer = -1;
if (loaded.get(ref) != null) {
buffer = ((Integer) loaded.get(ref)).intValue();
}
else {
System.out.println("Loading: " + ref);
try {
IntBuffer buf = BufferUtils.createIntBuffer(1);
InputStream in = Thread.currentThread().getContextClassLoader().
getResourceAsStream(ref);
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) {
System.out.println(e);
Sys.alert("Error", "Failed to load: " + ref + " - " + e.getMessage());
System.exit(0);
}
}
if (buffer == -1) {
throw new IOException("Unable to load: " + ref);
}
return new Sound(this, buffer);
}
/**
* Poll the streaming system
*
* @param delta The amount of time passed since last poll
*/
public void poll(int delta) {
if (!soundWorks) {
return;
}
if (music) {
if ((fadeIn) && (currentMusic != -1)) {
fade += 0.0005f * delta;
if (fade >= 1) {
fade = 1;
fadeIn = false;
}
AL10.alSourcef(currentMusic, AL10.AL_GAIN, fade * musicVolume);
}
}
else {
if (currentMusic != -1) {
fadeIn = true;
fade = 0;
AL10.alSourcef(currentMusic, AL10.AL_GAIN, fade * musicVolume);
}
}
}
/**
* Get the single instance of this class
*
* @return The single instnace of this class
*/
public static SoundStore get() {
return store;
}
}