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; } }