progress on adding base layer

This commit is contained in:
2018-09-11 00:05:34 -05:00
parent 10d9400342
commit bce143efbb
292 changed files with 10211 additions and 0 deletions

13
old/desktop/.classpath Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="bin/main" path="src">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry exported="true" kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer"/>
<classpathentry combineaccessrules="false" kind="src" path="/RhythmBullet-core"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

25
old/desktop/.project Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>RhythmBullet-desktop</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.springsource.ide.eclipse.gradle.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<linkedResources>
<link>
<name>assets</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/android/assets</locationURI>
</link>
</linkedResources>
</projectDescription>

View File

@@ -0,0 +1,4 @@
#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences
#Mon Sep 10 00:40:48 CDT 2018
org.springsource.ide.eclipse.gradle.linkedresources=assets/;
org.springsource.ide.eclipse.gradle.rootprojectloc=..

View File

@@ -0,0 +1,8 @@
#org.springsource.ide.eclipse.gradle.core.actions.GradleRefreshPreferences
#Mon Sep 10 00:40:48 CDT 2018
addResourceFilters=true
afterTasks=afterEclipseImport;
beforeTasks=cleanEclipse;eclipse;
enableAfterTasks=true
enableBeforeTasks=true
useHierarchicalNames=false

View File

@@ -0,0 +1,11 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

55
old/desktop/build.gradle Normal file
View File

@@ -0,0 +1,55 @@
apply plugin: "java"
sourceCompatibility = 1.6
sourceSets.main.java.srcDirs = [ "src/" ]
project.ext.mainClassName = "zero1hd.rhythmbullet.desktop.DesktopLauncher"
project.ext.assetsDir = new File("../android/assets");
task run(dependsOn: classes, type: JavaExec) {
main = project.mainClassName
classpath = sourceSets.main.runtimeClasspath
standardInput = System.in
workingDir = project.assetsDir
ignoreExitValue = true
}
task debug(dependsOn: classes, type: JavaExec) {
main = project.mainClassName
classpath = sourceSets.main.runtimeClasspath
standardInput = System.in
workingDir = project.assetsDir
ignoreExitValue = true
debug = true
}
task dist(type: Jar) {
from files(sourceSets.main.output.classesDir)
from files(sourceSets.main.output.resourcesDir)
from {configurations.compile.collect {zipTree(it)}}
from files(project.assetsDir);
manifest {
attributes 'Main-Class': project.mainClassName
}
}
dist.dependsOn classes
eclipse {
project {
name = appName + "-desktop"
linkedResource name: 'assets', type: '2', location: 'PARENT-1-PROJECT_LOC/android/assets'
}
}
task afterEclipseImport(description: "Post processing after project generation", group: "IDE") {
doLast {
def classpath = new XmlParser().parse(file(".classpath"))
new Node(classpath, "classpathentry", [ kind: 'src', path: 'assets' ]);
def writer = new FileWriter(file(".classpath"))
def printer = new XmlNodePrinter(new PrintWriter(writer))
printer.setPreserveWhitespace(true)
printer.print(classpath)
}
}

View File

@@ -0,0 +1,45 @@
package zero1hd.rhythmbullet.desktop;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.Input.Keys;
public enum Controls {
UP("Forward", Keys.UP), DOWN("Backwards", Keys.DOWN), LEFT("Left", Keys.LEFT), RIGHT("Right", Keys.RIGHT), SHOOT("Shoot", Keys.SPACE), ACCELERATE("Boost", Keys.SHIFT_LEFT);
private int keycode;
private final String friendly;
private final int defectKey;
private Controls(String friendly, int defectKey) {
this.friendly = friendly;
this.defectKey = defectKey;
this.keycode = defectKey;
}
public Controls setKeycode(int keycode) {
Controls[] controls = Controls.values();
for (int i = 0; i < controls.length; i++) {
if (controls[i] != this && controls[i].getKeycode() == keycode) {
return controls[i];
}
}
this.keycode = keycode;
return null;
}
public int getKeycode() {
return keycode;
}
public void loadKeyFromPreferences(Preferences preferences) {
setKeycode(preferences.getInteger(name(), defectKey));
}
public void saveKeyToPreferences(Preferences preferences) {
preferences.putInteger(name(), keycode);
}
@Override
public String toString() {
return friendly;
}
}

View File

@@ -0,0 +1,238 @@
package zero1hd.rhythmbullet.desktop;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox.CheckBoxStyle;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton.ImageButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
import com.badlogic.gdx.scenes.scene2d.ui.List.ListStyle;
import com.badlogic.gdx.scenes.scene2d.ui.ProgressBar.ProgressBarStyle;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane.ScrollPaneStyle;
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox.SelectBoxStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Slider.SliderStyle;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle;
import zero1hd.rhythmbullet.util.AssetPack;
public class DesktopAssetPack implements AssetPack {
private FreeTypeFontGenerator default_fontGenerator;
private FreeTypeFontGenerator darktech_ldr_fontGenerator;
@Override
public void initiate() {
default_fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/Gasalt-Regular.ttf"));
darktech_ldr_fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/darktech_ldr.ttf"));
}
@Override
public void queueTextures(AssetManager assetManager) {
assetManager.load("uiskin.atlas", TextureAtlas.class);
assetManager.load("Tech-Circle1.png", Texture.class);
assetManager.load("polyjet-standard.png", Texture.class);
assetManager.load("keyboard.atlas", TextureAtlas.class);
assetManager.load("cybercircle3B.png", Texture.class);
assetManager.load("title.png", Texture.class);
assetManager.load("cybercircle1.png", Texture.class);
assetManager.load("defaultCover.png", Texture.class);
assetManager.load("laser.png", Texture.class);
assetManager.load("pellet.png", Texture.class);
assetManager.load("shard.png", Texture.class);
assetManager.load("bar.png", Texture.class);
assetManager.load("flake.png", Texture.class);
assetManager.load("void_circle.png", Texture.class);
assetManager.load("tpSelector.png", Texture.class);
assetManager.load("backgrounds/mainBG.png", Texture.class);
assetManager.load("magic1.png", Texture.class);
}
@Override
public void queueSFX(AssetManager assetManager) {
assetManager.load("pop_open.ogg", Sound.class);
assetManager.load("pop_close.ogg", Sound.class);
assetManager.load("laser.ogg", Sound.class);
assetManager.load("explosion.ogg", Sound.class);
assetManager.load("disintegrate.ogg", Sound.class);
}
@Override
public void queueParticles(AssetManager assetManager) {
assetManager.load("standard_thrust.p", ParticleEffect.class);
assetManager.load("teleport-cloak.p", ParticleEffect.class);
assetManager.load("explosion-s.p", ParticleEffect.class);
assetManager.load("beateffect.p", ParticleEffect.class);
}
@Override
public void setupSkin(Skin skin) {
skin.add("default", Color.WHITE);
skin.add("inverse", Color.BLACK);
TextButtonStyle defaultTextButton = new TextButtonStyle();
defaultTextButton.up = skin.getDrawable("rect");
defaultTextButton.down = skin.getDrawable("rect-down");
defaultTextButton.font = skin.getFont("default-font");
defaultTextButton.fontColor = skin.getColor("default");
defaultTextButton.disabled = skin.getDrawable("rect-disabled");
skin.add("default", defaultTextButton);
TextButtonStyle subTextbutton = new TextButtonStyle(defaultTextButton);
subTextbutton.font = skin.getFont("sub-font");
skin.add("sub", subTextbutton);
TextButtonStyle windowTextButton = new TextButtonStyle(defaultTextButton);
windowTextButton.font = skin.getFont("window-font");
skin.add("window", windowTextButton);
SliderStyle defaultSlider = new SliderStyle(skin.getDrawable("default-slider"), skin.getDrawable("default-slider-knob"));
skin.add("default-horizontal", defaultSlider);
SliderStyle vertSlider = new SliderStyle(defaultSlider);
vertSlider.knob = skin.getDrawable("vertical-slider-knob");
skin.add("default-vertical", vertSlider);
ProgressBarStyle defaultProgressBar = new ProgressBarStyle(skin.getDrawable("default-slider"), skin.getDrawable("progress-slider-knob"));
LabelStyle defaultLabel = new LabelStyle();
defaultLabel.font = skin.getFont("default-font");
defaultLabel.fontColor = skin.getColor("default");
skin.add("default", defaultLabel);
TextFieldStyle defaultTextField = new TextFieldStyle(skin.getFont("sub-font"), skin.getColor("default"), skin.getDrawable("cursor"), skin.getDrawable("selection"), skin.getDrawable("textfield"));
skin.add("default", defaultTextField);
TextFieldStyle uiTextField = new TextFieldStyle(defaultTextField);
uiTextField.font = skin.getFont("window-font");
skin.add("ui", uiTextField);
WindowStyle defaultWindow = new WindowStyle(skin.getFont("window-font"), skin.getColor("default"), skin.getDrawable("default-window"));
skin.add("default", defaultWindow);
WindowStyle tintedWindow = new WindowStyle(defaultWindow);
tintedWindow.titleFontColor = skin.getColor("inverse");
tintedWindow.background = skin.getDrawable("tinted-window");
skin.add("tinted", tintedWindow);
ListStyle defaultList = new ListStyle(skin.getFont("window-font"), skin.getColor("inverse"), skin.getColor("default"), skin.getDrawable("selection"));
skin.add("default", defaultList);
ScrollPaneStyle defaultScrollPane = new ScrollPaneStyle();
defaultScrollPane.vScroll = skin.getDrawable("default-scroll");
defaultScrollPane.hScrollKnob = skin.getDrawable("default-round-large");
defaultScrollPane.hScroll = skin.getDrawable("default-scroll");
defaultScrollPane.vScrollKnob = skin.getDrawable("default-round-large");
skin.add("default", defaultScrollPane);
CheckBoxStyle defaultCheckBox = new CheckBoxStyle(skin.getDrawable("check-off"), skin.getDrawable("check-on"), skin.getFont("window-font"), skin.getColor("default"));
defaultCheckBox.checkboxOffDisabled = skin.getDrawable("check-disabled");
skin.add("default", defaultCheckBox);
SelectBoxStyle defaultSelectBox = new SelectBoxStyle(skin.getFont("default-font"), skin.getColor("default"), skin.getDrawable("default-select"), defaultScrollPane, defaultList);
skin.add("default", defaultSelectBox);
Gdx.app.debug("Prelaunch Debug Info", "UI Skin has been defined.");
CheckBoxStyle playButtonStyle = new CheckBoxStyle(defaultCheckBox);
playButtonStyle.checkboxOn = skin.getDrawable("play-down");
playButtonStyle.checkboxOff = skin.getDrawable("play");
skin.add("play-button", playButtonStyle);
ImageButtonStyle pauseButtonStyle = new ImageButtonStyle();
pauseButtonStyle.down = skin.getDrawable("pause-down");
pauseButtonStyle.up = skin.getDrawable("pause");
skin.add("pause-button", pauseButtonStyle);
ImageButtonStyle fastForwardButtonStyle = new ImageButtonStyle();
fastForwardButtonStyle.down = skin.getDrawable("fast-forward-down");
fastForwardButtonStyle.up = skin.getDrawable("fast-forward");
skin.add("fast-forward-button", fastForwardButtonStyle);
ImageButtonStyle reverseButtonStyle = new ImageButtonStyle();
reverseButtonStyle.down = skin.getDrawable("rewind-down");
reverseButtonStyle.up = skin.getDrawable("rewind");
skin.add("rewind-button", reverseButtonStyle);
CheckBoxStyle shuffleButtonStyle = new CheckBoxStyle(defaultCheckBox);
shuffleButtonStyle.checkboxOff = skin.getDrawable("shuffle");
shuffleButtonStyle.checkboxOn = skin.getDrawable("shuffle-down");
skin.add("shuffle-button", shuffleButtonStyle);
ButtonStyle selectableButton = new ButtonStyle();
selectableButton.checked = skin.getDrawable("holo-pane-down");
selectableButton.up = skin.getDrawable("holo-pane");
skin.add("selectable-button", selectableButton);
ButtonStyle selectableCleanButton = new ButtonStyle();
skin.add("selectable-clean-button", selectableCleanButton);
}
@Override
public void generateFonts(Skin skin) {
int height = Gdx.graphics.getHeight();
skin.add("window-font", default_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = 18;
}
}));
skin.add("sub-font", default_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = fontScale(0.05f, height);
}
}));
skin.add("default-font", default_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = fontScale(0.07f, height);
}
}));
skin.add("large-font", default_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = fontScale(0.085f, height);
}
}));
skin.add("special-font", darktech_ldr_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = fontScale(0.075f, height);
}
}));
}
public int fontScale(float fontSize, int height) {
int size = MathUtils.round(Gdx.graphics.getDensity()*(fontSize*height));
if (size >= 200) {
size = 200;
}
return size;
}
@Override
public void complete(AssetManager assetManager) {
assetManager.get("standard_thrust.p", ParticleEffect.class).flipY();
Preferences controlsPrefs = Gdx.app.getPreferences("RhythmBullet_Controls");
Controls.UP.loadKeyFromPreferences(controlsPrefs);
Controls.DOWN.loadKeyFromPreferences(controlsPrefs);
Controls.LEFT.loadKeyFromPreferences(controlsPrefs);
Controls.RIGHT.loadKeyFromPreferences(controlsPrefs);
Controls.SHOOT.loadKeyFromPreferences(controlsPrefs);
Controls.ACCELERATE.loadKeyFromPreferences(controlsPrefs);
}
@Override
public void dispose() {
darktech_ldr_fontGenerator.dispose();
default_fontGenerator.dispose();
}
}

