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