导航号,我的单页导航
文章目录

前言

本文抄的,音乐频谱相关,想单独记录于此,方便自己查阅和回顾。

具体可以看:https://github.com/wanliLiu/androidequalizer

Android音乐频谱显示

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();
    }
}

参考文章

  1. androidequalizer

© 版权声明
导航号,我的单页导航

暂无评论

暂无评论...