View File

@@ -0,0 +1,28 @@
package zero1hd.rhythmbullet.desktop;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.desktop.screens.SplashScreen;
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 = 320;
config.height = 320;
config.allowSoftwareMode = true;
core = new RhythmBullet();
core.setup(new SplashScreen(), new DesktopAssetPack(), screenConfig);
LwjglApplication app = new LwjglApplication(core, config);
}
}

View File

@@ -0,0 +1,79 @@
package zero1hd.rhythmbullet.desktop;
import org.lwjgl.opengl.Display;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import zero1hd.rhythmbullet.util.ScreenConfiguration;
public class DesktopScreenConfiguration implements ScreenConfiguration {
private LwjglApplicationConfiguration configuration;
private boolean vSync;
public DesktopScreenConfiguration(LwjglApplicationConfiguration configuration) {
this.configuration = configuration;
vSync = configuration.vSyncEnabled;
}
@Override
public void setFramesPerSecond(int fps) {
configuration.foregroundFPS = fps;
}
@Override
public int getTargetFramesPerSecond() {
return configuration.foregroundFPS;
}
@Override
public void setVsync(boolean useVsync) {
configuration.vSyncEnabled = useVsync;
Display.setVSyncEnabled(useVsync);
}
@Override
public boolean getVsync() {
return vSync;
}
@Override
public int getScreenWidth() {
return Display.getDesktopDisplayMode().getWidth();
}
@Override
public int getScreenHeight() {
return Display.getDesktopDisplayMode().getHeight();
}
@Override
public int getWindowPosX() {
return Display.getX();
}
@Override
public int getWindowPosY() {
return Display.getY();
}
@Override
public void setWindowLocationX(int x) {
Display.setLocation(x, getWindowPosY());
}
@Override
public void setWindowLocationY(int y) {
Display.setLocation(getWindowPosX(), y);
}
@Override
public void setWindowLocation(int x, int y) {
Display.setLocation(x, y);
}
@Override
public void queueBorderless(boolean borderless) {
System.setProperty("org.lwjgl.opengl.Window.undecorated", String.valueOf(borderless));
}
}

View File

