visualizer works in a basic manner, disposing needed, gui needs
tweaking, visualizer bar rate could be adjusted;
This commit is contained in:
@@ -2,8 +2,10 @@ 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;
|
||||
|
||||
@@ -11,33 +13,35 @@ import org.lwjgl.openal.AL11;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
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.BasicFFT;
|
||||
import zero1hd.rhythmbullet.audio.visualizer.PCMSystem;
|
||||
|
||||
public class PCMObtainer implements Observer, Disposable, PCMSystem {
|
||||
public class PCMObtainer implements Observer, PCMSystem {
|
||||
private int windowSize = 1024;
|
||||
private int sampleRate;
|
||||
private long millisPerWindow;
|
||||
private DecimalFormat df;
|
||||
private float[] PCM = new float[windowSize];
|
||||
private float[] frequencyBins = new float[windowSize / 2];
|
||||
private BasicFFT fft = new BasicFFT(windowSize);
|
||||
private FloatFFT_1D fft = new FloatFFT_1D(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;
|
||||
private volatile boolean updated;
|
||||
|
||||
|
||||
public PCMObtainer(MusicController musicController) {
|
||||
this.mc = musicController;
|
||||
mc.addObserver(this);
|
||||
@@ -51,6 +55,9 @@ public class PCMObtainer implements Observer, Disposable, PCMSystem {
|
||||
}
|
||||
|
||||
streamReadThread = new BufferStreamReadThread();
|
||||
|
||||
df = new DecimalFormat("#.###");
|
||||
df.setRoundingMode(RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
private synchronized void calcPCMData() {
|
||||
@@ -95,7 +102,7 @@ public class PCMObtainer implements Observer, Disposable, PCMSystem {
|
||||
}
|
||||
// put the new buffer into the remaining portion.
|
||||
playingBuffer.put(compareBuffer);
|
||||
|
||||
|
||||
synchronizeBufferWithPlayback();
|
||||
}
|
||||
|
||||
@@ -116,7 +123,8 @@ public class PCMObtainer implements Observer, Disposable, PCMSystem {
|
||||
|
||||
channelCount = mc.getCurrentMusicHeader().getChannelCount();
|
||||
sampleRate = mc.getCurrentMusicHeader().getSampleRate();
|
||||
|
||||
String millisPerWindowF = df.format(windowSize/(float) sampleRate);
|
||||
millisPerWindow = (long) (Float.valueOf(millisPerWindowF)*1000);
|
||||
playingBuffer = ShortBuffer.allocate(buffer.capacity() * 2);
|
||||
buffer.rewind();
|
||||
playingBuffer.put(buffer);
|
||||
@@ -134,7 +142,6 @@ public class PCMObtainer implements Observer, Disposable, PCMSystem {
|
||||
public float[] getFrequencyBins() {
|
||||
if (updated) {
|
||||
synchronized (this) {
|
||||
fft.fft(PCM);
|
||||
System.arraycopy(PCM, 1, frequencyBins, 0, frequencyBins.length);
|
||||
}
|
||||
}
|
||||
@@ -150,34 +157,50 @@ public class PCMObtainer implements Observer, Disposable, PCMSystem {
|
||||
private String name = "PCM-Audio-Processing";
|
||||
private Thread thread;
|
||||
private volatile boolean run = true;
|
||||
private boolean paused;
|
||||
private long timeOfLastRead;
|
||||
private int waitTime;
|
||||
|
||||
private long waitTime;
|
||||
private int syncC, normC;
|
||||
private float syncPercentage;
|
||||
@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();
|
||||
updated = true;
|
||||
windowsRead++;
|
||||
timeOfLastRead = TimeUtils.millis();
|
||||
//record time of read
|
||||
timeOfLastRead = TimeUtils.millis();
|
||||
|
||||
//calculate current pcm data and notify that there is new data
|
||||
calcPCMData();
|
||||
fft.realForward(PCM);
|
||||
updated = true;
|
||||
windowsRead++;
|
||||
|
||||
currentPlaybackWindow = (int) ((mc.getCurrentPosition() * sampleRate) / windowSize);
|
||||
if (windowsRead != currentPlaybackWindow) {
|
||||
synchronizeBufferWithPlayback();
|
||||
//contemplate synchronization
|
||||
try {
|
||||
currentPlaybackWindow = MathUtils.round((mc.getCurrentPosition() * sampleRate) / windowSize);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
if (run) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (windowsRead != currentPlaybackWindow) {
|
||||
synchronizeBufferWithPlayback();
|
||||
syncC++;
|
||||
} else {
|
||||
normC++;
|
||||
}
|
||||
syncPercentage = (syncC*100)/(float)(normC+syncC);
|
||||
System.out.printf("%.2f", syncPercentage);
|
||||
System.out.print("%\n");
|
||||
//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 {
|
||||
paused = true;
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
@@ -199,6 +222,7 @@ public class PCMObtainer implements Observer, Disposable, PCMSystem {
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
Gdx.app.debug("PCMObtainer", "stopping " + thread.getName());
|
||||
run = false;
|
||||
}
|
||||
}
|
||||
|
@@ -91,7 +91,7 @@ public class GraphicsOptions extends Table {
|
||||
pack();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
public void saveOptions() {
|
||||
Gdx.app.debug("Preferences", "Saved shading values values.");
|
||||
prefs.putInteger("glow shader", (int) glowShaderLevel.getValue());
|
||||
}
|
||||
|
@@ -34,7 +34,10 @@ public class Page extends Group implements Disposable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setStage(Stage stage) {
|
||||
public void setStage(Stage stage) {
|
||||
if (stage == null && !hasParent()) {
|
||||
dispose();
|
||||
}
|
||||
if (!(stage.getViewport() instanceof ScreenViewport)) {
|
||||
throw new IllegalArgumentException("Pages are explicitly for GUIs, and thus should have a 1:1 ratio between pixel and texture size for maximum clarity. This means that the stage should be using a ScreenViewport.");
|
||||
}
|
||||
@@ -50,7 +53,16 @@ public class Page extends Group implements Disposable {
|
||||
setPosition(baseXPos*getWidth(), baseYPos*getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(Group parent) {
|
||||
if (parent == null && getStage() == null) {
|
||||
dispose();
|
||||
}
|
||||
super.setParent(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Gdx.app.debug(getClass().getSimpleName(), "Disposing...");
|
||||
}
|
||||
}
|
||||
|
@@ -41,8 +41,8 @@ public class GraphicsPage extends Page {
|
||||
super.act(delta);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
graphicsTable.save();
|
||||
public void saveOptions() {
|
||||
graphicsTable.saveOptions();
|
||||
}
|
||||
|
||||
public int getBloomLevel() {
|
||||
|
@@ -111,9 +111,10 @@ public class MainPage extends Page implements Observer {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
dhv.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update(Observable o, Object arg) {
|
||||
if (o == mc) {
|
||||
|
@@ -99,11 +99,6 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
|
||||
@Override
|
||||
public void preAssetLoad() {
|
||||
stage.clear();
|
||||
mainPage.dispose();
|
||||
optionsPage.dispose();
|
||||
creditsPage.dispose();
|
||||
keybindPage.dispose();
|
||||
musicSelectionPage.dispose();
|
||||
bloomShader.dispose();
|
||||
bloomShader = null;
|
||||
background = null;
|
||||
@@ -187,7 +182,8 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
|
||||
|
||||
public void saveAll() {
|
||||
if (optionsPage != null) {
|
||||
optionsPage.saveOptions(rhythmBullet.getPreferences());
|
||||
optionsPage.saveOptions();
|
||||
graphicsPage.saveOptions();
|
||||
rhythmBullet.getPreferences().flush();
|
||||
}
|
||||
}
|
||||
@@ -203,6 +199,7 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Gdx.app.debug("Mainscreen", "disposing...");
|
||||
stage.dispose();
|
||||
musicMetadataController.dispose();
|
||||
screenBatch.dispose();
|
||||
|
@@ -22,9 +22,11 @@ public class OptionsPage extends Page {
|
||||
private ProgressBar fxVolSlider;
|
||||
private TextField directoryField;
|
||||
private float musicSearchTimer;
|
||||
private Preferences prefs;
|
||||
|
||||
public OptionsPage(MusicController musicController, Skin skin, Preferences preferences, ChangeListener backButtonListener, ChangeListener graphicsButtonListener, ChangeListener controlsButtonListener) {
|
||||
super(-1, 0, "General", skin);
|
||||
this.prefs = preferences;
|
||||
|
||||
//Back button
|
||||
TextButton backButton = new TextButton("Back", skin);
|
||||
@@ -140,13 +142,14 @@ public class OptionsPage extends Page {
|
||||
optionsTable.add(usageLabel).colspan(2);
|
||||
}
|
||||
|
||||
public void saveOptions(Preferences prefs) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user