diff options
-rw-r--r-- | src/com/pheelicks/visualizer/AudioData.java | 12 | ||||
-rw-r--r-- | src/com/pheelicks/visualizer/BarGraphRenderer.java | 62 | ||||
-rw-r--r-- | src/com/pheelicks/visualizer/FFTData.java | 12 | ||||
-rw-r--r-- | src/com/pheelicks/visualizer/Renderer.java | 64 | ||||
-rw-r--r-- | src/com/pheelicks/visualizer/VisualizerActivity.java | 31 | ||||
-rw-r--r-- | src/com/pheelicks/visualizer/VisualizerView.java | 66 |
6 files changed, 198 insertions, 49 deletions
diff --git a/src/com/pheelicks/visualizer/AudioData.java b/src/com/pheelicks/visualizer/AudioData.java new file mode 100644 index 0000000..9beb004 --- /dev/null +++ b/src/com/pheelicks/visualizer/AudioData.java @@ -0,0 +1,12 @@ +package com.pheelicks.visualizer; + +// Data class to explicitly indicate that these bytes are raw audio data +public class AudioData +{ + public AudioData(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] bytes; +} diff --git a/src/com/pheelicks/visualizer/BarGraphRenderer.java b/src/com/pheelicks/visualizer/BarGraphRenderer.java new file mode 100644 index 0000000..f1531e9 --- /dev/null +++ b/src/com/pheelicks/visualizer/BarGraphRenderer.java @@ -0,0 +1,62 @@ +package com.pheelicks.visualizer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; + +public class BarGraphRenderer extends Renderer +{ + private int mDivisions; + private Paint mPaint; + private boolean mTop; + + /** + * Renders the FFT data as a series of lines, in histogram form + * @param canvas + * @param divisions - must be a power of 2. Controls how many lines to draw + * @param paint - Paint to draw lines with + * @param top - whether to draw the lines at the top of the canvas, or the bottom + */ + public BarGraphRenderer(Canvas canvas, + int divisions, + Paint paint, + boolean top) + { + super(canvas); + mDivisions = divisions; + mPaint = paint; + mTop = top; + } + + @Override + public void onRender(AudioData data, Rect rect) + { + // Do nothing, we only display FFT data + } + + @Override + public void onRender(FFTData data, Rect rect) + { + for (int i = 0; i < data.bytes.length / mDivisions; i++) { + mFFTPoints[i * 4] = i * 4 * mDivisions; + mFFTPoints[i * 4 + 2] = i * 4 * mDivisions; + byte rfk = data.bytes[mDivisions * i]; + byte ifk = data.bytes[mDivisions * i + 1]; + float magnitude = (rfk * rfk + ifk * ifk); + int dbValue = (int) (10 * Math.log10(magnitude)); + + if(mTop) + { + mFFTPoints[i * 4 + 1] = 0; + mFFTPoints[i * 4 + 3] = (dbValue * 2 - 10); + } + else + { + mFFTPoints[i * 4 + 1] = rect.height(); + mFFTPoints[i * 4 + 3] = rect.height() - (dbValue * 2 - 10); + } + } + + mCanvas.drawLines(mFFTPoints, mPaint); + } +} diff --git a/src/com/pheelicks/visualizer/FFTData.java b/src/com/pheelicks/visualizer/FFTData.java new file mode 100644 index 0000000..6ae55eb --- /dev/null +++ b/src/com/pheelicks/visualizer/FFTData.java @@ -0,0 +1,12 @@ +package com.pheelicks.visualizer; + +// Data class to explicitly indicate that these bytes are the FFT of audio data +public class FFTData +{ + public FFTData(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] bytes; +} diff --git a/src/com/pheelicks/visualizer/Renderer.java b/src/com/pheelicks/visualizer/Renderer.java new file mode 100644 index 0000000..aa2a56b --- /dev/null +++ b/src/com/pheelicks/visualizer/Renderer.java @@ -0,0 +1,64 @@ +package com.pheelicks.visualizer; + +import android.graphics.Canvas; +import android.graphics.Rect; + +abstract public class Renderer +{ + // Canvas & Rect to render to + protected Canvas mCanvas; + + // Have these as members, so we don't have to re-create them each time + protected float[] mPoints; + protected float[] mFFTPoints; + public Renderer(Canvas canvas) + { + mCanvas = canvas; + } + + // As the display of raw/FFT audio will usually look different, subclasses + // will typically only implement one of the below methods + /** + * Implement this method to render the audio data onto the canvas + * @param data - Data to render + * @param rect - Rect to render into + */ + abstract public void onRender(AudioData data, Rect rect); + + /** + * Implement this method to render the FFT audio data onto the canvas + * @param data - Data to render + * @param rect - Rect to render into + */ + abstract public void onRender(FFTData data, Rect rect); + + + // These methods should actually be called for rendering + /** + * Render the audio data onto the canvas + * @param data - Data to render + * @param rect - Rect to render into + */ + final public void render(AudioData data, Rect rect) + { + if (mPoints == null || mPoints.length < data.bytes.length * 4) { + mPoints = new float[data.bytes.length * 4]; + } + + onRender(data, rect); + } + + /** + * Render the FFT data onto the canvas + * @param data - Data to render + * @param rect - Rect to render into + */ + final public void render(FFTData data, Rect rect) + { + if (mFFTPoints == null || mFFTPoints.length < data.bytes.length * 4) { + mFFTPoints = new float[data.bytes.length * 4]; + } + + onRender(data, rect); + } +} diff --git a/src/com/pheelicks/visualizer/VisualizerActivity.java b/src/com/pheelicks/visualizer/VisualizerActivity.java index 795e90e..031ac9d 100644 --- a/src/com/pheelicks/visualizer/VisualizerActivity.java +++ b/src/com/pheelicks/visualizer/VisualizerActivity.java @@ -18,12 +18,13 @@ public class VisualizerActivity extends Activity { mPlayer = MediaPlayer.create(this, R.raw.test); mPlayer.setLooping(true); + mPlayer.start(); linkVisualizer(mPlayer); } /** - * Links the visualizer to a player + * Links the visualizer to a player * TODO Refactor this into visualizer * @param player */ @@ -69,6 +70,34 @@ public class VisualizerActivity extends Activity { }); } + // Cleanup + @Override + protected void onPause() + { + if (isFinishing() && (mPlayer != null)) + { + mVisualizer.release(); + mPlayer.release(); + mPlayer = null; + } + + super.onPause(); + } + + @Override + protected void onDestroy() + { + if (mPlayer != null) + { + mPlayer.stop(); + mPlayer.release(); + mPlayer = null; + } + + super.onDestroy(); + } + + // Actions for buttons defined in xml public void startPressed(View view) { mPlayer.start(); diff --git a/src/com/pheelicks/visualizer/VisualizerView.java b/src/com/pheelicks/visualizer/VisualizerView.java index 94505b6..82d7100 100644 --- a/src/com/pheelicks/visualizer/VisualizerView.java +++ b/src/com/pheelicks/visualizer/VisualizerView.java @@ -35,16 +35,11 @@ class VisualizerView extends View { private Paint mCirclePaint = new Paint(); private Paint mLinePaint = new Paint(); - private Paint mFFTLineTopPaint = new Paint(); - private Paint mFFTLineBottomPaint = new Paint(); private Paint mSpecialLinePaint = new Paint(); private Paint mProgressLinePaint = new Paint(); private Paint mFlashPaint = new Paint(); private Paint mFadePaint = new Paint(); - final int fft_divis_top = 8; // Set to some factor of 2 to adjust number of FFT bars - final int fft_divis_bottom = 16; // Set to some factor of 2 to adjust number of FFT bars - // Usual BS of 3 constructors public VisualizerView(Context context, AttributeSet attrs, int defStyle) { @@ -81,13 +76,6 @@ class VisualizerView extends View { mProgressLinePaint.setAntiAlias(true); mProgressLinePaint.setColor(Color.argb(255, 22, 131, 255)); - mFFTLineTopPaint.setStrokeWidth(fft_divis_top * 3f); - mFFTLineTopPaint.setAntiAlias(true); - mFFTLineTopPaint.setColor(Color.argb(200, 233, 0, 44)); - - mFFTLineBottomPaint.setStrokeWidth(fft_divis_bottom * 3f); - mFFTLineBottomPaint.setAntiAlias(true); - mFFTLineBottomPaint.setColor(Color.argb(88, 0, 233, 44)); mFlashPaint.setColor(Color.argb(122, 255, 255, 255)); @@ -134,6 +122,10 @@ class VisualizerView extends View { Canvas mCanvas; Random mRandom = new Random(); float amplitude = 0; + + BarGraphRenderer mBarGraphRendererTop; + BarGraphRenderer mBarGraphRendererBottom; + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -175,6 +167,17 @@ class VisualizerView extends View { if(mCanvas == null) { mCanvas = new Canvas(mCanvasBitmap); + Paint paint = new Paint(); + paint.setStrokeWidth(50f); + paint.setAntiAlias(true); + paint.setColor(Color.argb(200, 233, 0, 44)); + mBarGraphRendererBottom = new BarGraphRenderer(mCanvas, 16, paint, false); + + Paint paint2 = new Paint(); + paint2.setStrokeWidth(12f); + paint2.setAntiAlias(true); + paint2.setColor(Color.argb(200, 11, 111, 233)); + mBarGraphRendererTop = new BarGraphRenderer(mCanvas, 4, paint2, true); } mCanvas.drawLines(mPoints, mCirclePaint); @@ -214,43 +217,10 @@ class VisualizerView extends View { return; } - if (mFFTPoints == null || mFFTPoints.length < mBytes.length * 4) { - mFFTPoints = new float[mFFTBytes.length * 4]; - } - - // Equalizer top - if(mFFTBytes != null) - { - for (int i = 0; i < mFFTBytes.length / fft_divis_top; i++) { - mFFTPoints[i * 4] = i * 4 * fft_divis_top; - mFFTPoints[i * 4 + 1] = 0; - mFFTPoints[i * 4 + 2] = i * 4 * fft_divis_top; - byte rfk = mFFTBytes[fft_divis_top * i]; - byte ifk = mFFTBytes[fft_divis_top * i + 1]; - float magnitude = (rfk * rfk + ifk * ifk); - int dbValue = (int) (10 * Math.log10(magnitude)); - mFFTPoints[i * 4 + 3] = (dbValue * 2 - 10); - } - - mCanvas.drawLines(mFFTPoints, mFFTLineTopPaint); - } + FFTData fftData = new FFTData(mFFTBytes); - // Equalizer bottom - if(mFFTBytes != null) - { - for (int i = 0; i < mFFTBytes.length / fft_divis_bottom; i++) { - mFFTPoints[i * 4] = i * 4 * fft_divis_bottom; - mFFTPoints[i * 4 + 1] = mRect.height() - 2; - mFFTPoints[i * 4 + 2] = i * 4 * fft_divis_bottom; - byte rfk = mFFTBytes[fft_divis_bottom * i]; - byte ifk = mFFTBytes[fft_divis_bottom * i + 1]; - float magnitude = (rfk * rfk + ifk * ifk); - int dbValue = (int) (10 * Math.log10(magnitude)); - mFFTPoints[i * 4 + 3] = mRect.height() - (dbValue * 4) - 2; - } - - mCanvas.drawLines(mFFTPoints, mFFTLineBottomPaint); - } + mBarGraphRendererTop.render(fftData, mRect); + mBarGraphRendererBottom.render(fftData, mRect); // We totally need a thing moving along the bottom float cX = mRect.width()*(SystemClock.currentThreadTimeMillis() - mFlashTime)/mFlashPeriod; |