Last active
December 6, 2017 12:25
-
-
Save MensObscura/52329f2abcf6fece6c4e33e2c0391215 to your computer and use it in GitHub Desktop.
Sound Wave Animation for vocal Listening
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
getActivity().runOnUiThread(new Runnable() { | |
public void run() { | |
mSvSoundWave.setData(bufferShort, length, sampleLength); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Result