最近在研究VAD算法,但调整参数时无法实时看到效果,于是决定将音频波形实时绘制出来,并且语音部分和噪音部分用不同颜色的线条显示,这样就能立即看到VAD算法对各种噪音类型的鲁棒性
为了分而治之,先研究怎么在Android下实时绘制随机生成波形的功能,后面再加入语音获取,以及根据VAD算法的结果用不同颜色线条显示 等功能。
查了下Canvas类,发现drawLines方法跟drawLine方法都是根据4个浮点数绘制一条直线,前者还需要开辟额外的空间,所以选择了后者,结果绘制速度很慢,在小米2s上绘制2秒 单通道16KHz采样率16bit位宽的音频,耗时达到700ms,无奈尝试前者,耗时下降至20ms(最快1ms)
由此也能推测出,后者开销这么大主要是花在drawLine的函数调用,而不是坐标点的生成。
代码
package com.happen23.games.waveviewer;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import java.util.Random;
/**
* Created by Administrator on 2016/5/14.
*/
public class VadView extends View {
private static final String TAG = "VadView";
Paint paint;
int audioSampleNum = 2 * 16000;
int widthPixels;
int heightPixels;
float points[];
short audio[];
public VadView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(1);
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
widthPixels = getWidth();
heightPixels = getHeight();
}
});
}
protected void drawWave2(Canvas canvas, short audio[]) {
for (int i = 0; i < audioSampleNum-1; i++){
points[4*i] = (float)i/audioSampleNum * widthPixels;
points[4*i+1] = heightPixels/2 + (float)audio[i]/32768 * heightPixels/2;
points[4*i+2] = (float)(i+1)/audioSampleNum * widthPixels;
points[4*i+3] = heightPixels/2 + (float)audio[i+1]/32768 * heightPixels/2;
};
canvas.drawLines(points, paint); //for循环外,效率高
}
protected void drawWave1(Canvas canvas, short audio[]){
float startX, startY, stopX, stopY;
for (int i = 0; i < audio.length-1; i++){
startX = (float)i/audioSampleNum * widthPixels;
startY = heightPixels/2 + (float)audio[i]/32768 * heightPixels/2;
stopX = (float)(i+1)/audioSampleNum * widthPixels;
stopY = heightPixels/2 + (float)audio[i+1]/32768 * heightPixels/2;
canvas.drawLine(startX, startY, stopX, stopY, paint); //for循环内,效率低
}
}
protected void onDraw(Canvas canvas) {
if (points == null) {
points = new float[audioSampleNum * 4];
audio = new short[audioSampleNum];
}
Random r = new Random();
for (int i = 0; i < audioSampleNum; i++){
audio[i] = (short)(r.nextInt(65536) - 32768);
}
Log.w(TAG, "start drawLine");
drawWave2(canvas, audio);
Log.w(TAG, "stop drawLine");
postDelayed(new Runnable() {
@Override
public void run() {
invalidate();
}
}, 200);
}
}