diff --git a/core/src/zero1hd/rhythmbullet/audio/MusicController.java b/core/src/zero1hd/rhythmbullet/audio/MusicController.java index 3582675..2059d22 100755 --- a/core/src/zero1hd/rhythmbullet/audio/MusicController.java +++ b/core/src/zero1hd/rhythmbullet/audio/MusicController.java @@ -18,14 +18,14 @@ import com.badlogic.gdx.files.FileHandle; * */ public class MusicController extends Observable implements OnCompletionListener, Observer { - public enum States { - Loaded, Playing; + public final class States { + public final Integer LOADED = 0, PLAYING = 1; } - + public final States states = new States(); private MusicList musicList; private MinimalAudioHeader musicHeader; private Music music; - private int currentPlaybackIndex; + private int currentlyPlayingIndex; private boolean autoPlay; private boolean shuffle; private Random rand; @@ -48,7 +48,7 @@ public class MusicController extends Observable implements OnCompletionListener, Gdx.app.debug("MusicListController", "Playing from MLC."); music.play(); music.setVolume(prefs.getFloat("music vol", 1f)); - notifyObservers(States.Playing); + notifyObservers(states.PLAYING); } else { 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 */ public void setMusicByIndex(int index) { - this.currentPlaybackIndex = 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() { - currentPlaybackIndex++; + currentlyPlayingIndex++; if (shuffle) { shuffle(false); } @@ -87,7 +96,7 @@ public class MusicController extends Observable implements OnCompletionListener, * Goes to the previous track */ public void previous() { - currentPlaybackIndex--; + currentlyPlayingIndex--; if (shuffle) { shuffle(false); } @@ -100,7 +109,7 @@ public class MusicController extends Observable implements OnCompletionListener, if (shuffle) { shuffle(false); } else { - currentPlaybackIndex++; + currentlyPlayingIndex++; } loadMusic(); play(); @@ -114,9 +123,9 @@ public class MusicController extends Observable implements OnCompletionListener, public void shuffle(boolean load) { Gdx.app.debug("MusicListController", "shuffled."); if (musicList.getTotal() == 0) { - currentPlaybackIndex = 0; + currentlyPlayingIndex = 0; } else { - currentPlaybackIndex = rand.nextInt(musicList.getTotal()); + currentlyPlayingIndex = rand.nextInt(musicList.getTotal()); } if (load) { loadMusic(); @@ -148,13 +157,13 @@ public class MusicController extends Observable implements OnCompletionListener, if (music != null) { music.dispose(); } - if (currentPlaybackIndex < 0) { - currentPlaybackIndex = musicList.getTotal()-1; + if (currentlyPlayingIndex < 0) { + currentlyPlayingIndex = musicList.getTotal()-1; } - if (currentPlaybackIndex >= musicList.getTotal()) { - currentPlaybackIndex = 0; + if (currentlyPlayingIndex >= musicList.getTotal()) { + currentlyPlayingIndex = 0; } - this.music = Gdx.audio.newMusic(musicList.getMusicArray().get(currentPlaybackIndex)); + this.music = Gdx.audio.newMusic(musicList.getMusicArray().get(currentlyPlayingIndex)); music.setOnCompletionListener(this); setChanged(); @@ -162,7 +171,7 @@ public class MusicController extends Observable implements OnCompletionListener, if (autoPlay) { play(); } - notifyObservers(States.Loaded); + notifyObservers(states.LOADED); } public MusicList getMusicList() { @@ -170,7 +179,7 @@ public class MusicController extends Observable implements OnCompletionListener, } public FileHandle getCurrentMusicFileHandle() { - return musicList.getSongFileHandleFromIndex(currentPlaybackIndex); + return musicList.getSongFileHandleFromIndex(currentlyPlayingIndex); } public MinimalAudioHeader getCurrentMusicHeader() { @@ -220,4 +229,8 @@ public class MusicController extends Observable implements OnCompletionListener, public Music getCurrentMusic() { return music; } + + public int getCurrentlyPlayingIndex() { + return currentlyPlayingIndex; + } } diff --git a/core/src/zero1hd/rhythmbullet/audio/MusicList.java b/core/src/zero1hd/rhythmbullet/audio/MusicList.java index 3f1a788..a86cb25 100755 --- a/core/src/zero1hd/rhythmbullet/audio/MusicList.java +++ b/core/src/zero1hd/rhythmbullet/audio/MusicList.java @@ -92,6 +92,11 @@ public class MusicList extends Observable { searched = true; } + + /** + * + * @return the amount of audio files discovered. + */ public int getTotal() { return musicList.size; } diff --git a/core/src/zero1hd/rhythmbullet/audio/MusicMetadataController.java b/core/src/zero1hd/rhythmbullet/audio/MusicMetadataController.java index 3d50dac..3262ebd 100755 --- a/core/src/zero1hd/rhythmbullet/audio/MusicMetadataController.java +++ b/core/src/zero1hd/rhythmbullet/audio/MusicMetadataController.java @@ -51,7 +51,9 @@ public class MusicMetadataController extends Observable implements Disposable, O } public int size() { - return metadataArray.size; + synchronized (loadingThread) { + return metadataArray.size; + } } public AudioMetadata getMetadata(int index) { diff --git a/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectable.java b/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectable.java index ab97ce0..92fb9d8 100755 --- a/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectable.java +++ b/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectable.java @@ -1,6 +1,8 @@ package zero1hd.rhythmbullet.graphics.ui.components; +import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Vector2; 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.Table; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; +import com.badlogic.gdx.utils.Array; import zero1hd.rhythmbullet.audio.metadata.AudioMetadata; @@ -21,16 +24,17 @@ public class MusicSelectable extends Button { private float timeSinceOnScreen; private AudioMetadata metadata; private Texture defaultAlbumArt; - - - public MusicSelectable(Skin skin, Texture defaultAlbumArt, AudioMetadata metadata) { + private Array queueList; + + public MusicSelectable(Skin skin, Texture defaultAlbumArt, AudioMetadata metadata, + Array queueList) { super(skin, "music-selectable"); this.metadata = metadata; this.defaultAlbumArt = defaultAlbumArt; - + this.queueList = queueList; album = new Image(defaultAlbumArt); add(album).expand().left(); - + informationTable = new Table(); name = new ShortenedLabel(metadata.getTitle(), skin, "default-font", skin.getColor("default")); informationTable.add(name).colspan(2).expandX(); @@ -41,34 +45,60 @@ public class MusicSelectable extends Button { informationTable.add(time).expandX(); add(informationTable).expand().fill(); } - + @Override public void act(float delta) { actualCoords.x = getX() + getParent().getX(); 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); } else { onScreenAct(delta); } super.act(delta); } - - public void onScreenAct(float delta) { - timeSinceOnScreen = 0; - if (metadata.getAlbumCover() == null) { - metadata.loadAlbumCover(); - album.setDrawable(new TextureRegionDrawable(new TextureRegion(metadata.getAlbumCover()))); + + @Override + public void draw(Batch batch, float parentAlpha) { + synchronized (album) { + super.draw(batch, parentAlpha); } } + public void onScreenAct(float delta) { + timeSinceOnScreen = 0; + if (!queueList.contains(this, true)) { + synchronized (queueList) { + queueList.add(this); + notify(); + } + } + } + public void offScreenAct(float delta) { if (metadata.getAlbumCover() != null) { timeSinceOnScreen += delta; if (timeSinceOnScreen >= 2) { + album.setDrawable(new TextureRegionDrawable(new TextureRegion(defaultAlbumArt))); 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(); + } } diff --git a/core/src/zero1hd/rhythmbullet/util/MusicSelectableButtonGroup.java b/core/src/zero1hd/rhythmbullet/util/MusicSelectableButtonGroup.java new file mode 100644 index 0000000..dea9ce9 --- /dev/null +++ b/core/src/zero1hd/rhythmbullet/util/MusicSelectableButtonGroup.java @@ -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 { + private Array 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); + } +} diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/audio/visualizer/PCMMachine.java b/desktop/src/zero1hd/rhythmbullet/desktop/audio/visualizer/PCMMachine.java index debefe4..b26f8c5 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/audio/visualizer/PCMMachine.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/audio/visualizer/PCMMachine.java @@ -10,6 +10,7 @@ import java.util.Observer; import org.lwjgl.openal.AL11; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.assets.AssetLoaderParameters.LoadedCallback; import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic; import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.TimeUtils; @@ -23,7 +24,7 @@ import zero1hd.rhythmbullet.audio.visualizer.BasicFFT; public class PCMMachine implements Observer, Disposable { private int windowSize = 1024; 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 ShortBuffer playingBuffer; private ShortBuffer compareBuffer; @@ -45,76 +46,76 @@ public class PCMMachine implements Observer, Disposable { bufferField.setAccessible(true); Field bufferSizeField = ClassReflection.getDeclaredField(OpenALMusic.class, "bufferSize"); bufferSizeField.setAccessible(true); - bufferSizeField.set(null, new Integer(4096*5)); - + bufferSizeField.set(null, new Integer(4096 * 5)); + buffer = ((ByteBuffer) bufferField.get(null)).asShortBuffer().asReadOnlyBuffer(); } catch (IllegalArgumentException | SecurityException | ReflectionException e) { e.printStackTrace(); Gdx.app.debug("Visualizer reflection", "Failed attempt at retrieving tempBuffer field."); Gdx.app.exit(); } - + streamReadThread = new BufferStreamReadThread(); } - + private synchronized void calcPCMData() { short chanVal; for (int sid = 0; sid < PCM.length && sid < playingBuffer.remaining(); sid++) { PCM[sid] = 0; - for (int channel = 0; channel < channelCount; channel ++) { + for (int channel = 0; channel < channelCount; channel++) { if (PCM[sid] < (chanVal = playingBuffer.get())) { 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(); - - //Begin comparison + + // Begin comparison buffer.rewind(); if (compareBuffer.compareTo(buffer) != 0) { bufferChanged(); - - - //Begin copying current buffer to the comparison buffer + + // Begin copying current buffer to the comparison buffer compareBuffer.clear(); compareBuffer.put(buffer); compareBuffer.flip(); } - - //Reset buffer to proper position. + + // Reset buffer to proper position. buffer.position(originalPos); } - + private void bufferChanged() { - //set position to beginning to prepare for overwrite + // set position to beginning to prepare for overwrite 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()) { - //put the second portion into the first... + // put the second portion into the first... 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); - + synchronizeBufferWithPlayback(); } - + private int calcBufferPosition() { int offset = (int) alGetSourcef(sourceID, AL11.AL_SAMPLE_OFFSET); - offset = (offset/windowSize) * windowSize; + offset = (offset / windowSize) * windowSize; return offset; } - + private void synchronizeBufferWithPlayback() { playingBuffer.position(calcBufferPosition()); - windowsRead = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize); + windowsRead = (int) ((mc.getCurrentPosition() * sampleRate) / windowSize); } - + private void setMusic() { try { Field sourceIDField = ClassReflection.getDeclaredField(OpenALMusic.class, "sourceID"); @@ -123,23 +124,23 @@ public class PCMMachine implements Observer, Disposable { } catch (ReflectionException e) { e.printStackTrace(); } - + channelCount = mc.getCurrentMusicHeader().getChannelCount(); sampleRate = mc.getCurrentMusicHeader().getSampleRate(); - - playingBuffer = ShortBuffer.allocate(buffer.capacity()*2); + + playingBuffer = ShortBuffer.allocate(buffer.capacity() * 2); buffer.rewind(); playingBuffer.put(buffer); playingBuffer.flip(); - + compareBuffer = ShortBuffer.allocate(buffer.capacity()); buffer.rewind(); compareBuffer.put(buffer); compareBuffer.flip(); - + buffer.rewind(); } - + public float[] getFrequencyBins() { if (updated) { synchronized (PCM) { @@ -149,11 +150,11 @@ public class PCMMachine implements Observer, Disposable { } return frequencyBins; } - + public int getWindowSize() { return windowSize; } - + private class BufferStreamReadThread implements Runnable { private String name = "PCM-Audio-Processing"; private Thread thread; @@ -161,7 +162,7 @@ public class PCMMachine implements Observer, Disposable { private boolean paused; private long timeOfLastRead; private int waitTime; - + @Override public void run() { while (run) { @@ -170,14 +171,14 @@ public class PCMMachine implements Observer, Disposable { timeOfLastRead = TimeUtils.millis(); paused = false; } - waitTime = sampleRate/windowSize/Gdx.graphics.getFramesPerSecond(); + waitTime = sampleRate / windowSize / Gdx.graphics.getFramesPerSecond(); if (TimeUtils.timeSinceMillis(timeOfLastRead) >= waitTime) { calcPCMData(); updated = true; windowsRead++; timeOfLastRead = TimeUtils.millis(); - - currentPlaybackWindow = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize); + + currentPlaybackWindow = (int) ((mc.getCurrentPosition() * sampleRate) / windowSize); if (windowsRead != currentPlaybackWindow) { synchronizeBufferWithPlayback(); } @@ -194,7 +195,7 @@ public class PCMMachine implements Observer, Disposable { } } } - + public void start() { if (thread == null && !thread.isAlive()) { thread = new Thread(this, name); @@ -205,7 +206,7 @@ public class PCMMachine implements Observer, Disposable { } } } - + public void stop() { run = false; } @@ -214,15 +215,10 @@ public class PCMMachine implements Observer, Disposable { @Override public void update(Observable o, Object arg) { if (o == mc) { - switch ((MusicController.States) arg) { - case Loaded: + if (arg == mc.states.LOADED) { setMusic(); - break; - case Playing: + } else if (arg == mc.states.PLAYING) { streamReadThread.start(); - break; - default: - break; } } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java index e098d81..52e9976 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java @@ -110,7 +110,7 @@ public class MainPage extends Page implements Observer { @Override 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); } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java index fb6cd0c..73da75b 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java @@ -233,7 +233,6 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen { ChangeListener analysisPageButtonListener = new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { - musicSelectionPage.getSelectedMusic(); setDisplayedPage(analysisPage); } }; diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java index d6b842f..c7e2b0c 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java @@ -1,6 +1,5 @@ package zero1hd.rhythmbullet.desktop.screens.main; -import java.util.HashMap; import java.util.Observable; import java.util.Observer; @@ -8,23 +7,20 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.Preferences; import com.badlogic.gdx.assets.AssetManager; -import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.TextureRegion; 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.InputListener; import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; 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.TextButton; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; -import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.utils.Array; 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.graphics.ui.components.MusicSelectable; import zero1hd.rhythmbullet.graphics.ui.components.ScrollText; +import zero1hd.rhythmbullet.util.MusicSelectableButtonGroup; public class MusicSelectionPage extends Page implements Observer { Preferences musicFileAnnotation; private MusicController mc; private MusicMetadataController mmc; - - private Array selectables; + private MusicSelectableButtonGroup selectables; + private Stack stackSelectables; private TextButton back; private Table musicTable; private ScrollPane musicTableScrollPane; + private musicSelectionLoaderThread thread; + private Table musicInfoTable; private Table musicSubInfo; private ScrollText songTitle; @@ -54,9 +53,9 @@ public class MusicSelectionPage extends Page implements Observer { private Image albumCover; private AssetManager assets; + private Skin skin; private boolean down, up; - private int musicSelectableIndex; private TextButton beginButton; @@ -67,6 +66,9 @@ public class MusicSelectionPage extends Page implements Observer { this.assets = assetManager; this.mc = musicController; this.mmc = musicMetadataController; + this.skin = skin; + stackSelectables = new Stack(); + selectables = new MusicSelectableButtonGroup(); musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation"); musicTable = new Table(); @@ -83,7 +85,7 @@ public class MusicSelectionPage extends Page implements Observer { back.addListener(backButtonListener); addActor(back); back.toFront(); - + addListener(new InputListener() { @Override public boolean keyDown(InputEvent event, int keycode) { @@ -127,6 +129,10 @@ public class MusicSelectionPage extends Page implements Observer { beginButton = new TextButton("Begin", skin); beginButton.addListener(beginButtonListener); + + mmc.addObserver(this); + + thread = new musicSelectionLoaderThread(); } @Override @@ -159,47 +165,51 @@ public class MusicSelectionPage extends Page implements Observer { if (songSelectionTimer > 0f) { songSelectionTimer -= delta; 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); } private void scrollDown() { - if ((musicSelectableIndex = (musicTable.getChildren().indexOf(currentlySelected, true) + 1)) == musicTable.getChildren().size) { - musicSelectableIndex = 0; - } - ((MusicSelectable)musicTable.getChildren().get(musicSelectableIndex)).select(); - musicTableScrollPane.scrollTo(currentlySelected.getX(), currentlySelected.getY(), currentlySelected.getWidth(), currentlySelected.getHeight()); + selectables.selectNext(); + musicTableScrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight()); } private void scrollUp() { - if ((musicSelectableIndex = (musicTable.getChildren().indexOf(currentlySelected, true) - 1)) < 0) { - musicSelectableIndex = musicTable.getChildren().size-1; - } - ((MusicSelectable)musicTable.getChildren().get(musicSelectableIndex)).select(); - musicTableScrollPane.scrollTo(currentlySelected.getX(), currentlySelected.getY(), currentlySelected.getWidth(), currentlySelected.getHeight()); + selectables.selectPrevious(); + musicTableScrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight()); } public FileHandle getSelectedMusic() { - if (currentlySelected != null) { - return currentlySelected.getMusicFile(); - } else { - return null; - } + return selectables.getChecked().getMetadata().getFileHandle(); } public void refreshUIList() { - for (int i = 0; i < selectables.size; i++) { - selectables.get(i).dispose(); - } + selectables.clear(); + mmc.loadAudioMetadata(); musicTable.clear(); selectables.clear(); musicInfoTable.clear(); musicSubInfo.clear(); - uiSongCount = 0; - uiSongInfoCount = 0; Gdx.app.debug("MusicSelectionPage", "Refreshing..."); @@ -226,18 +236,17 @@ public class MusicSelectionPage extends Page implements Observer { super.dispose(); } - @Override public void update(Observable o, Object arg) { - if (o == mc && arg == MusicController.States.Loaded) { - selectMusicUI(mc.getCurrentMusicFileHandle()); + if (o == mc && arg == mc.states.LOADED) { + 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 public void setCameraPositionToPage(Vector3 cameraPosition) { getStage().setKeyboardFocus(this); @@ -245,11 +254,49 @@ public class MusicSelectionPage extends Page implements Observer { } private class musicSelectionLoaderThread implements Runnable { - + private Thread thread; + private Array queueList; + private String name = "Music-Selection-Loader-Thread"; + private volatile boolean work; + + public musicSelectionLoaderThread() { + queueList = new Array<>(); + } + @Override 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; + } + } }