aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Palmer <felix.palmer@metaswitch.com>2011-12-02 13:48:18 -0800
committerFelix Palmer <felix.palmer@metaswitch.com>2011-12-02 13:50:17 -0800
commit348bb37f8e8fcb902c9a2f1dd59fd5c8ee3cae4d (patch)
tree129b8cb618731e0887c3da4881ba8bc652a99f91
parenta92a1757e7b882e0dbb00a7c580bfa3e09255083 (diff)
downloadandroid_external_android-visualizer-348bb37f8e8fcb902c9a2f1dd59fd5c8ee3cae4d.tar.gz
android_external_android-visualizer-348bb37f8e8fcb902c9a2f1dd59fd5c8ee3cae4d.tar.bz2
android_external_android-visualizer-348bb37f8e8fcb902c9a2f1dd59fd5c8ee3cae4d.zip
Added (messy) Visualizer code
-rw-r--r--AndroidManifest.xml2
-rw-r--r--res/drawable/bg.xml11
-rw-r--r--res/layout/main.xml11
-rw-r--r--src/com/pheelicks/visualizer/VisualizerActivity.java51
-rw-r--r--src/com/pheelicks/visualizer/VisualizerView.java289
5 files changed, 363 insertions, 1 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2adc0e5..f0a66df 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -21,5 +21,7 @@
</intent-filter>
</activity>
</application>
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest> \ No newline at end of file
diff --git a/res/drawable/bg.xml b/res/drawable/bg.xml
new file mode 100644
index 0000000..3fadd34
--- /dev/null
+++ b/res/drawable/bg.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:startColor="#acacac"
+ android:centerColor="#4f4f4f"
+ android:endColor="#3f3f3f"
+ android:angle="-90">
+ </gradient>
+</shape> \ No newline at end of file
diff --git a/res/layout/main.xml b/res/layout/main.xml
index 83899fc..a225cb7 100644
--- a/res/layout/main.xml
+++ b/res/layout/main.xml
@@ -2,12 +2,21 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
+ android:background="@drawable/bg"
android:orientation="vertical" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
- android:layout_weight="1" >
+ android:layout_margin="10dp"
+ android:layout_weight="1"
+ android:background="#000" >
+
+ <com.pheelicks.visualizer.VisualizerView
+ android:id="@+id/visualizerView"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+ </com.pheelicks.visualizer.VisualizerView>
</FrameLayout>
<LinearLayout
diff --git a/src/com/pheelicks/visualizer/VisualizerActivity.java b/src/com/pheelicks/visualizer/VisualizerActivity.java
index b56be76..795e90e 100644
--- a/src/com/pheelicks/visualizer/VisualizerActivity.java
+++ b/src/com/pheelicks/visualizer/VisualizerActivity.java
@@ -2,11 +2,13 @@ package com.pheelicks.visualizer;
import android.app.Activity;
import android.media.MediaPlayer;
+import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.view.View;
public class VisualizerActivity extends Activity {
private MediaPlayer mPlayer;
+ private Visualizer mVisualizer;
/** Called when the activity is first created. */
@Override
@@ -16,6 +18,55 @@ public class VisualizerActivity extends Activity {
mPlayer = MediaPlayer.create(this, R.raw.test);
mPlayer.setLooping(true);
+
+ linkVisualizer(mPlayer);
+ }
+
+ /**
+ * Links the visualizer to a player
+ * TODO Refactor this into visualizer
+ * @param player
+ */
+ private void linkVisualizer(MediaPlayer player)
+ {
+
+ final VisualizerView visualizerView = (VisualizerView) findViewById(R.id.visualizerView);
+
+ // Create the Visualizer object and attach it to our media player.
+ mVisualizer = new Visualizer(player.getAudioSessionId());
+ mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
+
+ // Pass through Visualizer data to VisualizerView
+ Visualizer.OnDataCaptureListener captureListener = new Visualizer.OnDataCaptureListener()
+ {
+ @Override
+ public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,
+ int samplingRate)
+ {
+ visualizerView.updateVisualizer(bytes);
+ }
+
+ @Override
+ public void onFftDataCapture(Visualizer visualizer, byte[] bytes,
+ int samplingRate)
+ {
+ visualizerView.updateVisualizerFFT(bytes);
+ }
+ };
+
+ mVisualizer.setDataCaptureListener(captureListener,
+ Visualizer.getMaxCaptureRate() / 2, true, true);
+
+ // Enabled Visualizer and disable when we're done with the stream
+ mVisualizer.setEnabled(true);
+ mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
+ {
+ @Override
+ public void onCompletion(MediaPlayer mediaPlayer)
+ {
+ mVisualizer.setEnabled(false);
+ }
+ });
}
public void startPressed(View view)
diff --git a/src/com/pheelicks/visualizer/VisualizerView.java b/src/com/pheelicks/visualizer/VisualizerView.java
new file mode 100644
index 0000000..94505b6
--- /dev/null
+++ b/src/com/pheelicks/visualizer/VisualizerView.java
@@ -0,0 +1,289 @@
+package com.pheelicks.visualizer;
+
+// WARNING!!! This file has more magic numbers in it than you could shake a
+// stick at
+
+import java.util.Random;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.media.audiofx.Visualizer;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * A class that draws waveform data received from a
+ * {@link Visualizer.OnDataCaptureListener#onWaveFormDataCapture }
+ */
+class VisualizerView extends View {
+ private static final String TAG = "VisualizerView";
+
+ private byte[] mBytes;
+ private byte[] mFFTBytes;
+ private float[] mPoints;
+ private float[] mFFTPoints;
+ private Rect mRect = new Rect();
+
+ 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)
+ {
+ super(context, attrs);
+ init();
+ }
+
+ public VisualizerView(Context context, AttributeSet attrs)
+ {
+ this(context, attrs, 0);
+ }
+
+ public VisualizerView(Context context)
+ {
+ this(context, null, 0);
+ }
+
+ private void init() {
+ mBytes = null;
+
+ mCirclePaint.setStrokeWidth(3f);
+ mCirclePaint.setAntiAlias(true);
+ mCirclePaint.setColor(Color.argb(255, 222, 92, 143));
+
+ mLinePaint.setStrokeWidth(1f);
+ mLinePaint.setAntiAlias(true);
+ mLinePaint.setColor(Color.argb(88, 0, 128, 255));
+
+ mSpecialLinePaint.setStrokeWidth(5f);
+ mSpecialLinePaint.setAntiAlias(true);
+ mSpecialLinePaint.setColor(Color.argb(188, 255, 255, 255));
+
+ mProgressLinePaint.setStrokeWidth(4f);
+ 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));
+
+ mFadePaint.setColor(Color.argb(238, 255, 255, 255)); // Adjust alpha to change how quickly the image fades
+ mFadePaint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
+ }
+
+ public void updateVisualizer(byte[] bytes) {
+ mBytes = bytes;
+ rotateColours();
+ invalidate();
+ }
+
+ boolean mFlash = false;
+ long mFlashTime = 0;
+ long mFlashPeriod = 4000;
+
+ public void flash() {
+ mFlash = true;
+ long now = SystemClock.currentThreadTimeMillis();
+ mFlashPeriod = now - mFlashTime;
+ mFlashTime = now;
+ invalidate();
+ }
+
+ public void updateVisualizerFFT(byte[] bytes) {
+ mFFTBytes = bytes;
+ invalidate();
+ }
+
+
+ float colorCounter = 0;
+ private void rotateColours()
+ {
+ 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));
+ mLinePaint.setColor(Color.argb(128, r, g, b));
+ mCirclePaint.setColor(Color.argb(255, g, b, r));
+ colorCounter += 0.03;
+ }
+
+ Bitmap mCanvasBitmap;
+ Canvas mCanvas;
+ Random mRandom = new Random();
+ float amplitude = 0;
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (mBytes == null) {
+ return;
+ }
+
+ if (mPoints == null || mPoints.length < mBytes.length * 4) {
+ mPoints = new float[mBytes.length * 4];
+ }
+
+ mRect.set(0, 0, getWidth(), getHeight());
+
+ for (int i = 0; i < mBytes.length - 1; i++) {
+ float[] cartPoint = {
+ (float)i / (mBytes.length - 1),
+ mRect.height() / 2 + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2) / 128
+ };
+
+ float[] polarPoint = toPolar(cartPoint);
+ mPoints[i * 4] = polarPoint[0];
+ mPoints[i * 4 + 1] = polarPoint[1];
+
+ float[] cartPoint2 = {
+ (float)(i + 1) / (mBytes.length - 1),
+ mRect.height() / 2 + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2) / 128
+ };
+
+ float[] polarPoint2 = toPolar(cartPoint2);
+ mPoints[i * 4 + 2] = polarPoint2[0];
+ mPoints[i * 4 + 3] = polarPoint2[1];
+ }
+
+ if(mCanvasBitmap == null)
+ {
+ mCanvasBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888);
+ }
+ if(mCanvas == null)
+ {
+ mCanvas = new Canvas(mCanvasBitmap);
+ }
+
+ mCanvas.drawLines(mPoints, mCirclePaint);
+
+
+ // Draw normal line - offset by amplitude
+ for (int i = 0; i < mBytes.length - 1; i++) {
+ mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1);
+ mPoints[i * 4 + 1] = mRect.height() / 2
+ + ((byte) (mBytes[i] + 128)) * (mRect.height() / 3) / 128;
+ mPoints[i * 4 + 2] = mRect.width() * (i + 1) / (mBytes.length - 1);
+ mPoints[i * 4 + 3] = mRect.height() / 2
+ + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 3) / 128;
+ }
+
+ // Calc amplitude for this waveform
+ float accumulator = 0;
+ for (int i = 0; i < mBytes.length - 1; i++) {
+ accumulator += Math.abs(mBytes[i]);
+ }
+
+ float amp = accumulator/(128 * mBytes.length);
+ if(amp > amplitude)
+ {
+ amplitude = amp;
+ // Occassionally, make a prominent line
+ mCanvas.drawLines(mPoints, mSpecialLinePaint);
+ }
+ else
+ {
+ amplitude *= 0.99;
+ mCanvas.drawLines(mPoints, mLinePaint);
+ }
+
+ // FFT time!!!!
+ if (mFFTBytes == null) {
+ 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);
+ }
+
+ // 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);
+ }
+
+ // We totally need a thing moving along the bottom
+ float cX = mRect.width()*(SystemClock.currentThreadTimeMillis() - mFlashTime)/mFlashPeriod;
+
+ mCanvas.drawLine(cX - 35, mRect.height(), cX, mRect.height(), mProgressLinePaint);
+
+ // Fade out old contents
+ mCanvas.drawPaint(mFadePaint);
+
+ if(mFlash)
+ {
+ mFlash = false;
+ mCanvas.drawPaint(mFlashPaint);
+ }
+
+ canvas.drawBitmap(mCanvasBitmap, new Matrix(), null);
+ modulation += 0.04;
+ }
+
+ float modulation = 0;
+ float aggresive = 0.33f;
+ private float[] toPolar(float[] cartesian)
+ {
+ double cX = mRect.width()/2;
+ double cY = mRect.height()/2;
+ double angle = (cartesian[0]) * 2 * Math.PI;
+ double radius = ((mRect.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;
+ }
+
+
+} \ No newline at end of file