package com.yeyupiaoling.androidclient; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.util.AttributeSet; import android.view.View; import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.List; public class AudioView extends View { // 频谱数量 private static final int LUMP_COUNT = 128; private static final int LUMP_WIDTH = 6; private static final int LUMP_SPACE = 2; private static final int LUMP_MIN_HEIGHT = LUMP_WIDTH; private static final int LUMP_MAX_HEIGHT = 200;//TODO: HEIGHT private static final int LUMP_SIZE = LUMP_WIDTH + LUMP_SPACE; private static final int LUMP_COLOR = Color.parseColor("#6de8fd"); private static final int WAVE_SAMPLING_INTERVAL = 3; private static final float SCALE = LUMP_MAX_HEIGHT / LUMP_COUNT; private ShowStyle upShowStyle = ShowStyle.STYLE_HOLLOW_LUMP; private ShowStyle downShowStyle = ShowStyle.STYLE_WAVE; private byte[] waveData; List pointList; private Paint lumpPaint; Path wavePath = new Path(); public AudioView(Context context) { super(context); init(); } public AudioView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public AudioView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { lumpPaint = new Paint(); lumpPaint.setAntiAlias(true); lumpPaint.setColor(LUMP_COLOR); lumpPaint.setStrokeWidth(2); lumpPaint.setStyle(Paint.Style.STROKE); } public void setWaveData(byte[] data) { this.waveData = readyData(data); genSamplingPoint(data); invalidate(); } public void setStyle(ShowStyle upShowStyle, ShowStyle downShowStyle) { this.upShowStyle = upShowStyle; this.downShowStyle = downShowStyle; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); wavePath.reset(); for (int i = 0; i < LUMP_COUNT; i++) { if (waveData == null) { canvas.drawRect((LUMP_WIDTH + LUMP_SPACE) * i, LUMP_MAX_HEIGHT - LUMP_MIN_HEIGHT, (LUMP_WIDTH + LUMP_SPACE) * i + LUMP_WIDTH, LUMP_MAX_HEIGHT, lumpPaint); continue; } switch (upShowStyle) { case STYLE_HOLLOW_LUMP: drawLump(canvas, i, false); break; case STYLE_WAVE: drawWave(canvas, i, false); break; default: break; } switch (downShowStyle) { case STYLE_HOLLOW_LUMP: drawLump(canvas, i, true); break; case STYLE_WAVE: drawWave(canvas, i, true); break; default: break; } } } /** * 预处理数据 * * @return */ private static byte[] readyData(byte[] fft) { byte[] newData = new byte[LUMP_COUNT]; byte abs; for (int i = 0; i < LUMP_COUNT; i++) { abs = (byte) Math.abs(fft[i]); //描述:Math.abs -128时越界 newData[i] = abs < 0 ? 127 : abs; } return newData; } /** * 绘制曲线 * * @param canvas * @param i * @param reversal */ private void drawWave(Canvas canvas, int i, boolean reversal) { if (pointList == null || pointList.size() < 2) { return; } float ratio = SCALE * (reversal ? -1 : 1); if (i < pointList.size() - 2) { Point point = pointList.get(i); Point nextPoint = pointList.get(i + 1); int midX = (point.x + nextPoint.x) >> 1; if (i == 0) { wavePath.moveTo(point.x, LUMP_MAX_HEIGHT - point.y * ratio); } wavePath.cubicTo(midX, LUMP_MAX_HEIGHT - point.y * ratio, midX, LUMP_MAX_HEIGHT - nextPoint.y * ratio, nextPoint.x, LUMP_MAX_HEIGHT - nextPoint.y * ratio); canvas.drawPath(wavePath, lumpPaint); } } /** * 绘制矩形条 */ private void drawLump(Canvas canvas, int i, boolean reversal) { int minus = reversal ? -1 : 1; float top = (LUMP_MAX_HEIGHT - (LUMP_MIN_HEIGHT + waveData[i] * SCALE) * minus); canvas.drawRect(LUMP_SIZE * i, top, LUMP_SIZE * i + LUMP_WIDTH, LUMP_MAX_HEIGHT, lumpPaint); } /** * 生成波形图的采样数据,减少计算量 * * @param data */ private void genSamplingPoint(byte[] data) { if (upShowStyle != ShowStyle.STYLE_WAVE && downShowStyle != ShowStyle.STYLE_WAVE) { return; } if (pointList == null) { pointList = new ArrayList<>(); } else { pointList.clear(); } pointList.add(new Point(0, 0)); for (int i = WAVE_SAMPLING_INTERVAL; i < LUMP_COUNT; i += WAVE_SAMPLING_INTERVAL) { pointList.add(new Point(LUMP_SIZE * i, waveData[i])); } pointList.add(new Point(LUMP_SIZE * LUMP_COUNT, 0)); } /** * 可视化样式 */ public enum ShowStyle { /** * 空心的矩形小块 */ STYLE_HOLLOW_LUMP, /** * 曲线 */ STYLE_WAVE, /** * 不显示 */ STYLE_NOTHING } }