package zero1hd.rhythmbullet.audio; import java.util.Observable; import java.util.Observer; import java.util.Random; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Preferences; import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.audio.Music.OnCompletionListener; import com.badlogic.gdx.files.FileHandle; /** * Manages current games music playback and does this in tandem with the {@link MusicList} by asking to retrieve files and then feeding it to LibGDX. * Notifies observers when a new song is loaded. * The loading model is like taking a disk and loading it into a player. It doesn't necessarily mean it'll play right away, but its ready and the only track that has a stream opened. * @author yunya * */ public class MusicController extends Observable implements OnCompletionListener, Observer { public final class States { public final Integer LOADED = new Integer(0), PLAYING = new Integer(1); } public final States states = new States(); private MusicList musicList; private MinimalAudioHeader musicHeader; private volatile Music music; private int currentlyPlayingIndex; private boolean autoPlay; private boolean shuffle; private Random rand; private Preferences prefs; public MusicController(MusicList musicList, Preferences prefs) { if (prefs == null) throw new NullPointerException("preferences can't be null..."); if (musicList == null) throw new NullPointerException("music list can't be null..."); musicList.addObserver(this); this.prefs = prefs; this.musicList = musicList; rand = new Random(); } /** * This play method automatically sets the volume. */ public void play() { if (music != null) { Gdx.app.debug("MusicController", "Playing from MLC."); music.play(); music.setVolume(prefs.getFloat("music vol", 1f)); setChanged(); notifyObservers(states.PLAYING); } } /** * Called to pause current song. Does nothing if no song is playing or loaded. */ public void pause() { if (music != null) { music.pause(); } } /** * Loads music based on the index in the {@link MusicList}. * @param index of music to play */ public void setMusicByIndex(int index) { this.currentlyPlayingIndex = index; loadMusic(); } /** * Loads music using the given file. The given file must be found in the {@link MusicList}. * This function gets the index of the given file within {@link MusicList} and passes that index to {@link #setMusicByIndex(index)}. * @param fileHandle to use. */ public void setMusicByFileHandle(FileHandle fileHandle) { setMusicByIndex(musicList.getMusicArray().indexOf(fileHandle, true)); } /** * Goes to the next track */ public void skip() { currentlyPlayingIndex++; if (shuffle) { shuffle(); } loadMusic(); } /** * Goes to the previous track */ public void previous() { currentlyPlayingIndex--; if (shuffle) { shuffle(); } loadMusic(); } @Override public void onCompletion(Music music) { if (autoPlay) { if (shuffle) { shuffle(); } else { currentlyPlayingIndex++; } loadMusic(); play(); } } /** * Shuffles the controller whether the shuffle boolean is true or false. */ public void shuffle() { Gdx.app.debug("MusicListController", "shuffled."); if (musicList.getTotal() == 0) { currentlyPlayingIndex = 0; } else { currentlyPlayingIndex = rand.nextInt(musicList.getTotal()); } } public void setAutoPlay(boolean autoPlay) { this.autoPlay = autoPlay; } public void setShuffle(boolean shuffle) { this.shuffle = shuffle; } public boolean isShuffle() { return shuffle; } public boolean isAutoPlay() { return autoPlay; } /** * Loads the current selected song. */ public void loadMusic() { Gdx.app.debug("MusicListController", "music is being loaded and listeners are being notified."); boolean playing = isPlaying(); musicHeader = null; if (music != null) { music.dispose(); } if (currentlyPlayingIndex < 0) { currentlyPlayingIndex = musicList.getTotal()-1; } if (currentlyPlayingIndex >= musicList.getTotal()) { currentlyPlayingIndex = 0; } if (musicList.getTotal() != 0) { this.music = Gdx.audio.newMusic(musicList.getMusicArray().get(currentlyPlayingIndex)); music.setOnCompletionListener(this); setChanged(); notifyObservers(states.LOADED); if (playing) { play(); } } } public MusicList getMusicList() { return musicList; } public FileHandle getCurrentMusicFileHandle() { return musicList.getSongFileHandleFromIndex(currentlyPlayingIndex); } public MinimalAudioHeader getCurrentMusicHeader() { if (musicHeader != null) { return musicHeader; } else { return musicHeader = musicList.newMinimalAudioHeader(getCurrentMusicFileHandle()); } } @Override public void update(Observable o, Object arg) { if (o == musicList) { if (arg == musicList.states.LOADING) { pause(); } else if (arg == musicList.states.COMPLETE) { if (shuffle) { shuffle(); } loadMusic(); if (autoPlay) { play(); } } } } public String getCurrentSongName() { return getCurrentMusicFileHandle().nameWithoutExtension(); } public float getCurrentPosition() { if (music != null) { return music.getPosition(); } return 0; } public void setMusicPosition(float position) { if (music != null) { music.setPosition(position); } } public boolean isPlaying() { if (music != null) { return music.isPlaying(); } return false; } /** * Returns the current music. In no circumstances should this be used to begin playing or it would be playing independent of the controller. * Doing otherwise would mean you are playing the music separately of the controller. * @return the {@link Music} that is currently "loaded" and ready to play. */ public Music getCurrentMusic() { return music; } public int getCurrentlyPlayingIndex() { return currentlyPlayingIndex; } public boolean hasSongLoaded() { if (music != null) { return true; } return false; } }