All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.lessvoid.nifty.sound.openal.slick.SoundStore Maven / Gradle / Ivy

Go to download

This is an OpenAL/lwjgl based SoundDevice for Nifty GUI. It was extracted out of Slick2D to be independent of the whole Slick2D library and to be used with none Slick2D based renderes. All credits for the original code go to Kevin Glass and all Slick2D contributers.

The newest version!
package de.lessvoid.nifty.sound.openal.slick;

import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
import org.lwjgl.BufferUtils;
import org.lwjgl.Sys;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.OpenALException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
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 java.util.Map;
import java.util.logging.Logger;

/**
 * Responsible for holding and playing the sounds used in the game.
 *
 * @author Kevin Glass
 * @author Rockstar setVolume cleanup
 */
public class SoundStore {
  private final Logger log = Logger.getLogger(SoundStore.class.getName());

  /**
   * The single instance of this class
   */
  @Nonnull
  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
   */
  @Nonnull
  private final Map 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
   */
  @Nullable
  private IntBuffer sources;
  /**
   * True if the sound system has been initialise
   */
  private boolean initiated = false;
  /**
   * The MODSound to be updated
   */
  @Nullable
  private MODSound mod;
  /**
   * The stream to be updated
   */
  @Nullable
  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
   */
  @Nonnull
  private final FloatBuffer sourceVel = BufferUtils.createFloatBuffer(3).put(new float[] { 0.0f, 0.0f, 0.0f });
  /**
   * The buffer used to set the position of a source
   */
  @Nonnull
  private final 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() {
    initiated = 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 && sources != null) {
      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 && sources != null) {
      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 || sources == null) {
      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 (initiated) {
      return;
    }
    log.fine("Initialising sounds..");
    initiated = true;

    AccessController.doPrivileged(new PrivilegedAction() {
      @Override
      @Nullable
      public Object run() {
        try {
          AL.create();
          soundWorks = true;
          sounds = true;
          music = true;
          log.fine("- Sound works");
        } catch (Exception e) {
          log.warning("Sound initialisation failure." + e.getMessage());
          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.fine("- " + sourceCount + " OpenAL source available");

      if (AL10.alGetError() != AL10.AL_NO_ERROR) {
        sounds = false;
        music = false;
        soundWorks = false;
        log.warning("- 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.fine("- Sounds source generated");
      }
    }
  }

  /**
   * Stop a particular sound source
   *
   * @param index The index of the source to stop
   */
  void stopSource(int index) {
    if (sources != null) {
      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 && sources != null) {
      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) {
    if (sources == null) {
      return false;
    }
    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() {
    if (sources == null) {
      return -1;
    }
    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 && sources != null) {
      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 != null ? sources.get(0) : -1;
  }

  /**
   * 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 && sources != null) {
      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
   */
  @Nonnull
  public Audio getMOD(@Nonnull String ref, @Nonnull final NiftyResourceLoader resourceLoader) 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
   */
  @Nonnull
  public Audio getMOD(@Nonnull 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
   */
  @Nonnull
  public Audio getMOD(String ref, InputStream in) throws IOException {
    if (!soundWorks) {
      return new NullAudio();
    }
    if (!initiated) {
      throw new RuntimeException("Can't load sounds until SoundStore is init(). Use the container init() method.");
    }
    if (deferred) {
      throw new RuntimeException("deferred is not supported");
    }

    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
   */
  @Nonnull
  public Audio getAIF(@Nonnull String ref, @Nonnull final NiftyResourceLoader resourceLoader) 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
   */
  @Nonnull
  public Audio getAIF(@Nonnull 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
   */
  @Nonnull
  public Audio getAIF(String ref, InputStream in) throws IOException {
    in = new BufferedInputStream(in);

    if (!soundWorks) {
      return new NullAudio();
    }
    if (!initiated) {
      throw new RuntimeException("Can't load sounds until SoundStore is init(). Use the container init() method.");
    }
    if (deferred) {
      throw new RuntimeException("deferred is not supported");
    }

    int buffer;

    if (loaded.get(ref) != null) {
      buffer = loaded.get(ref);
    } else {
      try {
        IntBuffer buf = BufferUtils.createIntBuffer(1);

        AiffData data = AiffData.create(in);
        if (data == null) {
          throw new IOException("Failed to create audio Aiff data.");
        }
        AL10.alGenBuffers(buf);
        AL10.alBufferData(buf.get(0), data.format, data.data, data.samplerate);

        loaded.put(ref, buf.get(0));
        buffer = buf.get(0);
      } catch (Exception e) {
        log.warning(e.toString());
        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
   */
  @Nonnull
  public Audio getWAV(@Nonnull String ref, @Nonnull final NiftyResourceLoader resourceLoader) 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
   */
  @Nonnull
  public Audio getWAV(@Nonnull 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
   */
  @Nonnull
  public Audio getWAV(String ref, @Nonnull InputStream in) throws IOException {
    if (!soundWorks) {
      return new NullAudio();
    }
    if (!initiated) {
      throw new RuntimeException("Can't load sounds until SoundStore is init(). Use the container init() method.");
    }
    if (deferred) {
      throw new RuntimeException("deferred is not supported");
    }

    int buffer;

    if (loaded.get(ref) != null) {
      buffer = loaded.get(ref);
    } else {
      try {
        IntBuffer buf = BufferUtils.createIntBuffer(1);

        WaveData data = WaveData.create(in);
        if (data == null) {
          throw new IOException("Failed to create audio wave data.");
        }
        AL10.alGenBuffers(buf);
        AL10.alBufferData(buf.get(0), data.format, data.data, data.samplerate);

        loaded.put(ref, buf.get(0));
        buffer = buf.get(0);
      } catch (Exception e) {
        log.warning(e.toString());
        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
   */
  @Nonnull
  public Audio getOggStream(String ref, final NiftyResourceLoader resourceLoader) throws IOException {
    if (!soundWorks || sources == null) {
      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, resourceLoader));
  }

  /**
   * 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
   */
  @Nonnull
  public Audio getOggStream(URL ref, final NiftyResourceLoader resourceLoader) throws IOException {
    if (!soundWorks || sources == null) {
      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, resourceLoader));
  }

  /**
   * 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
   */
  @Nonnull
  public Audio getOgg(@Nonnull String ref, @Nonnull final NiftyResourceLoader resourceLoader) 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
   */
  @Nonnull
  public Audio getOgg(@Nonnull 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
   */
  @Nonnull
  public Audio getOgg(String ref, InputStream in) throws IOException {
    if (!soundWorks) {
      return new NullAudio();
    }
    if (!initiated) {
      throw new RuntimeException("Can't load sounds until SoundStore is init(). Use the container init() method.");
    }
    if (deferred) {
      throw new RuntimeException("deferred is not supported");
    }

    int buffer;

    if (loaded.get(ref) != null) {
      buffer = loaded.get(ref);
    } 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, buf.get(0));

        buffer = buf.get(0);
      } catch (Exception e) {
        log.warning(e.toString());
        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(@Nullable MODSound sound) {
    if (!soundWorks || sources == null) {
      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(@Nullable OpenALStreamPlayer stream) {
    if (!soundWorks || sources == null) {
      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.warning("Error with OpenGL MOD Player on this this platform");
          log.warning(e.toString());
          mod = null;
        }
      }
      if (stream != null) {
        try {
          stream.update();
        } catch (OpenALException e) {
          log.warning("Error with OpenGL Streaming Player on this this platform");
          log.warning(e.toString());
          mod = null;
        }
      }
    }
  }

  /**
   * Check if the music is currently playing
   *
   * @return True if the music is playing
   */
  public boolean isMusicPlaying() {
    if (!soundWorks || sources == null) {
      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
   */
  @Nonnull
  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;
  }
}