【问题标题】:How to draw an audio waveform on Android如何在 Android 上绘制音频波形
【发布时间】:2014-08-11 17:24:48
【问题描述】:

我有一个自定义视图,我想用它在折线图中显示通过麦克风传入的音频幅度。 得到幅度和我没有问题的所有东西,画线也不是什么问题。

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

鉴于此:

我想把它翻译到左边:

然后画一条线到右边空间空间的一个新点

当然,第 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);

}

以上只是一个示例,我现在只是使用幅度的随机值来简化。我已经尝试了很多没有运气的事情。任何帮助将不胜感激。

【问题讨论】:

  • 大声告诉我。
  • 我应该是一名图形艺术家。 =p

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


【解决方案1】:

我最终通过将点保存到数组来完成这项工作。我在录制开始前画了一条白线。请注意,我使用 Guava 库中的 EvictingQueue 作为点的循环缓冲区以在一条线上渲染。要使用它,一旦录音开始调用 start(),当它结束时调用 stop。从您的活动中,您需要将 MediaRecorder getMaxAmplitude() 值发送到此类的 updateAmplitude() 方法,并且每隔 50 毫秒执行一次。该视图还支持旋转。

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

【讨论】:

  • 我需要创建波形,我想使用这段代码,但我不明白EvictingQueue是什么?可以发一下这门课的内容吗?
  • 谷歌是你的朋友!这是一个双关语,它是故意的。
  • @AshishTiwari 添加 compile 'com.google.guava:guava:22.0-android'
猜你喜欢
  • 1970-01-01
  • 2018-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多