@@ -0,0 +1,269 @@
package zero1hd.rhythmbullet.desktop.audio;
import static org.lwjgl.openal.AL10.alGetSourcef;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.text.DecimalFormat;
import java.util.Observable;
import java.util.Observer;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL11;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.TimeUtils;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.Field;
import com.badlogic.gdx.utils.reflect.ReflectionException;
import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.audio.visualizer.PCMSystem;
public class PCMObtainer implements Observer, PCMSystem {
private int windowSize = 1024;
private int sampleRate;
private long millisPerWindow;
private boolean songChanged;
private DecimalFormat df;
private float[] PCM = new float[windowSize];
private float[] frequencyBins = new float[windowSize / 2];
private FloatFFT_1D fft = new FloatFFT_1D(windowSize);
private ShortBuffer playingBuffer;
private ShortBuffer intermediateBuffer;
private ShortBuffer buffer;
private volatile int sourceID;
private int channelCount;
private MusicController mc;
private BufferStreamReadThread streamReadThread;
private int windowsRead;
private int currentPlaybackWindow;
private volatile boolean updated;
public PCMObtainer(MusicController musicController) {
this.mc = musicController;
mc.addObserver(this);
try {
Field bufferField = ClassReflection.getDeclaredField(OpenALMusic.class, "tempBuffer");
bufferField.setAccessible(true);
buffer = ((ByteBuffer) bufferField.get(null)).asShortBuffer().asReadOnlyBuffer();
playingBuffer = ShortBuffer.allocate(buffer.capacity());
intermediateBuffer = ShortBuffer.allocate(buffer.capacity());
} catch (IllegalArgumentException | SecurityException | ReflectionException e) {
Gdx.app.debug("Visualizer reflection", "Failed attempt at retrieving tempBuffer field.", e);
Gdx.app.exit();
}
streamReadThread = new BufferStreamReadThread();
df = new DecimalFormat("#.###");
df.setRoundingMode(RoundingMode.HALF_EVEN);
}
private boolean calcPCMData() {
synchronized (PCM) {
boolean empty = true;
short chanVal;
for (int sid = 0; sid < PCM.length && sid < playingBuffer.remaining(); sid++) {
PCM[sid] = 0;
for (int channel = 0; channel < channelCount; channel++) {
if (PCM[sid] < (chanVal = playingBuffer.get())) {
PCM[sid] = chanVal;
}
}
PCM[sid] /= Short.MAX_VALUE + 1f;
if (PCM[sid] != 0) {
empty = false;
}
}
return empty;
}
}
private void checkValidityOfPlaybackBuffer() {
// Begin comparison
buffer.rewind();
if (intermediateBuffer.compareTo(buffer) != 0) {
bufferChanged();
// Begin copying current buffer to the comparison buffer
intermediateBuffer.clear();
intermediateBuffer.put(buffer);
intermediateBuffer.flip();
}
}
private void bufferChanged() {
playingBuffer.rewind();
playingBuffer.put(intermediateBuffer);
playingBuffer.rewind();
synchronizeBufferWithPlayback();
}
private int calculateBufferPosition() {
int offset = (int) alGetSourcef(sourceID, AL11.AL_SAMPLE_OFFSET);
offset = (offset / windowSize) * windowSize;
return offset;
}
private boolean synchronizeBufferWithPlayback() {
int bufferPos = calculateBufferPosition();
synchronized (this) {
if (bufferPos <= playingBuffer.limit() && bufferPos >= 0) {
playingBuffer.position(bufferPos);
windowsRead = (int) ((mc.getCurrentPosition() * sampleRate) / windowSize);
return true;
}
}
return false;
}
@Override
public void loadMusic() {
Gdx.app.debug("PCMObtainer", "music loaded.");
sourceID = -1;
channelCount = mc.getCurrentMusicHeader().getChannelCount();
sampleRate = mc.getCurrentMusicHeader().getSampleRate();
String millisPerWindowF = df.format(windowSize/(float) sampleRate);
millisPerWindow = (long) (Float.valueOf(millisPerWindowF)*1000);
songChanged = true;
intermediateBuffer.clear();
playingBuffer.clear();
begin();
}
@Override
public float[] getFrequencyBins() {
if (mc.isPlaying()) {
if (updated) {
synchronized (PCM) {
fft.realForward(PCM);
System.arraycopy(PCM, 1, frequencyBins, 0, frequencyBins.length);
}
updated = false;
}
} else {
for (int freqID = 0; freqID < frequencyBins.length; freqID++) {
frequencyBins[freqID] = 0;
}
}
return frequencyBins;
}
@Override
public int getWindowSize() {
return windowSize;
}
private class BufferStreamReadThread implements Runnable {
private String name = "PCM-Audio-Processing";
private Thread thread;
private volatile boolean run = true;
private long timeOfLastRead;
private long waitTime;
@Override
public void run() {
while (run) {
if (mc.isPlaying()) {
//record time of read
timeOfLastRead = TimeUtils.millis();
//calculate current pcm data and notify that there is new data
checkValidityOfPlaybackBuffer();
updated = !calcPCMData();
windowsRead++;
//contemplate synchronization
currentPlaybackWindow = MathUtils.round((mc.getCurrentPosition() * sampleRate) / windowSize);
if (windowsRead != currentPlaybackWindow) {
synchronizeBufferWithPlayback();
}
//wait for a bit before reading again depending on the speed at which the system does playback.
waitTime = Math.max(0, millisPerWindow - TimeUtils.timeSinceMillis(timeOfLastRead));
try {
Thread.sleep(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Gdx.app.debug(thread.getName(), "stopped");
}
public void start() {
if (thread == null) {
thread = new Thread(this, name);
thread.setDaemon(true);
thread.start();
} else {
synchronized (this) {
notify();
}
}
}
public void stop() {
run = false;
}
}
@Override
public void update(Observable o, Object arg) {
if (o == mc) {
if (arg == mc.states.LOADED) {
loadMusic();
} else if (arg == mc.states.PLAYING) {
begin();
}
}
}
private void begin() {
if (mc.isPlaying()) {
if (sourceID == -1) {
try {
Field sourceIDField = ClassReflection.getDeclaredField(OpenALMusic.class, "sourceID");
sourceIDField.setAccessible(true);
sourceID = (int) sourceIDField.get(mc.getCurrentMusic());
} catch (ReflectionException e) {
e.printStackTrace();
}
}
streamReadThread.start();
}
}
@Override
public void dispose() {
streamReadThread.stop();
}
@Override
public boolean hasAudioChanged() {
if (songChanged) {
songChanged = false;
return true;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,14 @@
package zero1hd.rhythmbullet.desktop.audio.processor;
import com.badlogic.gdx.files.FileHandle;
import zero1hd.rhythmbullet.audio.AudioProcessorFactory;
import zero1hd.rhythmbullet.audio.MinimalAudioHeader;
import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
public class DesktopAudioProcessorFactory implements AudioProcessorFactory {
@Override
public AudioProcessor newMP3AudioProcessor(FileHandle fileHandle) {
return new MP3AudioProcessor(fileHandle, new MinimalAudioHeader(fileHandle));
}
}

View File

@@ -0,0 +1,158 @@
package zero1hd.rhythmbullet.desktop.audio.processor;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import javazoom.jl.decoder.Bitstream;
import javazoom.jl.decoder.BitstreamException;
import javazoom.jl.decoder.DecoderException;
import javazoom.jl.decoder.Header;
import javazoom.jl.decoder.MP3Decoder;
import javazoom.jl.decoder.OutputBuffer;
import zero1hd.rhythmbullet.audio.MinimalAudioHeader;
import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
public class MP3AudioProcessor implements AudioProcessor {
private boolean stereo;
private int sampleRate;
private long sampleFrames;
private FileHandle fileHandle;
private byte[] currentByteSet;
private byte[] workset;
Bitstream bitstream;
MP3Decoder decoder;
OutputBuffer sampleBuffer;
private int indexHead = -1;
public MP3AudioProcessor(FileHandle fileHandle, MinimalAudioHeader minimalAudioHeader) {
this.fileHandle = fileHandle;
bitstream = new Bitstream(fileHandle.read());
stereo = minimalAudioHeader.getChannelCount() == 1 ? false : true;
sampleRate = minimalAudioHeader.getSampleRate();
sampleFrames = minimalAudioHeader.estimateSampleFrames();
decoder = new MP3Decoder();
sampleBuffer = new OutputBuffer(stereo ? 2 : 1, false);
decoder.setOutputBuffer(sampleBuffer);
workset = new byte[(stereo ? 2 : 1)*2];
}
@Override
public boolean isStereo() {
return stereo;
}
@Override
public int getSampleRate() {
return sampleRate;
}
@Override
public int readSamples(short[] pcm) {
int samplesRead = 0;
for (int sid = 0; sid < pcm.length; sid++) {
for (int wsid = 0; wsid < workset.length; wsid++) {
workset[wsid] = nextByte();
}
if (currentByteSet != null) {
pcm[sid] += (workset[1] << 8) + (workset[0] & 0x00ff);
if (stereo) {
short altChan = (short) ((workset[3] << 8) + (workset[2] & 0x00ff));
sid++;
pcm[sid] = altChan;
}
samplesRead ++;
}
}
return samplesRead;
}
@Override
public int readFrames(float[] pcm) {
int framesRead = 0;
for (int sid = 0; sid < pcm.length; sid++) {
for (int wsid = 0; wsid < workset.length; wsid++) {
workset[wsid] = nextByte();
}
if (currentByteSet != null) {
pcm[sid] += (workset[1] << 8) + (workset[0] & 0x00ff);
if (stereo) {
short altChan = (short) ((workset[3] << 8) + (workset[2] & 0x00ff));
pcm[sid] = altChan > pcm[sid] ? altChan : pcm[sid];
}
framesRead++;
pcm[sid] /= Short.MAX_VALUE+1;
}
}
return framesRead;
}
public byte nextByte() {
indexHead++;
if (currentByteSet == null || indexHead >= currentByteSet.length) {
loadNextBuffer();
if (currentByteSet == null) {
return 0;
}
indexHead = 0;
}
return currentByteSet[indexHead];
}
public int loadNextBuffer() {
if (bitstream != null) {
int bytesRead = 0;
try {
Header header = bitstream.readFrame();
if (header != null) {
try {
decoder.decodeFrame(header, bitstream);
} catch (ArrayIndexOutOfBoundsException ae) {
Gdx.app.debug("Mp3Manager", "Last buffer reached since array was out of bounds.");
} catch (DecoderException de) {
Gdx.app.error("MP3 Decoder Error", de.toString());
}
bitstream.closeFrame();
bytesRead = sampleBuffer.reset();
currentByteSet = sampleBuffer.getBuffer();
} else {
currentByteSet = null;
}
} catch (BitstreamException be) {
be.printStackTrace();
}
return bytesRead;
} else {
return 0;
}
}
@Override
public FileHandle getMusicFileHandle() {
return fileHandle;
}
@Override
public long getSampleFrames() {
return sampleFrames;
}
@Override
public void dispose() {
Gdx.app.debug("MP3Manager", "Disposing...");
try {
bitstream.close();
bitstream = null;
} catch (BitstreamException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,78 @@
package zero1hd.rhythmbullet.desktop.graphics.ui.components;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.Align;
public class GraphicsOptions extends Table {
private Label resolutions;
private ResolutionButton
_3840x2160,
_2560x1440,
_1920x1200,
_1920x1080,
_1280x800,
_1280x720,
_1366x768;
public GraphicsOptions(Skin skin, final Preferences prefs) {
align(Align.center);
defaults().space(10f);
resolutions = new Label("Resolutions: ", skin);
add(resolutions).left();
row();
TextButton fullscreen = new TextButton("Fullscreen", skin);
fullscreen.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
if (!Gdx.graphics.isFullscreen()) {
Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
prefs.putBoolean("fullscreen", true);
prefs.flush();
}
}
});
add(fullscreen).fillX();
row();
_3840x2160 = new ResolutionButton(3840, 2160, skin, prefs);
add(_3840x2160).fillX();
row();
_2560x1440 = new ResolutionButton(2560, 1440, skin, prefs);
add(_2560x1440).fillX();
row();
_1920x1200 = new ResolutionButton(1920, 1200, skin, prefs);
add(_1920x1200).fillX();
row();
_1920x1080 = new ResolutionButton(1920, 1080, skin, prefs);
add(_1920x1080).fillX();
row();
_1280x800 = new ResolutionButton(1280, 800, skin, prefs);
add(_1280x800).fillX();
row();
_1366x768 = new ResolutionButton(1366, 768, skin, prefs);
add(_1366x768).fillX();
row();
_1280x720 = new ResolutionButton(1280, 720, skin, prefs);
add(_1280x720).fillX();
row();
pack();
}
}

View File

@@ -0,0 +1,36 @@
package zero1hd.rhythmbullet.desktop.graphics.ui.components;
import java.awt.Dimension;
import java.awt.Toolkit;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
public class ResolutionButton extends TextButton {
public ResolutionButton(int width, int height, Skin skin, Preferences prefs) {
super(width + "x" + height, skin);
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
if (screenDim.getWidth() < width || screenDim.getHeight() < height) {
setDisabled(true);
}
addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
Gdx.graphics.setWindowedMode(width, height);
prefs.putInteger("screen-width", width);
prefs.putInteger("screen-height", height);
prefs.putBoolean("fullscreen", false);
prefs.flush();
}
});
}
}

View File

@@ -0,0 +1,8 @@
package zero1hd.rhythmbullet.desktop.screens;
import com.badlogic.gdx.ScreenAdapter;
public class EndScreen extends ScreenAdapter {
public EndScreen() {
}
}

View File

@@ -0,0 +1,42 @@
package zero1hd.rhythmbullet.desktop.screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.audio.visualizer.CircularVisualizer;
import zero1hd.rhythmbullet.desktop.audio.PCMObtainer;
public class GameScreen extends ScreenAdapter {
private AssetManager assets;
private SpriteBatch batch;
private ExtendViewport viewport;
private CircularVisualizer circleVisualizer;
public GameScreen(AssetManager assets, Preferences prefs, MusicController musicController) {
this.assets = assets;
batch = new SpriteBatch();
viewport = new ExtendViewport(RhythmBullet.WORLD_WIDTH, RhythmBullet.WORLD_HEIGHT);
circleVisualizer = new CircularVisualizer(new PCMObtainer(musicController));
circleVisualizer.setCenter(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2);
circleVisualizer.setCamera(viewport.getCamera());
circleVisualizer.setColor(Color.CYAN.toFloatBits());
circleVisualizer.applyPositionChanges();
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(0.22f, 0.22f, 0.22f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
circleVisualizer.drawVisualizer();
super.render(delta);
}
}

View File

@@ -0,0 +1,58 @@
package zero1hd.rhythmbullet.desktop.screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.desktop.screens.main.MainScreen;
import zero1hd.rhythmbullet.util.InitialScreen;
public class SplashScreen extends ScreenAdapter implements InitialScreen {
private Texture splashTexture;
private SpriteBatch batch;
@Override
public void init() {
batch = new SpriteBatch();
splashTexture = new Texture(Gdx.files.internal("splash_texture.png"));
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(1f, 1f, 1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(splashTexture, 0, 0);
batch.end();
super.render(delta);
}
@Override
public void hide() {
splashTexture.dispose();
super.hide();
}
@Override
public void preAssetLoad() {
}
@Override
public void postAssetLoad() {
}
@Override
public void resize(int width, int height) {
super.resize(width, height);
}
@Override
public Screen advance(RhythmBullet game) {
return new MainScreen(game);
}
}

View File

@@ -0,0 +1,157 @@
package zero1hd.rhythmbullet.desktop.screens.main;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
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.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import zero1hd.rhythmbullet.audio.AudioMetadataController;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.audio.analyzer.AudioAnalyzerSection;
import zero1hd.rhythmbullet.audio.analyzer.DynamicAudioAnalyzer;
import zero1hd.rhythmbullet.audio.metadata.AudioMetadata;
import zero1hd.rhythmbullet.graphics.ui.Page;
public class AnalysisPage extends Page {
private TextButton backButton;
private DynamicAudioAnalyzer audioAnalyzer;
private Table table;
private Table adjustment;
private Label difficultyModLabel, healthModLabel, speedModLabel;
private Slider difficultyModifierSlider, healthModifierSlider, speedModifierSlider;
private Label diffModPercentLabel, heltModPercentLabel, speeModPercentLabel;
private Label progressLabel;
private TextButton confirmButton;
private Image albumImage;
private AudioMetadataController amc;
private MusicController mc;
public AnalysisPage(MusicController mc, AudioMetadataController amc, Skin skin, ChangeListener backButtonListener, ChangeListener confirmedButtonListener) {
super(2, 0);
table = new Table();
table.setFillParent(true);
table.defaults().space(10f);
addActor(table);
adjustment = new Table();
this.mc = mc;
this.amc = amc;
difficultyModLabel = new Label("Difficulty Modifier: ", skin, "sub-font", skin.getColor("default"));
difficultyModifierSlider = new Slider(1, 3, 0.5f, false, skin);
diffModPercentLabel = new Label(String.valueOf(difficultyModifierSlider.getValue()) + "x", skin);
difficultyModifierSlider.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
diffModPercentLabel.setText(String.valueOf(difficultyModifierSlider.getValue()) + "x");
}
});
adjustment.add(difficultyModLabel);
adjustment.add(difficultyModifierSlider).minWidth(0.5f*getWidth());
adjustment.add(diffModPercentLabel).spaceLeft(10f).center().expandX().fill();
adjustment.row();
healthModLabel = new Label("Health Modifier: ", skin, "sub-font", skin.getColor("default"));
healthModifierSlider = new Slider(1f, 3f, 0.5f, false, skin);
heltModPercentLabel = new Label(String.valueOf(healthModifierSlider.getValue()) + "x", skin);
healthModifierSlider.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
heltModPercentLabel.setText(String.valueOf(healthModifierSlider.getValue()) + "x");
}
});
adjustment.add(healthModLabel);
adjustment.add(healthModifierSlider).fillX();
adjustment.add(heltModPercentLabel).spaceLeft(10f);
adjustment.row();
speedModLabel = new Label("Speed Modifier: ", skin, "sub-font", skin.getColor("default"));
speedModifierSlider = new Slider(1, 3, 0.5f, false, skin);
speeModPercentLabel = new Label(String.valueOf(speedModifierSlider.getValue()) + "x", skin);
speedModifierSlider.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
speeModPercentLabel.setText(String.valueOf(speedModifierSlider.getValue()) + "x");
}
});
adjustment.add(speedModLabel);
adjustment.add(speedModifierSlider).fillX();
adjustment.add(speeModPercentLabel).spaceLeft(10f);
adjustment.row();
confirmButton = new TextButton("Confirm", skin);
confirmButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
confirmButton.setDisabled(true);
speedModifierSlider.setDisabled(true);
healthModifierSlider.setDisabled(true);
difficultyModifierSlider.setDisabled(true);
progressLabel.setText("Loading...");
}
});
confirmButton.addListener(confirmedButtonListener);
adjustment.add(confirmButton).colspan(3).fillX();
adjustment.row();
progressLabel = new Label("Please confirm... ", skin);
adjustment.add(progressLabel).colspan(2).left().spaceTop(20f);
backButton = new TextButton("Cancel", skin);
backButton.setWidth(backButton.getWidth() + 20);
backButton.addListener(backButtonListener);
backButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
if (audioAnalyzer != null) {
audioAnalyzer.stop();
audioAnalyzer = null;
albumImage.setDrawable(null);
mc.setLoop(false);
}
}
});
backButton.setPosition(15, getHeight()-backButton.getHeight()-25);
addActor(backButton);
}
public void processSong() {
mc.setLoop(true);
AudioMetadata metadata = amc.getAudioMetadata(mc.getCurrentMusicFileHandle());
metadata.loadAlbumCover();
albumImage = new Image(metadata.getAlbumCover());
table.clear();
table.add(albumImage).size(adjustment.getMinHeight());
table.row();
table.add(adjustment);
AudioAnalyzerSection bass = new AudioAnalyzerSection(1, 5, 1.7f, 3);
AudioAnalyzerSection midSection = new AudioAnalyzerSection(7, 25, 1.5f, 3);
audioAnalyzer = new DynamicAudioAnalyzer(mc.getMusicList().newAudioProcessor(mc.getCurrentMusicFileHandle()), bass, midSection);
audioAnalyzer.start();
}
@Override
public void dispose() {
super.dispose();
}
@Override
public void setCameraPositionToPage(Vector3 cameraPosition) {
confirmButton.setDisabled(false);
speedModifierSlider.setDisabled(false);
healthModifierSlider.setDisabled(false);
difficultyModifierSlider.setDisabled(false);
processSong();
super.setCameraPositionToPage(cameraPosition);
}
}

