Skip to content

Commit 6fa6928

Browse files
author
Felix Palmer
committed
Recfactor FFT rendering into separate class
1 parent 348bb37 commit 6fa6928

File tree

6 files changed

+198
-49
lines changed

6 files changed

+198
-49
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.pheelicks.visualizer;
2+
3+
// Data class to explicitly indicate that these bytes are raw audio data
4+
public class AudioData
5+
{
6+
public AudioData(byte[] bytes)
7+
{
8+
this.bytes = bytes;
9+
}
10+
11+
public byte[] bytes;
12+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.pheelicks.visualizer;
2+
3+
import android.graphics.Canvas;
4+
import android.graphics.Paint;
5+
import android.graphics.Rect;
6+
7+
public class BarGraphRenderer extends Renderer
8+
{
9+
private int mDivisions;
10+
private Paint mPaint;
11+
private boolean mTop;
12+
13+
/**
14+
* Renders the FFT data as a series of lines, in histogram form
15+
* @param canvas
16+
* @param divisions - must be a power of 2. Controls how many lines to draw
17+
* @param paint - Paint to draw lines with
18+
* @param top - whether to draw the lines at the top of the canvas, or the bottom
19+
*/
20+
public BarGraphRenderer(Canvas canvas,
21+
int divisions,
22+
Paint paint,
23+
boolean top)
24+
{
25+
super(canvas);
26+
mDivisions = divisions;
27+
mPaint = paint;
28+
mTop = top;
29+
}
30+
31+
@Override
32+
public void onRender(AudioData data, Rect rect)
33+
{
34+
// Do nothing, we only display FFT data
35+
}
36+
37+
@Override
38+
public void onRender(FFTData data, Rect rect)
39+
{
40+
for (int i = 0; i < data.bytes.length / mDivisions; i++) {
41+
mFFTPoints[i * 4] = i * 4 * mDivisions;
42+
mFFTPoints[i * 4 + 2] = i * 4 * mDivisions;
43+
byte rfk = data.bytes[mDivisions * i];
44+
byte ifk = data.bytes[mDivisions * i + 1];
45+
float magnitude = (rfk * rfk + ifk * ifk);
46+
int dbValue = (int) (10 * Math.log10(magnitude));
47+
48+
if(mTop)
49+
{
50+
mFFTPoints[i * 4 + 1] = 0;
51+
mFFTPoints[i * 4 + 3] = (dbValue * 2 - 10);
52+
}
53+
else
54+
{
55+
mFFTPoints[i * 4 + 1] = rect.height();
56+
mFFTPoints[i * 4 + 3] = rect.height() - (dbValue * 2 - 10);
57+
}
58+
}
59+
60+
mCanvas.drawLines(mFFTPoints, mPaint);
61+
}
62+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.pheelicks.visualizer;
2+
3+
// Data class to explicitly indicate that these bytes are the FFT of audio data
4+
public class FFTData
5+
{
6+
public FFTData(byte[] bytes)
7+
{
8+
this.bytes = bytes;
9+
}
10+
11+
public byte[] bytes;
12+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.pheelicks.visualizer;
2+
3+
import android.graphics.Canvas;
4+
import android.graphics.Rect;
5+
6+
abstract public class Renderer
7+
{
8+
// Canvas & Rect to render to
9+
protected Canvas mCanvas;
10+
11+
// Have these as members, so we don't have to re-create them each time
12+
protected float[] mPoints;
13+
protected float[] mFFTPoints;
14+
public Renderer(Canvas canvas)
15+
{
16+
mCanvas = canvas;
17+
}
18+
19+
// As the display of raw/FFT audio will usually look different, subclasses
20+
// will typically only implement one of the below methods
21+
/**
22+
* Implement this method to render the audio data onto the canvas
23+
* @param data - Data to render
24+
* @param rect - Rect to render into
25+
*/
26+
abstract public void onRender(AudioData data, Rect rect);
27+
28+
/**
29+
* Implement this method to render the FFT audio data onto the canvas
30+
* @param data - Data to render
31+
* @param rect - Rect to render into
32+
*/
33+
abstract public void onRender(FFTData data, Rect rect);
34+
35+
36+
// These methods should actually be called for rendering
37+
/**
38+
* Render the audio data onto the canvas
39+
* @param data - Data to render
40+
* @param rect - Rect to render into
41+
*/
42+
final public void render(AudioData data, Rect rect)
43+
{
44+
if (mPoints == null || mPoints.length < data.bytes.length * 4) {
45+
mPoints = new float[data.bytes.length * 4];
46+
}
47+
48+
onRender(data, rect);
49+
}
50+
51+
/**
52+
* Render the FFT data onto the canvas
53+
* @param data - Data to render
54+
* @param rect - Rect to render into
55+
*/
56+
final public void render(FFTData data, Rect rect)
57+
{
58+
if (mFFTPoints == null || mFFTPoints.length < data.bytes.length * 4) {
59+
mFFTPoints = new float[data.bytes.length * 4];
60+
}
61+
62+
onRender(data, rect);
63+
}
64+
}

src/com/pheelicks/visualizer/VisualizerActivity.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ public void onCreate(Bundle savedInstanceState) {
1818

1919
mPlayer = MediaPlayer.create(this, R.raw.test);
2020
mPlayer.setLooping(true);
21+
mPlayer.start();
2122

2223
linkVisualizer(mPlayer);
2324
}
2425

2526
/**
26-
* Links the visualizer to a player
27+
* Links the visualizer to a player
2728
* TODO Refactor this into visualizer
2829
* @param player
2930
*/
@@ -69,6 +70,34 @@ public void onCompletion(MediaPlayer mediaPlayer)
6970
});
7071
}
7172

