Path: blob/master/SLICK_HOME/src/org/newdawn/slick/Music.java
1456 views
package org.newdawn.slick;12import java.net.URL;3import java.util.ArrayList;45import org.newdawn.slick.openal.Audio;6import org.newdawn.slick.openal.AudioImpl;7import org.newdawn.slick.openal.SoundStore;8import org.newdawn.slick.util.Log;910/**11* A piece of music loaded and playable within the game. Only one piece of music can12* play at any given time and a channel is reserved so music will always play.13*14* @author kevin15* @author Nathan Sweet <[email protected]>16*/17public class Music {18/** The music currently being played or null if none */19private static Music currentMusic;2021/**22* Poll the state of the current music. This causes streaming music23* to stream and checks listeners. Note that if you're using a game container24* this will be auto-magically called for you.25*26* @param delta The amount of time since last poll27*/28public static void poll(int delta) {29if (currentMusic != null) {30SoundStore.get().poll(delta);31if (!SoundStore.get().isMusicPlaying()) {32if (!currentMusic.positioning) {33Music oldMusic = currentMusic;34currentMusic = null;35oldMusic.fireMusicEnded();36}37} else {38currentMusic.update(delta);39}40}41}4243/** The sound from FECK representing this music */44private Audio sound;45/** True if the music is playing */46private boolean playing;47/** The list of listeners waiting for notification that the music ended */48private ArrayList listeners = new ArrayList();49/** The volume of this music */50private float volume = 1.0f;51/** Start gain for fading in/out */52private float fadeStartGain;53/** End gain for fading in/out */54private float fadeEndGain;55/** Countdown for fading in/out */56private int fadeTime;57/** Duration for fading in/out */58private int fadeDuration;59/** True if music should be stopped after fading in/out */60private boolean stopAfterFade;61/** True if the music is being repositioned and it is therefore normal that it's not playing */62private boolean positioning;63/** The position that was requested */64private float requiredPosition = -1;6566/**67* Create and load a piece of music (either OGG or MOD/XM)68*69* @param ref The location of the music70* @throws SlickException71*/72public Music(String ref) throws SlickException {73this(ref, false);74}7576/**77* Create and load a piece of music (either OGG or MOD/XM)78*79* @param ref The location of the music80* @throws SlickException81*/82public Music(URL ref) throws SlickException {83this(ref, false);84}8586/**87* Create and load a piece of music (either OGG or MOD/XM)88*89* @param url The location of the music90* @param streamingHint A hint to indicate whether streaming should be used if possible91* @throws SlickException92*/93public Music(URL url, boolean streamingHint) throws SlickException {94SoundStore.get().init();95String ref = url.getFile();9697try {98if (ref.toLowerCase().endsWith(".ogg")) {99if (streamingHint) {100sound = SoundStore.get().getOggStream(url);101} else {102sound = SoundStore.get().getOgg(url.openStream());103}104} else if (ref.toLowerCase().endsWith(".wav")) {105sound = SoundStore.get().getWAV(url.openStream());106} else if (ref.toLowerCase().endsWith(".xm") || ref.toLowerCase().endsWith(".mod")) {107sound = SoundStore.get().getMOD(url.openStream());108} else if (ref.toLowerCase().endsWith(".aif") || ref.toLowerCase().endsWith(".aiff")) {109sound = SoundStore.get().getAIF(url.openStream());110} else {111throw new SlickException("Only .xm, .mod, .ogg, and .aif/f are currently supported.");112}113} catch (Exception e) {114Log.error(e);115throw new SlickException("Failed to load sound: "+url);116}117}118119/**120* Create and load a piece of music (either OGG or MOD/XM)121*122* @param ref The location of the music123* @param streamingHint A hint to indicate whether streaming should be used if possible124* @throws SlickException125*/126public Music(String ref, boolean streamingHint) throws SlickException {127SoundStore.get().init();128129try {130if (ref.toLowerCase().endsWith(".ogg")) {131if (streamingHint) {132sound = SoundStore.get().getOggStream(ref);133} else {134sound = SoundStore.get().getOgg(ref);135}136} else if (ref.toLowerCase().endsWith(".wav")) {137sound = SoundStore.get().getWAV(ref);138} else if (ref.toLowerCase().endsWith(".xm") || ref.toLowerCase().endsWith(".mod")) {139sound = SoundStore.get().getMOD(ref);140} else if (ref.toLowerCase().endsWith(".aif") || ref.toLowerCase().endsWith(".aiff")) {141sound = SoundStore.get().getAIF(ref);142} else {143throw new SlickException("Only .xm, .mod, .ogg, and .aif/f are currently supported.");144}145} catch (Exception e) {146Log.error(e);147throw new SlickException("Failed to load sound: "+ref);148}149}150151/**152* Add a listener to this music153*154* @param listener The listener to add155*/156public void addListener(MusicListener listener) {157listeners.add(listener);158}159160/**161* Remove a listener from this music162*163* @param listener The listener to remove164*/165public void removeListener(MusicListener listener) {166listeners.remove(listener);167}168169/**170* Fire notifications that this music ended171*/172private void fireMusicEnded() {173playing = false;174for (int i=0;i<listeners.size();i++) {175((MusicListener) listeners.get(i)).musicEnded(this);176}177}178179/**180* Fire notifications that this music was swapped out181*182* @param newMusic The new music that will be played183*/184private void fireMusicSwapped(Music newMusic) {185playing = false;186for (int i=0;i<listeners.size();i++) {187((MusicListener) listeners.get(i)).musicSwapped(this, newMusic);188}189}190/**191* Loop the music192*/193public void loop() {194loop(1.0f, 1.0f);195}196197/**198* Play the music199*/200public void play() {201play(1.0f, 1.0f);202}203204/**205* Play the music at a given pitch and volume206*207* @param pitch The pitch to play the music at (1.0 = default)208* @param volume The volume to play the music at (1.0 = default)209*/210public void play(float pitch, float volume) {211startMusic(pitch, volume, false);212}213214/**215* Loop the music at a given pitch and volume216*217* @param pitch The pitch to play the music at (1.0 = default)218* @param volume The volume to play the music at (1.0 = default)219*/220public void loop(float pitch, float volume) {221startMusic(pitch, volume, true);222}223224/**225* play or loop the music at a given pitch and volume226* @param pitch The pitch to play the music at (1.0 = default)227* @param volume The volume to play the music at (1.0 = default)228* @param loop if false the music is played once, the music is looped otherwise229*/230private void startMusic(float pitch, float volume, boolean loop) {231if (currentMusic != null) {232currentMusic.stop();233currentMusic.fireMusicSwapped(this);234}235236currentMusic = this;237if (volume < 0.0f)238volume = 0.0f;239if (volume > 1.0f)240volume = 1.0f;241242sound.playAsMusic(pitch, volume, loop);243playing = true;244setVolume(volume);245if (requiredPosition != -1) {246setPosition(requiredPosition);247}248}249250/**251* Pause the music playback252*/253public void pause() {254playing = false;255AudioImpl.pauseMusic();256}257258/**259* Stop the music playing260*/261public void stop() {262sound.stop();263}264265/**266* Resume the music playback267*/268public void resume() {269playing = true;270AudioImpl.restartMusic();271}272273/**274* Check if the music is being played275*276* @return True if the music is being played277*/278public boolean playing() {279return (currentMusic == this) && (playing);280}281282/**283* Set the volume of the music as a factor of the global volume setting284*285* @param volume The volume to play music at. 0 - 1, 1 is Max286*/287public void setVolume(float volume) {288// Bounds check289if(volume > 1) {290volume = 1;291} else if(volume < 0) {292volume = 0;293}294295this.volume = volume;296// This sound is being played as music297if (currentMusic == this) {298SoundStore.get().setCurrentMusicVolume(volume);299}300}301302/**303* Get the individual volume of the music304* @return The volume of this music, still effected by global SoundStore volume. 0 - 1, 1 is Max305*/306public float getVolume() {307return volume;308}309310/**311* Fade this music to the volume specified312*313* @param duration Fade time in milliseconds.314* @param endVolume The target volume315* @param stopAfterFade True if music should be stopped after fading in/out316*/317public void fade (int duration, float endVolume, boolean stopAfterFade) {318this.stopAfterFade = stopAfterFade;319fadeStartGain = volume;320fadeEndGain = endVolume;321fadeDuration = duration;322fadeTime = duration;323}324325/**326* Update the current music applying any effects that need to updated per327* tick.328*329* @param delta The amount of time in milliseconds thats passed since last update330*/331void update(int delta) {332if (!playing) {333return;334}335336if (fadeTime > 0) {337fadeTime -= delta;338if (fadeTime < 0) {339fadeTime = 0;340if (stopAfterFade) {341stop();342return;343}344}345346float offset = (fadeEndGain - fadeStartGain) * (1 - (fadeTime / (float)fadeDuration));347setVolume(fadeStartGain + offset);348}349}350351/**352* Seeks to a position in the music. For streaming music, seeking before the current position causes353* the stream to be reloaded.354*355* @param position Position in seconds.356* @return True if the seek was successful357*/358public boolean setPosition(float position) {359if (playing) {360requiredPosition = -1;361362positioning = true;363playing = false;364boolean result = sound.setPosition(position);365playing = true;366positioning = false;367368return result;369} else {370requiredPosition = position;371return false;372}373}374375/**376* The position into the sound thats being played377*378* @return The current position in seconds.379*/380public float getPosition () {381return sound.getPosition();382}383}384385386