Skip to content

Instantly share code, notes, and snippets.

@MensObscura
Last active December 6, 2017 12:25
Show Gist options
  • Save MensObscura/52329f2abcf6fece6c4e33e2c0391215 to your computer and use it in GitHub Desktop.
Save MensObscura/52329f2abcf6fece6c4e33e2c0391215 to your computer and use it in GitHub Desktop.
Sound Wave Animation for vocal Listening
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class SoundWaveView extends View {
private static final String TAG = SoundWaveView.class.getSimpleName();
public static final float PCM_MAXIMUM_VALUE = 32768.0f; // 16-bit signed = 32768
protected short[][] mHistoryData;
protected short[] mSampleData;
protected float[] mPoints;
protected int mSampleSize;
protected float mSampleLength;
protected float mTimePerSlot;
protected Paint mPaint;
protected Paint mSecondPaint;
protected Paint mThirdPaint;
protected Map<Float, List<Integer>> mData;
private float[] mSecondPoints;
private float[] mThirdPoints;
private boolean mEnableUpdate = true;
private int mScreenDensity;
public SoundWaveView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public SoundWaveView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SoundWaveView(Context context) {
super(context);
init(context);
}
protected void init(Context ctx) {
mScreenDensity = Utils.getDeviceDentsity(getContext());
mPaint = new Paint();
mSecondPaint = new Paint();
mThirdPaint = new Paint();
mPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorBlue));
mPaint.setStrokeWidth(6);
setLayerType(LAYER_TYPE_SOFTWARE, mPaint);
setLayerType(LAYER_TYPE_SOFTWARE, mSecondPaint);
setLayerType(LAYER_TYPE_SOFTWARE, mThirdPaint);
mPaint.setShadowLayer(3.0f, 0, 0, ContextCompat.getColor(getContext(), R.color.colorBlue));
mSecondPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorBlueLight));
mSecondPaint.setStrokeWidth(4);
mSecondPaint.setShadowLayer(2.0f, 0, 0, ContextCompat.getColor(getContext(), R.color.colorBlueLight));
mThirdPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorBlueLight));
mThirdPaint.setStrokeWidth(4);
mThirdPaint.setShadowLayer(2.0f, 0, 0, ContextCompat.getColor(getContext(), R.color.colorBlueLight));
mData = new TreeMap<>();
}
public void setData(short[] data, int sampleSize, float sampleLength) {
if (mSampleData != null) {
setHistoricData(mSampleData);
}
this.mSampleData = data;
smoothData();
this.mSampleSize = sampleSize;
this.mSampleLength = sampleLength;
this.mTimePerSlot = this.mSampleLength / this.mSampleSize;
invalidate();
}
private void setHistoricData(short[] sampleData) {
if (mHistoryData == null) {
mHistoryData = new short[3][];
}
for (int i = 0; i < mHistoryData.length - 1; i++) {
mHistoryData[i] = mHistoryData[i + 1];
}
mHistoryData[2] = sampleData;
}
@Override
protected void onDraw(Canvas canvas) {
if (mEnableUpdate) {
canvas.drawColor(Color.WHITE);
if (mPoints == null) {
mPoints = new float[canvas.getWidth() * 4];
mSecondPoints = new float[mPoints.length];
mThirdPoints = new float[mPoints.length];
}
mData.clear();
if (mSampleData != null) {
int numPoints = canvas.getWidth();
if (mSampleData.length < canvas.getWidth()) {
numPoints = mSampleData.length;
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.width = mSampleData.length;
setLayoutParams(layoutParams);
}
int step = 1;
int halfHeight = canvas.getHeight() / 2;
float oldY = halfHeight;
float maxValue = 0;
for (int i = 0; i < numPoints; i++) {
double val = mSampleData[i * step];
// calm down the edge
if (((float) i / numPoints) < (4.0 / 8)) {
val = (float) (val * (((double) i / numPoints)) / (3.0 / 8));
} else if (((double) i / numPoints) > (4.0 / 8)) {
val = (float) (val * (1 - (((((double) i / numPoints) - (5.0 / 8)) / (3.0 / 8)))));
}
float y = (((float) val / PCM_MAXIMUM_VALUE) * halfHeight);
//increase amplitude
y = (float) (y * (mScreenDensity / 100.0));
float absValue = Math.abs(y);
if (maxValue < absValue) {
maxValue = absValue;
}
y = (y + halfHeight);
mPoints[i * 4 + 0] = i;
mPoints[i * 4 + 1] = oldY;
mPoints[i * 4 + 2] = i + 1;
mPoints[i * 4 + 3] = y;
oldY = y;
}
// Avoid too hight Wave
if (maxValue > halfHeight) {
float ratio = halfHeight / maxValue;
for (int i = 0; i < mPoints.length; i++) {
if (i % 2 == 1) {
float point = mPoints[i];
point -= halfHeight;
mPoints[i] = (point * ratio) + halfHeight;
}
}
}
for (int i = 0; i < mPoints.length; i++) {
if (i % 2 == 1) {
float point = mPoints[i];
point -= halfHeight;
mSecondPoints[i] = (point / 2) + halfHeight;
} else {
mSecondPoints[i] = mPoints[i];
}
}
for (int i = 0; i < mSecondPoints.length; i++) {
if (i % 2 == 1) {
float point = mPoints[i];
point -= halfHeight;
mThirdPoints[i] = (point * -1) + halfHeight;
} else {
mThirdPoints[i] = mPoints[i];
}
}
canvas.drawLines(mThirdPoints, mThirdPaint);
canvas.drawLines(mSecondPoints, mSecondPaint);
canvas.drawLines(mPoints, mPaint);
}
} else {
int halfHeight = canvas.getHeight() / 2;
canvas.drawLine(0, halfHeight, canvas.getWidth(), halfHeight, mPaint);
}
}
private void smoothData() {
mSampleData = smoothingBy(10); //remove big differencies
mSampleData = smoothingBy(2); //smoothing little studs
mSampleData = meanWithHistoric();
}
private short[] meanWithHistoric() {
short[] sampleData = new short[mSampleData.length];
for (int i = 0; i < mSampleData.length; i++) {
int count = 1;
short data = mSampleData[i];
if (mHistoryData != null) {
for (int j = 0; j < mHistoryData.length; j++) {
if (mHistoryData[j] != null && i < mHistoryData[j].length) {
data += mHistoryData[j][i];
count++;
}
}
}
sampleData[i] = (short) (data / count);
}
return sampleData;
}
private short[] smoothingBy(int smoothingIndex) {
short[] smoothedData = new short[mSampleData.length - 2 * smoothingIndex];
if (mSampleData != null) {
for (int i = smoothingIndex; i < mSampleData.length - smoothingIndex; i++) {
smoothedData[i - smoothingIndex] = meanData(mSampleData, i - smoothingIndex, i + smoothingIndex + 1);
}
}
return smoothedData;
}
private short meanData(short[] sampleData, int startIndex, int endIndex) {
int sum = 0;
for (int i = startIndex; i < endIndex; i++) {
sum += sampleData[i];
}
return (short) (sum / (startIndex - endIndex + 1));
}
public void setEnableUpdate(boolean enable) {
mEnableUpdate = enable;
}
}
getActivity().runOnUiThread(new Runnable() {
public void run() {
mSvSoundWave.setData(bufferShort, length, sampleLength);
}
});
class Utils{
public static int getDeviceDentsity(Context context) {
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
return metrics.densityDpi;
}
}
@MensObscura
Copy link
Author

MensObscura commented Dec 6, 2017

Result

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment