changed to integer state system for notifier in music controller; music

selection page rewritten making better use of framework objects.
Untested (still)
This commit is contained in:
Harrison Deng 2018-08-03 13:17:01 -05:00
parent 45da676d0d
commit b857ffe4bd
9 changed files with 276 additions and 124 deletions

View File

@ -18,14 +18,14 @@ import com.badlogic.gdx.files.FileHandle;
* *
*/ */
public class MusicController extends Observable implements OnCompletionListener, Observer { public class MusicController extends Observable implements OnCompletionListener, Observer {
public enum States { public final class States {
Loaded, Playing; public final Integer LOADED = 0, PLAYING = 1;
} }
public final States states = new States();
private MusicList musicList; private MusicList musicList;
private MinimalAudioHeader musicHeader; private MinimalAudioHeader musicHeader;
private Music music; private Music music;
private int currentPlaybackIndex; private int currentlyPlayingIndex;
private boolean autoPlay; private boolean autoPlay;
private boolean shuffle; private boolean shuffle;
private Random rand; private Random rand;
@ -48,7 +48,7 @@ public class MusicController extends Observable implements OnCompletionListener,
Gdx.app.debug("MusicListController", "Playing from MLC."); Gdx.app.debug("MusicListController", "Playing from MLC.");
music.play(); music.play();
music.setVolume(prefs.getFloat("music vol", 1f)); music.setVolume(prefs.getFloat("music vol", 1f));
notifyObservers(States.Playing); notifyObservers(states.PLAYING);
} else { } else {
Gdx.app.debug("MusicListController", "failed to begin playing. Load the music!!!"); Gdx.app.debug("MusicListController", "failed to begin playing. Load the music!!!");
} }
@ -68,15 +68,24 @@ public class MusicController extends Observable implements OnCompletionListener,
* @param index of music to play * @param index of music to play
*/ */
public void setMusicByIndex(int index) { public void setMusicByIndex(int index) {
this.currentPlaybackIndex = index; this.currentlyPlayingIndex = index;
loadMusic(); 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 * Goes to the next track
*/ */
public void skip() { public void skip() {
currentPlaybackIndex++; currentlyPlayingIndex++;
if (shuffle) { if (shuffle) {
shuffle(false); shuffle(false);
} }
@ -87,7 +96,7 @@ public class MusicController extends Observable implements OnCompletionListener,
* Goes to the previous track * Goes to the previous track
*/ */
public void previous() { public void previous() {
currentPlaybackIndex--; currentlyPlayingIndex--;
if (shuffle) { if (shuffle) {
shuffle(false); shuffle(false);
} }
@ -100,7 +109,7 @@ public class MusicController extends Observable implements OnCompletionListener,
if (shuffle) { if (shuffle) {
shuffle(false); shuffle(false);
} else { } else {
currentPlaybackIndex++; currentlyPlayingIndex++;
} }
loadMusic(); loadMusic();
play(); play();
@ -114,9 +123,9 @@ public class MusicController extends Observable implements OnCompletionListener,
public void shuffle(boolean load) { public void shuffle(boolean load) {
Gdx.app.debug("MusicListController", "shuffled."); Gdx.app.debug("MusicListController", "shuffled.");
if (musicList.getTotal() == 0) { if (musicList.getTotal() == 0) {
currentPlaybackIndex = 0; currentlyPlayingIndex = 0;
} else { } else {
currentPlaybackIndex = rand.nextInt(musicList.getTotal()); currentlyPlayingIndex = rand.nextInt(musicList.getTotal());
} }
if (load) { if (load) {
loadMusic(); loadMusic();
@ -148,13 +157,13 @@ public class MusicController extends Observable implements OnCompletionListener,
if (music != null) { if (music != null) {
music.dispose(); music.dispose();
} }
if (currentPlaybackIndex < 0) { if (currentlyPlayingIndex < 0) {
currentPlaybackIndex = musicList.getTotal()-1; currentlyPlayingIndex = musicList.getTotal()-1;
} }
if (currentPlaybackIndex >= musicList.getTotal()) { if (currentlyPlayingIndex >= musicList.getTotal()) {
currentPlaybackIndex = 0; currentlyPlayingIndex = 0;
} }
this.music = Gdx.audio.newMusic(musicList.getMusicArray().get(currentPlaybackIndex)); this.music = Gdx.audio.newMusic(musicList.getMusicArray().get(currentlyPlayingIndex));
music.setOnCompletionListener(this); music.setOnCompletionListener(this);
setChanged(); setChanged();
@ -162,7 +171,7 @@ public class MusicController extends Observable implements OnCompletionListener,
if (autoPlay) { if (autoPlay) {
play(); play();
} }
notifyObservers(States.Loaded); notifyObservers(states.LOADED);
} }
public MusicList getMusicList() { public MusicList getMusicList() {
@ -170,7 +179,7 @@ public class MusicController extends Observable implements OnCompletionListener,
} }
public FileHandle getCurrentMusicFileHandle() { public FileHandle getCurrentMusicFileHandle() {
return musicList.getSongFileHandleFromIndex(currentPlaybackIndex); return musicList.getSongFileHandleFromIndex(currentlyPlayingIndex);
} }
public MinimalAudioHeader getCurrentMusicHeader() { public MinimalAudioHeader getCurrentMusicHeader() {
@ -220,4 +229,8 @@ public class MusicController extends Observable implements OnCompletionListener,
public Music getCurrentMusic() { public Music getCurrentMusic() {
return music; return music;
} }
public int getCurrentlyPlayingIndex() {
return currentlyPlayingIndex;
}
} }

View File

@ -92,6 +92,11 @@ public class MusicList extends Observable {
searched = true; searched = true;
} }
/**
*
* @return the amount of audio files discovered.
*/
public int getTotal() { public int getTotal() {
return musicList.size; return musicList.size;
} }

View File

@ -51,7 +51,9 @@ public class MusicMetadataController extends Observable implements Disposable, O
} }
public int size() { public int size() {
return metadataArray.size; synchronized (loadingThread) {
return metadataArray.size;
}
} }
public AudioMetadata getMetadata(int index) { public AudioMetadata getMetadata(int index) {

View File

@ -1,6 +1,8 @@
package zero1hd.rhythmbullet.graphics.ui.components; package zero1hd.rhythmbullet.graphics.ui.components;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.ui.Button; import com.badlogic.gdx.scenes.scene2d.ui.Button;
@ -9,6 +11,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.audio.metadata.AudioMetadata; import zero1hd.rhythmbullet.audio.metadata.AudioMetadata;
@ -21,16 +24,17 @@ public class MusicSelectable extends Button {
private float timeSinceOnScreen; private float timeSinceOnScreen;
private AudioMetadata metadata; private AudioMetadata metadata;
private Texture defaultAlbumArt; private Texture defaultAlbumArt;
private Array<MusicSelectable> queueList;
public MusicSelectable(Skin skin, Texture defaultAlbumArt, AudioMetadata metadata) { public MusicSelectable(Skin skin, Texture defaultAlbumArt, AudioMetadata metadata,
Array<MusicSelectable> queueList) {
super(skin, "music-selectable"); super(skin, "music-selectable");
this.metadata = metadata; this.metadata = metadata;
this.defaultAlbumArt = defaultAlbumArt; this.defaultAlbumArt = defaultAlbumArt;
this.queueList = queueList;
album = new Image(defaultAlbumArt); album = new Image(defaultAlbumArt);
add(album).expand().left(); add(album).expand().left();
informationTable = new Table(); informationTable = new Table();
name = new ShortenedLabel(metadata.getTitle(), skin, "default-font", skin.getColor("default")); name = new ShortenedLabel(metadata.getTitle(), skin, "default-font", skin.getColor("default"));
informationTable.add(name).colspan(2).expandX(); informationTable.add(name).colspan(2).expandX();
@ -41,34 +45,60 @@ public class MusicSelectable extends Button {
informationTable.add(time).expandX(); informationTable.add(time).expandX();
add(informationTable).expand().fill(); add(informationTable).expand().fill();
} }
@Override @Override
public void act(float delta) { public void act(float delta) {
actualCoords.x = getX() + getParent().getX(); actualCoords.x = getX() + getParent().getX();
actualCoords.y = getY() + getParent().getY(); actualCoords.y = getY() + getParent().getY();
if (actualCoords.y < 0-getHeight() || actualCoords.y > getStage().getHeight() || actualCoords.x < 0-getWidth() || actualCoords.x > getStage().getWidth()) { if (actualCoords.y < 0 - getHeight() || actualCoords.y > getStage().getHeight()
|| actualCoords.x < 0 - getWidth() || actualCoords.x > getStage().getWidth()) {
offScreenAct(delta); offScreenAct(delta);
} else { } else {
onScreenAct(delta); onScreenAct(delta);
} }
super.act(delta); super.act(delta);
} }
public void onScreenAct(float delta) { @Override
timeSinceOnScreen = 0; public void draw(Batch batch, float parentAlpha) {
if (metadata.getAlbumCover() == null) { synchronized (album) {
metadata.loadAlbumCover(); super.draw(batch, parentAlpha);
album.setDrawable(new TextureRegionDrawable(new TextureRegion(metadata.getAlbumCover())));
} }
} }
public void onScreenAct(float delta) {
timeSinceOnScreen = 0;
if (!queueList.contains(this, true)) {
synchronized (queueList) {
queueList.add(this);
notify();
}
}
}
public void offScreenAct(float delta) { public void offScreenAct(float delta) {
if (metadata.getAlbumCover() != null) { if (metadata.getAlbumCover() != null) {
timeSinceOnScreen += delta; timeSinceOnScreen += delta;
if (timeSinceOnScreen >= 2) { if (timeSinceOnScreen >= 2) {
album.setDrawable(new TextureRegionDrawable(new TextureRegion(defaultAlbumArt)));
metadata.unloadAlbumCover(); metadata.unloadAlbumCover();
} }
} }
} }
public void loadAlbumCover() {
metadata.loadAlbumCover();
synchronized (album) {
album.setDrawable(new TextureRegionDrawable(new TextureRegion(metadata.getAlbumCover())));
}
}
public AudioMetadata getMetadata() {
return metadata;
}
public FileHandle getFileHandle() {
return metadata.getFileHandle();
}
} }

View File

@ -0,0 +1,60 @@
package zero1hd.rhythmbullet.util;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.scenes.scene2d.ui.ButtonGroup;
import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.audio.metadata.AudioMetadata;
import zero1hd.rhythmbullet.graphics.ui.components.MusicSelectable;
public class MusicSelectableButtonGroup extends ButtonGroup<MusicSelectable> {
private Array<MusicSelectable> buttons;
public MusicSelectableButtonGroup() {
buttons = getButtons();
}
public void setChecked(AudioMetadata metadata) {
if (metadata == null) throw new IllegalArgumentException("metadata can't be null.");
MusicSelectable button;
for (int i = 0; i < buttons.size; i++) {
button = buttons.get(i);
if (button.getMetadata() == metadata) {
button.setChecked(true);
return;
}
}
}
public void setChecked(FileHandle fileHandle) {
if (fileHandle == null) throw new IllegalArgumentException("fileHandle can't be null.");
MusicSelectable button;
for (int i = 0; i < buttons.size; i++) {
button = buttons.get(i);
if (button.getFileHandle() == fileHandle) {
button.setChecked(true);
return;
}
}
}
public void selectNext() {
int index = getCheckedIndex() + 1;
if (index == buttons.size) {
index = 0;
}
buttons.get(index).setChecked(true);
}
public void selectPrevious() {
int index = getCheckedIndex() - 1;
if (index == -1) {
index = buttons.size -1;
}
buttons.get(index).setChecked(true);
}
public void setChecked(int index) {
buttons.get(index).setChecked(true);
}
}

View File

@ -10,6 +10,7 @@ import java.util.Observer;
import org.lwjgl.openal.AL11; import org.lwjgl.openal.AL11;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetLoaderParameters.LoadedCallback;
import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic; import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic;
import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.TimeUtils;
@ -23,7 +24,7 @@ import zero1hd.rhythmbullet.audio.visualizer.BasicFFT;
public class PCMMachine implements Observer, Disposable { public class PCMMachine implements Observer, Disposable {
private int windowSize = 1024; private int windowSize = 1024;
private float[] PCM = new float[windowSize]; private float[] PCM = new float[windowSize];
private float[] frequencyBins = new float[windowSize/2]; private float[] frequencyBins = new float[windowSize / 2];
private BasicFFT fft = new BasicFFT(windowSize); private BasicFFT fft = new BasicFFT(windowSize);
private ShortBuffer playingBuffer; private ShortBuffer playingBuffer;
private ShortBuffer compareBuffer; private ShortBuffer compareBuffer;
@ -45,76 +46,76 @@ public class PCMMachine implements Observer, Disposable {
bufferField.setAccessible(true); bufferField.setAccessible(true);
Field bufferSizeField = ClassReflection.getDeclaredField(OpenALMusic.class, "bufferSize"); Field bufferSizeField = ClassReflection.getDeclaredField(OpenALMusic.class, "bufferSize");
bufferSizeField.setAccessible(true); bufferSizeField.setAccessible(true);
bufferSizeField.set(null, new Integer(4096*5)); bufferSizeField.set(null, new Integer(4096 * 5));
buffer = ((ByteBuffer) bufferField.get(null)).asShortBuffer().asReadOnlyBuffer(); buffer = ((ByteBuffer) bufferField.get(null)).asShortBuffer().asReadOnlyBuffer();
} catch (IllegalArgumentException | SecurityException | ReflectionException e) { } catch (IllegalArgumentException | SecurityException | ReflectionException e) {
e.printStackTrace(); e.printStackTrace();
Gdx.app.debug("Visualizer reflection", "Failed attempt at retrieving tempBuffer field."); Gdx.app.debug("Visualizer reflection", "Failed attempt at retrieving tempBuffer field.");
Gdx.app.exit(); Gdx.app.exit();
} }
streamReadThread = new BufferStreamReadThread(); streamReadThread = new BufferStreamReadThread();
} }
private synchronized void calcPCMData() { private synchronized void calcPCMData() {
short chanVal; short chanVal;
for (int sid = 0; sid < PCM.length && sid < playingBuffer.remaining(); sid++) { for (int sid = 0; sid < PCM.length && sid < playingBuffer.remaining(); sid++) {
PCM[sid] = 0; PCM[sid] = 0;
for (int channel = 0; channel < channelCount; channel ++) { for (int channel = 0; channel < channelCount; channel++) {
if (PCM[sid] < (chanVal = playingBuffer.get())) { if (PCM[sid] < (chanVal = playingBuffer.get())) {
PCM[sid] = chanVal; PCM[sid] = chanVal;
} }
} }
PCM[sid] /= Short.MAX_VALUE+1f; PCM[sid] /= Short.MAX_VALUE + 1f;
} }
// Take down original buffer position so we don't need to sync again after...
//Take down original buffer position so we don't need to sync again after...
int originalPos = buffer.position(); int originalPos = buffer.position();
//Begin comparison // Begin comparison
buffer.rewind(); buffer.rewind();
if (compareBuffer.compareTo(buffer) != 0) { if (compareBuffer.compareTo(buffer) != 0) {
bufferChanged(); bufferChanged();
// Begin copying current buffer to the comparison buffer
//Begin copying current buffer to the comparison buffer
compareBuffer.clear(); compareBuffer.clear();
compareBuffer.put(buffer); compareBuffer.put(buffer);
compareBuffer.flip(); compareBuffer.flip();
} }
//Reset buffer to proper position. // Reset buffer to proper position.
buffer.position(originalPos); buffer.position(originalPos);
} }
private void bufferChanged() { private void bufferChanged() {
//set position to beginning to prepare for overwrite // set position to beginning to prepare for overwrite
playingBuffer.position(0); playingBuffer.position(0);
//if the backing playing buffer is full, we have two sections: A and B. A is the one before B, and is read from due to playback latency and offset. B is merely a buffer. // if the backing playing buffer is full, we have two sections: A and B. A is
// the one before B, and is read from due to playback latency and offset. B is
// merely a buffer.
if (playingBuffer.limit() == playingBuffer.capacity()) { if (playingBuffer.limit() == playingBuffer.capacity()) {
//put the second portion into the first... // put the second portion into the first...
playingBuffer.put(playingBuffer.array(), buffer.capacity(), buffer.capacity()); playingBuffer.put(playingBuffer.array(), buffer.capacity(), buffer.capacity());
} }
//put the new buffer into the remaining portion. // put the new buffer into the remaining portion.
playingBuffer.put(compareBuffer); playingBuffer.put(compareBuffer);
synchronizeBufferWithPlayback(); synchronizeBufferWithPlayback();
} }
private int calcBufferPosition() { private int calcBufferPosition() {
int offset = (int) alGetSourcef(sourceID, AL11.AL_SAMPLE_OFFSET); int offset = (int) alGetSourcef(sourceID, AL11.AL_SAMPLE_OFFSET);
offset = (offset/windowSize) * windowSize; offset = (offset / windowSize) * windowSize;
return offset; return offset;
} }
private void synchronizeBufferWithPlayback() { private void synchronizeBufferWithPlayback() {
playingBuffer.position(calcBufferPosition()); playingBuffer.position(calcBufferPosition());
windowsRead = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize); windowsRead = (int) ((mc.getCurrentPosition() * sampleRate) / windowSize);
} }
private void setMusic() { private void setMusic() {
try { try {
Field sourceIDField = ClassReflection.getDeclaredField(OpenALMusic.class, "sourceID"); Field sourceIDField = ClassReflection.getDeclaredField(OpenALMusic.class, "sourceID");
@ -123,23 +124,23 @@ public class PCMMachine implements Observer, Disposable {
} catch (ReflectionException e) { } catch (ReflectionException e) {
e.printStackTrace(); e.printStackTrace();
} }
channelCount = mc.getCurrentMusicHeader().getChannelCount(); channelCount = mc.getCurrentMusicHeader().getChannelCount();
sampleRate = mc.getCurrentMusicHeader().getSampleRate(); sampleRate = mc.getCurrentMusicHeader().getSampleRate();
playingBuffer = ShortBuffer.allocate(buffer.capacity()*2); playingBuffer = ShortBuffer.allocate(buffer.capacity() * 2);
buffer.rewind(); buffer.rewind();
playingBuffer.put(buffer); playingBuffer.put(buffer);
playingBuffer.flip(); playingBuffer.flip();
compareBuffer = ShortBuffer.allocate(buffer.capacity()); compareBuffer = ShortBuffer.allocate(buffer.capacity());
buffer.rewind(); buffer.rewind();
compareBuffer.put(buffer); compareBuffer.put(buffer);
compareBuffer.flip(); compareBuffer.flip();
buffer.rewind(); buffer.rewind();
} }
public float[] getFrequencyBins() { public float[] getFrequencyBins() {
if (updated) { if (updated) {
synchronized (PCM) { synchronized (PCM) {
@ -149,11 +150,11 @@ public class PCMMachine implements Observer, Disposable {
} }
return frequencyBins; return frequencyBins;
} }
public int getWindowSize() { public int getWindowSize() {
return windowSize; return windowSize;
} }
private class BufferStreamReadThread implements Runnable { private class BufferStreamReadThread implements Runnable {
private String name = "PCM-Audio-Processing"; private String name = "PCM-Audio-Processing";
private Thread thread; private Thread thread;
@ -161,7 +162,7 @@ public class PCMMachine implements Observer, Disposable {
private boolean paused; private boolean paused;
private long timeOfLastRead; private long timeOfLastRead;
private int waitTime; private int waitTime;
@Override @Override
public void run() { public void run() {
while (run) { while (run) {
@ -170,14 +171,14 @@ public class PCMMachine implements Observer, Disposable {
timeOfLastRead = TimeUtils.millis(); timeOfLastRead = TimeUtils.millis();
paused = false; paused = false;
} }
waitTime = sampleRate/windowSize/Gdx.graphics.getFramesPerSecond(); waitTime = sampleRate / windowSize / Gdx.graphics.getFramesPerSecond();
if (TimeUtils.timeSinceMillis(timeOfLastRead) >= waitTime) { if (TimeUtils.timeSinceMillis(timeOfLastRead) >= waitTime) {
calcPCMData(); calcPCMData();
updated = true; updated = true;
windowsRead++; windowsRead++;
timeOfLastRead = TimeUtils.millis(); timeOfLastRead = TimeUtils.millis();
currentPlaybackWindow = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize); currentPlaybackWindow = (int) ((mc.getCurrentPosition() * sampleRate) / windowSize);
if (windowsRead != currentPlaybackWindow) { if (windowsRead != currentPlaybackWindow) {
synchronizeBufferWithPlayback(); synchronizeBufferWithPlayback();
} }
@ -194,7 +195,7 @@ public class PCMMachine implements Observer, Disposable {
} }
} }
} }
public void start() { public void start() {
if (thread == null && !thread.isAlive()) { if (thread == null && !thread.isAlive()) {
thread = new Thread(this, name); thread = new Thread(this, name);
@ -205,7 +206,7 @@ public class PCMMachine implements Observer, Disposable {
} }
} }
} }
public void stop() { public void stop() {
run = false; run = false;
} }
@ -214,15 +215,10 @@ public class PCMMachine implements Observer, Disposable {
@Override @Override
public void update(Observable o, Object arg) { public void update(Observable o, Object arg) {
if (o == mc) { if (o == mc) {
switch ((MusicController.States) arg) { if (arg == mc.states.LOADED) {
case Loaded:
setMusic(); setMusic();
break; } else if (arg == mc.states.PLAYING) {
case Playing:
streamReadThread.start(); streamReadThread.start();
break;
default:
break;
} }
} }
} }

View File

@ -110,7 +110,7 @@ public class MainPage extends Page implements Observer {
@Override @Override
public void update(Observable o, Object arg) { public void update(Observable o, Object arg) {
if (o == mc && arg == MusicController.States.Loaded) { if (o == mc && arg == mc.states.LOADED) {
scrollText.setText("Currently playing: " + mc.getCurrentSongName(), null); scrollText.setText("Currently playing: " + mc.getCurrentSongName(), null);
} }
} }

View File

@ -233,7 +233,6 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
ChangeListener analysisPageButtonListener = new ChangeListener() { ChangeListener analysisPageButtonListener = new ChangeListener() {
@Override @Override
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
musicSelectionPage.getSelectedMusic();
setDisplayedPage(analysisPage); setDisplayedPage(analysisPage);
} }
}; };

View File

@ -1,6 +1,5 @@
package zero1hd.rhythmbullet.desktop.screens.main; package zero1hd.rhythmbullet.desktop.screens.main;
import java.util.HashMap;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
@ -8,23 +7,20 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Preferences; import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Stack;
import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.audio.MusicMetadataController; import zero1hd.rhythmbullet.audio.MusicMetadataController;
@ -32,18 +28,21 @@ import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.desktop.graphics.ui.pages.Page; import zero1hd.rhythmbullet.desktop.graphics.ui.pages.Page;
import zero1hd.rhythmbullet.graphics.ui.components.MusicSelectable; import zero1hd.rhythmbullet.graphics.ui.components.MusicSelectable;
import zero1hd.rhythmbullet.graphics.ui.components.ScrollText; import zero1hd.rhythmbullet.graphics.ui.components.ScrollText;
import zero1hd.rhythmbullet.util.MusicSelectableButtonGroup;
public class MusicSelectionPage extends Page implements Observer { public class MusicSelectionPage extends Page implements Observer {
Preferences musicFileAnnotation; Preferences musicFileAnnotation;
private MusicController mc; private MusicController mc;
private MusicMetadataController mmc; private MusicMetadataController mmc;
private MusicSelectableButtonGroup selectables;
private Array<MusicSelectable> selectables; private Stack stackSelectables;
private TextButton back; private TextButton back;
private Table musicTable; private Table musicTable;
private ScrollPane musicTableScrollPane; private ScrollPane musicTableScrollPane;
private musicSelectionLoaderThread thread;
private Table musicInfoTable; private Table musicInfoTable;
private Table musicSubInfo; private Table musicSubInfo;
private ScrollText songTitle; private ScrollText songTitle;
@ -54,9 +53,9 @@ public class MusicSelectionPage extends Page implements Observer {
private Image albumCover; private Image albumCover;
private AssetManager assets; private AssetManager assets;
private Skin skin;
private boolean down, up; private boolean down, up;
private int musicSelectableIndex;
private TextButton beginButton; private TextButton beginButton;
@ -67,6 +66,9 @@ public class MusicSelectionPage extends Page implements Observer {
this.assets = assetManager; this.assets = assetManager;
this.mc = musicController; this.mc = musicController;
this.mmc = musicMetadataController; this.mmc = musicMetadataController;
this.skin = skin;
stackSelectables = new Stack();
selectables = new MusicSelectableButtonGroup();
musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation"); musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation");
musicTable = new Table(); musicTable = new Table();
@ -83,7 +85,7 @@ public class MusicSelectionPage extends Page implements Observer {
back.addListener(backButtonListener); back.addListener(backButtonListener);
addActor(back); addActor(back);
back.toFront(); back.toFront();
addListener(new InputListener() { addListener(new InputListener() {
@Override @Override
public boolean keyDown(InputEvent event, int keycode) { public boolean keyDown(InputEvent event, int keycode) {
@ -127,6 +129,10 @@ public class MusicSelectionPage extends Page implements Observer {
beginButton = new TextButton("Begin", skin); beginButton = new TextButton("Begin", skin);
beginButton.addListener(beginButtonListener); beginButton.addListener(beginButtonListener);
mmc.addObserver(this);
thread = new musicSelectionLoaderThread();
} }
@Override @Override
@ -159,47 +165,51 @@ public class MusicSelectionPage extends Page implements Observer {
if (songSelectionTimer > 0f) { if (songSelectionTimer > 0f) {
songSelectionTimer -= delta; songSelectionTimer -= delta;
if (songSelectionTimer <= 0f) { if (songSelectionTimer <= 0f) {
setCurrentMusic(); }
}
if (selectables.getButtons().size == mc.getMusicList().getTotal()) {
if (selectables.getButtons().size != stackSelectables.getChildren().size) {
int index = selectables.getButtons().size - stackSelectables.getChildren().size - 1;
stackSelectables.add(selectables.getButtons().get(index));
} else {
if (selectables.getChecked() == null) {
selectables.setChecked(mc.getCurrentMusicFileHandle());
} else if (selectables.getChecked().getFileHandle() != mc.getCurrentMusicFileHandle()) {
songSelectionTimer += delta;
if (songSelectionTimer > 2f) {
mc.setMusicByFileHandle(selectables.getChecked().getFileHandle());
}
} else {
songSelectionTimer = 0;
}
} }
} }
super.act(delta); super.act(delta);
} }
private void scrollDown() { private void scrollDown() {
if ((musicSelectableIndex = (musicTable.getChildren().indexOf(currentlySelected, true) + 1)) == musicTable.getChildren().size) { selectables.selectNext();
musicSelectableIndex = 0; musicTableScrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight());
}
((MusicSelectable)musicTable.getChildren().get(musicSelectableIndex)).select();
musicTableScrollPane.scrollTo(currentlySelected.getX(), currentlySelected.getY(), currentlySelected.getWidth(), currentlySelected.getHeight());
} }
private void scrollUp() { private void scrollUp() {
if ((musicSelectableIndex = (musicTable.getChildren().indexOf(currentlySelected, true) - 1)) < 0) { selectables.selectPrevious();
musicSelectableIndex = musicTable.getChildren().size-1; musicTableScrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight());
}
((MusicSelectable)musicTable.getChildren().get(musicSelectableIndex)).select();
musicTableScrollPane.scrollTo(currentlySelected.getX(), currentlySelected.getY(), currentlySelected.getWidth(), currentlySelected.getHeight());
} }
public FileHandle getSelectedMusic() { public FileHandle getSelectedMusic() {
if (currentlySelected != null) { return selectables.getChecked().getMetadata().getFileHandle();
return currentlySelected.getMusicFile();
} else {
return null;
}
} }
public void refreshUIList() { public void refreshUIList() {
for (int i = 0; i < selectables.size; i++) { selectables.clear();
selectables.get(i).dispose();
}
mmc.loadAudioMetadata(); mmc.loadAudioMetadata();
musicTable.clear(); musicTable.clear();
selectables.clear(); selectables.clear();
musicInfoTable.clear(); musicInfoTable.clear();
musicSubInfo.clear(); musicSubInfo.clear();
uiSongCount = 0;
uiSongInfoCount = 0;
Gdx.app.debug("MusicSelectionPage", "Refreshing..."); Gdx.app.debug("MusicSelectionPage", "Refreshing...");
@ -226,18 +236,17 @@ public class MusicSelectionPage extends Page implements Observer {
super.dispose(); super.dispose();
} }
@Override @Override
public void update(Observable o, Object arg) { public void update(Observable o, Object arg) {
if (o == mc && arg == MusicController.States.Loaded) { if (o == mc && arg == mc.states.LOADED) {
selectMusicUI(mc.getCurrentMusicFileHandle()); if (selectables.getChecked().getFileHandle() != mc.getCurrentMusicFileHandle()) {
selectables.setChecked(mc.getCurrentlyPlayingIndex());
}
} else if (o == mmc) {
thread.start();
} }
} }
public void selectMusicUI(FileHandle fileHandle) {
selectables.get(mc.getMusicList().getMusicArray().indexOf(fileHandle, true)).select();
}
@Override @Override
public void setCameraPositionToPage(Vector3 cameraPosition) { public void setCameraPositionToPage(Vector3 cameraPosition) {
getStage().setKeyboardFocus(this); getStage().setKeyboardFocus(this);
@ -245,11 +254,49 @@ public class MusicSelectionPage extends Page implements Observer {
} }
private class musicSelectionLoaderThread implements Runnable { private class musicSelectionLoaderThread implements Runnable {
private Thread thread;
private Array<MusicSelectable> queueList;
private String name = "Music-Selection-Loader-Thread";
private volatile boolean work;
public musicSelectionLoaderThread() {
queueList = new Array<>();
}
@Override @Override
public void run() { public void run() {
// TODO Auto-generated method stub while (work) {
while (selectables.getButtons().size != mc.getMusicList().getTotal()) {
MusicSelectable selectable = new MusicSelectable(skin, assets.get("defaultCover.png"), mmc.getMetadata(selectables.getButtons().size), queueList);
selectables.add(selectable);
}
for (int i = 0; i < queueList.size; i++) {
queueList.get(i).loadAlbumCover();
queueList.removeIndex(i);
}
synchronized (queueList) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} }
public boolean start() {
if (thread == null) {
thread = new Thread(this, name);
thread.start();
return true;
}
synchronized (queueList) {
notify();
}
return false;
}
} }
} }