如何在Android上绘制音频波形

时间:2014-08-11 17:24:48

标签: android bitmap android-canvas android-custom-view

我有一个自定义视图,我想用它来显示通过线图中麦克风传入的音频幅度。 获得振幅和我没有遇到的所有问题,并绘制线条也不是真正的问题。

我想要做的是显示从最右边开始向左移动的幅度。因此,对于每个新样本,我想将位图转换为左侧,然后从最后一个点到新点绘制一条线。我不确定实现这一目标的最简单方法是什么。我最初能够通过绘制路径并且只是在每个样本的路径中添加一个新点来实现它,问题是在一分钟之后路径太大而无法绘制。所以我想到了它并希望切换到使用缓存的位图,在每次迭代时转换它,并从最后一个点绘制到新点。然而,这是很棘手的(在实验之后)。当我翻译位图时,它不会将最左边的像素移出位图,它只是在画布中移动整个位图,我无法将像素写入右侧。 以下是我正在尝试做的事情的描述:

鉴于此: enter image description here

我想将其翻译到左侧: enter image description here

然后在右边的空格区域画一条线到新点 enter image description here

当然,第2步和第3步应该基本上同时发生。

我怎样才能做到这一点?我完全接受新的想法,比如保存最多1个屏幕的所有点数,并在每次onDraw调用中抽出它们。我更喜欢将它们保存在位图中并进行某种翻译/剪辑等以实现同样的目的,并且开销可能更少。

private static final int MAX_AMPLITUDE = 32767;
float lx, ly;
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;

private void init() {

    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(5);
    mPaint.setColor(Color.Black);
}

 @Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (mBitmap != null) {
        mBitmap.recycle();
    }
    mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    mCanvas = new Canvas(mBitmap);
    height = h;
    width = w;
    ly = height;
    lx = width;
    amplitudeDivisor = ((float) MAX_AMPLITUDE / (float) height);
}

@Override
public void onDraw(Canvas canvas) {
    mAmplitude = (float)(MAX_AMPLITUDE * Math.random());
    float dx = width - delta;
    float dy = height - (mAmplitude / amplitudeDivisor);
    mCanvas.drawLine(lx, ly, dx, dy, mPaint);
    mCanvas.translate(-delta, 0);
    canvas.drawBitmap(mBitmap, 0, 0, mPaint);
    lx = dx;
    ly = dy;
    delta+=10;
    postInvalidateDelayed(200);

}

以上只是一个示例,我现在只是使用随机值来调整幅度。我尝试了一堆没有运气的东西。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

我最终通过将点保存到数组来实现这一点。我在录制开始前画了一条白线。请注意,我使用Guava库中的EvictingQueue作为点的循环缓冲区来在一条线上渲染。要使用此功能,一旦录制开始,请调用start(),并在结束时调用stop。根据您的活动,您需要将MediaRecorder getMaxAmplitude()值发送到此类的updateAmplitude()方法,并以50 ms的间隔执行此操作。该视图还支持旋转。

public class AmplitudeWaveFormView extends View {
    private static final String TAG = AmplitudeWaveFormView.class.getSimpleName();

    private static final int MAX_AMPLITUDE = 32767;
    private static final int SAMPLES_PER_SCREEN = 100;
    private float mAmplitude = 0;

    private Paint mRecordingPaint, mNotRecordingPaint;
    private int height = -1;
    private int width = -1;
    private boolean mIsStarted;

    private float[] lastPoints;

    private int oldWidth = -1, oldHeight = -1;
    private int mCurrentSample;
    private float amplitudeDivisor = 1;
    private float lx,ly, deltaX;


    private EvictingQueue<Float> mPointQueue;


    private int recordColor;

    private int notRecordingColor;


    public AmplitudeWaveFormView(Context context) {
        super(context);
        init();
    }

    public AmplitudeWaveFormView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public AmplitudeWaveFormView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    public void start() {
        mIsStarted = true;
    }


    public void stop() {
        mIsStarted = false;
    }
    public void updateAmplitude(float amplitude) {
        mAmplitude = amplitude;
        postInvalidate();
    }

    private void init() {
        recordColor = getResources().getColor(R.color.mint);
        notRecordingColor = getResources().getColor(R.color.alpine);
        mRecordingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mRecordingPaint.setStyle(Paint.Style.STROKE);
        mRecordingPaint.setStrokeWidth(5);
        mRecordingPaint.setColor(recordColor);

        mNotRecordingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mNotRecordingPaint.setStyle(Paint.Style.STROKE);
        mNotRecordingPaint.setStrokeWidth(5);
        mNotRecordingPaint.setColor(notRecordingColor);
    }

    @Override
    public void onSizeChanged(int w, int h, int oldw, int oldh) {
        height = h;
        width = w;
        ly = height;
        lx = width;
        deltaX =  (float)width / (float)SAMPLES_PER_SCREEN;
        amplitudeDivisor = ((float) MAX_AMPLITUDE / (float) height);

        mPointQueue = EvictingQueue.create(SAMPLES_PER_SCREEN * 4);
        if (lastPoints != null && lastPoints.length > 0) {
            float xScale = (float) width/oldWidth;
            float yScale = (float) height/oldHeight;
            Matrix matrix = new Matrix();
            matrix.setScale(xScale, yScale);
            matrix.mapPoints(lastPoints);
            mPointQueue.addAll(Floats.asList(lastPoints));
            ly = lastPoints[lastPoints.length-1];
            lx= lastPoints[lastPoints.length -2];
            lastPoints = null;
        }

    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            mCurrentSample = bundle.getInt("sample");
            lastPoints = bundle.getFloatArray("lines");
            oldWidth = bundle.getInt("oldWidth");
            oldHeight = bundle.getInt("oldHeight");
            state = ((Bundle) state).getParcelable("parent");

        }
        super.onRestoreInstanceState(state);
    }

    @Override
    public Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putFloatArray("lines", Floats.toArray(mPointQueue));
        bundle.putInt("sample", mCurrentSample);
        bundle.putParcelable("parent", super.onSaveInstanceState());
        bundle.putInt("oldWidth", width);
        bundle.putInt("oldHeight", height);
        return bundle;
    }


    @Override
    public void onDraw(Canvas canvas) {

        if (mIsStarted) {
            float x = lx + deltaX;
            float y = height - (mAmplitude / amplitudeDivisor);
            mPointQueue.add(lx);
            mPointQueue.add(ly);
            mPointQueue.add(x);
            mPointQueue.add(y);
            lastPoints = Floats.toArray(mPointQueue);
            lx = x;
            ly = y;
        }
        if (lastPoints != null && lastPoints.length > 0) {
            int len = mPointQueue.size() / 4 >= SAMPLES_PER_SCREEN ? SAMPLES_PER_SCREEN * 4 : mPointQueue.size();
            float translateX = width - lastPoints[lastPoints.length - 2];
            canvas.translate(translateX, 0);
            canvas.drawLines(lastPoints, 0, len, mRecordingPaint);
        }

        if (mCurrentSample <= SAMPLES_PER_SCREEN) {
            drawNotRecordingLine(canvas);
        }
        mCurrentSample++;
    }

    private void drawNotRecordingLine(Canvas canvas) {
        canvas.drawLine(0,height, width, height, mNotRecordingPaint);
    }
}