From d7008796f4788a6420a0a8bba57ef0caecfdcb36 Mon Sep 17 00:00:00 2001 From: Recrown Date: Sat, 18 Aug 2018 22:19:08 -0500 Subject: [PATCH] main difference: music selection screen now completely functional; other refactoring and changes to make better use of the framework were made; some cleanup happened; --- .../rhythmbullet/audio/MusicController.java | 20 +- .../zero1hd/rhythmbullet/audio/MusicList.java | 43 +- .../audio/MusicMetadataController.java | 7 +- .../audio/metadata/AudioMetadata.java | 2 +- .../audio/metadata/MP3Metadata.java | 46 +- .../audio/metadata/WAVMetadata.java | 26 +- .../DoubleHorizontalVisualizer.java | 42 +- .../rhythmbullet/graphics/ui/Page.java | 4 + .../graphics/ui/components/MusicControls.java | 10 +- .../ui/components/MusicSelectable.java | 108 ---- .../MusicSelectableButtonGroup.java | 59 --- .../ui/components/ShortenedLabel.java | 61 --- .../util/ScreenConfiguration.java | 12 +- .../rhythmbullet/desktop/DesktopLauncher.java | 21 +- .../desktop/DesktopScreenConfiguration.java | 29 +- .../desktop/audio/PCMObtainer.java | 10 +- .../desktop/screens/SplashScreen.java | 16 +- .../desktop/screens/main/MainPage.java | 5 +- .../desktop/screens/main/MainScreen.java | 4 +- .../screens/main/MusicSelectionPage.java | 462 +++++++++++++----- .../desktop/screens/main/OptionsPage.java | 4 +- 21 files changed, 521 insertions(+), 470 deletions(-) rename {desktop/src/zero1hd/rhythmbullet/desktop => core/src/zero1hd/rhythmbullet}/graphics/ui/components/MusicControls.java (86%) delete mode 100755 core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectable.java delete mode 100755 core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectableButtonGroup.java delete mode 100755 core/src/zero1hd/rhythmbullet/graphics/ui/components/ShortenedLabel.java diff --git a/core/src/zero1hd/rhythmbullet/audio/MusicController.java b/core/src/zero1hd/rhythmbullet/audio/MusicController.java index e3d0ad6..05dd06d 100755 --- a/core/src/zero1hd/rhythmbullet/audio/MusicController.java +++ b/core/src/zero1hd/rhythmbullet/audio/MusicController.java @@ -86,7 +86,7 @@ public class MusicController extends Observable implements OnCompletionListener, public void skip() { currentlyPlayingIndex++; if (shuffle) { - shuffle(false); + shuffle(); } loadMusic(); } @@ -97,7 +97,7 @@ public class MusicController extends Observable implements OnCompletionListener, public void previous() { currentlyPlayingIndex--; if (shuffle) { - shuffle(false); + shuffle(); } loadMusic(); } @@ -106,7 +106,7 @@ public class MusicController extends Observable implements OnCompletionListener, public void onCompletion(Music music) { if (autoPlay) { if (shuffle) { - shuffle(false); + shuffle(); } else { currentlyPlayingIndex++; } @@ -117,18 +117,14 @@ public class MusicController extends Observable implements OnCompletionListener, /** * Shuffles the controller whether the shuffle boolean is true or false. - * @param load whether this method should also make sure to load the music, dispose of the last one, etc. Normally called unless you plan to manually call it elsewhere. */ - public void shuffle(boolean load) { + public void shuffle() { Gdx.app.debug("MusicListController", "shuffled."); if (musicList.getTotal() == 0) { currentlyPlayingIndex = 0; } else { currentlyPlayingIndex = rand.nextInt(musicList.getTotal()); } - if (load) { - loadMusic(); - } } public void setAutoPlay(boolean autoPlay) { @@ -152,6 +148,7 @@ public class MusicController extends Observable implements OnCompletionListener, */ 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(); @@ -169,7 +166,7 @@ public class MusicController extends Observable implements OnCompletionListener, setChanged(); notifyObservers(states.LOADED); - if (autoPlay) { + if (playing) { play(); } } @@ -195,9 +192,12 @@ public class MusicController extends Observable implements OnCompletionListener, public void update(Observable o, Object arg) { if (o == musicList) { if (shuffle) { - shuffle(false); + shuffle(); } loadMusic(); + if (autoPlay) { + play(); + } } } diff --git a/core/src/zero1hd/rhythmbullet/audio/MusicList.java b/core/src/zero1hd/rhythmbullet/audio/MusicList.java index 8071953..4d7f9ab 100755 --- a/core/src/zero1hd/rhythmbullet/audio/MusicList.java +++ b/core/src/zero1hd/rhythmbullet/audio/MusicList.java @@ -1,10 +1,12 @@ package zero1hd.rhythmbullet.audio; +import java.util.Comparator; import java.util.Observable; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Sort; import zero1hd.rhythmbullet.audio.processor.AudioProcessor; import zero1hd.rhythmbullet.audio.processor.WAVAudioProcessor; @@ -20,19 +22,30 @@ public class MusicList extends Observable { private AudioProcessorFactory audioProcFactory; private volatile boolean searched; private String searchPath; + private Comparator compare; + public MusicList(AudioProcessorFactory audioProcessorFactory, String searchPath) { this.audioProcFactory = audioProcessorFactory; musicList = new Array<>(); setSearchPath(searchPath); + + compare = new Comparator() { + + @Override + public int compare(FileHandle o1, FileHandle o2) { + return o1.nameWithoutExtension().compareTo(o2.nameWithoutExtension()); + } + }; } /** * Wrapper method that uses async refresh. * Also notifies listeners that are on the main thread. - * @param refresh does a search whether or not path has changed. + * @param refresh does a search whether or not path has changed and whether or not this list has searched before this. */ public void asyncSearch(boolean refresh) { if (refresh) { + if (searchThread != null) { if (!searchThread.start()) { searchThread.stop(); @@ -45,7 +58,6 @@ public class MusicList extends Observable { } } else { if (searched && !hasChanged()) { - setChanged(); notifyObservers(); } else { asyncSearch(true); @@ -54,7 +66,7 @@ public class MusicList extends Observable { } public void setSearchPath(String searchPath) { - hasChanged(); + setChanged(); this.searchPath = searchPath; } @@ -88,6 +100,9 @@ public class MusicList extends Observable { Gdx.app.debug("MusicList", "Warning, this list has not completed it's search..."); Thread.dumpStack(); } + if (musicList.size == 0) { + return null; + } return newAudioProcessor(musicList.get(index)); } @@ -97,6 +112,9 @@ public class MusicList extends Observable { Gdx.app.debug("MusicList", "Warning, this list has not completed it's search..."); Thread.dumpStack(); } + if (musicList.size == 0) { + return null; + } return musicList.get(index); } @@ -130,22 +148,13 @@ public class MusicList extends Observable { @Override public void run() { Array obtainedAudioFiles = recursiveMusicSearch(directory); - if (Gdx.files.external("RhythmBullet").exists()) { - if (!Gdx.files.external("RhythmBullet/Alan Walker - Spectre.mp3").exists()) { - Gdx.files.internal("music/Alan Walker - Spectre.mp3").copyTo(Gdx.files.external("RhythmBullet/Alan Walker - Spectre.mp3")); - } - } else { - Gdx.files.internal("music/Alan Walker - Spectre.mp3").copyTo(Gdx.files.external("RhythmBullet/Alan Walker - Spectre.mp3")); - } + Sort.instance().sort(obtainedAudioFiles, compare); if (work) { musicList = obtainedAudioFiles; - musicList.add(Gdx.files.external("RhythmBullet/Alan Walker - Spectre.mp3")); - if (work) { - searched = true; - Gdx.app.debug("MusicList", "recursive async search completed."); - setChanged(); - notifyObservers(); - } + searched = true; + Gdx.app.debug("MusicList", "recursive async search completed."); + setChanged(); + notifyObservers(); } } diff --git a/core/src/zero1hd/rhythmbullet/audio/MusicMetadataController.java b/core/src/zero1hd/rhythmbullet/audio/MusicMetadataController.java index 3262ebd..e8612e7 100755 --- a/core/src/zero1hd/rhythmbullet/audio/MusicMetadataController.java +++ b/core/src/zero1hd/rhythmbullet/audio/MusicMetadataController.java @@ -3,6 +3,7 @@ package zero1hd.rhythmbullet.audio; import java.util.Observable; import java.util.Observer; +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Disposable; @@ -74,10 +75,11 @@ public class MusicMetadataController extends Observable implements Disposable, O private class MetadataLoadingThread implements Runnable { private Thread thread; private String name = "Metadata-Load"; - private volatile boolean work; + private volatile boolean work = true; @Override public void run() { + Gdx.app.debug(name, "loading..."); searching = true; for (int i = 0; i < metadataArray.size; i++) { metadataArray.get(i).dispose(); @@ -101,6 +103,8 @@ public class MusicMetadataController extends Observable implements Disposable, O } if (work) { searching = false; + Gdx.app.debug(name, "load complete."); + setChanged(); notifyObservers(); } } @@ -123,7 +127,6 @@ public class MusicMetadataController extends Observable implements Disposable, O @Override public void update(Observable o, Object arg) { if (o == musicList) { - loadingThread.stop(); loadAudioMetadata(); } } diff --git a/core/src/zero1hd/rhythmbullet/audio/metadata/AudioMetadata.java b/core/src/zero1hd/rhythmbullet/audio/metadata/AudioMetadata.java index 2ac23d8..c625fc3 100755 --- a/core/src/zero1hd/rhythmbullet/audio/metadata/AudioMetadata.java +++ b/core/src/zero1hd/rhythmbullet/audio/metadata/AudioMetadata.java @@ -46,7 +46,7 @@ public interface AudioMetadata extends Disposable { public int getLength(); /** - * + * Requires a OpenGL context. * @return the texture. Needs to be loaded before hand or else will return null. */ public Texture getAlbumCover(); diff --git a/core/src/zero1hd/rhythmbullet/audio/metadata/MP3Metadata.java b/core/src/zero1hd/rhythmbullet/audio/metadata/MP3Metadata.java index 994a93c..f7b506e8 100755 --- a/core/src/zero1hd/rhythmbullet/audio/metadata/MP3Metadata.java +++ b/core/src/zero1hd/rhythmbullet/audio/metadata/MP3Metadata.java @@ -1,6 +1,8 @@ package zero1hd.rhythmbullet.audio.metadata; import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; import org.jaudiotagger.audio.AudioFileIO; import org.jaudiotagger.audio.exceptions.CannotReadException; @@ -11,6 +13,7 @@ import org.jaudiotagger.audio.mp3.MP3File; import org.jaudiotagger.tag.TagException; import org.jaudiotagger.tag.id3.ID3v23FieldKey; import org.jaudiotagger.tag.id3.ID3v23Tag; +import org.jaudiotagger.tag.images.Artwork; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; @@ -22,6 +25,7 @@ public class MP3Metadata implements AudioMetadata { private int length; private Texture albumCover; private FileHandle fileHandle; + private byte[] imageData; public MP3Metadata(FileHandle fileHandle) { this.fileHandle = fileHandle; @@ -37,16 +41,18 @@ public class MP3Metadata implements AudioMetadata { } tag = (ID3v23Tag) mp3file.getTagAndConvertOrCreateAndSetDefault(); - + length = mp3file.getAudioHeader().getTrackLength(); - int min = (int) (length/60); - duration = (length/60) + ":" + (length - (min*60)); + SimpleDateFormat f = new SimpleDateFormat("m:ss"); + duration = f.format(new Date(length*1000)); author = tag.getFirst(ID3v23FieldKey.ARTIST); genre = tag.getFirst(ID3v23FieldKey.GENRE); title = tag.getFirst(ID3v23FieldKey.TITLE); - + if (title.isEmpty()) { + title = fileHandle.nameWithoutExtension(); + } } catch (IOException | CannotWriteException | CannotReadException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { Gdx.app.error("MP3Metadata", "Failed to read metadata of file: " + fileHandle.name()); } @@ -57,25 +63,17 @@ public class MP3Metadata implements AudioMetadata { MP3File mp3file; try { mp3file = (MP3File) AudioFileIO.read(fileHandle.file()); - - byte[] imageData = mp3file.getTag().getFirstArtwork().getBinaryData(); - Pixmap pixmap = new Pixmap(imageData, 0, imageData.length); - albumCover = new Texture(pixmap); - pixmap.dispose(); + Artwork art = mp3file.getTag().getFirstArtwork(); + if (art != null) { + imageData = art.getBinaryData(); + + } } catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { e.printStackTrace(); } } - @Override - public void unloadAlbumCover() { - if (albumCover != null) { - albumCover.dispose(); - albumCover = null; - } - } - @Override public String getAuthor() { return author; @@ -101,8 +99,22 @@ public class MP3Metadata implements AudioMetadata { return genre; } + @Override + public void unloadAlbumCover() { + if (albumCover != null) { + albumCover.dispose(); + albumCover = null; + } + } + @Override public Texture getAlbumCover() { + if (albumCover == null && imageData != null) { + Pixmap pixmap = new Pixmap(imageData, 0, imageData.length); + albumCover = new Texture(pixmap); + pixmap.dispose(); + imageData = null; + } return albumCover; } diff --git a/core/src/zero1hd/rhythmbullet/audio/metadata/WAVMetadata.java b/core/src/zero1hd/rhythmbullet/audio/metadata/WAVMetadata.java index 9b9b79b..199fe47 100755 --- a/core/src/zero1hd/rhythmbullet/audio/metadata/WAVMetadata.java +++ b/core/src/zero1hd/rhythmbullet/audio/metadata/WAVMetadata.java @@ -1,6 +1,8 @@ package zero1hd.rhythmbullet.audio.metadata; import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; import org.jaudiotagger.audio.AudioFile; import org.jaudiotagger.audio.AudioFileIO; @@ -10,6 +12,7 @@ import org.jaudiotagger.audio.exceptions.ReadOnlyFileException; import org.jaudiotagger.tag.FieldKey; import org.jaudiotagger.tag.Tag; import org.jaudiotagger.tag.TagException; +import org.jaudiotagger.tag.images.Artwork; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; @@ -21,6 +24,7 @@ public class WAVMetadata implements AudioMetadata { private int length; private Texture albumCover; private FileHandle fileHandle; + private byte[] imageData; public WAVMetadata(FileHandle fileHandle) { this.fileHandle = fileHandle; @@ -28,13 +32,16 @@ public class WAVMetadata implements AudioMetadata { try { AudioFile wav = AudioFileIO.read(fileHandle.file()); length = wav.getAudioHeader().getTrackLength(); - int min = (int) (length/60); - this.duration = (length/60) + ":" + (length - (min*60)); + SimpleDateFormat f = new SimpleDateFormat("m:ss"); + duration = f.format(new Date(length*1000)); Tag tag = wav.getTag(); title = tag.getFirst(FieldKey.TITLE); author = tag.getFirst(FieldKey.ARTIST); genre = tag.getFirst(FieldKey.GENRE); + if (title.isEmpty()) { + title = fileHandle.nameWithoutExtension(); + } } catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { Gdx.app.error("WAVMetadata", "Failed to read metadata of file: " + fileHandle.name()); } @@ -44,11 +51,10 @@ public class WAVMetadata implements AudioMetadata { public void loadAlbumCover() { try { AudioFile wav = AudioFileIO.read(fileHandle.file()); - - byte[] imageData = wav.getTag().getFirstArtwork().getBinaryData(); - Pixmap pixmap = new Pixmap(imageData, 0, imageData.length); - albumCover = new Texture(pixmap); - pixmap.dispose(); + Artwork art = wav.getTag().getFirstArtwork(); + if (art != null) { + imageData = art.getBinaryData(); + } } catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { e.printStackTrace(); @@ -82,6 +88,12 @@ public class WAVMetadata implements AudioMetadata { @Override public Texture getAlbumCover() { + if (albumCover == null && imageData != null) { + Pixmap pixmap = new Pixmap(imageData, 0, imageData.length); + albumCover = new Texture(pixmap); + pixmap.dispose(); + imageData = null; + } return albumCover; } @Override diff --git a/core/src/zero1hd/rhythmbullet/audio/visualizer/DoubleHorizontalVisualizer.java b/core/src/zero1hd/rhythmbullet/audio/visualizer/DoubleHorizontalVisualizer.java index ea65981..0f0f525 100755 --- a/core/src/zero1hd/rhythmbullet/audio/visualizer/DoubleHorizontalVisualizer.java +++ b/core/src/zero1hd/rhythmbullet/audio/visualizer/DoubleHorizontalVisualizer.java @@ -21,13 +21,13 @@ public class DoubleHorizontalVisualizer implements Disposable { private int binsPerBar; private float offset; private int boundaryThickness; - private float baseSensitivity; private float targetDelta; private float spacePercentage = 0.7f; - private int barCount = 90; - private float barChangeRate = 14f; - private int smoothRange = 1; - private int binsToInclude = 180; + private float baseSensitivity = 0.009f; + private int barCount = 120; + private float barChangeRate = 7f; + private int smoothRange = 2; + private int binsToInclude = 120; private Color color = new Color(0.5f, 0.6f, 0.8f, 0.46f); /** * @@ -35,11 +35,11 @@ public class DoubleHorizontalVisualizer implements Disposable { * @param width the width of the visualizer. * @param spacePercentage the percentage of a bar that should be space. */ - public DoubleHorizontalVisualizer(int width, int height, float baseSensitivity, int boundaryThickness, int targetFPS, MusicController musicController, PCMSystem PCMSystem) { + public DoubleHorizontalVisualizer(int width, int height, float heightSensitivity, int boundaryThickness, int targetFPS, MusicController musicController, PCMSystem PCMSystem) { this.barWidth = width/barCount; this.spaceBetweenBars = MathUtils.round(barWidth * spacePercentage); this.barWidth -= spaceBetweenBars; - this.baseSensitivity = baseSensitivity; + this.baseSensitivity *= heightSensitivity; pcm = PCMSystem; binsPerBar = (binsToInclude/barCount); this.width = width; @@ -55,11 +55,10 @@ public class DoubleHorizontalVisualizer implements Disposable { public void act(float delta) { float[] freqBins = pcm.getFrequencyBins(); for (int bar = 0; bar < amplitudes.length; bar++) { - float normalizedAmplitude = 0; + amplitudes[bar] = 0; for (int freq = bar*binsPerBar; freq < (bar*binsPerBar) + binsPerBar; freq++) { - normalizedAmplitude += Math.abs(freqBins[freq]); + amplitudes[bar] += Math.abs(freqBins[freq]) * baseSensitivity; } - amplitudes[bar] += normalizedAmplitude * baseSensitivity; amplitudes[bar] /= binsPerBar; } for (int bar = 0; bar < barHeights.length; bar++) { @@ -78,13 +77,22 @@ public class DoubleHorizontalVisualizer implements Disposable { int pixelsMoved = 0; - pixelsMoved = MathUtils.round(amplitudes[bar] - barHeights[bar]); - pixelsMoved = MathUtils.floor(pixelsMoved*targetDelta*barChangeRate); + int difference = MathUtils.round(amplitudes[bar] - barHeights[bar]); + pixelsMoved = MathUtils.floor(difference*targetDelta*barChangeRate); + if (pixelsMoved >= 0) { + if (barHeights[bar] + pixelsMoved > amplitudes[bar]) { + barHeights[bar] += MathUtils.round(difference*targetDelta); + } else { + barHeights[bar] += pixelsMoved; + } + } else { + if (barHeights[bar] + pixelsMoved < amplitudes[bar]) { + barHeights[bar] += MathUtils.round(difference*targetDelta); + } else { + barHeights[bar] += pixelsMoved; + } + } - - barHeights[bar] += pixelsMoved; - - if (barHeights[bar] < 0) barHeights[bar] = 0; } } @@ -103,7 +111,7 @@ public class DoubleHorizontalVisualizer implements Disposable { for (int bar = 0; bar < barCount; bar++) { shapeRenderer.setColor(color); shapeRenderer.rect(offset + (spaceBetweenBars+barWidth)*bar, y+height, barWidth, barHeights[bar]); - shapeRenderer.rect(offset + (spaceBetweenBars+barWidth)*bar, y-barHeights[barHeights.length - 1 - bar], barWidth, barHeights[barHeights.length - 1 - bar]); + shapeRenderer.rect(offset + (spaceBetweenBars+barWidth)*bar, y, barWidth, -barHeights[barHeights.length - 1 - bar]); } shapeRenderer.end(); Gdx.gl.glDisable(GL20.GL_BLEND); diff --git a/core/src/zero1hd/rhythmbullet/graphics/ui/Page.java b/core/src/zero1hd/rhythmbullet/graphics/ui/Page.java index 8183423..bb1a81a 100755 --- a/core/src/zero1hd/rhythmbullet/graphics/ui/Page.java +++ b/core/src/zero1hd/rhythmbullet/graphics/ui/Page.java @@ -66,4 +66,8 @@ public class Page extends Group implements Disposable { public void dispose() { Gdx.app.debug(getClass().getSimpleName(), "Disposing..."); } + + public void simpleDebug(String message) { + Gdx.app.debug(getClass().getSimpleName(), message); + } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/graphics/ui/components/MusicControls.java b/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicControls.java similarity index 86% rename from desktop/src/zero1hd/rhythmbullet/desktop/graphics/ui/components/MusicControls.java rename to core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicControls.java index 0642e14..2bafbc3 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/graphics/ui/components/MusicControls.java +++ b/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicControls.java @@ -1,4 +1,4 @@ -package zero1hd.rhythmbullet.desktop.graphics.ui.components; +package zero1hd.rhythmbullet.graphics.ui.components; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; @@ -19,11 +19,7 @@ public class MusicControls extends HorizontalGroup { reverse.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { - boolean wasPlaying = sc.isPlaying(); sc.previous(); - if (wasPlaying) { - sc.play(); - } } }); addActor(reverse); @@ -56,11 +52,7 @@ public class MusicControls extends HorizontalGroup { forward.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { - boolean wasPlaying = sc.isPlaying(); sc.skip(); - if (wasPlaying) { - sc.play(); - } } }); addActor(forward); diff --git a/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectable.java b/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectable.java deleted file mode 100755 index 1a6f878..0000000 --- a/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectable.java +++ /dev/null @@ -1,108 +0,0 @@ -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; -import com.badlogic.gdx.scenes.scene2d.ui.Image; -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; - -public class MusicSelectable extends Button { - private Vector2 actualCoords; - private Image album; - private Table informationTable; - private ShortenedLabel name, artist; - private Label time; - private float timeSinceOnScreen; - private AudioMetadata metadata; - private Texture defaultAlbumArt; - 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(); - informationTable.row(); - artist = new ShortenedLabel(metadata.getTitle(), skin, "sub-font", skin.getColor("default")); - informationTable.add(artist).expandX(); - time = new Label(metadata.getDuration(), skin, "sub-font", skin.getColor("default")); - 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()) { - offScreenAct(delta); - } else { - onScreenAct(delta); - } - super.act(delta); - } - - @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(); - Texture texture = defaultAlbumArt; - if (metadata.getAlbumCover() != null) { - texture = metadata.getAlbumCover(); - } - synchronized (album) { - album.setDrawable(new TextureRegionDrawable(new TextureRegion(texture))); - } - } - - public AudioMetadata getMetadata() { - return metadata; - } - - public FileHandle getFileHandle() { - return metadata.getFileHandle(); - } -} diff --git a/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectableButtonGroup.java b/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectableButtonGroup.java deleted file mode 100755 index 466a01b..0000000 --- a/core/src/zero1hd/rhythmbullet/graphics/ui/components/MusicSelectableButtonGroup.java +++ /dev/null @@ -1,59 +0,0 @@ -package zero1hd.rhythmbullet.graphics.ui.components; - -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; - -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/core/src/zero1hd/rhythmbullet/graphics/ui/components/ShortenedLabel.java b/core/src/zero1hd/rhythmbullet/graphics/ui/components/ShortenedLabel.java deleted file mode 100755 index 575b7ea..0000000 --- a/core/src/zero1hd/rhythmbullet/graphics/ui/components/ShortenedLabel.java +++ /dev/null @@ -1,61 +0,0 @@ -package zero1hd.rhythmbullet.graphics.ui.components; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.g2d.BitmapFont; -import com.badlogic.gdx.graphics.g2d.GlyphLayout; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.scenes.scene2d.ui.Label; -import com.badlogic.gdx.scenes.scene2d.ui.Skin; - -public class ShortenedLabel extends Label { - private String originalText; - private int targetWidth; - private GlyphLayout gl; - private BitmapFont font; - private Vector2 size; - - public ShortenedLabel(CharSequence text, Skin skin, String fontName, Color color) { - super(null, skin, fontName, color); - originalText = text.toString(); - font = skin.getFont(fontName); - if (text != null) { - gl = new GlyphLayout(skin.getFont(fontName), text); - } - size = new Vector2(); - - setWrap(true); - } - - public void resize() { - setToOriginalText(); - String text = getText().toString(); - while (gl.width > targetWidth && (text.length() - 4) > 0) { - text = text.substring(0, text.length() - 4).concat("..."); - gl.setText(font, text); - } - setText(text); - } - - public void setToOriginalText() { - setText(originalText); - gl.setText(font, originalText); - } - - @Override - public void layout() { - super.layout(); - size.x = getWidth(); - size.y = getHeight(); - targetWidth = (int) getStage().stageToScreenCoordinates(size).x; - resize(); - } - - public void setOriginalText(String originalText) { - this.originalText = originalText; - gl.setText(font, originalText); - } - - public int getTargetWidth() { - return targetWidth; - } -} diff --git a/core/src/zero1hd/rhythmbullet/util/ScreenConfiguration.java b/core/src/zero1hd/rhythmbullet/util/ScreenConfiguration.java index cc09df4..9ea1e51 100755 --- a/core/src/zero1hd/rhythmbullet/util/ScreenConfiguration.java +++ b/core/src/zero1hd/rhythmbullet/util/ScreenConfiguration.java @@ -3,9 +3,12 @@ package zero1hd.rhythmbullet.util; public interface ScreenConfiguration { public void setFramesPerSecond(int fps); - public void setVsync(boolean useVsync); + public int getTargetFramesPerSecond(); - public int getFramesPerSecond(); + /** + * @param useVsync whether or not to use vSync. + */ + public void setVsync(boolean useVsync); public boolean getVsync(); @@ -22,4 +25,9 @@ public interface ScreenConfiguration { public void setWindowLocationY(int y); public void setWindowLocation(int x, int y); + + /** + * Restarts the application. + */ + public void restart(); } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/DesktopLauncher.java b/desktop/src/zero1hd/rhythmbullet/desktop/DesktopLauncher.java index 2bee332..a5c737c 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/DesktopLauncher.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/DesktopLauncher.java @@ -1,24 +1,37 @@ package zero1hd.rhythmbullet.desktop; +import java.awt.Canvas; +import java.awt.Color; + +import javax.swing.JFrame; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.AWTGLCanvas; + import com.badlogic.gdx.backends.lwjgl.LwjglApplication; import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; +import com.badlogic.gdx.backends.lwjgl.LwjglGraphics; import zero1hd.rhythmbullet.RhythmBullet; import zero1hd.rhythmbullet.desktop.screens.SplashScreen; -public class DesktopLauncher { +public class DesktopLauncher { + public static void main (String[] arg) { RhythmBullet core; LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); DesktopScreenConfiguration screenConfig = new DesktopScreenConfiguration(config); + config.title = "Rhythm Bullet"; config.resizable = false; config.useHDPI = true; config.samples = 2; - config.width = 512; - config.height = 512; + config.allowSoftwareMode = true; core = new RhythmBullet(); core.setup(new SplashScreen(), new DesktopAssetPack(), screenConfig); - new LwjglApplication(core, config); + + while (screenConfig.shouldStart()) { + LwjglApplication app = new LwjglApplication(core, config); + } } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/DesktopScreenConfiguration.java b/desktop/src/zero1hd/rhythmbullet/desktop/DesktopScreenConfiguration.java index 04c3bb8..1f3fb0b 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/DesktopScreenConfiguration.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/DesktopScreenConfiguration.java @@ -2,13 +2,14 @@ package zero1hd.rhythmbullet.desktop; import org.lwjgl.opengl.Display; +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; import zero1hd.rhythmbullet.util.ScreenConfiguration; public class DesktopScreenConfiguration implements ScreenConfiguration { private LwjglApplicationConfiguration configuration; - + private boolean start = true; public DesktopScreenConfiguration(LwjglApplicationConfiguration configuration) { this.configuration = configuration; } @@ -18,16 +19,20 @@ public class DesktopScreenConfiguration implements ScreenConfiguration { configuration.foregroundFPS = fps; } + + @Override + public int getTargetFramesPerSecond() { + return configuration.foregroundFPS; + } + + /** + * Requires restart. Can be done by calling {@link #restart()} + */ @Override public void setVsync(boolean useVsync) { configuration.vSyncEnabled = useVsync; } - @Override - public int getFramesPerSecond() { - return configuration.foregroundFPS; - } - @Override public boolean getVsync() { return configuration.vSyncEnabled; @@ -67,4 +72,16 @@ public class DesktopScreenConfiguration implements ScreenConfiguration { public void setWindowLocation(int x, int y) { Display.setLocation(x, y); } + + @Override + public void restart() { + Gdx.app.exit(); + start = true; + } + + public boolean shouldStart() { + boolean should = start; + start = false; + return should; + } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/audio/PCMObtainer.java b/desktop/src/zero1hd/rhythmbullet/desktop/audio/PCMObtainer.java index 35b5734..e6496ec 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/audio/PCMObtainer.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/audio/PCMObtainer.java @@ -34,7 +34,7 @@ public class PCMObtainer implements Observer, PCMSystem { private ShortBuffer playingBuffer; private ShortBuffer intermediateBuffer; private ShortBuffer buffer; - private int sourceID; + private volatile int sourceID; private int channelCount; private MusicController mc; private BufferStreamReadThread streamReadThread; @@ -177,13 +177,7 @@ public class PCMObtainer implements Observer, PCMSystem { windowsRead++; //contemplate synchronization - try { - currentPlaybackWindow = MathUtils.round((mc.getCurrentPosition() * sampleRate) / windowSize); - } catch (UnsatisfiedLinkError ule) { - if (run) { - ule.printStackTrace(); - } - } + currentPlaybackWindow = MathUtils.round((mc.getCurrentPosition() * sampleRate) / windowSize); if (windowsRead != currentPlaybackWindow) { synchronizeBufferWithPlayback(); } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/SplashScreen.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/SplashScreen.java index 99f08ef..b8e854c 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/SplashScreen.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/SplashScreen.java @@ -22,6 +22,7 @@ public class SplashScreen extends ScreenAdapter implements InitialScreen { @Override public void init() { + stage = new Stage(new ScreenViewport()); splash = new Texture(Gdx.files.internal("splashlogo.png")); zero1HD = new Image(splash); } @@ -47,14 +48,19 @@ public class SplashScreen extends ScreenAdapter implements InitialScreen { @Override public void postAssetLoad() { - stage = new Stage(new ScreenViewport()); - stage.addActor(zero1HD); - zero1HD.setScale((Gdx.graphics.getHeight()*0.5f)/zero1HD.getHeight()*zero1HD.getScaleY()); + zero1HD.setScale((stage.getHeight()*0.8f)/(zero1HD.getHeight())); zero1HD.setColor(0f,1f,1f,0f); - zero1HD.setPosition((stage.getWidth() - zero1HD.getWidth()*zero1HD.getScaleX())/2f, (stage.getHeight() - zero1HD.getHeight()*zero1HD.getScaleY())/2f); + zero1HD.setPosition((stage.getWidth() - (zero1HD.getWidth()*zero1HD.getScaleX()))/2f, (stage.getHeight() - (zero1HD.getHeight()*zero1HD.getScaleY()))/2f); + stage.addActor(zero1HD); zero1HD.addAction(Actions.sequence(Actions.color(Color.WHITE, 1f), Actions.fadeOut(0.5f))); } - + + @Override + public void resize(int width, int height) { + stage.getViewport().update(width, height); + super.resize(width, height); + } + @Override public Screen createMainScreen(RhythmBullet game) { return new MainScreen(game); diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java index 9e73461..7286d47 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java @@ -20,8 +20,8 @@ import zero1hd.rhythmbullet.RhythmBullet; import zero1hd.rhythmbullet.audio.MusicController; import zero1hd.rhythmbullet.audio.visualizer.DoubleHorizontalVisualizer; import zero1hd.rhythmbullet.desktop.audio.PCMObtainer; -import zero1hd.rhythmbullet.desktop.graphics.ui.components.MusicControls; import zero1hd.rhythmbullet.graphics.ui.Page; +import zero1hd.rhythmbullet.graphics.ui.components.MusicControls; import zero1hd.rhythmbullet.graphics.ui.components.ScrollText; import zero1hd.rhythmbullet.util.ScreenConfiguration; @@ -46,7 +46,7 @@ public class MainPage extends Page implements Observer { this.mc = musicController; this.mc.addObserver(this); - dhv = new DoubleHorizontalVisualizer((int) getWidth(), (int) 0, 2.5f, 0, screenConfiguration.getFramesPerSecond(), mc, new PCMObtainer(mc)); + dhv = new DoubleHorizontalVisualizer((int) getWidth(), (int) 0, getHeight(), 0, screenConfiguration.getTargetFramesPerSecond(), mc, new PCMObtainer(mc)); dhv.setPosition(0, (int) ((getHeight() - dhv.getHeight())/2f)); title = new Image(assetManager.get("title.png", Texture.class)); @@ -95,7 +95,6 @@ public class MainPage extends Page implements Observer { scrollText.setWidth(0.5f*getWidth()); scrollText.setPosition(15, getHeight() - scrollText.getHeight()-30f); addActor(scrollText); - } @Override diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java index baab909..48858a5 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java @@ -62,7 +62,6 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen { musicController = new MusicController(musicList, core.getPreferences()); musicController.setAutoPlay(true); musicController.setShuffle(true); - musicMetadataController = new MusicMetadataController(musicList); listeners = new Listeners(); @@ -110,6 +109,7 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen { } background = null; musicController.deleteObservers(); + musicMetadataController.deleteObservers(); } @Override @@ -161,8 +161,6 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen { } }); - musicController.addObserver(musicSelectionPage); - musicController.addObserver(mainPage); musicController.getMusicList().asyncSearch(false); resizing = false; } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java index f308e1a..66ef7cb 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java @@ -10,27 +10,32 @@ import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; 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.math.Vector3; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; +import com.badlogic.gdx.scenes.scene2d.ui.Button; +import com.badlogic.gdx.scenes.scene2d.ui.ButtonGroup; 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.ui.VerticalGroup; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; +import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; +import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Array; import zero1hd.rhythmbullet.audio.MusicMetadataController; +import zero1hd.rhythmbullet.audio.metadata.AudioMetadata; import zero1hd.rhythmbullet.audio.MusicController; import zero1hd.rhythmbullet.graphics.ui.Page; -import zero1hd.rhythmbullet.graphics.ui.components.MusicSelectable; -import zero1hd.rhythmbullet.graphics.ui.components.MusicSelectableButtonGroup; import zero1hd.rhythmbullet.graphics.ui.components.ScrollText; public class MusicSelectionPage extends Page implements Observer { @@ -39,21 +44,13 @@ public class MusicSelectionPage extends Page implements Observer { private MusicController mc; private MusicMetadataController mmc; private MusicSelectableButtonGroup selectables; - private Stack stackSelectables; + private VerticalGroup vGroup; private TextButton back; - private ScrollPane musicTableScrollPane; + private ScrollPane scrollPane; - private ClickListener selectionListener; - private musicSelectionLoaderThread thread; + private musicSelectionLoaderThread selectionLoaderThread; - private Table musicInfoTable; - private Table musicSubInfo; - private ScrollText songTitle; - private Label author; - private Label songLength; - private Label previousTop; - private Label ratedDifficulty; - private Image albumCover; + private InformationTable musicInfoTable; private AssetManager assets; private Skin skin; @@ -63,23 +60,25 @@ public class MusicSelectionPage extends Page implements Observer { private TextButton beginButton; private float scrollTimer, scrollDelay = 0.2f, scrollDelMod, songSelectionTimer; - + private float musicSelectDelay; public MusicSelectionPage(AssetManager assetManager, Skin skin, MusicController musicController, MusicMetadataController musicMetadataController, ChangeListener backButtonListener, ChangeListener beginButtonListener) { super(1, 0); this.assets = assetManager; this.mc = musicController; this.mmc = musicMetadataController; this.skin = skin; - stackSelectables = new Stack(); + vGroup = new VerticalGroup(); + vGroup.space(10f); selectables = new MusicSelectableButtonGroup(); + selectables.setMinCheckCount(0); musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation"); - musicTableScrollPane = new ScrollPane(stackSelectables, skin); - musicTableScrollPane.setSize(0.45f*getWidth(), getHeight()); - musicTableScrollPane.setFadeScrollBars(false); - musicTableScrollPane.setOverscroll(false, false); - musicTableScrollPane.setColor(Color.BLUE); - addActor(musicTableScrollPane); + scrollPane = new ScrollPane(vGroup, skin); + scrollPane.setSize(0.45f*getWidth(), getHeight()); + scrollPane.setOverscroll(false, false); + scrollPane.setClamp(true); + scrollPane.setColor(Color.BLUE); + addActor(scrollPane); back = new TextButton("Back", skin); back.setWidth(back.getWidth()+20f); back.setPosition(getWidth()-back.getWidth()-15f, getHeight() - back.getHeight() - 15f); @@ -114,51 +113,15 @@ public class MusicSelectionPage extends Page implements Observer { } }); - musicInfoTable = new Table(); - musicInfoTable.defaults().center(); - musicInfoTable.setPosition(musicTableScrollPane.getWidth() + musicTableScrollPane.getX(), 0); - musicInfoTable.setSize(getWidth()-musicTableScrollPane.getWidth(), getHeight()); + musicInfoTable = new InformationTable(getWidth()-scrollPane.getWidth(), getHeight()); addActor(musicInfoTable); - musicSubInfo = new Table(skin); - musicSubInfo.setBackground("corner-panel"); - songTitle = new ScrollText("", null, skin, true, true); - author = new Label(null, skin, "sub-font", skin.getColor("default")); - songLength = new Label(null, skin, "sub-font", skin.getColor("default")); - previousTop = new Label(null, skin, "sub-font", skin.getColor("default")); - ratedDifficulty = new Label(null, skin, "sub-font", skin.getColor("default")); - albumCover = new Image(assets.get("defaultCover.png", Texture.class)); beginButton = new TextButton("Begin", skin); beginButton.addListener(beginButtonListener); mmc.addObserver(this); - - thread = new musicSelectionLoaderThread(); - - selectionListener = new ClickListener() { - public void clicked(InputEvent event, float x, float y) { - MusicSelectable selectable = (MusicSelectable) event.getListenerActor(); - if (selectable.getMetadata().getTitle() != null) { - songTitle.setText(selectable.getMetadata().getTitle(), null); - } else { - songTitle.setText(selectable.getFileHandle().nameWithoutExtension(), null); - } - - author.setText(selectable.getMetadata().getAuthor()); - - songLength.setText(selectable.getMetadata().getDuration()); - - previousTop.setText("..."); - - ratedDifficulty.setText("..."); - - if (selectable.getMetadata().getAlbumCover() != null) { - albumCover.setDrawable(new TextureRegionDrawable(new TextureRegion(selectable.getMetadata().getAlbumCover()))); - } else { - albumCover.setDrawable(new TextureRegionDrawable(new TextureRegion(assets.get("defaultCover.png", Texture.class)))); - } - } - }; + mc.addObserver(this); + selectionLoaderThread = new musicSelectionLoaderThread(); } @Override @@ -193,74 +156,58 @@ public class MusicSelectionPage extends Page implements Observer { if (songSelectionTimer <= 0f) { } } - - if (mc.getMusicList().isSearched() && 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; - } + if (mc.getMusicList().isSearched()) { + if (mc.getMusicList().getTotal() != 0) { + } } + + updateList(delta); + super.act(delta); } + private void updateList(float delta) { + if (mc.getMusicList().isSearched()) { + if (mc.getMusicList().getTotal() != 0) { + if (selectables.size() != mmc.size()) { + MusicSelectable selectable = new MusicSelectable(mmc.getMetadata(selectables.size())); + selectables.add(selectable); + } else if (selectables.size() != vGroup.getChildren().size) { + vGroup.addActor(selectables.getButtons().get(vGroup.getChildren().size)); + } else { + if (selectables.getChecked() == null) { + selectables.setMinCheckCount(1); + selectables.setChecked(mc.getCurrentMusicFileHandle()); + scrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight()); + } else if (selectables.getChecked().getMetadata().getFileHandle() != mc.getCurrentMusicFileHandle()) { + musicSelectDelay += delta; + if (musicSelectDelay >= 1f) { + mc.setMusicByFileHandle(selectables.getChecked().getMetadata().getFileHandle()); + musicSelectDelay = 0; + } + } + } + } else { + //TODO: Error message reporting empty music list or something + } + } + } + private void scrollDown() { selectables.selectNext(); - musicTableScrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight()); + scrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight()); } private void scrollUp() { selectables.selectPrevious(); - musicTableScrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight()); + scrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight()); } public FileHandle getSelectedMusic() { return selectables.getChecked().getMetadata().getFileHandle(); } - public void refreshUIList() { - selectables.clear(); - - mmc.loadAudioMetadata(); - selectables.clear(); - musicInfoTable.clear(); - musicSubInfo.clear(); - - Gdx.app.debug("MusicSelectionPage", "Refreshing..."); - - songTitle.setText("loading...", null); - musicInfoTable.add(songTitle).width(musicInfoTable.getWidth()*0.6f).spaceBottom(30f); - musicInfoTable.row(); - author.setText("..."); - musicSubInfo.add(author); - musicSubInfo.row(); - songLength.setText("..."); - musicSubInfo.add(songLength); - musicSubInfo.row(); - previousTop.setText("..."); - musicSubInfo.add(previousTop); - musicSubInfo.row(); - ratedDifficulty.setText("..."); - musicSubInfo.add(ratedDifficulty); - musicSubInfo.pack(); - musicInfoTable.add(musicSubInfo).spaceBottom(20f); - musicInfoTable.row(); - albumCover.setDrawable(new TextureRegionDrawable(new TextureRegion(assets.get("defaultCover.png", Texture.class)))); - musicInfoTable.add(albumCover).size(musicInfoTable.getWidth()/2f); - musicInfoTable.row(); - musicInfoTable.add(beginButton).spaceTop(20f).fillX(); - } - @Override public void dispose() { super.dispose(); @@ -269,7 +216,12 @@ public class MusicSelectionPage extends Page implements Observer { @Override public void update(Observable o, Object arg) { if (o == mmc) { - thread.start(); + musicInfoTable.setToDefault(); + selectionLoaderThread.start(); + } else if (o == mc) { + if (mc.getMusicList().getTotal() == selectables.size() && mc.getCurrentMusicFileHandle() != selectables.getChecked().getMetadata().getFileHandle()) { + selectables.setChecked(mc.getCurrentMusicFileHandle()); + } } } @@ -281,9 +233,9 @@ public class MusicSelectionPage extends Page implements Observer { private class musicSelectionLoaderThread implements Runnable { private Thread thread; - private Array queueList; + private Array queueList; private String name = "Music-Selection-Loader-Thread"; - private volatile boolean work; + private volatile boolean work = true; public musicSelectionLoaderThread() { queueList = new Array<>(); @@ -292,18 +244,23 @@ public class MusicSelectionPage extends Page implements Observer { @Override public void run() { while (work) { - while (selectables.getButtons().size != mc.getMusicList().getTotal()) { - MusicSelectable selectable = new MusicSelectable(skin, assets.get("defaultCover.png"), mmc.getMetadata(selectables.getButtons().size), queueList); - selectable.addListener(selectionListener); - selectables.add(selectable); + if (selectables.size() != mmc.size()) { + selectables.clear(); + for (int mid = 0; mid < mmc.size(); mid++) { + + } + selectables.uncheckAll(); + } else { + synchronized (this) { + while (queueList.size != 0) { + AudioMetadata metadata = queueList.pop(); + metadata.loadAlbumCover(); + } + } + } - for (int i = 0; i < queueList.size; i++) { - queueList.get(i).loadAlbumCover(); - queueList.removeIndex(i); - } - - synchronized (queueList) { + synchronized (this) { try { wait(); } catch (InterruptedException e) { @@ -318,12 +275,259 @@ public class MusicSelectionPage extends Page implements Observer { thread = new Thread(this, name); thread.start(); return true; - } - synchronized (queueList) { - notify(); + } else { + synchronized (this) { + notify(); + } } return false; } + public synchronized void queue(AudioMetadata metadata) { + if (!queueList.contains(metadata, true)) { + queueList.add(metadata); + notify(); + } + } + + } + + private class MusicSelectable extends Button { + private Vector2 actualCoords; + private Image albumCoverImage; + private Table informationTable; + private Label name, artist; + private Label time; + private float timeSinceOnScreen; + private AudioMetadata metadata; + private Texture defaultAlbumArt; + private TextureRegion albumArtTexture; + private boolean albumArtUsed, albumArtAttempted; + + public MusicSelectable(AudioMetadata metadata) { + super(skin, "music-selectable"); + + this.metadata = metadata; + this.defaultAlbumArt = assets.get("defaultCover.png"); + albumArtTexture = new TextureRegion(defaultAlbumArt); + + albumCoverImage = new Image(); + updateAlbumArtImage(defaultAlbumArt); + + setSize(getPrefWidth(), getPrefHeight()); + informationTable = new Table(); + informationTable.row().width(0.75f*getWidth()); + name = new Label(metadata.getTitle(), skin, "default-font", skin.getColor("default")); + name.setEllipsis(true); + informationTable.add(name).colspan(2).left().expand(); + informationTable.row(); + artist = new Label(metadata.getAuthor(), skin, "sub-font", skin.getColor("default")); + artist.setEllipsis(true); + informationTable.add(artist).left().width(getWidth()*0.375f); + time = new Label(metadata.getDuration(), skin, "sub-font", skin.getColor("default")); + informationTable.add(time).right(); + add(informationTable).spaceRight(15f).padLeft(15f); + + add(albumCoverImage).right().expandX().size(informationTable.getMinHeight()); + + albumCoverImage.setSize(100, 100); + actualCoords = new Vector2(); + } + + @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()) && selectables.getChecked() != this) { + offScreenAct(delta); + } else { + onScreenAct(delta); + } + super.act(delta); + } + + @Override + public void draw(Batch batch, float parentAlpha) { + synchronized (albumCoverImage) { + super.draw(batch, parentAlpha); + } + } + + public void onScreenAct(float delta) { + timeSinceOnScreen = 0; + if (metadata.getAlbumCover() != null && !albumArtUsed) { + updateAlbumArtImage(metadata.getAlbumCover()); + albumArtUsed = true; + } else if (!albumArtAttempted) { + selectionLoaderThread.queue(metadata); + albumArtAttempted = true; + } + } + + private void updateAlbumArtImage(Texture texture) { + if (texture == null) throw new IllegalArgumentException("Texture can't be null!"); + albumArtTexture.setRegion(texture); + albumCoverImage.setDrawable(new TextureRegionDrawable(albumArtTexture)); + } + + public void offScreenAct(float delta) { + if (metadata.getAlbumCover() != null) { + timeSinceOnScreen += delta; + if (timeSinceOnScreen >= 2) { + updateAlbumArtImage(defaultAlbumArt); + metadata.unloadAlbumCover(); + albumArtUsed = false; + albumArtAttempted = false; + } + } + } + + public AudioMetadata getMetadata() { + return metadata; + } + + public FileHandle getFileHandle() { + return metadata.getFileHandle(); + } + + @Override + public float getPrefWidth() { + return scrollPane.getScrollWidth(); + } + + @Override + public float getPrefHeight() { + return super.getPrefHeight(); + } + + public TextureRegion getAlbumArtTexture() { + return albumArtTexture; + } + } + + private class MusicSelectableButtonGroup extends ButtonGroup { + private Array buttons; + + public MusicSelectableButtonGroup() { + buttons = getButtons(); + } + + 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); + } + + @Override + protected boolean canCheck(MusicSelectable button, boolean newState) { + if (newState) { + musicInfoTable.setDisplayedSelectable(button); + } + return super.canCheck(button, newState); + } + + public int size() { + return buttons.size; + } + } + + private class InformationTable extends Table { + private ScrollText songTitle; + private Label author; + private Label musicDuration; + private Label previousTop; + private Label ratedDifficulty; + private Image albumCover; + + private Table subInformation; + private MusicSelectable displayedSelectable; + + public InformationTable(float width, float height) { + defaults().center(); + setPosition(scrollPane.getWidth() + scrollPane.getX(), 0); + setSize(width, height); + subInformation = new Table(skin); + subInformation.setBackground("corner-panel"); + albumCover = new Image(assets.get("defaultCover.png", Texture.class)); + songTitle = new ScrollText("", null, skin, true, true); + author = new Label(null, skin, "sub-font", skin.getColor("default")); + musicDuration = new Label(null, skin, "sub-font", skin.getColor("default")); + previousTop = new Label(null, skin, "sub-font", skin.getColor("default")); + ratedDifficulty = new Label(null, skin, "sub-font", skin.getColor("default")); + } + + public void setDisplayedSelectable(MusicSelectable displayedSelectable) { + this.displayedSelectable = displayedSelectable; + if (displayedSelectable != null) { + AudioMetadata metadata = displayedSelectable.getMetadata(); + albumCover.setDrawable(new TextureRegionDrawable(displayedSelectable.getAlbumArtTexture())); + songTitle.setText(metadata.getTitle(), null); + author.setText(metadata.getAuthor()); + musicDuration.setText(metadata.getDuration()); + //TODO previous top + //TODO rated difficulty + beginButton.setDisabled(false); + } else { + albumCover.setDrawable(new TextureRegionDrawable(new TextureRegion(assets.get("defaultCover.png", Texture.class)))); + songTitle.setText("loading...", null); + author.setText("..."); + musicDuration.setText("..."); + previousTop.setText("..."); + ratedDifficulty.setText("..."); + } + } + + public void setToDefault() { + clear(); + subInformation.clear(); + + albumCover.setDrawable(new TextureRegionDrawable(new TextureRegion(assets.get("defaultCover.png", Texture.class)))); + add(albumCover).size(getWidth()/2f).spaceBottom(25f); + row(); + songTitle.setText("...", null); + add(songTitle).width(getWidth()*0.6f).spaceBottom(30f); + row(); + author.setText("..."); + author.setEllipsis(true); + author.setAlignment(Align.center); + subInformation.add(author).expand(); + subInformation.row(); + musicDuration.setText("..."); + subInformation.add(musicDuration); + subInformation.row(); + previousTop.setText("..."); + subInformation.add(previousTop); + subInformation.row(); + ratedDifficulty.setText("..."); + subInformation.add(ratedDifficulty); + add(subInformation).width(0.4f*getWidth()); + row(); + add(beginButton).spaceTop(20f).fillX(); + beginButton.setDisabled(true); + } } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/OptionsPage.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/OptionsPage.java index 4b62d02..e178d81 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/OptionsPage.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/OptionsPage.java @@ -127,12 +127,12 @@ public class OptionsPage extends Page { optionsTable.row(); Label usageLabel = new Label("Current usage (lower the better): " + 100f*((float)(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/(float)Runtime.getRuntime().totalMemory()) + "%", skin) { - float refreshTime = 60; + float refreshTime = 20; @Override public void act(float delta) { refreshTime -= delta; if (refreshTime <= 0) { - refreshTime = 60; + refreshTime = 20; setText("Current usage (lower the better): " + 100f*((float)(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/(float)Runtime.getRuntime().totalMemory()) + "%"); } super.act(delta);