View File

@@ -0,0 +1,30 @@
package zero1hd.rhythmbullet.desktop.screens.main;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import zero1hd.rhythmbullet.graphics.ui.Page;
public class CreditsPage extends Page {
public CreditsPage(Skin skin) {
super(0, 1);
Label title = new Label("Credits", skin, "large-font", skin.getColor("default"));
title.setPosition(15, getHeight()-title.getHeight()-15);
addActor(title);
Label subtitle = new Label("This game wouldn't be possible without these people.", skin, "sub-font", skin.getColor("default"));
subtitle.setPosition(title.getX(), title.getY()-subtitle.getHeight());
addActor(subtitle);
Label listOfNames = new Label(
"TheClimbingHippo (texture)\n"
+ "Crepitus (sound effects)\n"
+ "Neoqueto - Darktech LDR (font)\n"
+ "Rémi Lagast - Gasalt (font)\n"
+ "Timour Jgenti - Iron Maiden (font)", skin, "sub-font", skin.getColor("default"));
listOfNames.setPosition(subtitle.getX()+16, subtitle.getY()-listOfNames.getHeight()-10);
addActor(listOfNames);
}
}

View File

@@ -0,0 +1,43 @@
package zero1hd.rhythmbullet.desktop.screens.main;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import zero1hd.rhythmbullet.desktop.graphics.ui.components.GraphicsOptions;
import zero1hd.rhythmbullet.graphics.ui.Page;
public class GraphicsPage extends Page {
private ScrollPane scrollPane;
private GraphicsOptions graphicsTable;
private TextButton backButton;
public GraphicsPage(Skin skin, Preferences preferences, ChangeListener backButtonListener) {
super(-1, 1);
graphicsTable = new GraphicsOptions(skin, preferences);
scrollPane = new ScrollPane(graphicsTable, skin);
scrollPane.setFadeScrollBars(false);
scrollPane.setFillParent(true);
addActor(scrollPane);
backButton = new TextButton("Back", skin);
backButton.setPosition(10, getHeight() - 10 - backButton.getHeight());
backButton.setWidth(backButton.getWidth() + 20);
backButton.addListener(backButtonListener);
addActor(backButton);
}
@Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
}
@Override
public void act(float delta) {
super.act(delta);
}
}

