Restructuring starting from the launcher up to obtaining PCM data from

the audio backend is complete but untested.
This commit is contained in:
Harrison Deng 2018-07-22 12:35:08 -05:00
parent 7782a6a44b
commit 0dce05050a
33 changed files with 1114 additions and 908 deletions

View File

@ -0,0 +1,45 @@
package zero1hd.rhythmbullet;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.utils.Disposable;
public interface AssetPack extends Disposable {
/**
* Called right after the game instance is created and passed to LWJGL. This method is called once for you to instantiate things for later use but require Libgdx functions.
*/
public void initiateResources();
/**
* Game manager calls this when it needs to load textures.
*/
public void queueTextures(AssetManager assetManager);
/**
* Game manager calls this when it needs to load sound effects.
*/
public void queueSFX(AssetManager assetManager);
/**
* Game manager calls this when it needs to load particles.
*/
public void queueParticles(AssetManager assetManager);
/**
* Game manager calls when it needs to load particles. Usually called after textures are loaded since the skin requires the other assets.
* @param skin the skin object to set up.
*/
public void setupSkin(Skin skin);
/**
* Game manager calls when it needs the fonts to be generated. Usually called right before setting up the skin itself since items in the skin need fonts.
*/
public void generateFonts(Skin skin);
/**
* Game manager calls this once all assets are loaded. This function should be used to make some in-code adjustments to assets that will be consistent throughout the game for that run.
* @param assetManager gives you access to the assets to modify.
*/
public void complete(AssetManager assetManager);
}

View File

@ -0,0 +1,7 @@
package zero1hd.rhythmbullet;
import com.badlogic.gdx.Screen;
public interface InitialScreen {
public Screen createMainScreen(RhythmBullet game);
}

View File

