diff options
Diffstat (limited to 'src/com/pheelicks/visualizer/renderer')
4 files changed, 347 insertions, 0 deletions
diff --git a/src/com/pheelicks/visualizer/renderer/BarGraphRenderer.java b/src/com/pheelicks/visualizer/renderer/BarGraphRenderer.java new file mode 100644 index 0000000..6a92fc8 --- /dev/null +++ b/src/com/pheelicks/visualizer/renderer/BarGraphRenderer.java @@ -0,0 +1,65 @@ +package com.pheelicks.visualizer.renderer; + +import com.pheelicks.visualizer.AudioData; +import com.pheelicks.visualizer.FFTData; + +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/renderer/CircleRenderer.java b/src/com/pheelicks/visualizer/renderer/CircleRenderer.java new file mode 100644 index 0000000..4f25c46 --- /dev/null +++ b/src/com/pheelicks/visualizer/renderer/CircleRenderer.java @@ -0,0 +1,108 @@ +package com.pheelicks.visualizer.renderer; + +import com.pheelicks.visualizer.AudioData; +import com.pheelicks.visualizer.FFTData; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; + +public class CircleRenderer extends Renderer +{ + private Paint mPaint; + private boolean mCycleColor; + + /** + * Renders the audio data onto a pulsing circle + * @param canvas + * @param paint - Paint to draw lines with + */ + public CircleRenderer(Canvas canvas, + Paint paint) + { + this(canvas, + paint, + false); + } + + /** + * Renders the audio data onto a pulsing circle + * @param canvas + * @param paint - Paint to draw lines with + * @param cycleColor - If true the color will change on each frame + */ + public CircleRenderer(Canvas canvas, + Paint paint, + boolean cycleColor) + { + super(canvas); + mPaint = paint; + mCycleColor = cycleColor; + } + + @Override + public void onRender(AudioData data, Rect rect) + { + if(mCycleColor) + { + cycleColor(); + } + + for (int i = 0; i < data.bytes.length - 1; i++) { + float[] cartPoint = { + (float)i / (data.bytes.length - 1), + rect.height() / 2 + ((byte) (data.bytes[i] + 128)) * (rect.height() / 2) / 128 + }; + + float[] polarPoint = toPolar(cartPoint, rect); + mPoints[i * 4] = polarPoint[0]; + mPoints[i * 4 + 1] = polarPoint[1]; + + float[] cartPoint2 = { + (float)(i + 1) / (data.bytes.length - 1), + rect.height() / 2 + ((byte) (data.bytes[i + 1] + 128)) * (rect.height() / 2) / 128 + }; + + float[] polarPoint2 = toPolar(cartPoint2, rect); + mPoints[i * 4 + 2] = polarPoint2[0]; + mPoints[i * 4 + 3] = polarPoint2[1]; + } + + mCanvas.drawLines(mPoints, mPaint); + + // Controls the pulsing rate + modulation += 0.04; + } + + @Override + public void onRender(FFTData data, Rect rect) + { + // Do nothing, we only display audio data + } + + float modulation = 0; + float aggresive = 0.33f; + private float[] toPolar(float[] cartesian, Rect rect) + { + double cX = rect.width()/2; + double cY = rect.height()/2; + double angle = (cartesian[0]) * 2 * Math.PI; + double radius = ((rect.width()/2) * (1 - aggresive) + aggresive * cartesian[1]/2) * (1.2 + Math.sin(modulation))/2.2; + float[] out = { + (float)(cX + radius * Math.sin(angle)), + (float)(cY + radius * Math.cos(angle)) + }; + return out; + } + + private float colorCounter = 0; + private void cycleColor() + { + int r = (int)Math.floor(128*(Math.sin(colorCounter) + 1)); + int g = (int)Math.floor(128*(Math.sin(colorCounter + 2) + 1)); + int b = (int)Math.floor(128*(Math.sin(colorCounter + 4) + 1)); + mPaint.setColor(Color.argb(128, r, g, b)); + colorCounter += 0.03; + } +} diff --git a/src/com/pheelicks/visualizer/renderer/LineRenderer.java b/src/com/pheelicks/visualizer/renderer/LineRenderer.java new file mode 100644 index 0000000..412ca6e --- /dev/null +++ b/src/com/pheelicks/visualizer/renderer/LineRenderer.java @@ -0,0 +1,107 @@ +package com.pheelicks.visualizer.renderer; + +import com.pheelicks.visualizer.AudioData; +import com.pheelicks.visualizer.FFTData; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; + +public class LineRenderer extends Renderer +{ + private Paint mPaint; + private Paint mFlashPaint; + private boolean mCycleColor; + private float amplitude = 0; + + + /** + * Renders the audio data onto a line. The line flashes on prominent beats + * @param canvas + * @param paint - Paint to draw lines with + * @param paint - Paint to draw flash with + */ + public LineRenderer(Canvas canvas, + Paint paint, + Paint flashPaint) + { + this(canvas, + paint, + flashPaint, + false); + } + + /** + * Renders the audio data onto a line. The line flashes on prominent beats + * @param canvas + * @param paint - Paint to draw lines with + * @param paint - Paint to draw flash with + * @param cycleColor - If true the color will change on each frame + */ + public LineRenderer(Canvas canvas, + Paint paint, + Paint flashPaint, + boolean cycleColor) + { + super(canvas); + mPaint = paint; + mFlashPaint = flashPaint; + mCycleColor = cycleColor; + } + + @Override + public void onRender(AudioData data, Rect rect) + { + if(mCycleColor) + { + cycleColor(); + } + + // Calculate points for line + for (int i = 0; i < data.bytes.length - 1; i++) { + mPoints[i * 4] = rect.width() * i / (data.bytes.length - 1); + mPoints[i * 4 + 1] = rect.height() / 2 + + ((byte) (data.bytes[i] + 128)) * (rect.height() / 3) / 128; + mPoints[i * 4 + 2] = rect.width() * (i + 1) / (data.bytes.length - 1); + mPoints[i * 4 + 3] = rect.height() / 2 + + ((byte) (data.bytes[i + 1] + 128)) * (rect.height() / 3) / 128; + } + + // Calc amplitude for this waveform + float accumulator = 0; + for (int i = 0; i < data.bytes.length - 1; i++) { + accumulator += Math.abs(data.bytes[i]); + } + + float amp = accumulator/(128 * data.bytes.length); + if(amp > amplitude) + { + // Amplitude is bigger than normal, make a prominent line + amplitude = amp; + mCanvas.drawLines(mPoints, mFlashPaint); + } + else + { + // Amplitude is nothing special, reduce the amplitude + amplitude *= 0.99; + mCanvas.drawLines(mPoints, mPaint); + } + } + + @Override + public void onRender(FFTData data, Rect rect) + { + // Do nothing, we only display audio data + } + + private float colorCounter = 0; + private void cycleColor() + { + int r = (int)Math.floor(128*(Math.sin(colorCounter) + 3)); + int g = (int)Math.floor(128*(Math.sin(colorCounter + 1) + 1)); + int b = (int)Math.floor(128*(Math.sin(colorCounter + 7) + 1)); + mPaint.setColor(Color.argb(128, r, g, b)); + colorCounter += 0.03; + } +} diff --git a/src/com/pheelicks/visualizer/renderer/Renderer.java b/src/com/pheelicks/visualizer/renderer/Renderer.java new file mode 100644 index 0000000..f633089 --- /dev/null +++ b/src/com/pheelicks/visualizer/renderer/Renderer.java @@ -0,0 +1,67 @@ +package com.pheelicks.visualizer.renderer; + +import com.pheelicks.visualizer.AudioData; +import com.pheelicks.visualizer.FFTData; + +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); + } +} |