main difference: music selection screen now completely functional; other
refactoring and changes to make better use of the framework were made; some cleanup happened;
This commit is contained in:
parent
fa8dd9622f
commit
d7008796f4
@ -86,7 +86,7 @@ public class MusicController extends Observable implements OnCompletionListener,
|
|||||||
public void skip() {
|
public void skip() {
|
||||||
currentlyPlayingIndex++;
|
currentlyPlayingIndex++;
|
||||||
if (shuffle) {
|
if (shuffle) {
|
||||||
shuffle(false);
|
shuffle();
|
||||||
}
|
}
|
||||||
loadMusic();
|
loadMusic();
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ public class MusicController extends Observable implements OnCompletionListener,
|
|||||||
public void previous() {
|
public void previous() {
|
||||||
currentlyPlayingIndex--;
|
currentlyPlayingIndex--;
|
||||||
if (shuffle) {
|
if (shuffle) {
|
||||||
shuffle(false);
|
shuffle();
|
||||||
}
|
}
|
||||||
loadMusic();
|
loadMusic();
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ public class MusicController extends Observable implements OnCompletionListener,
|
|||||||
public void onCompletion(Music music) {
|
public void onCompletion(Music music) {
|
||||||
if (autoPlay) {
|
if (autoPlay) {
|
||||||
if (shuffle) {
|
if (shuffle) {
|
||||||
shuffle(false);
|
shuffle();
|
||||||
} else {
|
} else {
|
||||||
currentlyPlayingIndex++;
|
currentlyPlayingIndex++;
|
||||||
}
|
}
|
||||||
@ -117,18 +117,14 @@ public class MusicController extends Observable implements OnCompletionListener,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuffles the controller whether the shuffle boolean is true or false.
|
* 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.");
|
Gdx.app.debug("MusicListController", "shuffled.");
|
||||||
if (musicList.getTotal() == 0) {
|
if (musicList.getTotal() == 0) {
|
||||||
currentlyPlayingIndex = 0;
|
currentlyPlayingIndex = 0;
|
||||||
} else {
|
} else {
|
||||||
currentlyPlayingIndex = rand.nextInt(musicList.getTotal());
|
currentlyPlayingIndex = rand.nextInt(musicList.getTotal());
|
||||||
}
|
}
|
||||||
if (load) {
|
|
||||||
loadMusic();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAutoPlay(boolean autoPlay) {
|
public void setAutoPlay(boolean autoPlay) {
|
||||||
@ -152,6 +148,7 @@ public class MusicController extends Observable implements OnCompletionListener,
|
|||||||
*/
|
*/
|
||||||
public void loadMusic() {
|
public void loadMusic() {
|
||||||
Gdx.app.debug("MusicListController", "music is being loaded and listeners are being notified.");
|
Gdx.app.debug("MusicListController", "music is being loaded and listeners are being notified.");
|
||||||
|
boolean playing = isPlaying();
|
||||||
musicHeader = null;
|
musicHeader = null;
|
||||||
if (music != null) {
|
if (music != null) {
|
||||||
music.dispose();
|
music.dispose();
|
||||||
@ -169,7 +166,7 @@ public class MusicController extends Observable implements OnCompletionListener,
|
|||||||
setChanged();
|
setChanged();
|
||||||
|
|
||||||
notifyObservers(states.LOADED);
|
notifyObservers(states.LOADED);
|
||||||
if (autoPlay) {
|
if (playing) {
|
||||||
play();
|
play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,9 +192,12 @@ public class MusicController extends Observable implements OnCompletionListener,
|
|||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
if (o == musicList) {
|
if (o == musicList) {
|
||||||
if (shuffle) {
|
if (shuffle) {
|
||||||
shuffle(false);
|
shuffle();
|
||||||
}
|
}
|
||||||
loadMusic();
|
loadMusic();
|
||||||
|
if (autoPlay) {
|
||||||
|
play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package zero1hd.rhythmbullet.audio;
|
package zero1hd.rhythmbullet.audio;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Observable;
|
import java.util.Observable;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
import com.badlogic.gdx.utils.Sort;
|
||||||
|
|
||||||
import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
|
import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
|
||||||
import zero1hd.rhythmbullet.audio.processor.WAVAudioProcessor;
|
import zero1hd.rhythmbullet.audio.processor.WAVAudioProcessor;
|
||||||
@ -20,19 +22,30 @@ public class MusicList extends Observable {
|
|||||||
private AudioProcessorFactory audioProcFactory;
|
private AudioProcessorFactory audioProcFactory;
|
||||||
private volatile boolean searched;
|
private volatile boolean searched;
|
||||||
private String searchPath;
|
private String searchPath;
|
||||||
|
private Comparator<FileHandle> compare;
|
||||||
|
|
||||||
public MusicList(AudioProcessorFactory audioProcessorFactory, String searchPath) {
|
public MusicList(AudioProcessorFactory audioProcessorFactory, String searchPath) {
|
||||||
this.audioProcFactory = audioProcessorFactory;
|
this.audioProcFactory = audioProcessorFactory;
|
||||||
musicList = new Array<>();
|
musicList = new Array<>();
|
||||||
setSearchPath(searchPath);
|
setSearchPath(searchPath);
|
||||||
|
|
||||||
|
compare = new Comparator<FileHandle>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(FileHandle o1, FileHandle o2) {
|
||||||
|
return o1.nameWithoutExtension().compareTo(o2.nameWithoutExtension());
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper method that uses async refresh.
|
* Wrapper method that uses async refresh.
|
||||||
* Also notifies listeners that are on the main thread.
|
* 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) {
|
public void asyncSearch(boolean refresh) {
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
|
|
||||||
if (searchThread != null) {
|
if (searchThread != null) {
|
||||||
if (!searchThread.start()) {
|
if (!searchThread.start()) {
|
||||||
searchThread.stop();
|
searchThread.stop();
|
||||||
@ -45,7 +58,6 @@ public class MusicList extends Observable {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (searched && !hasChanged()) {
|
if (searched && !hasChanged()) {
|
||||||
setChanged();
|
|
||||||
notifyObservers();
|
notifyObservers();
|
||||||
} else {
|
} else {
|
||||||
asyncSearch(true);
|
asyncSearch(true);
|
||||||
@ -54,7 +66,7 @@ public class MusicList extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setSearchPath(String searchPath) {
|
public void setSearchPath(String searchPath) {
|
||||||
hasChanged();
|
setChanged();
|
||||||
this.searchPath = searchPath;
|
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...");
|
Gdx.app.debug("MusicList", "Warning, this list has not completed it's search...");
|
||||||
Thread.dumpStack();
|
Thread.dumpStack();
|
||||||
}
|
}
|
||||||
|
if (musicList.size == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return newAudioProcessor(musicList.get(index));
|
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...");
|
Gdx.app.debug("MusicList", "Warning, this list has not completed it's search...");
|
||||||
Thread.dumpStack();
|
Thread.dumpStack();
|
||||||
}
|
}
|
||||||
|
if (musicList.size == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return musicList.get(index);
|
return musicList.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,24 +148,15 @@ public class MusicList extends Observable {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Array<FileHandle> obtainedAudioFiles = recursiveMusicSearch(directory);
|
Array<FileHandle> obtainedAudioFiles = recursiveMusicSearch(directory);
|
||||||
if (Gdx.files.external("RhythmBullet").exists()) {
|
Sort.instance().sort(obtainedAudioFiles, compare);
|
||||||
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"));
|
|
||||||
}
|
|
||||||
if (work) {
|
if (work) {
|
||||||
musicList = obtainedAudioFiles;
|
musicList = obtainedAudioFiles;
|
||||||
musicList.add(Gdx.files.external("RhythmBullet/Alan Walker - Spectre.mp3"));
|
|
||||||
if (work) {
|
|
||||||
searched = true;
|
searched = true;
|
||||||
Gdx.app.debug("MusicList", "recursive async search completed.");
|
Gdx.app.debug("MusicList", "recursive async search completed.");
|
||||||
setChanged();
|
setChanged();
|
||||||
notifyObservers();
|
notifyObservers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public boolean start() {
|
public boolean start() {
|
||||||
if (thread == null) {
|
if (thread == null) {
|
||||||
|
@ -3,6 +3,7 @@ package zero1hd.rhythmbullet.audio;
|
|||||||
import java.util.Observable;
|
import java.util.Observable;
|
||||||
import java.util.Observer;
|
import java.util.Observer;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.Disposable;
|
import com.badlogic.gdx.utils.Disposable;
|
||||||
@ -74,10 +75,11 @@ public class MusicMetadataController extends Observable implements Disposable, O
|
|||||||
private class MetadataLoadingThread implements Runnable {
|
private class MetadataLoadingThread implements Runnable {
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private String name = "Metadata-Load";
|
private String name = "Metadata-Load";
|
||||||
private volatile boolean work;
|
private volatile boolean work = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
Gdx.app.debug(name, "loading...");
|
||||||
searching = true;
|
searching = true;
|
||||||
for (int i = 0; i < metadataArray.size; i++) {
|
for (int i = 0; i < metadataArray.size; i++) {
|
||||||
metadataArray.get(i).dispose();
|
metadataArray.get(i).dispose();
|
||||||
@ -101,6 +103,8 @@ public class MusicMetadataController extends Observable implements Disposable, O
|
|||||||
}
|
}
|
||||||
if (work) {
|
if (work) {
|
||||||
searching = false;
|
searching = false;
|
||||||
|
Gdx.app.debug(name, "load complete.");
|
||||||
|
setChanged();
|
||||||
notifyObservers();
|
notifyObservers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +127,6 @@ public class MusicMetadataController extends Observable implements Disposable, O
|
|||||||
@Override
|
@Override
|
||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
if (o == musicList) {
|
if (o == musicList) {
|
||||||
loadingThread.stop();
|
|
||||||
loadAudioMetadata();
|
loadAudioMetadata();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ public interface AudioMetadata extends Disposable {
|
|||||||
public int getLength();
|
public int getLength();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Requires a OpenGL context.
|
||||||
* @return the texture. Needs to be loaded before hand or else will return null.
|
* @return the texture. Needs to be loaded before hand or else will return null.
|
||||||
*/
|
*/
|
||||||
public Texture getAlbumCover();
|
public Texture getAlbumCover();
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package zero1hd.rhythmbullet.audio.metadata;
|
package zero1hd.rhythmbullet.audio.metadata;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jaudiotagger.audio.AudioFileIO;
|
import org.jaudiotagger.audio.AudioFileIO;
|
||||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
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.TagException;
|
||||||
import org.jaudiotagger.tag.id3.ID3v23FieldKey;
|
import org.jaudiotagger.tag.id3.ID3v23FieldKey;
|
||||||
import org.jaudiotagger.tag.id3.ID3v23Tag;
|
import org.jaudiotagger.tag.id3.ID3v23Tag;
|
||||||
|
import org.jaudiotagger.tag.images.Artwork;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
@ -22,6 +25,7 @@ public class MP3Metadata implements AudioMetadata {
|
|||||||
private int length;
|
private int length;
|
||||||
private Texture albumCover;
|
private Texture albumCover;
|
||||||
private FileHandle fileHandle;
|
private FileHandle fileHandle;
|
||||||
|
private byte[] imageData;
|
||||||
|
|
||||||
public MP3Metadata(FileHandle fileHandle) {
|
public MP3Metadata(FileHandle fileHandle) {
|
||||||
this.fileHandle = fileHandle;
|
this.fileHandle = fileHandle;
|
||||||
@ -40,13 +44,15 @@ public class MP3Metadata implements AudioMetadata {
|
|||||||
|
|
||||||
|
|
||||||
length = mp3file.getAudioHeader().getTrackLength();
|
length = mp3file.getAudioHeader().getTrackLength();
|
||||||
int min = (int) (length/60);
|
SimpleDateFormat f = new SimpleDateFormat("m:ss");
|
||||||
duration = (length/60) + ":" + (length - (min*60));
|
duration = f.format(new Date(length*1000));
|
||||||
|
|
||||||
author = tag.getFirst(ID3v23FieldKey.ARTIST);
|
author = tag.getFirst(ID3v23FieldKey.ARTIST);
|
||||||
genre = tag.getFirst(ID3v23FieldKey.GENRE);
|
genre = tag.getFirst(ID3v23FieldKey.GENRE);
|
||||||
title = tag.getFirst(ID3v23FieldKey.TITLE);
|
title = tag.getFirst(ID3v23FieldKey.TITLE);
|
||||||
|
if (title.isEmpty()) {
|
||||||
|
title = fileHandle.nameWithoutExtension();
|
||||||
|
}
|
||||||
} catch (IOException | CannotWriteException | CannotReadException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
} catch (IOException | CannotWriteException | CannotReadException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||||
Gdx.app.error("MP3Metadata", "Failed to read metadata of file: " + fileHandle.name());
|
Gdx.app.error("MP3Metadata", "Failed to read metadata of file: " + fileHandle.name());
|
||||||
}
|
}
|
||||||
@ -57,25 +63,17 @@ public class MP3Metadata implements AudioMetadata {
|
|||||||
MP3File mp3file;
|
MP3File mp3file;
|
||||||
try {
|
try {
|
||||||
mp3file = (MP3File) AudioFileIO.read(fileHandle.file());
|
mp3file = (MP3File) AudioFileIO.read(fileHandle.file());
|
||||||
|
Artwork art = mp3file.getTag().getFirstArtwork();
|
||||||
|
if (art != null) {
|
||||||
|
imageData = art.getBinaryData();
|
||||||
|
|
||||||
byte[] imageData = mp3file.getTag().getFirstArtwork().getBinaryData();
|
}
|
||||||
Pixmap pixmap = new Pixmap(imageData, 0, imageData.length);
|
|
||||||
albumCover = new Texture(pixmap);
|
|
||||||
pixmap.dispose();
|
|
||||||
|
|
||||||
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unloadAlbumCover() {
|
|
||||||
if (albumCover != null) {
|
|
||||||
albumCover.dispose();
|
|
||||||
albumCover = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAuthor() {
|
public String getAuthor() {
|
||||||
return author;
|
return author;
|
||||||
@ -101,8 +99,22 @@ public class MP3Metadata implements AudioMetadata {
|
|||||||
return genre;
|
return genre;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unloadAlbumCover() {
|
||||||
|
if (albumCover != null) {
|
||||||
|
albumCover.dispose();
|
||||||
|
albumCover = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Texture getAlbumCover() {
|
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;
|
return albumCover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package zero1hd.rhythmbullet.audio.metadata;
|
package zero1hd.rhythmbullet.audio.metadata;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jaudiotagger.audio.AudioFile;
|
import org.jaudiotagger.audio.AudioFile;
|
||||||
import org.jaudiotagger.audio.AudioFileIO;
|
import org.jaudiotagger.audio.AudioFileIO;
|
||||||
@ -10,6 +12,7 @@ import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
|||||||
import org.jaudiotagger.tag.FieldKey;
|
import org.jaudiotagger.tag.FieldKey;
|
||||||
import org.jaudiotagger.tag.Tag;
|
import org.jaudiotagger.tag.Tag;
|
||||||
import org.jaudiotagger.tag.TagException;
|
import org.jaudiotagger.tag.TagException;
|
||||||
|
import org.jaudiotagger.tag.images.Artwork;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
@ -21,6 +24,7 @@ public class WAVMetadata implements AudioMetadata {
|
|||||||
private int length;
|
private int length;
|
||||||
private Texture albumCover;
|
private Texture albumCover;
|
||||||
private FileHandle fileHandle;
|
private FileHandle fileHandle;
|
||||||
|
private byte[] imageData;
|
||||||
|
|
||||||
public WAVMetadata(FileHandle fileHandle) {
|
public WAVMetadata(FileHandle fileHandle) {
|
||||||
this.fileHandle = fileHandle;
|
this.fileHandle = fileHandle;
|
||||||
@ -28,13 +32,16 @@ public class WAVMetadata implements AudioMetadata {
|
|||||||
try {
|
try {
|
||||||
AudioFile wav = AudioFileIO.read(fileHandle.file());
|
AudioFile wav = AudioFileIO.read(fileHandle.file());
|
||||||
length = wav.getAudioHeader().getTrackLength();
|
length = wav.getAudioHeader().getTrackLength();
|
||||||
int min = (int) (length/60);
|
SimpleDateFormat f = new SimpleDateFormat("m:ss");
|
||||||
this.duration = (length/60) + ":" + (length - (min*60));
|
duration = f.format(new Date(length*1000));
|
||||||
|
|
||||||
Tag tag = wav.getTag();
|
Tag tag = wav.getTag();
|
||||||
title = tag.getFirst(FieldKey.TITLE);
|
title = tag.getFirst(FieldKey.TITLE);
|
||||||
author = tag.getFirst(FieldKey.ARTIST);
|
author = tag.getFirst(FieldKey.ARTIST);
|
||||||
genre = tag.getFirst(FieldKey.GENRE);
|
genre = tag.getFirst(FieldKey.GENRE);
|
||||||
|
if (title.isEmpty()) {
|
||||||
|
title = fileHandle.nameWithoutExtension();
|
||||||
|
}
|
||||||
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||||
Gdx.app.error("WAVMetadata", "Failed to read metadata of file: " + fileHandle.name());
|
Gdx.app.error("WAVMetadata", "Failed to read metadata of file: " + fileHandle.name());
|
||||||
}
|
}
|
||||||
@ -44,11 +51,10 @@ public class WAVMetadata implements AudioMetadata {
|
|||||||
public void loadAlbumCover() {
|
public void loadAlbumCover() {
|
||||||
try {
|
try {
|
||||||
AudioFile wav = AudioFileIO.read(fileHandle.file());
|
AudioFile wav = AudioFileIO.read(fileHandle.file());
|
||||||
|
Artwork art = wav.getTag().getFirstArtwork();
|
||||||
byte[] imageData = wav.getTag().getFirstArtwork().getBinaryData();
|
if (art != null) {
|
||||||
Pixmap pixmap = new Pixmap(imageData, 0, imageData.length);
|
imageData = art.getBinaryData();
|
||||||
albumCover = new Texture(pixmap);
|
}
|
||||||
pixmap.dispose();
|
|
||||||
|
|
||||||
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -82,6 +88,12 @@ public class WAVMetadata implements AudioMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Texture getAlbumCover() {
|
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;
|
return albumCover;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
|
@ -21,13 +21,13 @@ public class DoubleHorizontalVisualizer implements Disposable {
|
|||||||
private int binsPerBar;
|
private int binsPerBar;
|
||||||
private float offset;
|
private float offset;
|
||||||
private int boundaryThickness;
|
private int boundaryThickness;
|
||||||
private float baseSensitivity;
|
|
||||||
private float targetDelta;
|
private float targetDelta;
|
||||||
private float spacePercentage = 0.7f;
|
private float spacePercentage = 0.7f;
|
||||||
private int barCount = 90;
|
private float baseSensitivity = 0.009f;
|
||||||
private float barChangeRate = 14f;
|
private int barCount = 120;
|
||||||
private int smoothRange = 1;
|
private float barChangeRate = 7f;
|
||||||
private int binsToInclude = 180;
|
private int smoothRange = 2;
|
||||||
|
private int binsToInclude = 120;
|
||||||
private Color color = new Color(0.5f, 0.6f, 0.8f, 0.46f);
|
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 width the width of the visualizer.
|
||||||
* @param spacePercentage the percentage of a bar that should be space.
|
* @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.barWidth = width/barCount;
|
||||||
this.spaceBetweenBars = MathUtils.round(barWidth * spacePercentage);
|
this.spaceBetweenBars = MathUtils.round(barWidth * spacePercentage);
|
||||||
this.barWidth -= spaceBetweenBars;
|
this.barWidth -= spaceBetweenBars;
|
||||||
this.baseSensitivity = baseSensitivity;
|
this.baseSensitivity *= heightSensitivity;
|
||||||
pcm = PCMSystem;
|
pcm = PCMSystem;
|
||||||
binsPerBar = (binsToInclude/barCount);
|
binsPerBar = (binsToInclude/barCount);
|
||||||
this.width = width;
|
this.width = width;
|
||||||
@ -55,11 +55,10 @@ public class DoubleHorizontalVisualizer implements Disposable {
|
|||||||
public void act(float delta) {
|
public void act(float delta) {
|
||||||
float[] freqBins = pcm.getFrequencyBins();
|
float[] freqBins = pcm.getFrequencyBins();
|
||||||
for (int bar = 0; bar < amplitudes.length; bar++) {
|
for (int bar = 0; bar < amplitudes.length; bar++) {
|
||||||
float normalizedAmplitude = 0;
|
amplitudes[bar] = 0;
|
||||||
for (int freq = bar*binsPerBar; freq < (bar*binsPerBar) + binsPerBar; freq++) {
|
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;
|
amplitudes[bar] /= binsPerBar;
|
||||||
}
|
}
|
||||||
for (int bar = 0; bar < barHeights.length; bar++) {
|
for (int bar = 0; bar < barHeights.length; bar++) {
|
||||||
@ -78,13 +77,22 @@ public class DoubleHorizontalVisualizer implements Disposable {
|
|||||||
|
|
||||||
|
|
||||||
int pixelsMoved = 0;
|
int pixelsMoved = 0;
|
||||||
pixelsMoved = MathUtils.round(amplitudes[bar] - barHeights[bar]);
|
int difference = MathUtils.round(amplitudes[bar] - barHeights[bar]);
|
||||||
pixelsMoved = MathUtils.floor(pixelsMoved*targetDelta*barChangeRate);
|
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;
|
barHeights[bar] += pixelsMoved;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (barHeights[bar] + pixelsMoved < amplitudes[bar]) {
|
||||||
|
barHeights[bar] += MathUtils.round(difference*targetDelta);
|
||||||
|
} else {
|
||||||
|
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++) {
|
for (int bar = 0; bar < barCount; bar++) {
|
||||||
shapeRenderer.setColor(color);
|
shapeRenderer.setColor(color);
|
||||||
shapeRenderer.rect(offset + (spaceBetweenBars+barWidth)*bar, y+height, barWidth, barHeights[bar]);
|
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();
|
shapeRenderer.end();
|
||||||
Gdx.gl.glDisable(GL20.GL_BLEND);
|
Gdx.gl.glDisable(GL20.GL_BLEND);
|
||||||
|
@ -66,4 +66,8 @@ public class Page extends Group implements Disposable {
|
|||||||
public void dispose() {
|
public void dispose() {
|
||||||
Gdx.app.debug(getClass().getSimpleName(), "Disposing...");
|
Gdx.app.debug(getClass().getSimpleName(), "Disposing...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void simpleDebug(String message) {
|
||||||
|
Gdx.app.debug(getClass().getSimpleName(), message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.Actor;
|
||||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||||
@ -19,11 +19,7 @@ public class MusicControls extends HorizontalGroup {
|
|||||||
reverse.addListener(new ChangeListener() {
|
reverse.addListener(new ChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void changed(ChangeEvent event, Actor actor) {
|
public void changed(ChangeEvent event, Actor actor) {
|
||||||
boolean wasPlaying = sc.isPlaying();
|
|
||||||
sc.previous();
|
sc.previous();
|
||||||
if (wasPlaying) {
|
|
||||||
sc.play();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
addActor(reverse);
|
addActor(reverse);
|
||||||
@ -56,11 +52,7 @@ public class MusicControls extends HorizontalGroup {
|
|||||||
forward.addListener(new ChangeListener() {
|
forward.addListener(new ChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void changed(ChangeEvent event, Actor actor) {
|
public void changed(ChangeEvent event, Actor actor) {
|
||||||
boolean wasPlaying = sc.isPlaying();
|
|
||||||
sc.skip();
|
sc.skip();
|
||||||
if (wasPlaying) {
|
|
||||||
sc.play();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
addActor(forward);
|
addActor(forward);
|
@ -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<MusicSelectable> queueList;
|
|
||||||
|
|
||||||
public MusicSelectable(Skin skin, Texture defaultAlbumArt, AudioMetadata metadata,
|
|
||||||
Array<MusicSelectable> 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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<MusicSelectable> {
|
|
||||||
private Array<MusicSelectable> buttons;
|
|
||||||
|
|
||||||
public MusicSelectableButtonGroup() {
|
|
||||||
buttons = getButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChecked(AudioMetadata metadata) {
|
|
||||||
if (metadata == null) throw new IllegalArgumentException("metadata can't be null.");
|
|
||||||
MusicSelectable button;
|
|
||||||
for (int i = 0; i < buttons.size; i++) {
|
|
||||||
button = buttons.get(i);
|
|
||||||
if (button.getMetadata() == metadata) {
|
|
||||||
button.setChecked(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChecked(FileHandle fileHandle) {
|
|
||||||
if (fileHandle == null) throw new IllegalArgumentException("fileHandle can't be null.");
|
|
||||||
MusicSelectable button;
|
|
||||||
for (int i = 0; i < buttons.size; i++) {
|
|
||||||
button = buttons.get(i);
|
|
||||||
if (button.getFileHandle() == fileHandle) {
|
|
||||||
button.setChecked(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void selectNext() {
|
|
||||||
int index = getCheckedIndex() + 1;
|
|
||||||
if (index == buttons.size) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
buttons.get(index).setChecked(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void selectPrevious() {
|
|
||||||
int index = getCheckedIndex() - 1;
|
|
||||||
if (index == -1) {
|
|
||||||
index = buttons.size -1;
|
|
||||||
}
|
|
||||||
buttons.get(index).setChecked(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChecked(int index) {
|
|
||||||
buttons.get(index).setChecked(true);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,9 +3,12 @@ package zero1hd.rhythmbullet.util;
|
|||||||
public interface ScreenConfiguration {
|
public interface ScreenConfiguration {
|
||||||
public void setFramesPerSecond(int fps);
|
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();
|
public boolean getVsync();
|
||||||
|
|
||||||
@ -22,4 +25,9 @@ public interface ScreenConfiguration {
|
|||||||
public void setWindowLocationY(int y);
|
public void setWindowLocationY(int y);
|
||||||
|
|
||||||
public void setWindowLocation(int x, int y);
|
public void setWindowLocation(int x, int y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the application.
|
||||||
|
*/
|
||||||
|
public void restart();
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,37 @@
|
|||||||
package zero1hd.rhythmbullet.desktop;
|
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.LwjglApplication;
|
||||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||||
|
import com.badlogic.gdx.backends.lwjgl.LwjglGraphics;
|
||||||
|
|
||||||
import zero1hd.rhythmbullet.RhythmBullet;
|
import zero1hd.rhythmbullet.RhythmBullet;
|
||||||
import zero1hd.rhythmbullet.desktop.screens.SplashScreen;
|
import zero1hd.rhythmbullet.desktop.screens.SplashScreen;
|
||||||
|
|
||||||
public class DesktopLauncher {
|
public class DesktopLauncher {
|
||||||
|
|
||||||
public static void main (String[] arg) {
|
public static void main (String[] arg) {
|
||||||
RhythmBullet core;
|
RhythmBullet core;
|
||||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||||
DesktopScreenConfiguration screenConfig = new DesktopScreenConfiguration(config);
|
DesktopScreenConfiguration screenConfig = new DesktopScreenConfiguration(config);
|
||||||
|
|
||||||
config.title = "Rhythm Bullet";
|
config.title = "Rhythm Bullet";
|
||||||
config.resizable = false;
|
config.resizable = false;
|
||||||
config.useHDPI = true;
|
config.useHDPI = true;
|
||||||
config.samples = 2;
|
config.samples = 2;
|
||||||
config.width = 512;
|
config.allowSoftwareMode = true;
|
||||||
config.height = 512;
|
|
||||||
core = new RhythmBullet();
|
core = new RhythmBullet();
|
||||||
core.setup(new SplashScreen(), new DesktopAssetPack(), screenConfig);
|
core.setup(new SplashScreen(), new DesktopAssetPack(), screenConfig);
|
||||||
new LwjglApplication(core, config);
|
|
||||||
|
while (screenConfig.shouldStart()) {
|
||||||
|
LwjglApplication app = new LwjglApplication(core, config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,14 @@ package zero1hd.rhythmbullet.desktop;
|
|||||||
|
|
||||||
import org.lwjgl.opengl.Display;
|
import org.lwjgl.opengl.Display;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||||
|
|
||||||
import zero1hd.rhythmbullet.util.ScreenConfiguration;
|
import zero1hd.rhythmbullet.util.ScreenConfiguration;
|
||||||
|
|
||||||
public class DesktopScreenConfiguration implements ScreenConfiguration {
|
public class DesktopScreenConfiguration implements ScreenConfiguration {
|
||||||
private LwjglApplicationConfiguration configuration;
|
private LwjglApplicationConfiguration configuration;
|
||||||
|
private boolean start = true;
|
||||||
public DesktopScreenConfiguration(LwjglApplicationConfiguration configuration) {
|
public DesktopScreenConfiguration(LwjglApplicationConfiguration configuration) {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
@ -18,16 +19,20 @@ public class DesktopScreenConfiguration implements ScreenConfiguration {
|
|||||||
configuration.foregroundFPS = fps;
|
configuration.foregroundFPS = fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTargetFramesPerSecond() {
|
||||||
|
return configuration.foregroundFPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires restart. Can be done by calling {@link #restart()}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setVsync(boolean useVsync) {
|
public void setVsync(boolean useVsync) {
|
||||||
configuration.vSyncEnabled = useVsync;
|
configuration.vSyncEnabled = useVsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getFramesPerSecond() {
|
|
||||||
return configuration.foregroundFPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean getVsync() {
|
public boolean getVsync() {
|
||||||
return configuration.vSyncEnabled;
|
return configuration.vSyncEnabled;
|
||||||
@ -67,4 +72,16 @@ public class DesktopScreenConfiguration implements ScreenConfiguration {
|
|||||||
public void setWindowLocation(int x, int y) {
|
public void setWindowLocation(int x, int y) {
|
||||||
Display.setLocation(x, y);
|
Display.setLocation(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void restart() {
|
||||||
|
Gdx.app.exit();
|
||||||
|
start = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldStart() {
|
||||||
|
boolean should = start;
|
||||||
|
start = false;
|
||||||
|
return should;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ public class PCMObtainer implements Observer, PCMSystem {
|
|||||||
private ShortBuffer playingBuffer;
|
private ShortBuffer playingBuffer;
|
||||||
private ShortBuffer intermediateBuffer;
|
private ShortBuffer intermediateBuffer;
|
||||||
private ShortBuffer buffer;
|
private ShortBuffer buffer;
|
||||||
private int sourceID;
|
private volatile int sourceID;
|
||||||
private int channelCount;
|
private int channelCount;
|
||||||
private MusicController mc;
|
private MusicController mc;
|
||||||
private BufferStreamReadThread streamReadThread;
|
private BufferStreamReadThread streamReadThread;
|
||||||
@ -177,13 +177,7 @@ public class PCMObtainer implements Observer, PCMSystem {
|
|||||||
windowsRead++;
|
windowsRead++;
|
||||||
|
|
||||||
//contemplate synchronization
|
//contemplate synchronization
|
||||||
try {
|
|
||||||
currentPlaybackWindow = MathUtils.round((mc.getCurrentPosition() * sampleRate) / windowSize);
|
currentPlaybackWindow = MathUtils.round((mc.getCurrentPosition() * sampleRate) / windowSize);
|
||||||
} catch (UnsatisfiedLinkError ule) {
|
|
||||||
if (run) {
|
|
||||||
ule.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (windowsRead != currentPlaybackWindow) {
|
if (windowsRead != currentPlaybackWindow) {
|
||||||
synchronizeBufferWithPlayback();
|
synchronizeBufferWithPlayback();
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ public class SplashScreen extends ScreenAdapter implements InitialScreen {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
|
stage = new Stage(new ScreenViewport());
|
||||||
splash = new Texture(Gdx.files.internal("splashlogo.png"));
|
splash = new Texture(Gdx.files.internal("splashlogo.png"));
|
||||||
zero1HD = new Image(splash);
|
zero1HD = new Image(splash);
|
||||||
}
|
}
|
||||||
@ -47,14 +48,19 @@ public class SplashScreen extends ScreenAdapter implements InitialScreen {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postAssetLoad() {
|
public void postAssetLoad() {
|
||||||
stage = new Stage(new ScreenViewport());
|
zero1HD.setScale((stage.getHeight()*0.8f)/(zero1HD.getHeight()));
|
||||||
stage.addActor(zero1HD);
|
|
||||||
zero1HD.setScale((Gdx.graphics.getHeight()*0.5f)/zero1HD.getHeight()*zero1HD.getScaleY());
|
|
||||||
zero1HD.setColor(0f,1f,1f,0f);
|
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)));
|
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
|
@Override
|
||||||
public Screen createMainScreen(RhythmBullet game) {
|
public Screen createMainScreen(RhythmBullet game) {
|
||||||
return new MainScreen(game);
|
return new MainScreen(game);
|
||||||
|
@ -20,8 +20,8 @@ import zero1hd.rhythmbullet.RhythmBullet;
|
|||||||
import zero1hd.rhythmbullet.audio.MusicController;
|
import zero1hd.rhythmbullet.audio.MusicController;
|
||||||
import zero1hd.rhythmbullet.audio.visualizer.DoubleHorizontalVisualizer;
|
import zero1hd.rhythmbullet.audio.visualizer.DoubleHorizontalVisualizer;
|
||||||
import zero1hd.rhythmbullet.desktop.audio.PCMObtainer;
|
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.Page;
|
||||||
|
import zero1hd.rhythmbullet.graphics.ui.components.MusicControls;
|
||||||
import zero1hd.rhythmbullet.graphics.ui.components.ScrollText;
|
import zero1hd.rhythmbullet.graphics.ui.components.ScrollText;
|
||||||
import zero1hd.rhythmbullet.util.ScreenConfiguration;
|
import zero1hd.rhythmbullet.util.ScreenConfiguration;
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ public class MainPage extends Page implements Observer {
|
|||||||
this.mc = musicController;
|
this.mc = musicController;
|
||||||
this.mc.addObserver(this);
|
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));
|
dhv.setPosition(0, (int) ((getHeight() - dhv.getHeight())/2f));
|
||||||
|
|
||||||
title = new Image(assetManager.get("title.png", Texture.class));
|
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.setWidth(0.5f*getWidth());
|
||||||
scrollText.setPosition(15, getHeight() - scrollText.getHeight()-30f);
|
scrollText.setPosition(15, getHeight() - scrollText.getHeight()-30f);
|
||||||
addActor(scrollText);
|
addActor(scrollText);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,7 +62,6 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
|
|||||||
musicController = new MusicController(musicList, core.getPreferences());
|
musicController = new MusicController(musicList, core.getPreferences());
|
||||||
musicController.setAutoPlay(true);
|
musicController.setAutoPlay(true);
|
||||||
musicController.setShuffle(true);
|
musicController.setShuffle(true);
|
||||||
|
|
||||||
musicMetadataController = new MusicMetadataController(musicList);
|
musicMetadataController = new MusicMetadataController(musicList);
|
||||||
|
|
||||||
listeners = new Listeners();
|
listeners = new Listeners();
|
||||||
@ -110,6 +109,7 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
|
|||||||
}
|
}
|
||||||
background = null;
|
background = null;
|
||||||
musicController.deleteObservers();
|
musicController.deleteObservers();
|
||||||
|
musicMetadataController.deleteObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -161,8 +161,6 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
musicController.addObserver(musicSelectionPage);
|
|
||||||
musicController.addObserver(mainPage);
|
|
||||||
musicController.getMusicList().asyncSearch(false);
|
musicController.getMusicList().asyncSearch(false);
|
||||||
resizing = false;
|
resizing = false;
|
||||||
}
|
}
|
||||||
|
@ -10,27 +10,32 @@ import com.badlogic.gdx.assets.AssetManager;
|
|||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
|
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||||
|
import com.badlogic.gdx.math.Vector2;
|
||||||
import com.badlogic.gdx.math.Vector3;
|
import com.badlogic.gdx.math.Vector3;
|
||||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.Button;
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.ButtonGroup;
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Label;
|
import com.badlogic.gdx.scenes.scene2d.ui.Label;
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
|
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
|
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Stack;
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table;
|
import com.badlogic.gdx.scenes.scene2d.ui.Table;
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup;
|
||||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
||||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
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.scenes.scene2d.utils.TextureRegionDrawable;
|
||||||
|
import com.badlogic.gdx.utils.Align;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
|
||||||
import zero1hd.rhythmbullet.audio.MusicMetadataController;
|
import zero1hd.rhythmbullet.audio.MusicMetadataController;
|
||||||
|
import zero1hd.rhythmbullet.audio.metadata.AudioMetadata;
|
||||||
import zero1hd.rhythmbullet.audio.MusicController;
|
import zero1hd.rhythmbullet.audio.MusicController;
|
||||||
import zero1hd.rhythmbullet.graphics.ui.Page;
|
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;
|
import zero1hd.rhythmbullet.graphics.ui.components.ScrollText;
|
||||||
|
|
||||||
public class MusicSelectionPage extends Page implements Observer {
|
public class MusicSelectionPage extends Page implements Observer {
|
||||||
@ -39,21 +44,13 @@ public class MusicSelectionPage extends Page implements Observer {
|
|||||||
private MusicController mc;
|
private MusicController mc;
|
||||||
private MusicMetadataController mmc;
|
private MusicMetadataController mmc;
|
||||||
private MusicSelectableButtonGroup selectables;
|
private MusicSelectableButtonGroup selectables;
|
||||||
private Stack stackSelectables;
|
private VerticalGroup vGroup;
|
||||||
private TextButton back;
|
private TextButton back;
|
||||||
private ScrollPane musicTableScrollPane;
|
private ScrollPane scrollPane;
|
||||||
|
|
||||||
private ClickListener selectionListener;
|
private musicSelectionLoaderThread selectionLoaderThread;
|
||||||
private musicSelectionLoaderThread thread;
|
|
||||||
|
|
||||||
private Table musicInfoTable;
|
private InformationTable musicInfoTable;
|
||||||
private Table musicSubInfo;
|
|
||||||
private ScrollText songTitle;
|
|
||||||
private Label author;
|
|
||||||
private Label songLength;
|
|
||||||
private Label previousTop;
|
|
||||||
private Label ratedDifficulty;
|
|
||||||
private Image albumCover;
|
|
||||||
|
|
||||||
private AssetManager assets;
|
private AssetManager assets;
|
||||||
private Skin skin;
|
private Skin skin;
|
||||||
@ -63,23 +60,25 @@ public class MusicSelectionPage extends Page implements Observer {
|
|||||||
private TextButton beginButton;
|
private TextButton beginButton;
|
||||||
|
|
||||||
private float scrollTimer, scrollDelay = 0.2f, scrollDelMod, songSelectionTimer;
|
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) {
|
public MusicSelectionPage(AssetManager assetManager, Skin skin, MusicController musicController, MusicMetadataController musicMetadataController, ChangeListener backButtonListener, ChangeListener beginButtonListener) {
|
||||||
super(1, 0);
|
super(1, 0);
|
||||||
this.assets = assetManager;
|
this.assets = assetManager;
|
||||||
this.mc = musicController;
|
this.mc = musicController;
|
||||||
this.mmc = musicMetadataController;
|
this.mmc = musicMetadataController;
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
stackSelectables = new Stack();
|
vGroup = new VerticalGroup();
|
||||||
|
vGroup.space(10f);
|
||||||
selectables = new MusicSelectableButtonGroup();
|
selectables = new MusicSelectableButtonGroup();
|
||||||
|
selectables.setMinCheckCount(0);
|
||||||
musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation");
|
musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation");
|
||||||
|
|
||||||
musicTableScrollPane = new ScrollPane(stackSelectables, skin);
|
scrollPane = new ScrollPane(vGroup, skin);
|
||||||
musicTableScrollPane.setSize(0.45f*getWidth(), getHeight());
|
scrollPane.setSize(0.45f*getWidth(), getHeight());
|
||||||
musicTableScrollPane.setFadeScrollBars(false);
|
scrollPane.setOverscroll(false, false);
|
||||||
musicTableScrollPane.setOverscroll(false, false);
|
scrollPane.setClamp(true);
|
||||||
musicTableScrollPane.setColor(Color.BLUE);
|
scrollPane.setColor(Color.BLUE);
|
||||||
addActor(musicTableScrollPane);
|
addActor(scrollPane);
|
||||||
back = new TextButton("Back", skin);
|
back = new TextButton("Back", skin);
|
||||||
back.setWidth(back.getWidth()+20f);
|
back.setWidth(back.getWidth()+20f);
|
||||||
back.setPosition(getWidth()-back.getWidth()-15f, getHeight() - back.getHeight() - 15f);
|
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 = new InformationTable(getWidth()-scrollPane.getWidth(), getHeight());
|
||||||
musicInfoTable.defaults().center();
|
|
||||||
musicInfoTable.setPosition(musicTableScrollPane.getWidth() + musicTableScrollPane.getX(), 0);
|
|
||||||
musicInfoTable.setSize(getWidth()-musicTableScrollPane.getWidth(), getHeight());
|
|
||||||
addActor(musicInfoTable);
|
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 = new TextButton("Begin", skin);
|
||||||
beginButton.addListener(beginButtonListener);
|
beginButton.addListener(beginButtonListener);
|
||||||
|
|
||||||
mmc.addObserver(this);
|
mmc.addObserver(this);
|
||||||
|
mc.addObserver(this);
|
||||||
thread = new musicSelectionLoaderThread();
|
selectionLoaderThread = 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))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -193,74 +156,58 @@ public class MusicSelectionPage extends Page implements Observer {
|
|||||||
if (songSelectionTimer <= 0f) {
|
if (songSelectionTimer <= 0f) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mc.getMusicList().isSearched()) {
|
||||||
|
if (mc.getMusicList().getTotal() != 0) {
|
||||||
|
|
||||||
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));
|
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 {
|
} else {
|
||||||
if (selectables.getChecked() == null) {
|
if (selectables.getChecked() == null) {
|
||||||
|
selectables.setMinCheckCount(1);
|
||||||
selectables.setChecked(mc.getCurrentMusicFileHandle());
|
selectables.setChecked(mc.getCurrentMusicFileHandle());
|
||||||
} else if (selectables.getChecked().getFileHandle() != mc.getCurrentMusicFileHandle()) {
|
scrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight());
|
||||||
songSelectionTimer += delta;
|
} else if (selectables.getChecked().getMetadata().getFileHandle() != mc.getCurrentMusicFileHandle()) {
|
||||||
if (songSelectionTimer > 2f) {
|
musicSelectDelay += delta;
|
||||||
mc.setMusicByFileHandle(selectables.getChecked().getFileHandle());
|
if (musicSelectDelay >= 1f) {
|
||||||
|
mc.setMusicByFileHandle(selectables.getChecked().getMetadata().getFileHandle());
|
||||||
|
musicSelectDelay = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
songSelectionTimer = 0;
|
//TODO: Error message reporting empty music list or something
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.act(delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scrollDown() {
|
private void scrollDown() {
|
||||||
selectables.selectNext();
|
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() {
|
private void scrollUp() {
|
||||||
selectables.selectPrevious();
|
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() {
|
public FileHandle getSelectedMusic() {
|
||||||
return selectables.getChecked().getMetadata().getFileHandle();
|
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
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
@ -269,7 +216,12 @@ public class MusicSelectionPage extends Page implements Observer {
|
|||||||
@Override
|
@Override
|
||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
if (o == mmc) {
|
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 class musicSelectionLoaderThread implements Runnable {
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private Array<MusicSelectable> queueList;
|
private Array<AudioMetadata> queueList;
|
||||||
private String name = "Music-Selection-Loader-Thread";
|
private String name = "Music-Selection-Loader-Thread";
|
||||||
private volatile boolean work;
|
private volatile boolean work = true;
|
||||||
|
|
||||||
public musicSelectionLoaderThread() {
|
public musicSelectionLoaderThread() {
|
||||||
queueList = new Array<>();
|
queueList = new Array<>();
|
||||||
@ -292,18 +244,23 @@ public class MusicSelectionPage extends Page implements Observer {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (work) {
|
while (work) {
|
||||||
while (selectables.getButtons().size != mc.getMusicList().getTotal()) {
|
if (selectables.size() != mmc.size()) {
|
||||||
MusicSelectable selectable = new MusicSelectable(skin, assets.get("defaultCover.png"), mmc.getMetadata(selectables.getButtons().size), queueList);
|
selectables.clear();
|
||||||
selectable.addListener(selectionListener);
|
for (int mid = 0; mid < mmc.size(); mid++) {
|
||||||
selectables.add(selectable);
|
|
||||||
|
}
|
||||||
|
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 {
|
try {
|
||||||
wait();
|
wait();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@ -318,12 +275,259 @@ public class MusicSelectionPage extends Page implements Observer {
|
|||||||
thread = new Thread(this, name);
|
thread = new Thread(this, name);
|
||||||
thread.start();
|
thread.start();
|
||||||
return true;
|
return true;
|
||||||
}
|
} else {
|
||||||
synchronized (queueList) {
|
synchronized (this) {
|
||||||
notify();
|
notify();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
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<MusicSelectable> {
|
||||||
|
private Array<MusicSelectable> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,12 +127,12 @@ public class OptionsPage extends Page {
|
|||||||
optionsTable.row();
|
optionsTable.row();
|
||||||
|
|
||||||
Label usageLabel = new Label("Current usage (lower the better): " + 100f*((float)(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/(float)Runtime.getRuntime().totalMemory()) + "%", skin) {
|
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
|
@Override
|
||||||
public void act(float delta) {
|
public void act(float delta) {
|
||||||
refreshTime -= delta;
|
refreshTime -= delta;
|
||||||
if (refreshTime <= 0) {
|
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()) + "%");
|
setText("Current usage (lower the better): " + 100f*((float)(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/(float)Runtime.getRuntime().totalMemory()) + "%");
|
||||||
}
|
}
|
||||||
super.act(delta);
|
super.act(delta);
|
||||||
|
Loading…
Reference in New Issue
Block a user