@ -13,28 +13,14 @@ import com.badlogic.gdx.assets.loaders.TextureLoader;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.assets.loaders.resolvers.ResolutionFileResolver.Resolution;
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.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.ScrollPane.ScrollPaneStyle;
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox.SelectBoxStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
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.GenericFileTypeHandler;
import zero1hd.rhythmbullet.util.RoundingResolutionHandler;
import zero1hd.rhythmbullet.util.AdvancedResizeScreen;
import zero1hd.rhythmbullet.util.ResizeReadyScreen;
public class RhythmBullet extends Game {
@ -42,34 +28,37 @@ public class RhythmBullet extends Game {
public static final int WORLD_HEIGHT = 48;
public static final int SPAWN_CIRCLE_RADIUS = 6;
public static int pixels_per_unit;
private boolean initComplete = false;
private boolean initiated;
private boolean resizing;
private int screenWidth, screenHeight;
public static final String VERSION = "(0.1)R1-PreAlpha";
private AssetManager assetManager = new AssetManager();
private Skin defaultSkin = new Skin();
private FreeTypeFontGenerator default_fontGenerator;
private FreeTypeFontGenerator darktech_ldr_fontGenerator;
private Skin skin;
TextureAtlas skinAtlas;
private Preferences prefs;
private RoundingResolutionHandler rRHandler;
private Screen initialScreen;
private AssetPack assetPack;
public void setInitialScreen(Screen initialScreen) {
/**
* This should be called before passed to LWJGL. Setup for system-dependent items such as UI and assets.
* @param initialScreen the first screen to go to.
* @param assetPack the asset package to be used.
*/
public void setup(Screen initialScreen, AssetPack assetPack) {
this.initialScreen = initialScreen;
this.assetPack = assetPack;
}
@Override
public void create() {
Gdx.app.setLogLevel(Application.LOG_DEBUG);
prefs = Gdx.app.getPreferences("RhythmBullet Preferences");
setScreen(initialScreen);
if (getPrefs().getBoolean("fullscreen", true)) {
Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
} else {
Gdx.graphics.setWindowedMode(getPrefs().getInteger("screen-width"), getPrefs().getInteger("screen-height"));
}
assetPack.initiateResources();
prefs = Gdx.app.getPreferences("RhythmBullet Preferences");
Resolution[] resolution = {
new Resolution(1280, 720, "1280x720"),
@ -88,57 +77,68 @@ public class RhythmBullet extends Game {
assetManager.setLoader(Texture.class, new TextureLoader(rRHandler));
assetManager.setLoader(ParticleEffect.class, new ParticleEffectLoader(genericFileFinder));
assetManager.setLoader(Sound.class, new SoundLoader(genericFileFinder));
default_fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/Gasalt-Regular.ttf"));
darktech_ldr_fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/darktech_ldr.ttf"));
getrRHandler().setResolution(getPrefs().getInteger("screen-width"), getPrefs().getInteger("screen-height"));
rRHandler.setResolution(getPrefs().getInteger("screen-width"), getPrefs().getInteger("screen-height"));
queueAssets();
setScreen(initialScreen);
screenWidth = Gdx.graphics.getWidth();
screenHeight = Gdx.graphics.getHeight();
pixels_per_unit = (int) (Float.valueOf(screenHeight)/Float.valueOf(WORLD_HEIGHT));
}
public void checkAssetQueue() {
if (!initComplete) {
if (assetManager.update()) {
generateFonts(Gdx.graphics.getHeight());
defineSkinStyles();
setInitComplete();
}
}
}
public void checkResize() {
if (resizing) {
if (assetManager.update()) {
Gdx.app.debug("Resize", "Post transition is happening");
resizing = false;
generateFonts(Gdx.graphics.getHeight());
defineSkinStyles();
assetManager.get("standard_thrust.p", ParticleEffect.class).flipY();
((AdvancedResizeScreen) getScreen()).postAssetLoad();
}
if (getPrefs().getBoolean("fullscreen", true)) {
Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
} else {
Gdx.graphics.setWindowedMode(getPrefs().getInteger("screen-width"), getPrefs().getInteger("screen-height"));
}
}
@Override
public void render() {
checkResize();
checkAssetQueue();
super.render();
}
public boolean checkAssetQueue() {
if (assetManager.update()) {
if (skin != null) skin.dispose();
skin = new Skin();
skinAtlas = assetManager.get("uiskin.atlas", TextureAtlas.class);
getSkinSkin().addRegions(skinAtlas);
assetPack.generateFonts(skin);
assetPack.setupSkin(skin);
assetPack.complete(assetManager);
if (resizing) {
Gdx.app.debug("Resize", "Post resize is starting...");
if (getScreen() instanceof ResizeReadyScreen) {
((ResizeReadyScreen) getScreen()).postAssetLoad();
} else {
throw new IllegalStateException("Cannot perform window resize on a screen that isn't using a resize ready screen.");
}
Gdx.app.debug("Resize", "Post resize has ended.");
if (!initiated) {
setScreen(((InitialScreen) initialScreen).createMainScreen(this));
initiated = true;
}
resizing = false;
}
return true;
}
return false;
}
@Override
public void setScreen(Screen screen) {
if (screen instanceof AdvancedResizeScreen) {
AdvancedResizeScreen advancedResizeScreen = (AdvancedResizeScreen) screen;
if (screen instanceof ResizeReadyScreen) {
ResizeReadyScreen advancedResizeScreen = (ResizeReadyScreen) screen;
try {
advancedResizeScreen.preAssetLoad();
} catch (NullPointerException cleanScreen) {
//Tried to perform pre-asset reload, but had uninitialized objects, meaning this is a new screen, or "clean" screen.
} finally {
advancedResizeScreen.postAssetLoad();
}
@ -146,235 +146,61 @@ public class RhythmBullet extends Game {
super.setScreen(screen);
}
@Override
public void dispose() {
Gdx.app.debug("Core", "disposing...");
if (initComplete) {
skinAtlas.dispose();
getDefaultSkin().dispose();
default_fontGenerator.dispose();
darktech_ldr_fontGenerator.dispose();
assetManager.dispose();
getScreen().dispose();
}
super.dispose();
}
@Override
public void resize(int width, int height) {
Gdx.app.debug("RhythmBullet/resize", "Previous size:" + screenWidth + "x" + screenHeight + " new size: " + width + "x" + height);
Gdx.app.debug("RhythmBullet/resize", "Current size:" + screenWidth + "x" + screenHeight + " new size: " + width + "x" + height);
if (width != screenWidth || height != screenHeight) {
screenWidth = Gdx.graphics.getWidth();
screenHeight = Gdx.graphics.getHeight();
pixels_per_unit = (int) (Float.valueOf(screenHeight)/Float.valueOf(WORLD_HEIGHT));
if (initComplete) {
Gdx.app.debug("Resize", "Pre-transition is happening. Using resolution " + width + "x" + height);
Gdx.app.debug("Resize", "Pre-resize is happening. Resizing to " + width + "x" + height);
rRHandler.setResolution(width, height);
((AdvancedResizeScreen) getScreen()).preAssetLoad();
assetManager.clear();
if (getScreen() instanceof ResizeReadyScreen) {
((ResizeReadyScreen) getScreen()).preAssetLoad();
} else {
throw new IllegalStateException("Cannot perform window resize on a screen that isn't using a resize ready screen.");
}
prefs.putInteger("screen-width", width);
prefs.putInteger("screen-height", height);
prefs.flush();
resizing = true;
assetManager.clear();
queueAssets();
}
}
super.resize(width, height);
}
public int fontScale(float fontSize, int height) {
int size = MathUtils.round(Gdx.graphics.getDensity()*(fontSize*height));
if (size >= 200) {
size = 200;
}
return size;
public void queueAssets() {
assetPack.queueTextures(assetManager);
assetPack.queueSFX(assetManager);
assetPack.queueParticles(assetManager);
}
public AssetManager getAssetManager() {
return assetManager;
}
public Skin getDefaultSkin() {
return defaultSkin;
public Skin getSkinSkin() {
return skin;
}
public Preferences getPrefs() {
return prefs;
}
public void setInitComplete() {
initComplete = true;
@Override
public void dispose() {
Gdx.app.debug("Core", "disposing...");
try {
skinAtlas.dispose();
getSkinSkin().dispose();
assetManager.dispose();
getScreen().dispose();
assetPack.dispose();
} catch (NullPointerException npe) {
//Means the game was closed before everything was initiated.
}
public boolean isInitComplete() {
return initComplete;
}
public RoundingResolutionHandler getrRHandler() {
return rRHandler;
}
public void queueAssets() {
assetManager.load("uiskin.atlas", TextureAtlas.class);
assetManager.load("Tech-Circle1.png", Texture.class);
assetManager.load("polyjet-standard.png", Texture.class);
assetManager.load("standard_thrust.p", ParticleEffect.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("teleport-cloak.p", ParticleEffect.class);
assetManager.load("pop_open.ogg", Sound.class);
assetManager.load("pop_close.ogg", Sound.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("laser.ogg", Sound.class);
assetManager.load("explosion.ogg", Sound.class);
assetManager.load("disintegrate.ogg", Sound.class);
assetManager.load("explosion-s.p", ParticleEffect.class);
assetManager.load("beateffect.p", ParticleEffect.class);
assetManager.load("tpSelector.png", Texture.class);
assetManager.load("magic1.png", Texture.class);
assetManager.load("backgrounds/mainBG.png", Texture.class);
}
public void generateFonts(final int height) {
defaultSkin = new Skin();
Gdx.app.debug("Prelaunch Debug Info", "Generating fonts with screen height of " + height);
skinAtlas = assetManager.get("uiskin.atlas", TextureAtlas.class);
getDefaultSkin().addRegions(skinAtlas);
getDefaultSkin().add("window-font", default_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = 18;
}
}));
getDefaultSkin().add("sub-font", default_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = fontScale(0.05f, height);
}
}));
getDefaultSkin().add("default-font", default_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = fontScale(0.07f, height);
}
}));
getDefaultSkin().add("large-font", default_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = fontScale(0.085f, height);
}
}));
getDefaultSkin().add("special-font", darktech_ldr_fontGenerator.generateFont(new FreeTypeFontParameter() {
{
size = fontScale(0.075f, height);
}
}));
}
public void defineSkinStyles() {
getDefaultSkin().add("default", Color.WHITE);
getDefaultSkin().add("inverse", Color.BLACK);
TextButtonStyle defaultTextButton = new TextButtonStyle();
defaultTextButton.up = getDefaultSkin().getDrawable("rect");
defaultTextButton.down = getDefaultSkin().getDrawable("rect-down");
defaultTextButton.font = getDefaultSkin().getFont("default-font");
defaultTextButton.fontColor = getDefaultSkin().getColor("default");
defaultTextButton.disabled = getDefaultSkin().getDrawable("rect-disabled");
getDefaultSkin().add("default", defaultTextButton);
TextButtonStyle subTextbutton = new TextButtonStyle(defaultTextButton);
subTextbutton.font = getDefaultSkin().getFont("sub-font");
getDefaultSkin().add("sub", subTextbutton);
TextButtonStyle windowTextButton = new TextButtonStyle(defaultTextButton);
windowTextButton.font = getDefaultSkin().getFont("window-font");
getDefaultSkin().add("window", windowTextButton);
TextButtonStyle textButtonLeft = new TextButtonStyle();
textButtonLeft.up = getDefaultSkin().getDrawable("left-button");
textButtonLeft.down = getDefaultSkin().getDrawable("left-button-down");
textButtonLeft.font = getDefaultSkin().getFont("default-font");
textButtonLeft.fontColor = getDefaultSkin().getColor("default");
getDefaultSkin().add("left", textButtonLeft);
SliderStyle defaultSlider = new SliderStyle(getDefaultSkin().getDrawable("default-slider"), getDefaultSkin().getDrawable("default-slider-knob"));
getDefaultSkin().add("default-horizontal", defaultSlider);
SliderStyle vertSlider = new SliderStyle(defaultSlider);
vertSlider.knob = getDefaultSkin().getDrawable("vertical-slider-knob");
getDefaultSkin().add("default-vertical", vertSlider);
LabelStyle defaultLabel = new LabelStyle();
defaultLabel.font = getDefaultSkin().getFont("default-font");
defaultLabel.fontColor = getDefaultSkin().getColor("default");
getDefaultSkin().add("default", defaultLabel);
TextFieldStyle defaultTextField = new TextFieldStyle(getDefaultSkin().getFont("sub-font"), getDefaultSkin().getColor("default"), getDefaultSkin().getDrawable("cursor"), getDefaultSkin().getDrawable("selection"), getDefaultSkin().getDrawable("textfield"));
getDefaultSkin().add("default", defaultTextField);
TextFieldStyle uiTextField = new TextFieldStyle(defaultTextField);
uiTextField.font = getDefaultSkin().getFont("window-font");
getDefaultSkin().add("ui", uiTextField);
WindowStyle defaultWindow = new WindowStyle(getDefaultSkin().getFont("window-font"), getDefaultSkin().getColor("default"), getDefaultSkin().getDrawable("default-window"));
getDefaultSkin().add("default", defaultWindow);
WindowStyle tintedWindow = new WindowStyle(defaultWindow);
tintedWindow.titleFontColor = getDefaultSkin().getColor("inverse");
tintedWindow.background = getDefaultSkin().getDrawable("tinted-window");
getDefaultSkin().add("tinted", tintedWindow);
ListStyle defaultList = new ListStyle(getDefaultSkin().getFont("window-font"), getDefaultSkin().getColor("inverse"), getDefaultSkin().getColor("default"), getDefaultSkin().getDrawable("selection"));
getDefaultSkin().add("default", defaultList);
ScrollPaneStyle defaultScrollPane = new ScrollPaneStyle();
defaultScrollPane.vScroll = getDefaultSkin().getDrawable("default-scroll");
defaultScrollPane.hScrollKnob = getDefaultSkin().getDrawable("default-round-large");
defaultScrollPane.hScroll = getDefaultSkin().getDrawable("default-scroll");
defaultScrollPane.vScrollKnob = getDefaultSkin().getDrawable("default-round-large");
getDefaultSkin().add("default", defaultScrollPane);
CheckBoxStyle defaultCheckBox = new CheckBoxStyle(getDefaultSkin().getDrawable("check-off"), getDefaultSkin().getDrawable("check-on"), getDefaultSkin().getFont("window-font"), getDefaultSkin().getColor("default"));
defaultCheckBox.checkboxOffDisabled = getDefaultSkin().getDrawable("check-disabled");
getDefaultSkin().add("default", defaultCheckBox);
SelectBoxStyle defaultSelectBox = new SelectBoxStyle(getDefaultSkin().getFont("default-font"), getDefaultSkin().getColor("default"), getDefaultSkin().getDrawable("default-select"), defaultScrollPane, defaultList);
getDefaultSkin().add("default", defaultSelectBox);
Gdx.app.debug("Prelaunch Debug Info", "UI Skin has been defined.");
CheckBoxStyle playButtonStyle = new CheckBoxStyle(defaultCheckBox);
playButtonStyle.checkboxOn = getDefaultSkin().getDrawable("play-down");
playButtonStyle.checkboxOff = getDefaultSkin().getDrawable("play");
getDefaultSkin().add("play-button", playButtonStyle);
ImageButtonStyle pauseButtonStyle = new ImageButtonStyle();
pauseButtonStyle.down = getDefaultSkin().getDrawable("pause-down");
pauseButtonStyle.up = getDefaultSkin().getDrawable("pause");
getDefaultSkin().add("pause-button", pauseButtonStyle);
ImageButtonStyle fastForwardButtonStyle = new ImageButtonStyle();
fastForwardButtonStyle.down = getDefaultSkin().getDrawable("fast-forward-down");
fastForwardButtonStyle.up = getDefaultSkin().getDrawable("fast-forward");
getDefaultSkin().add("fast-forward-button", fastForwardButtonStyle);
ImageButtonStyle reverseButtonStyle = new ImageButtonStyle();
reverseButtonStyle.down = getDefaultSkin().getDrawable("rewind-down");
reverseButtonStyle.up = getDefaultSkin().getDrawable("rewind");
getDefaultSkin().add("rewind-button", reverseButtonStyle);
CheckBoxStyle shuffleButtonStyle = new CheckBoxStyle(defaultCheckBox);
shuffleButtonStyle.checkboxOff = getDefaultSkin().getDrawable("shuffle");
shuffleButtonStyle.checkboxOn = getDefaultSkin().getDrawable("shuffle-down");
getDefaultSkin().add("shuffle-button", shuffleButtonStyle);
super.dispose();
}
}

View File

@ -0,0 +1,12 @@
package zero1hd.rhythmbullet.audio;
import com.badlogic.gdx.files.FileHandle;
import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
public interface AudioProcessorFactory {
/**
* @return a new {@link #zero1hd.rhythmbullet.audio.processor.AudioProcessor()} from the appropriate platform.
*/
public AudioProcessor newMP3AudioProcessor(FileHandle fileHandle);
}

View File

@ -0,0 +1,37 @@
package zero1hd.rhythmbullet.audio;
import java.io.IOException;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.AudioHeader;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.tag.TagException;
import com.badlogic.gdx.files.FileHandle;
public class MinimalAudioHeader {
private int sampleRate, channelCount;
public MinimalAudioHeader(FileHandle musicFile) {
try {
AudioFile file = AudioFileIO.read(musicFile.file());
AudioHeader header = file.getAudioHeader();
sampleRate = header.getSampleRateAsNumber();
channelCount = (header.getChannels().equals("Mono") ? 1 : 2);
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException
| InvalidAudioFrameException e) {
e.printStackTrace();
}
}
public int getSampleRate() {
return sampleRate;
}
public int getChannelCount() {
return channelCount;
}
}

View File

@ -0,0 +1,223 @@
package zero1hd.rhythmbullet.audio;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Music.OnCompletionListener;
import com.badlogic.gdx.files.FileHandle;
/**
* Manages current games music playback and does this in tandem with the {@link MusicList} by asking to retrieve files and then feeding it to LibGDX.
* Notifies observers when a new song is loaded.
* The loading model is like taking a disk and loading it into a player. It doesn't necessarily mean it'll play right away, but its ready and the only track that has a stream opened.
* @author yunya
*
*/
public class MusicController extends Observable implements OnCompletionListener, Observer {
public enum States {
Loaded, Playing;
}
private MusicList musicList;
private MinimalAudioHeader musicHeader;
private Music music;
private int currentPlaybackIndex;
private boolean autoPlay;
private boolean shuffle;
private Random rand;
private Preferences prefs;
public MusicController(MusicList musicList, Preferences prefs) {
if (prefs == null) throw new NullPointerException("preferences can't be null...");
if (musicList == null) throw new NullPointerException("music list can't be null...");
musicList.addObserver(this);
this.prefs = prefs;
this.musicList = musicList;
rand = new Random();
}
/**
* This play method automatically sets the volume.
*/
public void play() {
if (music != null) {
Gdx.app.debug("MusicListController", "Playing from MLC.");
music.play();
music.setVolume(prefs.getFloat("music vol", 1f));
notifyObservers(States.Playing);
} else {
Gdx.app.debug("MusicListController", "failed to begin playing. Load the music!!!");
}
}
/**
* Called to pause current song. Does nothing if no song is playing or loaded.
*/
public void pause() {
if (music != null) {
music.pause();
}
}
/**
* Loads music based on the index in the {@link MusicList}.
* @param index of music to play
*/
public void setMusicByIndex(int index) {
this.currentPlaybackIndex = index;
loadMusic();
}
/**
* Goes to the next track
*/
public void skip() {
currentPlaybackIndex++;
if (shuffle) {
shuffle(false);
}
loadMusic();
}
/**
* Goes to the previous track
*/
public void previous() {
currentPlaybackIndex--;
if (shuffle) {
shuffle(false);
}
loadMusic();
}
@Override
public void onCompletion(Music music) {
if (autoPlay) {
if (shuffle) {
shuffle(false);
} else {
currentPlaybackIndex++;
}
loadMusic();
play();
}
}
/**
* 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) {
Gdx.app.debug("MusicListController", "shuffled.");
if (musicList.getTotal() == 0) {
currentPlaybackIndex = 0;
} else {
currentPlaybackIndex = rand.nextInt(musicList.getTotal());
}
if (load) {
loadMusic();
}
}
public void setAutoPlay(boolean autoPlay) {
this.autoPlay = autoPlay;
}
public void setShuffle(boolean shuffle) {
this.shuffle = shuffle;
}
public boolean isShuffle() {
return shuffle;
}
public boolean isAutoPlay() {
return autoPlay;
}
/**
* Loads the current selected song.
*/
public void loadMusic() {
Gdx.app.debug("MusicListController", "music is being loaded and listeners are being notified.");
musicHeader = null;
if (music != null) {
music.dispose();
}
if (currentPlaybackIndex < 0) {
currentPlaybackIndex = musicList.getTotal()-1;
}
if (currentPlaybackIndex >= musicList.getTotal()) {
currentPlaybackIndex = 0;
}
this.music = Gdx.audio.newMusic(musicList.getMusicArray().get(currentPlaybackIndex));
music.setOnCompletionListener(this);
setChanged();
if (autoPlay) {
play();
}
notifyObservers(States.Loaded);
}
public MusicList getMusicList() {
return musicList;
}
public FileHandle getCurrentMusicFileHandle() {
return musicList.getSongFileHandleFromIndex(currentPlaybackIndex);
}
public MinimalAudioHeader getCurrentMusicHeader() {
if (musicHeader != null) {
return musicHeader;
} else {
return musicList.newMinimalAudioHeader(getCurrentMusicFileHandle());
}
}
@Override
public void update(Observable o, Object arg) {
if (o == musicList) {
loadMusic();
if (autoPlay) {
play();
}
}
}
public String getCurrentSongName() {
return getCurrentMusicFileHandle().nameWithoutExtension();
}
public float getCurrentPosition() {
if (music != null) {
return music.getPosition();
}
return 0;
}
public void setMusicPosition(float position) {
if (music != null) {
music.setPosition(position);
}
}
public boolean isPlaying() {
return music.isPlaying();
}
/**
* Returns the current music. In no circumstances should this be used to begin playing or it would be playing independent of the controller.
* Doing otherwise would mean you are playing the music separately of the controller.
* @return
*/
public Music getCurrentMusic() {
return music;
}
}

View File

@ -0,0 +1,135 @@
package zero1hd.rhythmbullet.audio;
import java.util.Observable;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
import zero1hd.rhythmbullet.audio.processor.WAVAudioProcessor;
/**
* A music list made to store paths to all the songs within a given directory. Can activate the music calling {@link #newAudioProcessor(FileHandle)} and its derivatives.
* Is observable, meaning, given there are observers, will notify when the list completes a refresh.
* @author yunya
*/
public class MusicList extends Observable {
private Array<FileHandle> musicList;
private RecursiveMusicSearchThread searchThread;
private AudioProcessorFactory audioProcFactory;
private boolean searched;
public MusicList(AudioProcessorFactory audioProcessorFactory) {
this.audioProcFactory = audioProcessorFactory;
musicList = new Array<>();
searchThread = new RecursiveMusicSearchThread("Music Search Thread");
}
/**
* Wrapper method that uses async refresh.
* Also notifies listeners that are on the main thread.
*/
public void asyncSearch() {
searchThread.start();
}
public void setSearchPath(String searchPath) {
searchThread.setSearchDirectory(Gdx.files.absolute(searchPath));
setChanged();
}
/**
* @param file
* @return a {@link #zero1hd.rhythmbullet.audio.processor.AudioProcessor()} of the given music file. Will return null if theres a format error.
*/
public AudioProcessor newAudioProcessor(FileHandle file) {
if (file.extension().equalsIgnoreCase("wav")) {
return new WAVAudioProcessor(file);
} else if (file.extension().equalsIgnoreCase("mp3")) {
return audioProcFactory.newMP3AudioProcessor(file);
}
return null;
}
/**
*
* @param file the music file that you need the header for.
* @return the header containing minimal info of the song.
*/
public MinimalAudioHeader newMinimalAudioHeader(FileHandle file) {
return new MinimalAudioHeader(file);
}
public AudioProcessor newAudioProcessorFromIndex(int index) {
if (!searched) Gdx.app.debug("SongList", "Warning, this list hasn't even searched yet...");
return newAudioProcessor(musicList.get(index));
}
public FileHandle getSongFileHandleFromIndex(int index) {
if (!searched) Gdx.app.debug("SongList", "Warning, this list hasn't even searched yet...");
return musicList.get(index);
}
public Array<FileHandle> getMusicArray() {
return musicList;
}
public boolean isSearched() {
return searched;
}
private void searchComplete() {
notifyObservers();
searched = true;
}
public int getTotal() {
return musicList.size;
}
private class RecursiveMusicSearchThread implements Runnable {
private Thread thread;
private String threadName;
private FileHandle directory;
public RecursiveMusicSearchThread(String name) {
this.threadName = name;
}
public void setSearchDirectory(FileHandle directory) {
this.directory = directory;
}
@Override
public void run() {
musicList = recursiveMusicSearch(directory);
searchComplete();
}
public void start() {
if (thread == null && !thread.isAlive()) {
thread = new Thread(this, threadName);
thread.start();
} else {
throw new IllegalStateException("Two " + threadName + " instances (threads) were created. This is not allowed for optimization as there is no reason to have two running.");
}
}
private Array<FileHandle> recursiveMusicSearch(FileHandle fileHandle) {
Array<FileHandle> musicFiles = new Array<>();
FileHandle[] files = fileHandle.list();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
musicFiles.addAll(recursiveMusicSearch(files[i]));
} else {
if (files[i].extension().equalsIgnoreCase("wav") || files[i].extension().equalsIgnoreCase("mp3")) {
musicFiles.add(files[i]);
}
}
}
return musicFiles;
}
}
}