View File

@@ -0,0 +1,224 @@
package zero1hd.rhythmbullet.desktop.screens.main;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
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.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.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.desktop.Controls;
import zero1hd.rhythmbullet.graphics.ui.Page;
public class KeybindPage extends Page {
private ButtonGroup<KeyChangeButton> buttonOptions;
private VerticalGroup group;
private ScrollPane scrollPane;
private Skin skin;
TextureAtlas keyTextures;
private Preferences keyBindPrefs;
private InputListener inputListener;
private ChangeListener checkedListener;
public KeybindPage(AssetManager assetManager, Skin skin, ChangeListener backButtonListener) {
super(-1, -1);
setTouchable(Touchable.enabled);
this.skin = skin;
keyTextures = assetManager.get("keyboard.atlas", TextureAtlas.class);
this.keyBindPrefs = Gdx.app.getPreferences("PolyJet_Controls");
TextButton backButton = new TextButton("Back", skin);
backButton.setWidth(backButton.getWidth()+20f);
backButton.setPosition(getWidth()-backButton.getWidth()-15f, getHeight() - backButton.getHeight() - 15f);
backButton.addListener(backButtonListener);
addActor(backButton);
inputListener = new InputListener() {
@Override
public boolean keyUp(InputEvent event, int keycode) {
KeyChangeButton button = buttonOptions.getChecked();
if (button != null) {
if (keycode != Keys.ESCAPE) {
button.attemptSetKeycode(keycode);
}
getStage().setKeyboardFocus(null);
}
return super.keyUp(event, keycode);
}
};
checkedListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
KeyChangeButton button = (KeyChangeButton) actor;
if (button.isChecked()) {
button.ready();
} else {
button.done();
}
}
};
buttonOptions = new ButtonGroup<>();
buttonOptions.setMinCheckCount(0);
buttonOptions.setMaxCheckCount(1);
group = new VerticalGroup();
group.setTouchable(Touchable.childrenOnly);
group.space(15f);
Controls[] controls = Controls.values();
for (int i = 0; i < controls.length; i++) {
KeyChangeButton button = new KeyChangeButton(controls[i]);
buttonOptions.add(button);
group.addActor(button);
}
scrollPane = new ScrollPane(group);
scrollPane.setHeight(Math.min(getHeight(), group.getPrefHeight()));
scrollPane.setWidth(group.getPrefWidth());
scrollPane.setPosition((getWidth()-scrollPane.getWidth())/2f, (getHeight()-scrollPane.getHeight())/2f);
scrollPane.setTouchable(Touchable.childrenOnly);
addActor(scrollPane);
addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
if (hit(x, y, true) == event.getListenerActor()) {
simpleDebug("unchecking selected key change buttons.");
buttonOptions.uncheckAll();
}
super.clicked(event, x, y);
}
});
}
@Override
public void setCameraPositionToPage(Vector3 cameraPosition) {
getStage().setScrollFocus(scrollPane);
super.setCameraPositionToPage(cameraPosition);
}
@Override
public void dispose() {
save();
super.dispose();
}
public void save() {
keyBindPrefs.flush();
}
public class KeyChangeButton extends Button {
private Image keyImage;
private Label nameLabel;
private Controls control;
private boolean error;
public KeyChangeButton(Controls control) {
super(skin, "selectable-clean-button");
this.control = control;
nameLabel = new Label(control.toString(), skin);
keyImage = new Image(getIcon(control.getKeycode()));
add(nameLabel).expandX().left().spaceRight(20f);
add(keyImage).expandX().right();
addListener(inputListener);
addListener(checkedListener);
}
public void attemptSetKeycode(int keycode) {
simpleDebug("Attempting to set keycode to " + keycode);
TextureRegion textureRegion = getIcon(keycode);
if (textureRegion != null) {
setKeycode(keycode, textureRegion);
error = false;
} else {
error = true;
}
setChecked(false);
}
public void setKeycode(int keycode, TextureRegion texture) {
Controls conflict = control.setKeycode(keycode);
if (conflict != null) {
Array<KeyChangeButton> buttons = buttonOptions.getButtons();
for (int i = 0; i < buttons.size; i++) {
System.out.println(buttons.get(i).getControl());
if (buttons.get(i).getControl() == conflict) {
KeyChangeButton b = buttons.get(i);
scrollPane.scrollTo(b.getX(), b.getY(), b.getWidth(), b.getHeight());
b.done(true);
return;
}
}
}
TextureRegion textureRegion = getIcon(keycode);
if (textureRegion != null) {
keyImage.setDrawable(new TextureRegionDrawable(texture));
control.saveKeyToPreferences(keyBindPrefs);
}
}
public void ready() {
getStage().setKeyboardFocus(this);
keyImage.setColor(Color.ORANGE);
}
public void done() {
done(error);
}
public void done(boolean error) {
if (error) {
keyImage.addAction(Actions.repeat(3, Actions.sequence(Actions.color(Color.RED, 0.2f), Actions.color(Color.WHITE, 0.1f))));
} else {
keyImage.addAction(Actions.color(Color.WHITE, 0.2f));
}
}
public Controls getControl() {
return control;
}
}
public TextureRegion getIcon(int keycode) {
switch (keycode) {
case Keys.ALT_LEFT:
case Keys.ALT_RIGHT:
return keyTextures.findRegion("Keyboard_Black_Alt");
case Keys.SHIFT_LEFT:
case Keys.SHIFT_RIGHT:
return keyTextures.findRegion("Keyboard_Black_Shift");
case Keys.LEFT_BRACKET:
return keyTextures.findRegion("Keyboard_Black_Bracket_Left");
case Keys.RIGHT_BRACKET:
return keyTextures.findRegion("Keyboard_Black_Bracket_Right");
case Keys.SEMICOLON:
return keyTextures.findRegion("Keyboard_Black_Semicolon");
case Keys.SLASH:
return keyTextures.findRegion("Keyboard_Black_Slash");
case Keys.NUM:
return keyTextures.findRegion("Keyboard_Black_Num_Lock");
case Keys.ESCAPE:
return null;
default:
return keyTextures.findRegion("Keyboard_Black_" + Keys.toString(keycode).replace(' ', '_'));
}
}
}

View File

