Progress on mission 'async everything'

This commit is contained in:
Harrison Deng 2017-12-07 16:47:50 -06:00
parent a41923fa12
commit a858fa9159
5 changed files with 160 additions and 118 deletions

View File

@ -12,13 +12,15 @@ import org.jaudiotagger.audio.wav.WavTag;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.TagException;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.PixmapIO;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.utils.Disposable;
public class SongInfo implements Disposable {
public class MusicInfo implements Disposable {
private long durationInSeconds;
private String songName;
@ -26,16 +28,15 @@ public class SongInfo implements Disposable {
private String author;
private int previousTop;
private int ratedDifficulty;
private byte[] albumWorkBytes;
private boolean invalidMusic;
private boolean containsInfo;
private FileHandle musicFile;
private Preferences musicAnnotation;
public SongInfo(FileHandle musicFile, Preferences musicData) {
public MusicInfo(FileHandle musicFile, Preferences musicAnnotation) {
this.musicFile = musicFile;
this.musicAnnotation = musicData;
this.musicAnnotation = musicAnnotation;
}
/**
@ -50,7 +51,12 @@ public class SongInfo implements Disposable {
durationInSeconds = mp3File.getAudioHeader().getTrackLength();
if (mp3File.getTag() != null && mp3File.getTag().getFirstArtwork() != null) {
albumWorkBytes = mp3File.getTag().getFirstArtwork().getBinaryData();
if (!Gdx.files.external("RhythmBullet/AlbumArt/" + musicFile.name() + ".png").exists()) {
byte[] albumWorkBytes = mp3File.getTag().getFirstArtwork().getBinaryData();
Pixmap pixmap = new Pixmap(albumWorkBytes, 0, albumWorkBytes.length);
PixmapIO.writePNG(Gdx.files.external("RhythmBullet/AlbumArt/" + musicFile.name() + ".png"), pixmap);
pixmap.dispose();
}
}
songName = mp3File.getTag().getFirst(FieldKey.TITLE);
@ -91,13 +97,17 @@ public class SongInfo implements Disposable {
containsInfo = true;
}
public void setupTexture(Texture defaultAlbumCover) {
if (albumWorkBytes != null) {
Pixmap albumCoverFromBytes = new Pixmap(albumWorkBytes, 0, albumWorkBytes.length);
albumCover = new Texture(albumCoverFromBytes);
albumCoverFromBytes.dispose();
/**
* The album art as a texture.
* Can return null if contains info is false.
* Not automatically disposed.
* @return
*/
public Texture loadTexture() {
if (Gdx.files.external("RhythmBullet/AlbumArt/" + musicFile.name() + ".png").exists()) {
return new Texture(Gdx.files.external("RhythmBullet/AlbumArt/" + musicFile.name() + ".png"));
} else {
this.albumCover = defaultAlbumCover;
return null;
}
}
@ -141,4 +151,8 @@ public class SongInfo implements Disposable {
public boolean hasInformation() {
return containsInfo;
}
public FileHandle getMusicFile() {
return musicFile;
}
}

View File

@ -0,0 +1,57 @@
package zero1hd.rhythmbullet.audio;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
public class MusicInfoController implements Disposable {
private MusicList musicList;
private ExecutorService exec;
private Array<MusicInfo> songInfoArray;
private Preferences musicAnnotation;
private boolean doneLoading;
public MusicInfoController(MusicList musicList) {
this.musicList = musicList;
songInfoArray = new Array<>();
exec = Executors.newSingleThreadExecutor();
musicAnnotation = Gdx.app.getPreferences("MusicAnnotation");
}
public MusicList getMusicList() {
return musicList;
}
/**
* Non-blocking, loads on separate thread.
*/
public void loadSongInfo() {
doneLoading = false;
songInfoArray.clear();
exec.submit(() -> {
for (int i = 0; i < musicList.getAmountOfMusic(); i++) {
MusicInfo musicInfo = new MusicInfo(musicList.getMusicList().get(i), musicAnnotation);
musicInfo.loadInfo();
songInfoArray.add(musicInfo);
}
doneLoading = true;
});
}
@Override
public void dispose() {
exec.shutdown();
}
/**
* Verify if loading song info is done.
* @return
*/
public synchronized boolean isDoneLoading() {
return doneLoading;
}
}

View File

@ -1,70 +1,51 @@
package zero1hd.rhythmbullet.graphics.ui.components;
import com.badlogic.gdx.Preferences;
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.scenes.scene2d.InputEvent;
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.ui.VerticalGroup;
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Disposable;
import zero1hd.rhythmbullet.audio.SongInfo;
import zero1hd.rhythmbullet.audio.MusicInfo;
import zero1hd.rhythmbullet.graphics.ui.pages.MusicSelectionPage;
public class MusicSelectable extends WidgetGroup implements Disposable {
private Table table;
private Image imageIcon;
private ShortenedTextLabel displayName;
private Label durationLabel;
private ShortenedTextLabel authorLabel;
private FileHandle musicFile;
private Texture albumCover;
private SongInfo songInfo;
private boolean selected;
private VerticalGroup vGroup;
private MusicSelectionPage msp;
private MusicInfo musicInfo;
public MusicSelectable(FileHandle musicFile, Preferences musicAnnotation, Skin skin, Texture defaultAlbumC, MusicSelectionPage msp) {
private FileHandle musicFile;
public MusicSelectable(FileHandle musicFile, Skin skin, MusicSelectionPage msp) {
this.musicFile = musicFile;
table = new Table(skin);
table.setBackground("holo-pane");
table.setFillParent(true);
vGroup = new VerticalGroup();
this.msp = msp;
setName(musicFile.name());
table.defaults().pad(2f);
this.albumCover = defaultAlbumC;
this.musicFile = musicFile;
songInfo = new SongInfo(musicFile, musicAnnotation);
imageIcon = new Image(albumCover);
table.add(imageIcon).size(180f).left().expandX();
setName(musicFile.nameWithoutExtension());
table.defaults().pad(5f).space(15f);
displayName = new ShortenedTextLabel(musicFile.nameWithoutExtension().replace('_', ' '), skin, "sub-font", skin.getColor("default"));
vGroup.addActor(displayName);
table.add(displayName);
durationLabel = new Label("Loading...", skin, "sub-font", skin.getColor("default"));
vGroup.addActor(durationLabel);
table.add(durationLabel);
authorLabel = new ShortenedTextLabel("Loading...", skin, "sub-font", skin.getColor("default"));
vGroup.addActor(authorLabel);
table.add(vGroup).expandX().fillX().center();
table.add(authorLabel);
table.pack();
addActor(table);
addListener(new ClickListener() {
@Override
@ -78,27 +59,27 @@ public class MusicSelectable extends WidgetGroup implements Disposable {
/**
* updates the UI side of information.
* needs to be called in thread with gl context.
* @param musicInfo the music information for this song.
*/
public void updateInfo() {
displayName.setOriginalText(songInfo.getMusicName());
public void updateInfo(MusicInfo musicInfo) {
this.musicInfo = musicInfo;
displayName.setOriginalText(musicInfo.getMusicName());
durationLabel.setText("Runtime: "
+ ((songInfo.getDurationInSeconds() / 60 < 1) ? "00" : songInfo.getDurationInSeconds() / 60) + ":"
+ ((songInfo.getDurationInSeconds() - (songInfo.getDurationInSeconds() / 60) * 60) < 10
? "0" + (songInfo.getDurationInSeconds() - (songInfo.getDurationInSeconds() / 60) * 60)
: (songInfo.getDurationInSeconds() - (songInfo.getDurationInSeconds() / 60) * 60)));
authorLabel.setOriginalText("Author: " + songInfo.getAuthor());
+ ((musicInfo.getDurationInSeconds() / 60 < 1) ? "00" : musicInfo.getDurationInSeconds() / 60) + ":"
+ ((musicInfo.getDurationInSeconds() - (musicInfo.getDurationInSeconds() / 60) * 60) < 10
? "0" + (musicInfo.getDurationInSeconds() - (musicInfo.getDurationInSeconds() / 60) * 60)
: (musicInfo.getDurationInSeconds() - (musicInfo.getDurationInSeconds() / 60) * 60)));
authorLabel.setOriginalText("Author: " + musicInfo.getAuthor());
authorLabel.setToOriginalText();
songInfo.setupTexture(albumCover);
albumCover = songInfo.getAlbumCover();
imageIcon.setDrawable((new TextureRegionDrawable(new TextureRegion(albumCover))));
}
@Override
public void layout() {
displayName.setTargetWidth((int) (getWidth() - 300));
authorLabel.setTargetWidth((int) (getWidth() - 300));
displayName.setTargetWidth((int) (getWidth()/3f));
authorLabel.setTargetWidth((int) (getWidth()/3f));
displayName.resize();
authorLabel.resize();
super.layout();
}
@ -112,21 +93,8 @@ public class MusicSelectable extends WidgetGroup implements Disposable {
super.act(delta);
}
public FileHandle getMusicFile() {
return musicFile;
}
public SongInfo getAudioInfo() {
return songInfo;
}
@Override
public void dispose() {
songInfo.dispose();
}
public boolean isMusicInvalid() {
return songInfo.isInvalidMusic();
return musicInfo.isInvalidMusic();
}
/**
@ -152,4 +120,16 @@ public class MusicSelectable extends WidgetGroup implements Disposable {
public float getPrefHeight() {
return table.getMinHeight();
}
@Override
public void dispose() {
}
public FileHandle getMusicFile() {
return musicFile;
}
public MusicInfo getMusicInfo() {
return musicInfo;
}
}

View File

@ -2,8 +2,6 @@ package zero1hd.rhythmbullet.graphics.ui.pages;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
@ -28,7 +26,8 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.audio.MusicManager;
import zero1hd.rhythmbullet.audio.SongInfo;
import zero1hd.rhythmbullet.audio.MusicInfo;
import zero1hd.rhythmbullet.audio.MusicInfoController;
import zero1hd.rhythmbullet.audio.MusicListController;
import zero1hd.rhythmbullet.graphics.ui.components.MusicSelectable;
import zero1hd.rhythmbullet.graphics.ui.components.ScrollText;
@ -38,6 +37,7 @@ public class MusicSelectionPage extends Page implements Observer {
private boolean extraInfoDone;
private MusicListController mc;
private MusicInfoController mic;
private Array<MusicSelectable> selectables;
private Table musicTable;
private ScrollPane musicTableScrollPane;
@ -61,13 +61,15 @@ public class MusicSelectionPage extends Page implements Observer {
private int musicSelectableIndex;
private TextButton beginButton;
private int uiSongCount;
private float scrollTimer, scrollDelay = 0.2f, scrollDelMod, songSelectionTimer;
private ExecutorService exec;
public MusicSelectionPage(Skin skin, MusicListController musicListController, AssetManager assetManager, Vector3 cameraTarget, AnalysisPage ap) {
setTextureBackground(assetManager.get("gradients.atlas", TextureAtlas.class).findRegion("red-round"));
this.skin = skin;
this.mc = musicListController;
mic = new MusicInfoController(mc.getMusicList());
musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation");
this.assets = assetManager;
musicTable = new Table();
@ -137,8 +139,6 @@ public class MusicSelectionPage extends Page implements Observer {
ap.processSong(mc.getMusicList().getAudioData(getSelectedMusic()));
}
});
exec = Executors.newSingleThreadExecutor();
}
@Override
@ -175,14 +175,20 @@ public class MusicSelectionPage extends Page implements Observer {
}
}
super.act(delta);
if (uiSongCount < selectables.size) {
musicTable.add(selectables.get(uiSongCount)).expandX().fillX();
uiSongCount++;
musicTable.row();
}
}
public FileHandle getSelectedMusic() {
return currentlySelected.getMusicFile();
}
public SongInfo getSelectedMusicInfo() {
return currentlySelected.getAudioInfo();
public MusicInfo getSelectedMusicInfo() {
return currentlySelected.getMusicInfo();
}
public void refreshUIList() {
@ -191,6 +197,8 @@ public class MusicSelectionPage extends Page implements Observer {
musicInfoTable.clear();
musicSubInfo.clear();
extraInfoDone = false;
uiSongCount = 0;
mic.loadSongInfo();
for (int i = 0; i < selectables.size; i++) {
selectables.get(i).dispose();
@ -199,44 +207,24 @@ public class MusicSelectionPage extends Page implements Observer {
Gdx.app.debug("MusicSelectionPage", "Refreshing...");
for (int i = 0; i < mc.getMusicList().getAmountOfMusic(); i++) {
MusicSelectable selectable = new MusicSelectable(mc.getMusicList().getSongFileHandleFromIndex(i), musicFileAnnotation, skin, assets.get("defaultCover.png", Texture.class), this);
MusicSelectable selectable = new MusicSelectable(mc.getMusicList().getSongFileHandleFromIndex(i), skin, this);
selectables.add(selectable);
musicTable.add(selectable).expandX().fillX();
musicTable.row();
}
exec.submit(() -> {
for (int i = 0; i < selectables.size; i++) {
MusicSelectable selectable = selectables.get(i);
selectable.getAudioInfo().loadInfo();
Gdx.app.postRunnable(() -> {
selectable.updateInfo();
});
}
extraInfoDone = true;
musicInfoTable.add(songTitle).width(musicInfoTable.getWidth()*0.6f).spaceBottom(30f);
musicInfoTable.row();
musicSubInfo.add(author);
musicSubInfo.row();
musicSubInfo.add(songLength);
musicSubInfo.row();
musicSubInfo.add(previousTop);
musicSubInfo.row();
musicSubInfo.add(ratedDifficulty);
musicSubInfo.pack();
musicInfoTable.add(musicSubInfo).spaceBottom(20f);
musicInfoTable.row();
musicInfoTable.add(albumCover).size(musicInfoTable.getWidth()/2f);
musicInfoTable.row();
musicInfoTable.add(beginButton).spaceTop(20f).fillX();
if (currentlySelected != null) {
updateInformation();
}
Gdx.app.debug("MusicSelectionPage", "Refresh complete. " + selectables.size + " songs loaded.");
});
musicInfoTable.add(songTitle).width(musicInfoTable.getWidth()*0.6f).spaceBottom(30f);
musicInfoTable.row();
musicSubInfo.add(author);
musicSubInfo.row();
musicSubInfo.add(songLength);
musicSubInfo.row();
musicSubInfo.add(previousTop);
musicSubInfo.row();
musicSubInfo.add(ratedDifficulty);
musicSubInfo.pack();
musicInfoTable.add(musicSubInfo).spaceBottom(20f);
musicInfoTable.row();
musicInfoTable.add(albumCover).size(musicInfoTable.getWidth()/2f);
musicInfoTable.row();
musicInfoTable.add(beginButton).spaceTop(20f).fillX();
}
@Override
@ -276,7 +264,6 @@ public class MusicSelectionPage extends Page implements Observer {
if ((musicSelectableIndex = (musicTable.getChildren().indexOf(currentlySelected, true) - 1)) < 0) {
musicSelectableIndex = musicTable.getChildren().size-1;
}
deselectAll();
((MusicSelectable)musicTable.getChildren().get(musicSelectableIndex)).select();
musicTableScrollPane.scrollTo(currentlySelected.getX(), currentlySelected.getY(), currentlySelected.getWidth(), currentlySelected.getHeight());
}
@ -312,20 +299,23 @@ public class MusicSelectionPage extends Page implements Observer {
}
}
/**
* This should only be called when everything is loaded.
*/
private void updateInformation() {
songTitle.setText(currentlySelected.getAudioInfo().getMusicName(), null);
author.setText("Author: " + currentlySelected.getAudioInfo().getAuthor());
songTitle.setText(currentlySelected.getMusicInfo().getMusicName(), null);
author.setText("Author: " + currentlySelected.getMusicInfo().getAuthor());
long lengthInSeconds = currentlySelected.getAudioInfo().getDurationInSeconds();
long lengthInSeconds = currentlySelected.getMusicInfo().getDurationInSeconds();
int min = (int) (lengthInSeconds/60);
int sec = (int) (lengthInSeconds - (min*60));
songLength.setText("Length: " + min + ":" + (sec > 9 ? sec : "0" + sec));
previousTop.setText("Highscore: " + currentlySelected.getAudioInfo().getPreviousTop());
previousTop.setText("Highscore: " + currentlySelected.getMusicInfo().getPreviousTop());
String difficulty = (getSelectedMusicInfo().getRatedDifficulty() == -1 ? "N/A" : String.valueOf(getSelectedMusicInfo().getRatedDifficulty()));
ratedDifficulty.setText("Rated Difficulty: " + difficulty);
albumCover.setDrawable((new TextureRegionDrawable(new TextureRegion(currentlySelected.getAudioInfo().getAlbumCover()))));
albumCover.setDrawable((new TextureRegionDrawable(new TextureRegion(currentlySelected.getMusicInfo().getAlbumCover()))));
}
}

View File

@ -44,6 +44,7 @@ public class MainMenu extends ScreenAdapter implements TransitionAdapter {
private RhythmBullet core;
private MusicListController mlc;
private float lerpAlpha;
private ShaderProgram gaussianBlurShader;