View File

@ -0,0 +1,102 @@
package zero1hd.rhythmbullet.audio;
import java.util.Observable;
import java.util.Observer;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import zero1hd.rhythmbullet.audio.metadata.AudioMetadata;
import zero1hd.rhythmbullet.audio.metadata.MP3Metadata;
import zero1hd.rhythmbullet.audio.metadata.WAVMetadata;
public class MusicMetadataController implements Disposable, Observer {
private MusicList musicList;
private Array<AudioMetadata> metadataArray;
private MetadataLoadingThread loadingThread;
private volatile boolean searching;
public MusicMetadataController(MusicList musicList) {
this.musicList = musicList;
metadataArray = new Array<>();
}
public MusicList getMusicList() {
return musicList;
}
/**
* Non-blocking, loads on separate thread.
*/
public void loadSongInfo() {
loadingThread.start();
}
public boolean isDone() {
return (metadataArray.size == musicList.getMusicArray().size);
}
@Override
public void dispose() {
for (int i = 0; i < metadataArray.size; i++) {
metadataArray.get(i).dispose();
}
}
public int size() {
return metadataArray.size;
}
public AudioMetadata getMetadata(int index) {
synchronized (loadingThread) {
return metadataArray.get(index);
}
}
public AudioMetadata getInfo(FileHandle filehandle) {
return metadataArray.get(musicList.getMusicArray().indexOf(filehandle, true));
}
public boolean isSearching() {
return searching;
}
private class MetadataLoadingThread implements Runnable {
private Thread thread;
private String name = "Metadata-Load";
@Override
public void run() {
searching = true;
for (int i = 0; i < metadataArray.size; i++) {
metadataArray.get(i).dispose();
}
metadataArray.clear();
for (int i = 0; i < musicList.getTotal(); i++) {
FileHandle musicFile = musicList.getMusicArray().get(i);
synchronized (this) {
if (musicFile.extension().equalsIgnoreCase("wav")) {
metadataArray.add(new WAVMetadata(musicFile));
} else if (musicFile.extension().equalsIgnoreCase("mp3")) {
metadataArray.add(new MP3Metadata(musicFile));
}
}
}
searching = false;
}
public void start() {
if (thread != null && !thread.isAlive()) {
thread = new Thread(this, name);
thread.start();
}
}
}
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
}
}

View File

@ -1,29 +0,0 @@
package zero1hd.rhythmbullet.audio;
public class RhythmBulletMetadata {
private String highScore = "0", lastPlayed = "N/A", difficulty = "N/A";
public void setHighScore(String highScore) {
this.highScore = highScore;
}
public void setLastPlayed(String lastPlayed) {
this.lastPlayed = lastPlayed;
}
public void setDifficulty(String difficulty) {
this.difficulty = difficulty;
}
public String getHighScore() {
return highScore;
}
public String getDifficulty() {
return difficulty;
}
public String getLastPlayed() {
return lastPlayed;
}
}

View File

@ -4,7 +4,7 @@
*
*/
package zero1hd.rhythmbullet.audio;
package zero1hd.rhythmbullet.audio.metadata;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Texture;

View File

@ -1,4 +1,4 @@
package zero1hd.rhythmbullet.audio;
package zero1hd.rhythmbullet.audio.metadata;
import java.io.IOException;

View File

@ -1,4 +1,4 @@
package zero1hd.rhythmbullet.audio;
package zero1hd.rhythmbullet.audio.metadata;
import java.io.IOException;

View File

@ -7,6 +7,7 @@ import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
public class WAVAudioProcessor implements AudioProcessor {
@ -17,11 +18,18 @@ public class WAVAudioProcessor implements AudioProcessor {
private AudioInputStream audioInputStream;
private boolean initiated;
public WAVAudioProcessor(FileHandle fileHandle, int windowSize) throws IOException, UnsupportedAudioFileException {
public WAVAudioProcessor(FileHandle fileHandle) {
this.fileHandle = fileHandle;
AudioFormat format = AudioSystem.getAudioFileFormat(fileHandle.file()).getFormat();
AudioFormat format;
try {
format = AudioSystem.getAudioFileFormat(fileHandle.file()).getFormat();
stereo = format.getChannels() > 1 ? true : false;
sampleRate = (int) format.getSampleRate();
} catch (UnsupportedAudioFileException | IOException e) {
Gdx.app.debug("WAVAudioProcessor", "Couldn't instantiate WAVAUdioProcessor due to error.");
e.printStackTrace();
}
}
@Override

View File

@ -12,7 +12,6 @@ import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import zero1hd.rhythmbullet.audio.MusicManager;
public class HorizontalVisualizer implements Disposable {
private Pixmap pixmap;

View File

@ -1,6 +1,6 @@
package zero1hd.rhythmbullet.util;
public interface AdvancedResizeScreen {
public interface ResizeReadyScreen {
/**
* called before assets are cleared from memory.
*/

View File

@ -0,0 +1,224 @@
package zero1hd.rhythmbullet.desktop;
import com.badlogic.gdx.Gdx;
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.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.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.AssetPack;
public class DesktopAssetPack implements AssetPack {
private FreeTypeFontGenerator default_fontGenerator;
private FreeTypeFontGenerator darktech_ldr_fontGenerator;
@Override
public void initiateResources() {
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);
TextButtonStyle textButtonLeft = new TextButtonStyle();
textButtonLeft.up = skin.getDrawable("left-button");
textButtonLeft.down = skin.getDrawable("left-button-down");
textButtonLeft.font = skin.getFont("default-font");
textButtonLeft.fontColor = skin.getColor("default");
skin.add("left", textButtonLeft);
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);
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);
}
@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();
}
@Override
public void dispose() {
darktech_ldr_fontGenerator.dispose();
default_fontGenerator.dispose();
}
}

View File

@ -1,8 +1,5 @@
package zero1hd.rhythmbullet.desktop;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
@ -11,16 +8,15 @@ import zero1hd.rhythmbullet.desktop.screens.SplashScreen;
public class DesktopLauncher {
public static void main (String[] arg) {
Logger.getLogger("org.jaudiotagger").setLevel(Level.OFF);
RhythmBullet core;
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = "Rhythm Bullet";
config.resizable = false;
config.allowSoftwareMode = true;
config.useHDPI = true;
core = new RhythmBullet();
core.setInitialScreen(new SplashScreen(core));
core.setup(new SplashScreen(), new DesktopAssetPack());
new LwjglApplication(core, config);

View File

@ -1,64 +0,0 @@
package zero1hd.rhythmbullet.desktop.audio;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import zero1hd.rhythmbullet.audio.MusicInfo;
public class MusicInfoController implements Disposable {
private MusicList musicList;
private ExecutorService exec;
private Array<MusicInfo> songInfoArray;
private Preferences musicAnnotation;
public MusicInfoController(MusicList musicList) {
this.musicList = musicList;
songInfoArray = new Array<>();
exec = Executors.newSingleThreadExecutor();
musicAnnotation = Gdx.app.getPreferences("MusicAnnotation");
}
public MusicList getMusicList() {
return musicList;
}
/**
* Non-blocking, loads on separate thread.
*/
public void loadSongInfo() {
for (int i = 0; i < songInfoArray.size; i++) {
songInfoArray.get(i).dispose();
}
songInfoArray.clear();
exec.submit(() -> {
for (int i = 0; i < musicList.getTotal(); i++) {
MusicInfo musicInfo = new MusicInfo(musicList.getMusicArray().get(i), musicAnnotation);
musicInfo.loadInfo();
songInfoArray.add(musicInfo);
}
});
}
public boolean isDone() {
return (songInfoArray.size == musicList.getMusicArray().size);
}
@Override
public void dispose() {
exec.shutdown();
}
public Array<MusicInfo> getSongInfoArray() {
return songInfoArray;
}
public MusicInfo getInfo(FileHandle filehandle) {
return songInfoArray.get(musicList.getMusicArray().indexOf(filehandle, true));
}
}

View File

@ -1,129 +0,0 @@
package zero1hd.rhythmbullet.desktop.audio;
import java.util.Comparator;
import java.util.Observable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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.MusicManager;
import zero1hd.rhythmbullet.desktop.audio.processor.MP3AudioProcessor;
public class MusicList extends Observable {
private Array<FileHandle> musicArray;
private String searchPath;
private boolean searched;
private ExecutorService exec;
public MusicList() {
musicArray = new Array<>();
exec = Executors.newSingleThreadExecutor();
}
private Array<FileHandle> recursiveMusicFileList(FileHandle fileHandle) {
Array<FileHandle> musicFiles = new Array<>();
FileHandle[] files = fileHandle.list();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
musicFiles.addAll(recursiveMusicFileList(files[i]));
} else {
if (files[i].extension().equalsIgnoreCase("wav") || files[i].extension().equalsIgnoreCase("mp3")) {
musicFiles.add(files[i]);
}
}
}
return musicFiles;
}
/**
* refreshes song list, notifies any observers of the refresh while passing no argument to the notifier.
* Blocking.
*/
public void refresh(boolean notifyOnCompletion) {
searched = false;
musicArray.clear();
Gdx.app.debug("SongController", "Searching path: " + searchPath);
if (Gdx.files.absolute(searchPath).exists() && Gdx.files.absolute(searchPath).isDirectory()) {
musicArray.addAll(recursiveMusicFileList(Gdx.files.absolute(searchPath)));
}
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"));
}
musicArray.add(Gdx.files.external("RhythmBullet/Alan Walker - Spectre.mp3"));
setChanged();
Sort.instance().sort(musicArray, new Comparator<FileHandle>() {
@Override
public int compare(FileHandle o1, FileHandle o2) {
return o1.nameWithoutExtension().compareTo(o2.nameWithoutExtension());
}
});
searched = true;
if (notifyOnCompletion) {
notifyObservers();
}
}
/**
* Wrapper method that uses asynch refresh.
* Also notifies listeners that are on the main thread.
*/
public void asynchRefresh() {
searched = false;
exec.submit(() -> {
refresh(false);
Gdx.app.debug("Asynch-MusicList", "Async refresh done. Notification has been sent.");
Gdx.app.postRunnable(() -> {
notifyObservers();
searched = true;
});
});
}
public void setSearchPath(String searchPath) {
this.searchPath = searchPath;
setChanged();
}
/**
* creates an audio manager for the given music file.
* @param file
* @return a music manager of the given music file.
*/
public MusicManager getAudioData(FileHandle file) {
if (file.extension().equalsIgnoreCase("wav")) {
return new WAVManager(file);
} else if (file.extension().equalsIgnoreCase("mp3")) {
return new MP3AudioProcessor(file);
}
return null;
}
public MusicManager getMusicManagerFromIndex(int index) {
if (!searched) Gdx.app.debug("SongList", "Warning, this list hasn't even searched yet...");
return getAudioData(musicArray.get(index));
}
public int getTotal() {
return musicArray.size;
}
public FileHandle getSongFileHandleFromIndex(int index) {
if (!searched) Gdx.app.debug("SongList", "Warning, this list hasn't even searched yet...");
return musicArray.get(index);
}
public Array<FileHandle> getMusicArray() {
return musicArray;
}
public boolean isSearched() {
return searched;
}
}

View File

@ -1,154 +0,0 @@
package zero1hd.rhythmbullet.desktop.audio;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Music.OnCompletionListener;
import zero1hd.rhythmbullet.audio.MusicManager;
public class MusicListController extends Observable implements OnCompletionListener, Observer {
private MusicList musicList;
private MusicManager mm;
private int currentPlaybackID;
private boolean autoPlay;
private boolean shuffle;
private Random rand;
private Preferences prefs;
public MusicListController(MusicList musicList, Preferences prefs) {
if (prefs == null) throw new NullPointerException("preferences can't be null...");
if (musicList == null) throw new NullPointerException("music list can't be null...");
musicList.addObserver(this);
this.prefs = prefs;
this.musicList = musicList;
rand = new Random();
}
/**
* This play method automatically sets the volume.
*/
public void play() {
if (mm != null) {
Gdx.app.debug("MusicListController", "Playing from MLC.");
mm.play();
mm.setVolume(prefs.getFloat("music vol", 1f));
} else {
Gdx.app.debug("MusicListController", "failed to begin playing. Load the music!!!");
}
}
public void pause() {
if (mm != null) {
mm.pause();
}
}
public void setMusicByIndex(int index) {
this.currentPlaybackID = index;
loadMusic();
}
public void skip() {
currentPlaybackID++;
if (shuffle) {
shuffle(false);
}
loadMusic();
}
public void previous() {
currentPlaybackID--;
if (shuffle) {
shuffle(false);
}
loadMusic();
}
@Override
public void onCompletion(Music music) {
if (autoPlay) {
if (shuffle) {
shuffle(false);
} else {
currentPlaybackID++;
}
loadMusic();
play();
}
}
/**
* 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 elswhere.
*/
public void shuffle(boolean load) {
Gdx.app.debug("MusicListController", "shuffled.");
if (musicList.getTotal() == 0) {
currentPlaybackID = 0;
} else {
currentPlaybackID = rand.nextInt(musicList.getTotal());
}
if (load) {
loadMusic();
}
}
public void setAutoPlay(boolean autoPlay) {
this.autoPlay = autoPlay;
}
public void setShuffle(boolean shuffle) {
this.shuffle = shuffle;
}
public boolean isShuffle() {
return shuffle;
}
public boolean isAutoPlay() {
return autoPlay;
}
/**
* Loads the current selected song.
*/
public void loadMusic() {
Gdx.app.debug("MusicListController", "music is being loaded and listeners are being notified.");
if (mm != null) {
mm.dispose();
}
if (currentPlaybackID < 0) {
currentPlaybackID = musicList.getTotal()-1;
}
if (currentPlaybackID >= musicList.getTotal()) {
currentPlaybackID = 0;
}
this.mm = musicList.getMusicManagerFromIndex(currentPlaybackID);
mm.setOnCompletionListener(this);
setChanged();
notifyObservers(mm);
}
public MusicList getMusicList() {
return musicList;
}
public MusicManager getCurrentMusicManager() {
return mm;
}
@Override
public void update(Observable o, Object arg) {
if (o == musicList) {
loadMusic();
play();
}
}
}

View File

@ -1,73 +0,0 @@
package zero1hd.rhythmbullet.desktop.audio.map;
import org.apache.commons.math3.random.MersenneTwister;
import com.badlogic.gdx.utils.FloatArray;
import zero1hd.rhythmbullet.audio.AudioDataPackage;
import zero1hd.rhythmbullet.entity.EntityManager;
import zero1hd.rhythmbullet.entity.coordinator.CoordinatorManager;
import zero1hd.rhythmbullet.game.EntitySpawnInfo;
import zero1hd.rhythmbullet.game.GamePlayMap;
public class RhythmMapAlgorithm implements Runnable {
private EntityManager em;
private CoordinatorManager cm;
private FloatArray bassPeaks;
private FloatArray mPeaks;
private FloatArray umPeaks;
private MersenneTwister rand;
private GamePlayMap map;
private float avgBass;
private float avgUM;
private float avgSPB;
private float umMax, bassMax;
private float speedMod, healthMod, difficultyMod;
private float windowPerSecond;
private volatile int progress;
public RhythmMapAlgorithm(EntityManager em, CoordinatorManager cm, AudioDataPackage adp, float speedMod, float healthMod, float difficultyMod) {
this.cm = cm;
this.em = em;
bassPeaks = adp.getBassPeaks();
mPeaks = adp.getmPeaks();
umPeaks = adp.getuMPeaks();
map = new GamePlayMap(adp.getMusicInfo(), umPeaks.size);
rand = new MersenneTwister(adp.getPUID());
avgSPB = adp.getAvgSPB();
windowPerSecond = 1/adp.getSecPerWin();
this.speedMod = speedMod;
this.healthMod = healthMod;
this.difficultyMod = difficultyMod;
umMax = adp.getuMMaxval();
avgUM = adp.getuMAvg();
bassMax = adp.getBassMaxVal();
avgBass = adp.getBassAvg();
}
EntitySpawnInfo esi;
@Override
public void run() {
map.beginBuild();
//TODO Map gen algorithm
map.endBuild();
}
public synchronized GamePlayMap getMap() {
return map;
}
public synchronized int getProgress() {
return progress;
}
}

View File

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

View File

@ -4,56 +4,64 @@ import static org.lwjgl.openal.AL10.alGetSourcef;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.util.Observable;
import java.util.Observer;
import org.lwjgl.openal.AL11;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic;
import com.badlogic.gdx.graphics.g2d.Batch;
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 zero1hd.rhythmbullet.audio.visualizer.MusicManagerFFT;
import zero1hd.rhythmbullet.audio.visualizer.Visualizer;
import zero1hd.rhythmbullet.audio.MinimalAudioHeader;
import zero1hd.rhythmbullet.audio.MusicController;
public class DesktopVisualizer extends MusicManagerFFT implements Visualizer {
public class DesktopVisualizer implements Observer {
private int windowSize = 1024;
private float[] PCM = new float[windowSize];
private ShortBuffer playingBuffer;
private ShortBuffer compareBuffer;
private ShortBuffer buffer;
private int sourceID;
private int channelCount;
private int sampleRate;
private MusicController mc;
private BufferStreamReadThread streamReadThread;
private int windowsRead;
private int currentPlaybackWindow;
public DesktopVisualizer() {
super();
try {
Field bufferField = ClassReflection.getDeclaredField(OpenALMusic.class, "tempBuffer");
bufferField.setAccessible(true);
Field bufferSizeField = ClassReflection.getDeclaredField(OpenALMusic.class, "bufferSize");
bufferSizeField.setAccessible(true);
bufferSizeField.set(null, new Integer(4096*5));
buffer = ((ByteBuffer) bufferField.get(null)).asShortBuffer().asReadOnlyBuffer();
} catch (IllegalArgumentException | SecurityException | ReflectionException e) {
e.printStackTrace();
Gdx.app.debug("Visualizer reflection", "Failed attempt at retrieving tempBuffer field.");
Gdx.app.exit();
}
streamReadThread = new BufferStreamReadThread();
}
/* (non-Javadoc)
* @see zero1hd.rhythmbullet.desktop.audio.visualizer.Visualizer#calcPCMData()
*/
@Override
public void calcPCMData() {
private synchronized void calcPCMData() {
short chanVal;
int bufferPosOffset = 0;
bufferPosOffset = updateBufferPosition();
playingBuffer.position(Math.max(playingBuffer.limit()+bufferPosOffset, 0));
for (int sid = 0; sid < getAudioPCM().length && sid < playingBuffer.remaining(); sid++) {
getAudioPCM()[sid] = 0;
for (int channel = 0; channel < mm.getChannelCount(); channel ++) {
if (getAudioPCM()[sid] < (chanVal = playingBuffer.get())) {
getAudioPCM()[sid] = 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;
}
}
getAudioPCM()[sid] /= Short.MAX_VALUE+1f;
PCM[sid] /= Short.MAX_VALUE+1f;
}
@ -86,26 +94,31 @@ public class DesktopVisualizer extends MusicManagerFFT implements Visualizer {
}
//put the new buffer into the remaining portion.
playingBuffer.put(compareBuffer);
synchronizeBufferWithPlayback();
}
private int updateBufferPosition() {
int pos = (int) ((alGetSourcef(sourceID, AL11.AL_SAMPLE_OFFSET))-buffer.limit()-mm.getChannelCount()*mm.getReadWindowSize());
return pos;
private int calcBufferPosition() {
int offset = (int) alGetSourcef(sourceID, AL11.AL_SAMPLE_OFFSET);
offset = (offset/windowSize) * windowSize;
return offset;
}
/* (non-Javadoc)
* @see zero1hd.rhythmbullet.desktop.audio.visualizer.Visualizer#setMM(zero1hd.rhythmbullet.audio.MusicManager)
*/
@Override
public void setMM(MusicManager mm) {
private void synchronizeBufferWithPlayback() {
playingBuffer.position(calcBufferPosition());
windowsRead = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize);
}
public void setMusic(MinimalAudioHeader header) {
try {
Field sourceIDField = ClassReflection.getDeclaredField(OpenALMusic.class, "sourceID");
sourceIDField.setAccessible(true);
sourceID = (int) sourceIDField.get(mm.getMusic());
sourceID = (int) sourceIDField.get(mc.getCurrentMusic());
} catch (ReflectionException e) {
e.printStackTrace();
}
int originalPos = buffer.position();
channelCount = header.getChannelCount();
sampleRate = header.getSampleRate();
playingBuffer = ShortBuffer.allocate(buffer.capacity()*2);
buffer.rewind();
@ -117,28 +130,76 @@ public class DesktopVisualizer extends MusicManagerFFT implements Visualizer {
compareBuffer.put(buffer);
compareBuffer.flip();
buffer.position(originalPos);
this.mm = mm;
buffer.rewind();
}
super.setMM(mm);
public synchronized float[] getAudioPCM() {
return PCM;
}
public class BufferStreamReadThread implements Runnable {
private String name = "PCM-Audio-Processing";
private Thread thread;
private boolean run, paused;
private volatile long timeOfLastRead;
private int waitTime;
@Override
public void run() {
while (run) {
if (mc.isPlaying()) {
if (paused) {
timeOfLastRead = TimeUtils.millis();
paused = false;
}
waitTime = sampleRate/windowSize/Gdx.graphics.getFramesPerSecond();
if (TimeUtils.timeSinceMillis(timeOfLastRead) >= waitTime) {
calcPCMData();
windowsRead++;
timeOfLastRead = TimeUtils.millis();
currentPlaybackWindow = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize);
if (windowsRead != currentPlaybackWindow) {
synchronizeBufferWithPlayback();
}
}
} else {
synchronized (this) {
try {
paused = true;
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public synchronized void start() {
if (thread == null && !thread.isAlive()) {
thread = new Thread(this, name);
thread.start();
} else {
notify();
}
}
}
@Override
public MusicManager getMM() {
return mm;
public void update(Observable o, Object arg) {
if (o == mc) {
switch ((MusicController.States) arg) {
case Loaded:
setMusic(mc.getCurrentMusicHeader());
break;
case Playing:
streamReadThread.start();
break;
default:
break;
}
@Override
public void render(Batch batch, float parentAlpha) {
}
@Override
public float[] getAudioPCMData() {
return getAudioPCM();
}
@Override
public void fft() {
calculate();
}
}

View File

@ -11,7 +11,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.Align;
import zero1hd.rhythmbullet.desktop.screens.MainMenuScreen;
import zero1hd.rhythmbullet.desktop.screens.MainScreen;
public class GraphicsOptions extends Table {
private Label resolutions, shaders;
@ -28,7 +28,7 @@ public class GraphicsOptions extends Table {
_1366x768;
public GraphicsOptions(final MainMenuScreen mainMenu, Skin skin, final Preferences prefs) {
public GraphicsOptions(final MainScreen mainMenu, Skin skin, final Preferences prefs) {
align(Align.center);
defaults().space(10f);
this.prefs = prefs;

View File

@ -1,19 +1,19 @@
package zero1hd.rhythmbullet.desktop.graphics.ui.components;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.ui.Widget;
import com.badlogic.gdx.utils.Disposable;
import zero1hd.rhythmbullet.audio.MusicManager;
import zero1hd.rhythmbullet.audio.visualizer.HorizontalVisualizer;
import zero1hd.rhythmbullet.desktop.audio.visualizer.DesktopVisualizer;
public class HorizontalVisualizerWidget extends Widget implements Disposable {
private HorizontalVisualizer vis;
private boolean updatePositioning = true;
private MusicManager mm;
private Music music;
private boolean initialLoad;
private float visRefreshRate;
private float timer;
@ -29,13 +29,13 @@ public class HorizontalVisualizerWidget extends Widget implements Disposable {
@Override
public void act(float delta) {
if (mm != null && mm.isFinishedLoading() && !initialLoad) {
visRefreshRate = mm.getReadWindowSize()/mm.getSampleRate();
Gdx.app.debug("Visualizer", "\nsample count: " + mm.getSampleCount()
+ "\nDuration in seconds: " + mm.getDuration() +
"\nSample rate: " + mm.getSampleRate() +
if (music != null && music.isFinishedLoading() && !initialLoad) {
visRefreshRate = music.getReadWindowSize()/music.getSampleRate();
Gdx.app.debug("Visualizer", "\nsample count: " + music.getSampleCount()
+ "\nDuration in seconds: " + music.getDuration() +
"\nSample rate: " + music.getSampleRate() +
"\nRefresh rate: " + visRefreshRate);
vis.setMM(mm);
vis.setMusic(music);
initialLoad = true;
}
@ -46,7 +46,7 @@ public class HorizontalVisualizerWidget extends Widget implements Disposable {
vis.update(delta);
updateVisualizerProperties();
if (mm != null && initialLoad) {
if (music != null && initialLoad) {
if (timer >= visRefreshRate) {
timer = 0;
vis.calculate();
@ -58,9 +58,9 @@ public class HorizontalVisualizerWidget extends Widget implements Disposable {
super.act(delta);
}
public void setMM(MusicManager mm) {
public void setMusic(Music music) {
initialLoad = false;
this.mm = mm;
this.music = music;
}
@Override

View File

@ -7,13 +7,13 @@ import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import zero1hd.rhythmbullet.desktop.audio.MusicListController;
import zero1hd.rhythmbullet.audio.MusicController;
public class MusicControls extends HorizontalGroup {
private ImageButton reverse, forward;
private CheckBox shuffle, play;
private float disableTimer;
public MusicControls(Skin skin, final MusicListController sc) {
public MusicControls(Skin skin, final MusicController sc) {
reverse = new ImageButton(skin, "rewind-button");
reverse.addListener(new ChangeListener() {
@Override

View File

@ -39,7 +39,7 @@ public class AnalysisPage extends Page {
addActor(table);
this.core = core;
adjustment = new Table();
Skin skin = core.getDefaultSkin();
Skin skin = core.getSkinSkin();
difficultyModLabel = new Label("Difficulty Modifier: ", skin, "sub-font", skin.getColor("default"));
difficultyModifierSlider = new Slider(1, 3, 0.5f, false, skin);

View File

@ -10,14 +10,14 @@ 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.desktop.screens.MainMenuScreen;
import zero1hd.rhythmbullet.desktop.screens.MainScreen;
public class GraphicsOptionsPage extends Page {
private ScrollPane scrollPane;
private GraphicsOptions graphicsTable;
private TextButton backButton;
public GraphicsOptionsPage(Skin skin, Preferences prefs, final MainMenuScreen menu, AssetManager assets) {
public GraphicsOptionsPage(Skin skin, Preferences prefs, final MainScreen menu, AssetManager assets) {
graphicsTable = new GraphicsOptions(menu, skin, prefs);
scrollPane = new ScrollPane(graphicsTable, skin);
scrollPane.setFadeScrollBars(false);

View File

@ -14,17 +14,16 @@ import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.Align;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.audio.MusicManager;
import zero1hd.rhythmbullet.desktop.audio.MusicListController;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.desktop.graphics.ui.components.MusicControls;
import zero1hd.rhythmbullet.desktop.graphics.ui.components.TitleBarVisualizer;
import zero1hd.rhythmbullet.desktop.screens.MainMenuScreen;
import zero1hd.rhythmbullet.desktop.screens.MainScreen;
import zero1hd.rhythmbullet.graphics.ui.components.ScrollText;
public class MainPage extends Page implements Observer {
private Label versionLabel;
private MusicListController mlc;
private MusicController mc;
private TitleBarVisualizer titleBar;
private Table table;
private TextButton playButton;
@ -32,17 +31,17 @@ public class MainPage extends Page implements Observer {
private TextButton quitButton;
private MusicControls musicControls;
private MainMenuScreen mMenu;
private MainScreen mMenu;
private ScrollText scrollText;
public MainPage(RhythmBullet core, final Vector3 targetPosition, MusicListController mlc, final MainMenuScreen mainMenu) {
this.mlc = mlc;
public MainPage(RhythmBullet core, Vector3 targetPosition, MusicController mlc, MainScreen mainMenu) {
this.mc = mlc;
this.mMenu = mainMenu;
titleBar = new TitleBarVisualizer(core.getAssetManager());
addActor(titleBar);
versionLabel = new Label("Version: " + RhythmBullet.VERSION, core.getDefaultSkin(), "sub-font",
core.getDefaultSkin().getColor("default"));
versionLabel = new Label("Version: " + RhythmBullet.VERSION, core.getSkinSkin(), "sub-font",
core.getSkinSkin().getColor("default"));
versionLabel.setPosition(3, 3);
addActor(versionLabel);
@ -51,7 +50,7 @@ public class MainPage extends Page implements Observer {
table.align(Align.center);
table.defaults().space(10f);
addActor(table);
playButton = new TextButton("Start!", core.getDefaultSkin());
playButton = new TextButton("Start!", core.getSkinSkin());
playButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
@ -63,7 +62,7 @@ public class MainPage extends Page implements Observer {
table.row();
optionsButton = new TextButton("Options", core.getDefaultSkin(), "sub");
optionsButton = new TextButton("Options", core.getSkinSkin(), "sub");
optionsButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
@ -74,7 +73,7 @@ public class MainPage extends Page implements Observer {
table.row();
quitButton = new TextButton("Quit", core.getDefaultSkin(), "sub");
quitButton = new TextButton("Quit", core.getSkinSkin(), "sub");
quitButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
@ -83,11 +82,11 @@ public class MainPage extends Page implements Observer {
});
table.add(quitButton).fillX();
musicControls = new MusicControls(core.getDefaultSkin(), mlc);
musicControls = new MusicControls(core.getSkinSkin(), mlc);
musicControls.setPosition((getWidth()-musicControls.getMinWidth() - 20f), getHeight()-musicControls.getMinHeight()-20f);
addActor(musicControls);
scrollText = new ScrollText("...", "...", core.getDefaultSkin(), false, true);
scrollText = new ScrollText("...", "...", core.getSkinSkin(), false, true);
scrollText.setWidth(0.5f*getWidth());
scrollText.setPosition(15, getHeight() - scrollText.getHeight()-25f);
addActor(scrollText);
@ -111,21 +110,8 @@ public class MainPage extends Page implements Observer {
@Override
public void update(Observable o, Object arg) {
if (o == mlc.getMusicList() && mlc.getMusicList().isSearched()) {
mlc.shuffle(true);
MusicManager mm = mlc.getCurrentMusicManager();
updateVisualsForDifferentSong(mm);
mMenu.getMusicSelectionPage().refreshUIList();
} else if (o == mlc) {
MusicManager mm = mlc.getCurrentMusicManager();
mlc.play();
updateVisualsForDifferentSong(mm);
if (o == mc && arg == MusicController.States.Loaded) {
scrollText.setText("Currently playing: " + mc.getCurrentSongName(), null);
}
}
public void updateVisualsForDifferentSong(MusicManager mm) {
titleBar.getHvisual().setMM(mm);
scrollText.setText("Currently playing: " + mm.getBasicSongName(), null);
}
}

View File

@ -26,17 +26,17 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array;
import zero1hd.rhythmbullet.audio.MusicInfo;
import zero1hd.rhythmbullet.audio.MusicMetadataController;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.audio.MusicManager;
import zero1hd.rhythmbullet.desktop.audio.MusicInfoController;
import zero1hd.rhythmbullet.desktop.audio.MusicListController;
import zero1hd.rhythmbullet.desktop.graphics.ui.components.MusicSelectable;
import zero1hd.rhythmbullet.graphics.ui.components.ScrollText;
public class MusicSelectionPage extends Page implements Observer {
Preferences musicFileAnnotation;
private MusicListController mc;
private MusicInfoController mic;
private MusicController mc;
private MusicMetadataController mic;
private Array<MusicSelectable> selectables;
private Table musicTable;
private ScrollPane musicTableScrollPane;
@ -65,7 +65,7 @@ public class MusicSelectionPage extends Page implements Observer {
private int uiSongInfoCount;
private float scrollTimer, scrollDelay = 0.2f, scrollDelMod, songSelectionTimer;
public MusicSelectionPage(Skin skin, MusicListController musicListController, MusicInfoController musicInfoController, AssetManager assetManager, final Vector3 cameraTarget, final AnalysisPage ap) {
public MusicSelectionPage(Skin skin, MusicController musicListController, MusicMetadataController musicInfoController, AssetManager assetManager, final Vector3 cameraTarget, final AnalysisPage ap) {
this.assets = assetManager;
this.skin = skin;
this.mc = musicListController;
@ -139,7 +139,7 @@ public class MusicSelectionPage extends Page implements Observer {
public void changed(ChangeEvent event, Actor actor) {
if (getSelectedMusic() != null) {
cameraTarget.x = 2.5f*getWidth();
ap.processSong(mc.getMusicList().getAudioData(getSelectedMusic()), (albumCoverTexture != null ? albumCoverTexture : assets.get("defaultCover.png", Texture.class)), mic.getInfo(getSelectedMusic()));
ap.processSong(mc.getMusicList().newAudioProcessor(getSelectedMusic()), (albumCoverTexture != null ? albumCoverTexture : assets.get("defaultCover.png", Texture.class)), mic.getInfo(getSelectedMusic()));
}
}
});
@ -195,7 +195,7 @@ public class MusicSelectionPage extends Page implements Observer {
selectMusicUI(mc.getCurrentMusicManager());
}
} else if (uiSongInfoCount < selectables.size && mic.isDone()) {
selectables.get(uiSongInfoCount).updateInfo(mic.getSongInfoArray().get(uiSongInfoCount));
selectables.get(uiSongInfoCount).updateInfo(mic.getMetadataArray().get(uiSongInfoCount));
uiSongInfoCount++;
if (uiSongInfoCount == selectables.size) {
updateInformation();
@ -291,9 +291,8 @@ public class MusicSelectionPage extends Page implements Observer {
@Override
public void update(Observable o, Object arg) {
if (o == mc) {
MusicManager mm = (MusicManager) arg;
selectMusicUI(mm);
if (o == mc && arg == MusicController.States.Loaded) {
selectMusicUI(mc.getCurrentMusic());
}
}

View File

@ -14,7 +14,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.desktop.audio.MusicListController;
import zero1hd.rhythmbullet.audio.MusicController;
public class OptionsPage extends Page {
Table optionsTable;
@ -23,11 +23,11 @@ public class OptionsPage extends Page {
private TextField directoryField;
private float musicSearchTimer;
public OptionsPage(final RhythmBullet core, final Vector3 targetPosition, KeybindOptionsPage moreOptionsPage, MusicListController mlc) {
super("General", core.getDefaultSkin());
public OptionsPage(final RhythmBullet core, final Vector3 targetPosition, KeybindOptionsPage moreOptionsPage, MusicController mlc) {
super("General", core.getSkinSkin());
//Back button
TextButton backButton = new TextButton("Back", core.getDefaultSkin());
TextButton backButton = new TextButton("Back", core.getSkinSkin());
backButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
@ -43,12 +43,12 @@ public class OptionsPage extends Page {
optionsTable.setSize(getWidth(), getHeight());
addActor(optionsTable);
Label musicVolSliderLabel = new Label("Music Volume: ", core.getDefaultSkin());
Label musicVolSliderLabel = new Label("Music Volume: ", core.getSkinSkin());
optionsTable.add(musicVolSliderLabel);
musicVolSlider = new Slider(0, 100, 0.1f, false, core.getDefaultSkin());
musicVolSlider = new Slider(0, 100, 0.1f, false, core.getSkinSkin());
musicVolSlider.setValue(core.getPrefs().getFloat("music vol", 100f)*100f);
optionsTable.add(musicVolSlider).minWidth(0.3f*getWidth());
final Label musicVolPercentage = new Label(MathUtils.round(musicVolSlider.getValue()) + "%", core.getDefaultSkin());
final Label musicVolPercentage = new Label(MathUtils.round(musicVolSlider.getValue()) + "%", core.getSkinSkin());
musicVolSlider.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
@ -62,12 +62,12 @@ public class OptionsPage extends Page {
optionsTable.row();
Label fxVolSliderLabel = new Label("FX Volume: ", core.getDefaultSkin());
Label fxVolSliderLabel = new Label("FX Volume: ", core.getSkinSkin());
optionsTable.add(fxVolSliderLabel);
fxVolSlider = new Slider(0, 100, 0.1f, false, core.getDefaultSkin());
fxVolSlider = new Slider(0, 100, 0.1f, false, core.getSkinSkin());
fxVolSlider.setValue(core.getPrefs().getFloat("fx vol", 100f)*100f);
optionsTable.add(fxVolSlider).fillX();
final Label fxVolPercentage = new Label(MathUtils.round(fxVolSlider.getValue()) + "%", core.getDefaultSkin());
final Label fxVolPercentage = new Label(MathUtils.round(fxVolSlider.getValue()) + "%", core.getSkinSkin());
fxVolSlider.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
@ -80,16 +80,16 @@ public class OptionsPage extends Page {
optionsTable.row();
Label musicDirectoryLabel = new Label("Music Directory: ", core.getDefaultSkin());
Label musicDirectoryLabel = new Label("Music Directory: ", core.getSkinSkin());
optionsTable.add(musicDirectoryLabel);
directoryField = new TextField(null, core.getDefaultSkin() ) {
directoryField = new TextField(null, core.getSkinSkin() ) {
@Override
public void act(float delta) {
if (musicSearchTimer > 0) {
musicSearchTimer -= delta;
if (musicSearchTimer <= 0) {
mlc.getMusicList().setSearchPath(directoryField.getText());
mlc.getMusicList().asynchRefresh();
mlc.getMusicList().asyncSearch();
}
}
super.act(delta);
@ -106,7 +106,7 @@ public class OptionsPage extends Page {
optionsTable.row();
TextButton keybindSettings = new TextButton("Set Controls", core.getDefaultSkin());
TextButton keybindSettings = new TextButton("Set Controls", core.getSkinSkin());
keybindSettings.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
@ -117,7 +117,7 @@ public class OptionsPage extends Page {
optionsTable.row();
TextButton graphicsSettings = new TextButton("Graphics", core.getDefaultSkin());
TextButton graphicsSettings = new TextButton("Graphics", core.getSkinSkin());
graphicsSettings.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
@ -128,7 +128,7 @@ public class OptionsPage extends Page {
optionsTable.row();
Label fpsLabel = new Label("", core.getDefaultSkin()) {
Label fpsLabel = new Label("", core.getSkinSkin()) {
@Override
public void act(float delta) {
setText("Current Frames Per Second: " + Gdx.graphics.getFramesPerSecond());
@ -139,7 +139,7 @@ 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()) + "%", core.getDefaultSkin()) {
Label usageLabel = new Label("Current usage (lower the better): " + 100f*((float)(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/(float)Runtime.getRuntime().totalMemory()) + "%", core.getSkinSkin()) {
float refreshTime = 60;
@Override
public void act(float delta) {

View File

@ -20,10 +20,10 @@ import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.audio.MusicManager;
import zero1hd.rhythmbullet.desktop.audio.MusicInfoController;
import zero1hd.rhythmbullet.desktop.audio.MusicList;
import zero1hd.rhythmbullet.desktop.audio.MusicListController;
import zero1hd.rhythmbullet.audio.MusicMetadataController;
import zero1hd.rhythmbullet.audio.MusicList;
import zero1hd.rhythmbullet.audio.MusicController;
import zero1hd.rhythmbullet.desktop.audio.processor.DesktopAudioProcessorFactory;
import zero1hd.rhythmbullet.desktop.graphics.ui.pages.AnalysisPage;
import zero1hd.rhythmbullet.desktop.graphics.ui.pages.CreditsPage;
import zero1hd.rhythmbullet.desktop.graphics.ui.pages.KeybindOptionsPage;
@ -31,9 +31,9 @@ import zero1hd.rhythmbullet.desktop.graphics.ui.pages.MainPage;
import zero1hd.rhythmbullet.desktop.graphics.ui.pages.MusicSelectionPage;
import zero1hd.rhythmbullet.desktop.graphics.ui.pages.OptionsPage;
import zero1hd.rhythmbullet.desktop.graphics.ui.pages.GraphicsOptionsPage;
import zero1hd.rhythmbullet.util.AdvancedResizeScreen;
import zero1hd.rhythmbullet.util.ResizeReadyScreen;
public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScreen {
public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
public Stage stage;
private Vector3 cameraPosition;
@ -46,8 +46,8 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
private AnalysisPage analysisPage;
private RhythmBullet core;
private MusicListController mlc;
private MusicInfoController mic;
private MusicController mlc;
private MusicMetadataController mmc;
private float lerpAlpha;
@ -66,19 +66,18 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
private Batch screenBatch;
private ScreenViewport screenViewport;
public MainMenuScreen(RhythmBullet core) {
public MainScreen(RhythmBullet core) {
this.core = core;
stage = new Stage(new ScreenViewport());
cameraPosition = new Vector3(stage.getCamera().position);
MusicList musicList = new MusicList();
MusicList musicList = new MusicList(new DesktopAudioProcessorFactory());
musicList.setSearchPath(core.getPrefs().getString("music dir"));
musicList.asynchRefresh();
mlc = new MusicListController(musicList, core.getPrefs());
mlc = new MusicController(musicList, core.getPrefs());
mlc.setAutoPlay(true);
mlc.setShuffle(true);
mic = new MusicInfoController(musicList);
mmc = new MusicMetadataController(musicList);
}
@Override
@ -105,7 +104,7 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
fboRegion.setTexture(normalBuffer.getColorBufferTexture());
screenBatch.setShader(brightFilterShader);
screenBatch.setProjectionMatrix(screenViewport.getCamera().combined);
screenBatch.begin(); //BATCH STARTS HERE
screenBatch.begin(); //SCREEN BATCH STARTS HERE
screenBatch.draw(fboRegion, 0, 0, stage.getWidth(), stage.getHeight());
screenBatch.flush();
lightFilterBuffer.end();
@ -180,6 +179,8 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
@Override
public void postAssetLoad() {
mlc.deleteObservers();
background = core.getAssetManager().get("backgrounds/mainBG.png", Texture.class);
screenBatch = new SpriteBatch();
@ -188,11 +189,11 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
stage.addActor(mainPage);
//End main menu
keybindPage = new KeybindOptionsPage(core.getDefaultSkin(), core.getAssetManager(), cameraPosition);
keybindPage = new KeybindOptionsPage(core.getSkinSkin(), core.getAssetManager(), cameraPosition);
keybindPage.setPosition(-1f*Gdx.graphics.getWidth(), -1f*Gdx.graphics.getHeight());
stage.addActor(keybindPage);
graphicsPage = new GraphicsOptionsPage(core.getDefaultSkin(), core.getPrefs(), this, core.getAssetManager());
graphicsPage = new GraphicsOptionsPage(core.getSkinSkin(), core.getPrefs(), this, core.getAssetManager());
graphicsPage.setPosition(-1f*Gdx.graphics.getWidth(), 1f*Gdx.graphics.getHeight());
stage.addActor(graphicsPage);
@ -201,7 +202,7 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
optionsPage.setPosition(-Gdx.graphics.getWidth(), 0);
stage.addActor(optionsPage);
creditsPage = new CreditsPage(core.getDefaultSkin());
creditsPage = new CreditsPage(core.getSkinSkin());
creditsPage.setPosition(0, Gdx.graphics.getHeight());
stage.addActor(creditsPage);
@ -209,7 +210,7 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
analysisPage.setPosition(2*Gdx.graphics.getWidth(), 0f);
stage.addActor(analysisPage);
musicSelectionPage = new MusicSelectionPage(core.getDefaultSkin(), mlc, mic, core.getAssetManager(), cameraPosition, analysisPage);
musicSelectionPage = new MusicSelectionPage(core.getSkinSkin(), mlc, mmc, core.getAssetManager(), cameraPosition, analysisPage);
musicSelectionPage.setPosition(1f*Gdx.graphics.getWidth(), 0f);
stage.addActor(musicSelectionPage);
stage.addListener(new InputListener() {
@ -233,17 +234,12 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
super.clicked(event, x, y);
}
});
mlc.getMusicList().deleteObservers();
mlc.deleteObservers();
mlc.addObserver(musicSelectionPage);
mlc.addObserver(mainPage);
mlc.getMusicList().addObserver(mainPage);
if (mlc.getMusicList().isSearched() && mlc.getCurrentMusicManager() != null) {
MusicManager mManager = mlc.getCurrentMusicManager();
mainPage.updateVisualsForDifferentSong(mManager);
musicSelectionPage.refreshUIList();
}
mlc.getMusicList().asyncSearch();
}
@Override
@ -281,7 +277,7 @@ public class MainMenuScreen extends ScreenAdapter implements AdvancedResizeScree
if (gaussianBlurShader != null) {
dismantleShaders();
}
mic.dispose();
mmc.dispose();
screenBatch.dispose();
super.dispose();
}

View File

@ -1,6 +1,7 @@
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.Color;
import com.badlogic.gdx.graphics.GL20;
@ -10,18 +11,21 @@ import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import zero1hd.rhythmbullet.InitialScreen;
import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.util.AdvancedResizeScreen;
import zero1hd.rhythmbullet.util.ResizeReadyScreen;
public class SplashScreen extends ScreenAdapter implements AdvancedResizeScreen {
public class SplashScreen extends ScreenAdapter implements ResizeReadyScreen, InitialScreen {
private Stage stage;
private RhythmBullet core;
private Texture splash;
private Image zero1HD;
private boolean done;
public SplashScreen(RhythmBullet core) {
this.core = core;
public SplashScreen() {
splash = new Texture(Gdx.files.internal("splashlogo.png"));
zero1HD = new Image(splash);
zero1HD.setScale((Gdx.graphics.getHeight()*0.8f)/zero1HD.getHeight());
zero1HD.setColor(0f,1f,1f,0.1f);
stage.addActor(zero1HD);
}
@Override
@ -34,49 +38,31 @@ public class SplashScreen extends ScreenAdapter implements AdvancedResizeScreen
Gdx.gl.glClearColor(1f, 1f, 1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
if (!zero1HD.hasActions() && core.isInitComplete()) {
attemptMoveOn();
}
stage.draw();
super.render(delta);
}
private void attemptMoveOn() {
if (!done) {
Gdx.app.debug("Loading Screen", "queue has all been loaded. Action is done playing.");
done = true;
core.setScreen(new MainMenuScreen(core));
zero1HD.remove();
}
}
@Override
public void hide() {
core.setInitComplete();
splash.dispose();
super.hide();
}
@Override
public void preAssetLoad() {
if (stage != null) {
stage.dispose();
splash.dispose();
}
}
@Override
public void postAssetLoad() {
stage = new Stage(new ScreenViewport());
splash = new Texture(Gdx.files.internal("splashlogo.png"));
zero1HD = new Image(splash);
zero1HD.setScale((Gdx.graphics.getHeight()*0.8f)/zero1HD.getHeight());
zero1HD.setColor(0f,1f,1f,0f);
stage.addActor(zero1HD);
zero1HD.setPosition((stage.getWidth() - zero1HD.getWidth()*zero1HD.getScaleX())/2f, (stage.getHeight() - zero1HD.getHeight()*zero1HD.getScaleY())/2f);
zero1HD.addAction(Actions.sequence(Actions.color(Color.WHITE, 1f), Actions.fadeOut(0.5f)));
}
@Override
public Screen createMainScreen(RhythmBullet game) {
return new MainScreen(game);
}
}