@@ -0,0 +1,209 @@
package zero1hd.rhythmbullet.desktop.screens.main;
import java.util.Observable;
import java.util.Observer;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.ParticleEffectPool;
import com.badlogic.gdx.graphics.g2d.ParticleEffectPool.PooledEffect;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.audio.AudioMetadataController;
import zero1hd.rhythmbullet.audio.visualizer.DoubleHorizontalVisualizer;
import zero1hd.rhythmbullet.desktop.audio.PCMObtainer;
import zero1hd.rhythmbullet.graphics.ui.Page;
import zero1hd.rhythmbullet.graphics.ui.components.MusicControls;
import zero1hd.rhythmbullet.graphics.ui.components.ScrollingText;
import zero1hd.rhythmbullet.util.ScreenConfiguration;
public class MainPage extends Page implements Observer {
private MusicController mc;
private AudioMetadataController amc;
private Label versionLabel;
private Image title;
private Table menuTable;
private TextButton playButton;
private TextButton optionsButton;
private TextButton quitButton;
private MusicControls musicControls;
private ScrollingText scrollText;
private DoubleHorizontalVisualizer dhv;
private boolean playParticles = true;
private ParticleEffectPool particlePool;
private Array<PooledEffect> particles;
private float particleScale;
public MainPage(MusicController musicController, AudioMetadataController mmc, AssetManager assetManager, Skin skin, ScreenConfiguration screenConfiguration, ChangeListener playButtonListener, ChangeListener optionsButtonListener) {
super(0, 0);
this.mc = musicController;
this.mc.addObserver(this);
mc.getMusicList().addObserver(this);
this.amc = mmc;
this.amc.addObserver(this);
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));
title.setScale((getHeight()/3f)/title.getHeight());
if (title.getWidth()*title.getScaleX() > getWidth() - getWidth()*0.075f) {
title.setScale((getWidth()*(1f-0.075f))/title.getWidth()*getScaleX());
}
title.setPosition((getWidth()-title.getWidth()*title.getScaleX())/2f, (getHeight()-title.getHeight()*title.getScaleY())/2f);
addActor(title);
versionLabel = new Label("Version: " + RhythmBullet.VERSION, skin, "sub-font", skin.getColor("default"));
versionLabel.setPosition(3, 3);
addActor(versionLabel);
menuTable = new Table();
menuTable.setSize(getWidth(), title.getY());
menuTable.align(Align.center);
menuTable.defaults().space(10f);
addActor(menuTable);
playButton = new TextButton("Start!", skin);
playButton.addListener(playButtonListener);
menuTable.add(playButton).width(Gdx.graphics.getWidth()*0.2f);
menuTable.row();
optionsButton = new TextButton("Options", skin, "sub");
optionsButton.addListener(optionsButtonListener);
menuTable.add(optionsButton).fillX();
menuTable.row();
quitButton = new TextButton("Quit", skin, "sub");
quitButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
Gdx.app.exit();
}
});
menuTable.add(quitButton).fillX();
musicControls = new MusicControls(skin, mc);
musicControls.setPosition((getWidth()-musicControls.getWidth() - 15f), 15f);
addActor(musicControls);
scrollText = new ScrollingText("...", "...", skin, false, true, 1f/screenConfiguration.getTargetFramesPerSecond());
scrollText.setWidth(0.5f*getWidth());
scrollText.setPosition(15, getHeight() - scrollText.getHeight()-30f);
addActor(scrollText);
if (mc.getMusicList().isSearched() && amc.isSameSizeMusicList()) {
scrollText.setText("Currently playing: " + amc.getAudioMetadata(mc.getCurrentMusicFileHandle()).getTitle(), null);
dhv.updateMusic();
}
particles = new Array<>();
ParticleEffect particle = assetManager.get("beateffect.p", ParticleEffect.class);
particleScale = getWidth()/particle.findEmitter("main").getSpawnWidth().getHighMax();
particlePool = new ParticleEffectPool(assetManager.get("beateffect.p", ParticleEffect.class), 0, 16);
}
@Override
public void act(float delta) {
dhv.act(delta);
super.act(delta);
}
@Override
public void draw(Batch batch, float parentAlpha) {
dhv.draw(batch, parentAlpha);
updateParticles(batch);
super.draw(batch, parentAlpha);
}
private void updateParticles(Batch batch) {
if (playParticles) {
if (particles.size < 16 && dhv.isSignificantBeat()) {
PooledEffect particle = particlePool.obtain();
particle.scaleEffect(particleScale);
particles.add(particle);
}
if (particles.size > 0) {
for (int i = particles.size - 1; i >= 0 && particles.size > 0; i--) {
PooledEffect particle = particles.get(i);
particle.draw(batch, Gdx.graphics.getDeltaTime());
if (particle.isComplete()) {
particles.removeIndex(i);
particle.free();
}
}
}
}
}
public void stopParticles() {
playParticles = false;
}
@Override
public void dispose() {
dhv.dispose();
mc.getMusicList().deleteObserver(this);
for (int i = particles.size - 1; i >= 0 && particles.size > 0; i--) {
particles.get(i).free();
particles.clear();
}
super.dispose();
}
@Override
public void setCameraPositionToPage(Vector3 cameraPosition) {
getStage().setScrollFocus(null);
playParticles = true;
super.setCameraPositionToPage(cameraPosition);
}
@Override
public void update(Observable o, Object arg) {
if (o == mc) {
if (arg == mc.states.LOADED) {
if (amc.isSameSizeMusicList()) {
scrollText.setText("Currently playing: " + amc.getAudioMetadata(mc.getCurrentMusicFileHandle()).getTitle(), null);
} else {
scrollText.setText("Currently playing: " + mc.getCurrentMusicFileHandle().nameWithoutExtension().replace('_', ' '), null);
}
} else if (arg == mc.states.PAUSED) {
if (amc.isSameSizeMusicList()) {
scrollText.setText("Currently paused: " + amc.getAudioMetadata(mc.getCurrentMusicFileHandle()).getTitle(), null);
} else {
scrollText.setText("Currently paused: " + mc.getCurrentMusicFileHandle().nameWithoutExtension().replace('_', ' '), null);
}
}
} else if (o == amc) {
if (amc.isSameSizeMusicList()) {
scrollText.setText("Currently playing: " + amc.getAudioMetadata(mc.getCurrentMusicFileHandle()).getTitle(), null);
}
} else if (o == mc.getMusicList()) {
if (arg == mc.getMusicList().states.EMPTY) {
scrollText.setText("Couldn't find MP3/WAV files", null);
} else if (arg == mc.getMusicList().states.LOADING) {
scrollText.setText("Loading...", null);
}
}
}
}

View File

