Skip to content

Instantly share code, notes, and snippets.

@MensObscura
Last active December 6, 2017 12:25
Show Gist options
  • Select an option

  • Save MensObscura/52329f2abcf6fece6c4e33e2c0391215 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown
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