253 lines
6.2 KiB
Java
Executable File

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