@@ -0,0 +1,248 @@
package zero1hd.rhythmbullet.desktop.screens.main;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.audio.AudioMetadataController;
import zero1hd.rhythmbullet.audio.MusicList;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.desktop.audio.processor.DesktopAudioProcessorFactory;
import zero1hd.rhythmbullet.graphics.shaders.BloomShader;
import zero1hd.rhythmbullet.graphics.ui.Page;
import zero1hd.rhythmbullet.util.ResizeReadyScreen;
public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
private Stage stage;
private Vector3 cameraPosition;
private PageChangeListeners listeners;
private String selectedPage;
private MainPage mainPage;
private OptionsPage optionsPage;
private KeybindPage keybindPage;
private GraphicsPage graphicsPage;
private CreditsPage creditsPage;
private MusicSelectionPage musicSelectionPage;
private AnalysisPage analysisPage;
private MusicController musicController;
private AudioMetadataController musicMetadataController;
private RhythmBullet rhythmBullet;
private BloomShader bloomShader;
private Texture background;
private Batch screenBatch;
private boolean resizing;
public MainScreen(RhythmBullet core) {
this.rhythmBullet = core;
stage = new Stage(new ScreenViewport());
cameraPosition = new Vector3(stage.getCamera().position);
MusicList musicList = new MusicList(new DesktopAudioProcessorFactory(), core.getPreferences().getString("music dir"));
musicController = new MusicController(musicList, core.getPreferences());
musicController.setAutoPlay(true);
musicController.setShuffle(true);
musicMetadataController = new AudioMetadataController(musicList);
listeners = new PageChangeListeners();
screenBatch = new SpriteBatch();
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (!resizing) {
stage.act(delta);
if (bloomShader != null) {
bloomShader.begin();
draw();
bloomShader.end(stage.getWidth(), stage.getHeight());
} else {
draw();
}
}
if (stage.getCamera().position.x != cameraPosition.x || stage.getCamera().position.y != cameraPosition.y) {
stage.getCamera().position.lerp(cameraPosition, 0.15f);
stage.getViewport().apply();
}
super.render(delta);
}
private void draw() {
stage.getViewport().apply();
screenBatch.begin();
screenBatch.draw(background, 0, 0, stage.getViewport().getScreenWidth(), stage.getViewport().getScreenHeight());
screenBatch.end();
stage.draw();
}
@Override
public void preAssetLoad() {
resizing = true;
stage.clear();
if (bloomShader != null) {
bloomShader.dispose();
bloomShader = null;
}
background = null;
musicController.deleteObservers();
musicMetadataController.deleteObservers();
}
@Override
public void postAssetLoad() {
resizing = false;
bloomShader = new BloomShader(screenBatch);
background = rhythmBullet.getAssetManager().get("backgrounds/mainBG.png", Texture.class);
mainPage = new MainPage(musicController, musicMetadataController, rhythmBullet.getAssetManager(), rhythmBullet.getSkin(), rhythmBullet.getScreenConfiguration(), listeners.musicSelectionPageButtonListener, listeners.optionsPageButtonListener);
stage.addActor(mainPage);
//End main menu
optionsPage = new OptionsPage(musicController, rhythmBullet.getSkin(), rhythmBullet.getPreferences(), listeners.returnToMainPageListener, listeners.graphicsPageButtonListener, listeners.keybindPageButtonListener);
stage.addActor(optionsPage);
keybindPage = new KeybindPage(rhythmBullet.getAssetManager(), rhythmBullet.getSkin(), listeners.optionsPageButtonListener);
stage.addActor(keybindPage);
graphicsPage = new GraphicsPage(rhythmBullet.getSkin(), rhythmBullet.getPreferences(), listeners.optionsPageButtonListener);
stage.addActor(graphicsPage);
creditsPage = new CreditsPage(rhythmBullet.getSkin());
stage.addActor(creditsPage);
musicSelectionPage = new MusicSelectionPage(rhythmBullet.getAssetManager(), rhythmBullet.getSkin(), musicController, musicMetadataController, rhythmBullet.getScreenConfiguration(), listeners.returnToMainPageListener, listeners.analysisPageButtonListener);
stage.addActor(musicSelectionPage);
analysisPage = new AnalysisPage(musicController, musicMetadataController, rhythmBullet.getSkin(), listeners.musicSelectionPageButtonListener, listeners.confirmedSongListener);
stage.addActor(analysisPage);
musicController.getMusicList().attemptAsyncSearch(false);
if (selectedPage != null) {
for (Actor actor : stage.getActors().items) {
if (actor.getName().equals(selectedPage)) {
setDisplayedPage((Page) actor);
break;
}
}
}
}
@Override
public void show() {
Gdx.input.setInputProcessor(stage);
super.show();
}
@Override
public void hide() {
saveAll();
super.hide();
}
public void saveAll() {
if (optionsPage != null) {
optionsPage.saveOptions();
rhythmBullet.getPreferences().flush();
}
}
@Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, false);
super.resize(width, height);
}
@Override
public void dispose() {
Gdx.app.debug("Mainscreen", "disposing...");
stage.dispose();
musicMetadataController.dispose();
screenBatch.dispose();
super.dispose();
}
public void setDisplayedPage(Page page) {
Gdx.app.debug("Mainscreen", "Switching to " + page.getName());
page.setCameraPositionToPage(cameraPosition);
this.selectedPage = page.getName();
}
private class PageChangeListeners {
ChangeListener musicSelectionPageButtonListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setDisplayedPage(musicSelectionPage);
mainPage.stopParticles();
}
};
ChangeListener analysisPageButtonListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setDisplayedPage(analysisPage);
}
};
ChangeListener returnToMainPageListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setDisplayedPage(mainPage);
}
};
ChangeListener optionsPageButtonListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setDisplayedPage(optionsPage);
mainPage.stopParticles();
}
};
ChangeListener graphicsPageButtonListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setDisplayedPage(graphicsPage);
}
};
ChangeListener keybindPageButtonListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setDisplayedPage(keybindPage);
}
};
ChangeListener creditPageButtonListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setDisplayedPage(creditsPage);
mainPage.stopParticles();
}
};
ChangeListener confirmedSongListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
}
};
}
}

View File

