目录
前言
本文抄的,音乐频谱相关,想单独记录于此,方便自己查阅和回顾。
具体可以看:https://github.com/wanliLiu/androidequalizer
PS: 图片截图Github上的
正文
VisualizerWaveView2.java
图中第一个
public class VisualizerWaveView2 extends View { // bytes数组保存了波形抽样点的值 private byte[] mBuffer; private float[] mPoints; private Paint mPaint = new Paint(); private Rect mRect = new Rect(); private byte mType = 0; public VisualizerWaveView2(Context context) { this(context, null); } public VisualizerWaveView2(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public VisualizerWaveView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mBuffer = null; mPaint.setStrokeWidth(1f); mPaint.setAntiAlias(true);//抗锯齿 mPaint.setColor(Color.GREEN);//画笔颜色 mPaint.setStyle(Paint.Style.FILL); } public void updateVisualizer(byte[] ftt) { mBuffer = ftt; // 通知该组件重绘自己。 invalidate(); } @Override public boolean onTouchEvent(MotionEvent me) { // 当用户触碰该组件时,切换波形类型 if (me.getAction() != MotionEvent.ACTION_DOWN) { return false; } mType++; if (mType >= 3) { mType = 0; } return true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mBuffer == null) { return; } // 绘制白色背景 // canvas.drawColor(Color.WHITE); // 使用rect对象记录该组件的宽度和高度 mRect.set(0, 0, getWidth(), getHeight()); switch (mType) { // -------绘制块状的波形图------- case 0: for (int i = 0; i < mBuffer.length - 1; i++) { float left = getWidth() * i * 1.0f / (mBuffer.length - 1); // 根据波形值计算该矩形的高度 float top = mRect.height() - (byte) (mBuffer[i + 1] + 128) * 1.0f * mRect.height() / 128; float right = left + 1; float bottom = mRect.height(); canvas.drawRect(left, top, right, bottom, mPaint); } break; // -------绘制柱状的波形图(每隔18个抽样点绘制一个矩形)------- case 1: for (int i = 0; i < mBuffer.length - 1; i += 18) { float left = mRect.width() * 1.0f * i / (mBuffer.length - 1); // 根据波形值计算该矩形的高度 float top = mRect.height() - (byte) (mBuffer[i + 1] + 128) * 1.0f * mRect.height() / 128; float right = left + 6; float bottom = mRect.height(); canvas.drawRect(left, top, right, bottom, mPaint); } break; // -------绘制曲线波形图------- case 2: // 如果point数组还未初始化 if (mPoints == null || mPoints.length < mBuffer.length * 4) { mPoints = new float[mBuffer.length * 4]; } for (int i = 0; i < mBuffer.length - 1; i++) { // 计算第i个点的x坐标 mPoints[i * 4] = mRect.width() * 1.0f * i / (mBuffer.length - 1); // 根据bytes[i]的值(波形点的值)计算第i个点的y坐标 mPoints[i * 4 + 1] = (mRect.height() * 1.0f / 2) + ((byte) (mBuffer[i] + 128)) * 128 / (mRect.height() * 1.0f / 2); // 计算第i+1个点的x坐标 mPoints[i * 4 + 2] = mRect.width() * (i + 1) * 1.0f / (mBuffer.length - 1); // 根据bytes[i+1]的值(波形点的值)计算第i+1个点的y坐标 mPoints[i * 4 + 3] = (mRect.height() * 1.0f / 2) + ((byte) (mBuffer[i + 1] + 128)) * 128 / (mRect.height() * 1.0f / 2); } // 绘制波形曲线 canvas.drawLines(mPoints, mPaint); break; } } }
VisualizerWaveView.java
效果图中第二个
public class VisualizerWaveView extends View { private byte[] mBuffer; private float[] mPoints; private final Rect mRect = new Rect(); private final Paint mForePaint = new Paint(); public VisualizerWaveView(Context context) { this(context, null); } public VisualizerWaveView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public VisualizerWaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mBuffer = null; mForePaint.setStrokeWidth(1f); mForePaint.setAntiAlias(true); mForePaint.setColor(Color.GREEN); } public void updateVisualizer(byte[] waveForm) { mBuffer = waveForm; invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mBuffer == null) { return; } if (mPoints == null || mPoints.length < mBuffer.length * 4) { mPoints = new float[mBuffer.length * 4]; } // 绘制白色背景 // canvas.drawColor(Color.WHITE); mRect.set(0, 0, getWidth(), getHeight()); //绘制波形 for (int i = 0; i < mBuffer.length - 1; i++) { mPoints[i * 4] = mRect.width() * i * 1.0f / (mBuffer.length - 1); mPoints[i * 4 + 1] = mRect.height() * 1.0f / 2 + ((byte) (mBuffer[i] + 128)) * (mRect.height() * 1.0f / 2) / 128; mPoints[i * 4 + 2] = mRect.width() * (i + 1) * 1.0f / (mBuffer.length - 1); mPoints[i * 4 + 3] = mRect.height() * 1.0f / 2 + ((byte) (mBuffer[i + 1] + 128)) * (mRect.height() * 1.0f / 2) / 128; } canvas.drawLines(mPoints, mForePaint); } }
VisualizerFFTView.java
图中第三个
public class VisualizerFFTView extends View { private byte[] mBuffer; private float[] mPoints; private final Rect mRect = new Rect(); private final Paint mForePaint = new Paint(); private final int mSpectrumNum = 48; public VisualizerFFTView(Context context) { this(context, null); } public VisualizerFFTView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public VisualizerFFTView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mBuffer = null; mForePaint.setStrokeWidth(8f); mForePaint.setAntiAlias(true); mForePaint.setColor(Color.rgb(0, 128, 255)); } public void updateVisualizer(byte[] fft) { byte[] model = new byte[fft.length / 2 + 1]; model[0] = (byte) Math.abs(fft[0]); for (int i = 2, j = 1; j < mSpectrumNum; ) { model[j] = (byte) Math.hypot(fft[i], fft[i + 1]); i += 2; j++; } mBuffer = model; invalidate();; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mBuffer == null) { return; } if (mPoints == null || mPoints.length < mBuffer.length * 4) { mPoints = new float[mBuffer.length * 4]; } // 绘制白色背景 // canvas.drawColor(Color.WHITE); mRect.set(0, 0, getWidth(), getHeight()); //绘制频谱 final int baseX = mRect.width() / mSpectrumNum; final int height = mRect.height(); for (int i = 0; i < mSpectrumNum; i++) { if (mBuffer[i] < 0) { mBuffer[i] = 127; } final int xi = baseX * i + baseX / 2; mPoints[i * 4] = xi; mPoints[i * 4 + 1] = height; mPoints[i * 4 + 2] = xi; mPoints[i * 4 + 3] = height - mBuffer[i]; } canvas.drawLines(mPoints, mForePaint); } }
VisualizerFFTView2.java
图中第四个
public class VisualizerFFTView2 extends View { private final int DN_W = 470;//view宽度与单个音频块占比 - 正常480 需微调 private final int DN_H = 360;//view高度与单个音频块占比 private final int DN_SL = 15;//单个音频块宽度 private final int DN_SW = 5;//单个音频块高度 private int hgap = 0; private int vgap = 0; private int levelStep = 0; private float strokeWidth = 0; private float strokeLength = 0; protected final static int MAX_LEVEL = 30;//音量柱·音频块 - 最大个数 protected final static int CYLINDER_NUM = 26;//音量柱 - 最大个数 protected Paint mPaint = null;//画笔 protected byte[] mData = new byte[CYLINDER_NUM];//音量柱 数组 boolean mDataEn = true; public VisualizerFFTView2(Context context) { this(context, null); } public VisualizerFFTView2(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public VisualizerFFTView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init(){ mPaint = new Paint();//初始化画笔工具 mPaint.setAntiAlias(true);//抗锯齿 mPaint.setColor(Color.RED);//画笔颜色 mPaint.setStrokeJoin(Paint.Join.ROUND); //频块圆角 mPaint.setStrokeCap(Paint.Cap.ROUND); //频块圆角 levelStep = 230 / MAX_LEVEL; } //执行 Layout 操作 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); float w, h, xr, yr; w = right - left; h = bottom - top; xr = w / (float) DN_W; yr = h / (float) DN_H; strokeWidth = DN_SW * yr; strokeLength = DN_SL * xr; hgap = (int) ((w - strokeLength * CYLINDER_NUM) / (CYLINDER_NUM + 1)); vgap = (int) (h / (MAX_LEVEL + 2));//频谱块高度 mPaint.setStrokeWidth(strokeWidth); //设置频谱块宽度 } //绘制频谱块和倒影 protected void drawCylinder(Canvas canvas, float x, byte value) { if (value == 0) { value = 1; }//最少有一个频谱块 for (int i = 0; i < value; i++) { //每个能量柱绘制value个能量块 float y = (getHeight() / 2 - i * vgap - vgap);//计算y轴坐标 float y1 = (getHeight() / 2 + i * vgap + vgap); //绘制频谱块 mPaint.setColor(Color.RED);//画笔颜色 canvas.drawLine(x, y, (x + strokeLength), y, mPaint);//绘制频谱块 //绘制音量柱倒影 if (i <= 6 && value > 0) { mPaint.setColor(Color.RED);//画笔颜色 mPaint.setAlpha(100 - (100 / 6 * i));//倒影颜色 canvas.drawLine(x, y1, (x + strokeLength), y1, mPaint);//绘制频谱块 } } } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); // canvas.drawColor(Color.WHITE); int j = -4; for (int i = 0; i < CYLINDER_NUM / 2 - 4; i++) { //绘制25个能量柱 drawCylinder(canvas, strokeWidth / 2 + hgap + i * (hgap + strokeLength), mData[i]); } for (int i = CYLINDER_NUM; i >= CYLINDER_NUM / 2 - 4; i--) { j++; drawCylinder(canvas, strokeWidth / 2 + hgap + (CYLINDER_NUM / 2 + j - 1) * (hgap + strokeLength), mData[i - 1]); } } /** * @param fft */ public void updateVisualizer(byte[] fft) { byte[] model = new byte[fft.length / 2 + 1]; if (mDataEn) { model[0] = (byte) Math.abs(fft[1]); int j = 1; for (int i = 2; i < fft.length; ) { model[j] = (byte) Math.hypot(fft[i], fft[i + 1]); i += 2; j++; } } else { for (int i = 0; i < CYLINDER_NUM; i++) { model[i] = 0; } } for (int i = 0; i < CYLINDER_NUM; i++) { final byte a = (byte) (Math.abs(model[CYLINDER_NUM - i]) / levelStep); final byte b = mData[i]; if (a > b) { mData[i] = a; } else { if (b > 0) { mData[i]--; } } } invalidate(); } }
参考文章
-
《》
暂无评论...
随机推荐
lateinit和by lazy简单使用
前言记录一下Kotlin中lateinit和by lazy简单使用。正文lateinit为延迟初始化属性。lateinit用于延迟初始化一个var可变属性。属性类型必须是非空的且不能是原始类型(如 Int, Double)。经常在类中定义属性时class Person{ v...
Ubuntu14编译Android6.0
今天(2016年11月6日)终于编译成功,参考的教程有很多,本来也想写一写,但发现自己可能对编译这东西考虑不全,误导了人,所以只好放弃自己写教程的想法。一:下载Android源码关于这一步,网上有下载好的源码,或者去清华大学开源软件镜像站下载,都很快的。Android6.0源码,网盘下...
Launcher的两次启动
前言这个问题,类似的之前也有记录过,但由于自己记性太差,又忘了怎么分析,因此重新记录于此,方便自己查阅。好记性不如烂笔头正文问题Android P机器开机启动后,发现Launcher3的Activity(也就是Launcher)启动了两次。日志只截取了部分且重要的日志Activit...
HandlerThread的使用
HandlerThread简介HandlerThread是一个Android已封装好的轻量级的异步类。HandlerThread 继承Thread,本质就是个Thread,与普通Thread 的区别在于实现了自己的Looper,可以单独分发和处理消息。作用:用来线程间的消息传递,主要是子线...
Kotlin空值处理简介
前言简单记录一下Kotlin中变量的空值处理的方式。记录于此方便自己查阅。正文Kotlin把变量分成两种类型,一种是可空类型的变量,一种是非空类型的变量。一般情况下,一个变量默认是非空类型。当某个变量的值可以为空时,必须在声明处的数据类型后添加“?”来标识该引用可为空。var name...
Android之获取图片高宽方法的简单记录
前言本文非原创,大佬的基础上进行修改和调试,下面三种方式我都测试过。感谢大佬们分享。好记性不如烂笔头总结如果只获取高宽,推荐使用BitmapFactory.Options如果要加载图片和获取高宽,推荐使用Glide如果只是加载jpg图片,可以考虑ExifInterface,否则不推荐...