73+
// Cleanup
74+
@Override
75+
protected void onPause()
76+
{
77+
if (isFinishing() && (mPlayer != null))
78+
{
79+
mVisualizer.release();
80+
mPlayer.release();
81+
mPlayer = null;
82+
}
83+
84+
super.onPause();
85+
}
86+
87+
@Override
88+
protected void onDestroy()
89+
{
90+
if (mPlayer != null)
91+
{
92+
mPlayer.stop();
93+
mPlayer.release();
94+
mPlayer = null;
95+
}
96+
97+
super.onDestroy();
98+
}
99+
100+
// Actions for buttons defined in xml
72101
public void startPressed(View view)
73102
{
74103
mPlayer.start();

src/com/pheelicks/visualizer/VisualizerView.java

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,11 @@ class VisualizerView extends View {
3535

3636
private Paint mCirclePaint = new Paint();
3737
private Paint mLinePaint = new Paint();
38-
private Paint mFFTLineTopPaint = new Paint();
39-
private Paint mFFTLineBottomPaint = new Paint();
4038
private Paint mSpecialLinePaint = new Paint();
4139
private Paint mProgressLinePaint = new Paint();
4240
private Paint mFlashPaint = new Paint();
4341
private Paint mFadePaint = new Paint();
4442

45-
final int fft_divis_top = 8; // Set to some factor of 2 to adjust number of FFT bars
46-
final int fft_divis_bottom = 16; // Set to some factor of 2 to adjust number of FFT bars
47-
4843
// Usual BS of 3 constructors
4944
public VisualizerView(Context context, AttributeSet attrs, int defStyle)
5045
{
@@ -81,13 +76,6 @@ private void init() {
8176
mProgressLinePaint.setAntiAlias(true);
8277
mProgressLinePaint.setColor(Color.argb(255, 22, 131, 255));
8378

84-
mFFTLineTopPaint.setStrokeWidth(fft_divis_top * 3f);
85-
mFFTLineTopPaint.setAntiAlias(true);
86-
mFFTLineTopPaint.setColor(Color.argb(200, 233, 0, 44));
87-
88-
mFFTLineBottomPaint.setStrokeWidth(fft_divis_bottom * 3f);
89-
mFFTLineBottomPaint.setAntiAlias(true);
90-
mFFTLineBottomPaint.setColor(Color.argb(88, 0, 233, 44));
9179

9280
mFlashPaint.setColor(Color.argb(122, 255, 255, 255));
9381

@@ -134,6 +122,10 @@ private void rotateColours()
134122
Canvas mCanvas;
135123
Random mRandom = new Random();
136124
float amplitude = 0;
125+
126+
BarGraphRenderer mBarGraphRendererTop;
127+
BarGraphRenderer mBarGraphRendererBottom;
128+
137129
@Override
138130
protected void onDraw(Canvas canvas) {
139131
super.onDraw(canvas);
@@ -175,6 +167,17 @@ protected void onDraw(Canvas canvas) {
175167
if(mCanvas == null)
176168
{
177169
mCanvas = new Canvas(mCanvasBitmap);
170+
Paint paint = new Paint();
171+
paint.setStrokeWidth(50f);
172+
paint.setAntiAlias(true);
173+
paint.setColor(Color.argb(200, 233, 0, 44));
174+
mBarGraphRendererBottom = new BarGraphRenderer(mCanvas, 16, paint, false);
175+
176+
Paint paint2 = new Paint();
177+
paint2.setStrokeWidth(12f);
178+
paint2.setAntiAlias(true);
179+
paint2.setColor(Color.argb(200, 11, 111, 233));
180+
mBarGraphRendererTop = new BarGraphRenderer(mCanvas, 4, paint2, true);
178181
}
179182

180183
mCanvas.drawLines(mPoints, mCirclePaint);
@@ -214,43 +217,10 @@ protected void onDraw(Canvas canvas) {
214217
return;
215218
}
216219

217-
if (mFFTPoints == null || mFFTPoints.length < mBytes.length * 4) {
218-
mFFTPoints = new float[mFFTBytes.length * 4];
219-
}
220-
221-
// Equalizer top
222-
if(mFFTBytes != null)
223-
{
224-
for (int i = 0; i < mFFTBytes.length / fft_divis_top; i++) {
225-
mFFTPoints[i * 4] = i * 4 * fft_divis_top;
226-
mFFTPoints[i * 4 + 1] = 0;
227-
mFFTPoints[i * 4 + 2] = i * 4 * fft_divis_top;
228-
byte rfk = mFFTBytes[fft_divis_top * i];
229-
byte ifk = mFFTBytes[fft_divis_top * i + 1];
230-
float magnitude = (rfk * rfk + ifk * ifk);
231-
int dbValue = (int) (10 * Math.log10(magnitude));
232-
mFFTPoints[i * 4 + 3] = (dbValue * 2 - 10);
233-
}
234-
235-
mCanvas.drawLines(mFFTPoints, mFFTLineTopPaint);
236-
}
220+
FFTData fftData = new FFTData(mFFTBytes);
237221

238-
// Equalizer bottom
239-
if(mFFTBytes != null)
240-
{
241-
for (int i = 0; i < mFFTBytes.length / fft_divis_bottom; i++) {
242-
mFFTPoints[i * 4] = i * 4 * fft_divis_bottom;
243-
mFFTPoints[i * 4 + 1] = mRect.height() - 2;
244-
mFFTPoints[i * 4 + 2] = i * 4 * fft_divis_bottom;
245-
byte rfk = mFFTBytes[fft_divis_bottom * i];
246-
byte ifk = mFFTBytes[fft_divis_bottom * i + 1];
247-
float magnitude = (rfk * rfk + ifk * ifk);
248-
int dbValue = (int) (10 * Math.log10(magnitude));
249-
mFFTPoints[i * 4 + 3] = mRect.height() - (dbValue * 4) - 2;
250-
}
251-
252-
mCanvas.drawLines(mFFTPoints, mFFTLineBottomPaint);
253-
}
222+
mBarGraphRendererTop.render(fftData, mRect);
223+
mBarGraphRendererBottom.render(fftData, mRect);
254224

255225
// We totally need a thing moving along the bottom
256226
float cX = mRect.width()*(SystemClock.currentThreadTimeMillis() - mFlashTime)/mFlashPeriod;

0 commit comments

Comments
 (0)