@@ -0,0 +1,585 @@
package zero1hd.rhythmbullet.desktop.screens.main;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.LinkedBlockingQueue;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.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.Actor;
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.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.TextureRegionDrawable;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.audio.AudioMetadataController;
import zero1hd.rhythmbullet.audio.metadata.AudioMetadata;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.graphics.ui.Page;
import zero1hd.rhythmbullet.graphics.ui.components.ScrollingText;
import zero1hd.rhythmbullet.util.ScreenConfiguration;
public class MusicSelectionPage extends Page implements Observer {
Preferences musicFileAnnotation;
private MusicController mc;
private AudioMetadataController mmc;
private MusicSelectableButtonGroup selectables;
private VerticalGroup vGroup;
private TextButton back;
private ScrollPane scrollPane;
private ScreenConfiguration screenConfig;
private musicSelectionLoaderThread selectionLoaderThread;
private InformationTable musicInfoTable;
private AssetManager assets;
private Skin skin;
private boolean down, up;
private boolean frameUsed;
private TextButton beginButton;
private float scrollTimer, scrollDelay = 0.2f, scrollDelMod, songSelectionTimer;
private float musicSelectDelay;
public MusicSelectionPage(AssetManager assetManager, Skin skin, MusicController musicController, AudioMetadataController musicMetadataController, ScreenConfiguration screenConfiguration, ChangeListener backButtonListener, ChangeListener beginButtonListener) {
super(1, 0);
this.assets = assetManager;
this.mc = musicController;
this.mmc = musicMetadataController;
this.skin = skin;
vGroup = new VerticalGroup();
vGroup.space(10f);
selectables = new MusicSelectableButtonGroup();
selectables.setMinCheckCount(0);
musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation");
this.screenConfig = screenConfiguration;
scrollPane = new ScrollPane(vGroup, skin);
scrollPane.setSize(0.45f*getWidth(), getHeight());
scrollPane.setOverscroll(false, false);
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);
back.addListener(backButtonListener);
addActor(back);
back.toFront();
addListener(new InputListener() {
@Override
public boolean keyDown(InputEvent event, int keycode) {
scrollTimer = 0;
scrollDelMod = 1f;
if (keycode == Keys.DOWN) {
down = true;
}
if (keycode == Keys.UP) {
up = true;
}
return super.keyDown(event, keycode);
}
@Override
public boolean keyUp(InputEvent event, int keycode) {
if (keycode == Keys.DOWN) {
down = false;
}
if (keycode == Keys.UP) {
up = false;
}
return super.keyUp(event, keycode);
}
});
musicInfoTable = new InformationTable(getWidth()-scrollPane.getWidth(), getHeight());
addActor(musicInfoTable);
beginButton = new TextButton("Begin", skin);
beginButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
if (musicSelectDelay != 0) {
mc.setMusicByFileHandle(getSelectedMusic());
musicSelectDelay = 0f;
}
}
});
beginButton.addListener(beginButtonListener);
mmc.addObserver(this);
mc.addObserver(this);
mc.getMusicList().addObserver(this);
selectionLoaderThread = new musicSelectionLoaderThread();
musicInfoTable.setToDefault();
}
@Override
public void act(float delta) {
if (frameUsed) {
frameUsed = false;
}
if (down) {
if (scrollDelMod > 0.25f) {
scrollDelMod -= delta/0.5f;
}
if (scrollTimer <= 0) {
scrollTimer = scrollDelay*scrollDelMod;
scrollDown();
} else {
scrollTimer -= delta;
}
}
if (up) {
if (scrollDelMod > 0.25f) {
scrollDelMod -= delta/0.5f;
}
if (scrollTimer <= 0) {
scrollTimer = scrollDelay*scrollDelMod;
scrollUp();
} else {
scrollTimer -= delta;
}
}
if (songSelectionTimer > 0f) {
songSelectionTimer -= delta;
if (songSelectionTimer <= 0f) {
}
}
if (mc.getMusicList().isSearched()) {
if (mc.getMusicList().getTotal() != 0) {
}
}
updateList(delta);
super.act(delta);
}
private synchronized void updateList(float delta) {
if (mc.getMusicList().isSearched()) {
if (mc.getMusicList().getTotal() != 0) {
if (mmc.isSameSizeMusicList() && !mmc.isSearching()) {
if (selectables.size() != mmc.size()) {
MusicSelectable selectable = new MusicSelectable(mmc.getAudioMetadata(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() {
if (selectables.selectNext()) {
scrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight());
}
}
private void scrollUp() {
if (selectables.selectPrevious()) {
scrollPane.scrollTo(selectables.getChecked().getX(), selectables.getChecked().getY(), selectables.getChecked().getWidth(), selectables.getChecked().getHeight());
}
}
public FileHandle getSelectedMusic() {
return selectables.getChecked().getMetadata().getFileHandle();
}
@Override
public void dispose() {
mc.getMusicList().deleteObserver(this);
selectionLoaderThread.stop();
super.dispose();
}
@Override
public void update(Observable o, Object arg) {
if (o == mmc) {
selectionLoaderThread.start();
} else if (o == mc) {
if (selectables.getChecked() != null && mc.getMusicList().getTotal() == selectables.size() && mc.getCurrentMusicFileHandle() != selectables.getChecked().getMetadata().getFileHandle()) {
selectables.setChecked(mc.getCurrentMusicFileHandle());
}
} else if (o == mc.getMusicList()) {
if (arg == mc.getMusicList().states.LOADING) {
synchronized (this) {
vGroup.clear();
selectables.clear();
musicInfoTable.setToDefault();
selectionLoaderThread.clear();
}
}
}
}
@Override
public void setCameraPositionToPage(Vector3 cameraPosition) {
getStage().setKeyboardFocus(this);
getStage().setScrollFocus(scrollPane);
super.setCameraPositionToPage(cameraPosition);
}
private class musicSelectionLoaderThread implements Runnable {
private Thread thread;
private String name = "Music-Selection-Loader-Thread";
private volatile boolean work = true;
private LinkedBlockingQueue<MusicSelectable> queue;
public musicSelectionLoaderThread() {
queue = new LinkedBlockingQueue<>();
}
@Override
public void run() {
while (work) {
try {
MusicSelectable selectable = queue.take();
if (!selectable.isOffScreen()) {
selectable.getMetadata().loadAlbumCover();
selectable.loadAttempted();
simpleDebug("Loaded album cover of " + selectable.getMetadata().getTitle());
} else {
simpleDebug("Skipping " + selectable.getMetadata().getTitle());
}
} catch (InterruptedException e) {
simpleDebug("Thread was interupted.");
}
}
}
public boolean start() {
if (thread == null) {
thread = new Thread(this, name);
thread.setDaemon(true);
thread.start();
return true;
}
return false;
}
public void stop() {
clear();
if (thread != null) {
thread.interrupt();
}
work = false;
}
public void clear() {
queue.clear();
}
public void queue(MusicSelectable selectable) {
queue.add(selectable);
}
}
private class MusicSelectable extends Button {
private Vector2 actualCoords;
private Image albumCoverImage;
private Table informationTable;
private Label name, artist;
private Label time;
private float timeSinceChanged;
private AudioMetadata metadata;
private Texture defaultAlbumArt;
private TextureRegion albumArtTexture;
private volatile boolean offScreen, albumArtDisplayed, albumArtQueued, noReset;
public MusicSelectable(AudioMetadata metadata) {
super(skin, "selectable-button");
this.metadata = metadata;
this.defaultAlbumArt = assets.get("defaultCover.png");
albumArtTexture = new TextureRegion(defaultAlbumArt);
albumCoverImage = new Image();
updateAlbumArtImage(defaultAlbumArt);
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() - getStage().getHeight()*0.5f || actualCoords.y > getStage().getHeight()*1.5f) && selectables.getChecked() != this) {
offScreenAct(delta);
} else {
onScreenAct(delta);
}
super.act(delta);
}
@Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
}
public void onScreenAct(float delta) {
if (offScreen) {
offScreen = false;
timeSinceChanged = 0;
} else if (timeSinceChanged < 0.05f) {
timeSinceChanged += delta;
} else {
if (!frameUsed && metadata.getAlbumCover() != null && !albumArtDisplayed) {
updateAlbumArtImage(metadata.getAlbumCover());
albumArtDisplayed = true;
frameUsed = true;
} else if (!albumArtQueued) {
selectionLoaderThread.queue(this);
albumArtQueued = 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 (!offScreen) {
offScreen = true;
timeSinceChanged = 0;
}
if (metadata.getAlbumCover() != null) {
timeSinceChanged += delta;
if (timeSinceChanged >= 2) {
updateAlbumArtImage(defaultAlbumArt);
metadata.unloadAlbumCover();
albumArtDisplayed = false;
albumArtQueued = false;
noReset = 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;
}
public boolean isOffScreen() {
if (offScreen && !noReset) {
albumArtDisplayed = false;
albumArtQueued = false;
}
return offScreen;
}
public void loadAttempted() {
noReset = true;
}
}
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 boolean selectNext() {
int index = getCheckedIndex() + 1;
if (index == buttons.size) {
return false;
}
buttons.get(index).setChecked(true);
return true;
}
public boolean selectPrevious() {
int index = getCheckedIndex() - 1;
if (index == -1) {
return false;
}
buttons.get(index).setChecked(true);
return true;
}
@Override
protected boolean canCheck(MusicSelectable button, boolean newState) {
if (newState) {
musicInfoTable.setDisplayedSelectable(button);
musicSelectDelay = 0f;
}
return super.canCheck(button, newState);
}
public int size() {
return buttons.size;
}
}
private class InformationTable extends Table {
private ScrollingText songTitle;
private Label author;
private Label musicDuration;
private Label previousTop;
private Label ratedDifficulty;
private Image albumCover;
private Table subInformation;
public InformationTable(float width, float height) {
defaults().center();
setPosition(scrollPane.getWidth() + scrollPane.getX(), 0);
setSize(width, height);
subInformation = new Table(skin);
albumCover = new Image(assets.get("defaultCover.png", Texture.class));
songTitle = new ScrollingText("", null, skin, true, true, 1f/screenConfig.getTargetFramesPerSecond());
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) {
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);
}
}
}

View File

@@ -0,0 +1,163 @@
package zero1hd.rhythmbullet.desktop.screens.main;
import java.text.NumberFormat;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ProgressBar;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.graphics.ui.Page;
public class OptionsPage extends Page {
Table optionsTable;
private ProgressBar musicVolSlider;
private ProgressBar fxVolSlider;
private TextField directoryField;
private float musicSearchTimer;
private Preferences prefs;
private NumberFormat percentFormat;
public OptionsPage(MusicController musicController, Skin skin, Preferences preferences, ChangeListener backButtonListener, ChangeListener graphicsButtonListener, ChangeListener controlsButtonListener) {
super(-1, 0, "General", skin);
this.prefs = preferences;
percentFormat = NumberFormat.getPercentInstance();
percentFormat.setMaximumFractionDigits(1);
//Back button
TextButton backButton = new TextButton("Back", skin);
backButton.addListener(backButtonListener);
backButton.setWidth(backButton.getWidth() + 20);
backButton.setPosition(getWidth() - backButton.getWidth() - 10, getHeightBelowTitle() + 5);
addActor(backButton);
optionsTable = new Table();
optionsTable.defaults().space(10f);
addActor(optionsTable);
Label musicVolSliderLabel = new Label("Music Volume: ", skin);
optionsTable.add(musicVolSliderLabel);
musicVolSlider = new Slider(0, 100, 1f, false, skin);
musicVolSlider.setValue(preferences.getFloat("music vol", 100f));
optionsTable.add(musicVolSlider).minWidth(0.3f*getWidth());
final Label musicVolPercentage = new Label(percentFormat.format(musicVolSlider.getPercent()), skin);
musicVolSlider.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
musicVolPercentage.setText(percentFormat.format(musicVolSlider.getPercent()));
musicController.getCurrentMusic().setVolume(musicVolSlider.getPercent());
preferences.putFloat("music vol", musicVolSlider.getValue());
}
});
optionsTable.add(musicVolPercentage).expandX().left();
optionsTable.row();
Label fxVolSliderLabel = new Label("FX Volume: ", skin);
optionsTable.add(fxVolSliderLabel);
fxVolSlider = new Slider(0, 100, 1f, false, skin);
fxVolSlider.setValue(preferences.getFloat("fx vol", 100f));
optionsTable.add(fxVolSlider).fillX();
final Label fxVolPercentage = new Label(percentFormat.format(fxVolSlider.getPercent()), skin);
fxVolSlider.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
fxVolPercentage.setText(percentFormat.format(fxVolSlider.getPercent()));
preferences.putFloat("fx vol", fxVolSlider.getValue());
}
});
optionsTable.add(fxVolPercentage).left();
optionsTable.row();
Label musicDirectoryLabel = new Label("Music Directory: ", skin);
optionsTable.add(musicDirectoryLabel);
directoryField = new TextField(null, skin ) {
@Override
public void act(float delta) {
if (musicSearchTimer > 0) {
musicSearchTimer -= delta;
if (musicSearchTimer <= 0) {
musicController.getMusicList().setSearchPath(directoryField.getText());
musicController.getMusicList().attemptAsyncSearch(false);
}
}
super.act(delta);
}
};
directoryField.setText(preferences.getString("music dir", System.getProperty("user.home")+System.getProperty("file.separator")+"Music"));
directoryField.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
musicSearchTimer = 2;
}
});
optionsTable.add(directoryField).fillX();
optionsTable.row();
TextButton keybindSettings = new TextButton("Set Controls", skin);
keybindSettings.addListener(controlsButtonListener);
optionsTable.add(keybindSettings).colspan(2).fillX();
optionsTable.row();
TextButton graphicsSettings = new TextButton("Graphics", skin);
graphicsSettings.addListener(graphicsButtonListener);
optionsTable.add(graphicsSettings).colspan(2).fillX();
optionsTable.row();
Label fpsLabel = new Label("", skin) {
@Override
public void act(float delta) {
setText("Current FPS: " + Gdx.graphics.getFramesPerSecond());
super.act(delta);
}
};
optionsTable.add(fpsLabel).colspan(2);
optionsTable.row();
Label usageLabel = new Label("Current Usage (JVM): ", skin) {
float refreshTime = 20;
@Override
public void act(float delta) {
refreshTime -= delta;
if (refreshTime <= 0) {
refreshTime = 4;
String formatted = percentFormat.format(((float)(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/(float)Runtime.getRuntime().totalMemory()));
setText("Current usage (JVM): " + formatted);
}
super.act(delta);
}
};
optionsTable.add(usageLabel).colspan(2);
optionsTable.pack();
optionsTable.setX((getWidth()-optionsTable.getWidth())/2f);
optionsTable.setSize(getWidth() - optionsTable.getX(), backButton.getY());
}
public void saveOptions() {
prefs.putString("music dir", directoryField.getText());
Gdx.app.debug("Preferences", "Saved all basic options page values.");
}
@Override
public void dispose() {
saveOptions();
super.dispose();
}
}