diff --git a/core/src/zero1hd/rhythmbullet/audio/visualizer/BasicVisualizer.java b/core/src/zero1hd/rhythmbullet/audio/visualizer/BasicVisualizer.java index 339b617..a5a12fc 100755 --- a/core/src/zero1hd/rhythmbullet/audio/visualizer/BasicVisualizer.java +++ b/core/src/zero1hd/rhythmbullet/audio/visualizer/BasicVisualizer.java @@ -64,7 +64,7 @@ public class BasicVisualizer extends VisualizerCore { super.render(batch, parentAlpha); } - public void modify(float delta) { + public void update(float delta) { //Averaging bins together for (int i = 0; i < barCount; i++) { float barHeight = 0; @@ -104,13 +104,13 @@ public class BasicVisualizer extends VisualizerCore { @Override public void setMM(MusicManager mm) { - super.setMM(mm); maxAvgHeight = 0; currentAvg = 0; - float validBins = (5500/((mm.getSampleRate()/2)/((audioPCM.length/2)+1))); + float validBins = (5500/((mm.getSampleRate()/2)/((mm.getReadWindowSize()/2)+1))); Gdx.app.debug("Visualizer", "valid frequency bins " + validBins); binsPerBar = MathUtils.round((validBins/barCount)); barHeights = new float[barCount]; + super.setMM(mm); } @Override diff --git a/core/src/zero1hd/rhythmbullet/audio/visualizer/VisualizerCore.java b/core/src/zero1hd/rhythmbullet/audio/visualizer/VisualizerCore.java index 83586cb..5c02996 100755 --- a/core/src/zero1hd/rhythmbullet/audio/visualizer/VisualizerCore.java +++ b/core/src/zero1hd/rhythmbullet/audio/visualizer/VisualizerCore.java @@ -11,44 +11,36 @@ import zero1hd.rhythmbullet.util.MusicManager; public class VisualizerCore implements Disposable { protected MusicManager mm; private FloatFFT_1D fft; - float[] audioPCM; protected float width, height; protected float xPos, yPos; protected int barCount; private boolean calc; private ReentrantLock lock; - private float updateRate; - private float updateTimer; + protected float[] audioPCM; + public VisualizerCore(int width, int height, int x, int y) { this.height = height; this.width = width; this.xPos = x; this.yPos = y; - updateRate = 1f/60f; lock = new ReentrantLock(); } public void calculate(float delta) { if (mm != null && calc && mm.isPlaying()) { - updateTimer += delta; - if (updateTimer >= updateRate) { - //TODO use current buffer being played - lock.lock(); - mm.readSamples(audioPCM); - fft.realForward(audioPCM); - lock.unlock(); - updateTimer = 0; - } + lock.lock(); + fft.realForward(audioPCM); + lock.unlock(); } } public void setMM(MusicManager mm) { lock.lock(); + calc = false; if (audioPCM == null || audioPCM.length != mm.getReadWindowSize()) { - calc = false; + audioPCM = new float[mm.getReadWindowSize()]; fft = new FloatFFT_1D(mm.getReadWindowSize()); } - audioPCM = new float[mm.getReadWindowSize()]; this.mm = mm; calc = true; lock.unlock(); @@ -57,7 +49,7 @@ public class VisualizerCore implements Disposable { public void render(Batch batch, float parentAlpha) { } - public void modify(float delta) { + public void update(float delta) { } @Override @@ -98,11 +90,7 @@ public class VisualizerCore implements Disposable { return mm; } - public void setUpdateRate(float updateRate) { - this.updateRate = updateRate; - } - - public float getUpdateRate() { - return updateRate; + public float[] getAudioPCM() { + return audioPCM; } } diff --git a/core/src/zero1hd/rhythmbullet/util/MusicManager.java b/core/src/zero1hd/rhythmbullet/util/MusicManager.java index 12f6434..e3bb40e 100755 --- a/core/src/zero1hd/rhythmbullet/util/MusicManager.java +++ b/core/src/zero1hd/rhythmbullet/util/MusicManager.java @@ -1,6 +1,7 @@ package zero1hd.rhythmbullet.util; +import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.audio.Music.OnCompletionListener; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Disposable; @@ -64,6 +65,10 @@ public interface MusicManager extends Disposable { public void setVolume(float percent); + /** + * @return the amount of channels this audio file has. + */ + public int getChannelCount(); /** * If the the properties of the song are done loading * @return whether its done loading @@ -86,4 +91,6 @@ public interface MusicManager extends Disposable { * @return the amount of windows that have been read. */ public int framesRead(); + + public Music getMusic(); } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/audio/Mp3Manager.java b/desktop/src/zero1hd/rhythmbullet/desktop/audio/Mp3Manager.java index f334471..b372def 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/audio/Mp3Manager.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/audio/Mp3Manager.java @@ -265,4 +265,14 @@ public class Mp3Manager implements MusicManager { public int framesRead() { return readIndex; } + + @Override + public int getChannelCount() { + return channels; + } + + @Override + public Music getMusic() { + return playbackMusic; + } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/audio/WAVManager.java b/desktop/src/zero1hd/rhythmbullet/desktop/audio/WAVManager.java index e0eaa8a..14beb23 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/audio/WAVManager.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/audio/WAVManager.java @@ -141,4 +141,14 @@ public class WAVManager implements MusicManager { public int framesRead() { return readIndex; } + + @Override + public int getChannelCount() { + return d.getChannels(); + } + + @Override + public Music getMusic() { + return music; + } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/graphics/ui/components/Visualizer.java b/desktop/src/zero1hd/rhythmbullet/desktop/graphics/ui/components/Visualizer.java index 35ea444..ce0a156 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/graphics/ui/components/Visualizer.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/graphics/ui/components/Visualizer.java @@ -1,10 +1,21 @@ package zero1hd.rhythmbullet.desktop.graphics.ui.components; +import static org.lwjgl.openal.AL10.*; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +import org.lwjgl.openal.AL11; + import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic; 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 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.BasicVisualizer; import zero1hd.rhythmbullet.util.MusicManager; @@ -14,8 +25,20 @@ public class Visualizer extends Widget implements Disposable { private boolean updatePositioning = true; private boolean mmSet; private MusicManager mm; + private ShortBuffer buffer; + private int sourceID; public Visualizer() { vis = new BasicVisualizer(); + + try { + Field tempBufferField = ClassReflection.getDeclaredField(OpenALMusic.class, "tempBuffer"); + tempBufferField.setAccessible(true); + buffer = ((ByteBuffer) tempBufferField.get(null)).asShortBuffer(); + } catch (IllegalArgumentException | SecurityException | ReflectionException e) { + e.printStackTrace(); + Gdx.app.debug("Visualizer reflection", "Failed attempt at retrieving tempBuffer field."); + Gdx.app.exit(); + } } @Override @@ -34,18 +57,47 @@ public class Visualizer extends Widget implements Disposable { mmSet = true; } - vis.modify(delta); + vis.update(delta); updateVisualizerProperties(); if (updatePositioning) { vis.updatePositionInfo(); vis.setxPos((getWidth() - vis.getActualWidth())/2f); } + if (mmSet) { + setupPCMData(); + } vis.calculate(delta); super.act(delta); } + public void setupPCMData() { + short chanVal; + + int pos = (int) ((alGetSourcef(sourceID, AL11.AL_SAMPLE_OFFSET)-4)); + try { + buffer.position((int) Math.max(0, pos)); + } catch (IllegalArgumentException outOfBounds) { + System.out.println(outOfBounds); + } + for (int sid = 0; sid < vis.getAudioPCM().length && sid < buffer.remaining(); sid++) { + for (int channel = 0; channel < mm.getChannelCount(); channel ++) { + if (vis.getAudioPCM()[sid] < (chanVal = buffer.get())) { + vis.getAudioPCM()[sid] = chanVal; + } + } + vis.getAudioPCM()[sid] /= Short.MAX_VALUE+1; + } + } public void setMM(MusicManager mm) { + try { + Field sourceIDField = ClassReflection.getDeclaredField(OpenALMusic.class, "sourceID"); + sourceIDField.setAccessible(true); + sourceID = (int) sourceIDField.get(mm.getMusic()); + } catch (ReflectionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } this.mm = mm; mmSet = false; }