SurfaceView绘制录音波形图
本文简单记录由View绘制转为SurfaceView绘制的波形图问题.
上代码:
public class VoiceLineView extends View {
private final int LINE = 0;
private final int RECT = 1;
private int middleLineColor = Color.BLACK;
private int voiceLineColor = Color.BLACK;
private float middleLineHeight = 4;
private Paint paint;
private Paint paintVoicLine; /**
* 灵敏度
*/
private int sensibility = 4;
private float maxVolume = 100;
private float translateX = 0;
private boolean isSet = false;
/**
* 振幅
*/
private float amplitude = 1;
/**
* 音量
*/
private float volume = 10;
private int fineness = 1;
private float targetVolume = 1;
private long speedY = 50;
private float rectWidth = 25;
private float rectSpace = 5;
private float rectInitHeight = 4;
private List<Rect> rectList;
private long lastTime = 0;
private int lineSpeed = 90;
List<Path> paths = null;
public VoiceLineView(Context context) {
super(context);
}
public VoiceLineView(Context context, AttributeSet attrs) {
super(context, attrs);
initAtts(context, attrs);
}
public VoiceLineView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAtts(context, attrs);
}
private void initAtts(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.voiceView);
mode = typedArray.getInt(R.styleable.voiceView_viewMode, 0);
voiceLineColor = typedArray.getColor(R.styleable.voiceView_voiceLine, Color.BLACK);
maxVolume = typedArray.getFloat(R.styleable.voiceView_maxVolume, 100);
sensibility = typedArray.getInt(R.styleable.voiceView_sensibility, 4);
if (mode == RECT) {
rectWidth = typedArray.getDimension(R.styleable.voiceView_rectWidth, 25);
rectSpace = typedArray.getDimension(R.styleable.voiceView_rectSpace, 5);
rectInitHeight = typedArray.getDimension(R.styleable.voiceView_rectInitHeight, 4);
} else {
middleLineColor = typedArray.getColor(R.styleable.voiceView_middleLine, Color.BLACK);
middleLineHeight = typedArray.getDimension(R.styleable.voiceView_middleLineHeight, 4);
lineSpeed = typedArray.getInt(R.styleable.voiceView_lineSpeed, 90);
fineness = typedArray.getInt(R.styleable.voiceView_fineness, 1);
paths = new ArrayList<>(20);
for (int i = 0; i < 20; i++) {
paths.add(new Path());
}
}
typedArray.recycle();
}
@Override
protected void onDraw(Canvas canvas) { drawMiddleLine(canvas);
drawVoiceLine(canvas); run();
}
private void drawMiddleLine(Canvas canvas) {
if (paint == null) {
paint = new Paint();
paint.setColor(middleLineColor);
paint.setAntiAlias(true);
}
canvas.save();
canvas.drawRect(0, getHeight() / 2 - middleLineHeight / 2, getWidth(), getHeight() / 2 + middleLineHeight / 2, paint);
canvas.restore();
}
private void drawVoiceLine(Canvas canvas) {
lineChange();
if (paintVoicLine == null) {
paintVoicLine = new Paint();
paintVoicLine.setColor(voiceLineColor);
paintVoicLine.setAntiAlias(true);
paintVoicLine.setStyle(Paint.Style.STROKE);
paintVoicLine.setStrokeWidth(2);
}
canvas.save();
int moveY = getHeight() / 2;
for (int i = 0; i < paths.size(); i++) {
paths.get(i).reset();
paths.get(i).moveTo(getWidth(), getHeight() / 2);
}
for (float i = getWidth() - 1; i >= 0; i -= fineness) {
amplitude = 4 * volume * i / getWidth() - 4 * volume * i * i / getWidth() / getWidth();
for (int n = 1; n <= paths.size(); n++) {
float sin = amplitude * (float) Math.sin((i - Math.pow(1.22, n)) * Math.PI / 180 - translateX);
paths.get(n - 1).lineTo(i, (2 * n * sin / paths.size() - 15 * sin / paths.size() + moveY));
}
}
for (int n = 0; n < paths.size(); n++) {
if (n == paths.size() - 1) {
paintVoicLine.setAlpha(255);
} else {
paintVoicLine.setAlpha(n * 130 / paths.size());
}
if (paintVoicLine.getAlpha() > 0) {
canvas.drawPath(paths.get(n), paintVoicLine);
}
}
canvas.restore();
}
public void setVolume(int volume) {
if (volume > maxVolume * sensibility / 25) {
isSet = true;
this.targetVolume = getHeight() * volume / 2 / maxVolume;
}
}
private void lineChange() {
if (lastTime == 0) {
lastTime = System.currentTimeMillis();
translateX += 1.5;
} else {
if (System.currentTimeMillis() - lastTime > lineSpeed) {
lastTime = System.currentTimeMillis();
translateX += 1.5;
} else {
return;
}
}
if (volume < targetVolume && isSet) {
volume += getHeight() / 30;
} else {
isSet = false;
if (volume <= 10) {
volume = 10;
} else {
if (volume < getHeight() / 30) {
volume -= getHeight() / 60;
} else {
volume -= getHeight() / 30;
}
}
}
}
public void run() { invalidate(); }
}
上面是View的canvas实现的波形图绘制,cpu占用率比较高,在20%左右,一边录音一边绘制,手机发烫是最明显的感觉了.
下面我们换成SurfaceView绘制的
public class VoiceLineSurfaceView extends SurfaceView implements Runnable,SurfaceView.Callback {
private final int LINE = 0;
private final int RECT = 1;
private int middleLineColor = Color.BLACK;
private int voiceLineColor = Color.BLACK;
private float middleLineHeight = 4;
private Paint paint;
private Paint paintVoicLine;/**
* 灵敏度
*/
private int sensibility = 4;
private float maxVolume = 100;
private float translateX = 0;
private boolean isSet = false;
/**
* 振幅
*/
private float amplitude = 1;
/**
* 音量
*/
private float volume = 10;
private int fineness = 1;
private float targetVolume = 1;
private long speedY = 50;
private float rectWidth = 25;
private float rectSpace = 5;
private float rectInitHeight = 4;
private List<Rect> rectList;
private long lastTime = 0;
private int lineSpeed = 90;
private SurfaceHolder surfaceHolder;
List<Path> paths = null;
private boolean isWaveDrawing = false;
private boolean isMiddleLineDrawing = true;
private Thread thread;
private Canvas canvas;
public VoiceLineSurfaceView(Context context) {
super(context);
}
public VoiceLineSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
initAtts(context, attrs);
}
public VoiceLineSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAtts(context, attrs);
}
private void initAtts(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.voiceView);
mode = typedArray.getInt(R.styleable.voiceView_viewMode, 0);
voiceLineColor = typedArray.getColor(R.styleable.voiceView_voiceLine, Color.BLACK);
maxVolume = typedArray.getFloat(R.styleable.voiceView_maxVolume, 100);
sensibility = typedArray.getInt(R.styleable.voiceView_sensibility, 4);
middleLineColor = typedArray.getColor(R.styleable.voiceView_middleLine, Color.BLACK);
middleLineHeight = typedArray.getDimension(R.styleable.voiceView_middleLineHeight, 4);
lineSpeed = typedArray.getInt(R.styleable.voiceView_lineSpeed, 90);
fineness = typedArray.getInt(R.styleable.voiceView_fineness, 1);
paths = new ArrayList<>(20);
for (int i = 0; i < 20; i++) {
paths.add(new Path());
}
typedArray.recycle();
setZOrderOnTop(true);
getHolder().setFormat(PixelFormat.TRANSLUENT);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
}
private void drawMiddleLine(Canvas canvas) {
if (paint == null) {
paint = new Paint();
paint.setColor(middleLineColor);
paint.setAntiAlias(true);
}
canvas.save();
canvas.drawRect(0, getHeight() / 2 - middleLineHeight / 2, getWidth(), getHeight() / 2 + middleLineHeight / 2, paint);
canvas.restore();
}
private void drawVoiceLine(Canvas canvas) {
lineChange();
if (paintVoicLine == null) {
paintVoicLine = new Paint();
paintVoicLine.setColor(voiceLineColor);
paintVoicLine.setAntiAlias(true);
paintVoicLine.setStyle(Paint.Style.STROKE);
paintVoicLine.setStrokeWidth(2);
}
canvas.save();
int moveY = getHeight() / 2;
for (int i = 0; i < paths.size(); i++) {
paths.get(i).reset();
paths.get(i).moveTo(getWidth(), getHeight() / 2);
}
for (float i = getWidth() - 1; i >= 0; i -= fineness) {
amplitude = 4 * volume * i / getWidth() - 4 * volume * i * i / getWidth() / getWidth();
for (int n = 1; n <= paths.size(); n++) {
float sin = amplitude * (float) Math.sin((i - Math.pow(1.22, n)) * Math.PI / 180 - translateX);
paths.get(n - 1).lineTo(i, (2 * n * sin / paths.size() - 15 * sin / paths.size() + moveY));
}
}
for (int n = 0; n < paths.size(); n++) {
if (n == paths.size() - 1) {
paintVoicLine.setAlpha(255);
} else {
paintVoicLine.setAlpha(n * 130 / paths.size());
}
if (paintVoicLine.getAlpha() > 0) {
canvas.drawPath(paths.get(n), paintVoicLine);
}
}
canvas.restore();
}
public void setVolume(int volume) {
if (volume > maxVolume * sensibility / 25) {
isSet = true;
this.targetVolume = getHeight() * volume / 2 / maxVolume;
}
}
private void lineChange() {
if (lastTime == 0) {
lastTime = System.currentTimeMillis();
translateX += 1.5;
} else {
if (System.currentTimeMillis() - lastTime > lineSpeed) {
lastTime = System.currentTimeMillis();
translateX += 1.5;
} else {
return;
}
}
if (volume < targetVolume && isSet) {
volume += getHeight() / 30;
} else {
isSet = false;
if (volume <= 10) {
volume = 10;
} else {
if (volume < getHeight() / 30) {
volume -= getHeight() / 60;
} else {
volume -= getHeight() / 30;
}
}
}
}
@Override
public void surfaceCreated(){
}
@Override
public void surfaceChanged(){
isMiddleLineDrawing = true;
thread = new Thread(this);
thread.start();
}
@Override
public void surfaceDestroyed(){
isMiddleLineDrawing = false;
}
@Override
public void run() {
while(isMiddleLineDrawing){
canvas = surfaceHolder.lockCanvas();
if(canvas==null){
return;
}
canvas.drawColor(Color.TRANSLUENT,PorterDuff.Mode.CLEAR);
if(isWaveDrawing){
drawVoiceLine(canvas);
}
drawMiddleLine(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void setDrawing(boolean drawing){
isMiddleLineDrawing = true;
isWaveDrawing = drawing;
}
}
SurfaceView绘制录音波形图的更多相关文章
- C# NAudio录音和播放音频文件-实时绘制音频波形图(从音频流数据获取,而非设备获取)
NAudio的录音和播放录音都有对应的类,我在使用Wav格式进行录音和播放录音时使用的类时WaveIn和WaveOut,这两个类是对功能的回调和一些事件触发. 在WaveIn和WaveOut之外还有对 ...
- C# NAudio录音和播放音频文件及实时绘制音频波形图(从音频流数据获取,而非设备获取)
下午写了一篇关于NAudio的录音.播放和波形图的博客,不太满意,感觉写的太乱,又总结了下 NAudio是个相对成熟.开源的C#音频开发工具,它包含录音.播放录音.格式转换.混音调整等功能.本次介绍主 ...
- ios 绘制wav波形图
最近研究了如何在iOS上绘制wav波形图.查了很多资料,都没能找到一个很完整的介绍,我这里总结一下一些经验. 首先需要了解wav的这3个重要指标:采样率.采样位数.声道数.下面以16KHz, 16Bi ...
- android SurfaceView绘制实现原理解析
在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面.由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独 ...
- Android -- SurfaceView绘制
SurfaceView SurfaceView是View的一个特殊子类,它的目的是另外提供一个线程进行绘制操作. 步骤 1.用SurfaceView进行绘制,首先要创建一个类,继承 SurfaceVi ...
- Android: 利用SurfaceView绘制股票滑动直线解决延迟问题
1.背景介绍 最近项目要绘制股票走势图,并绘制能够跟随手指滑动的指示线(Indicator)来精确查看股票价格和日期.如下图所示: 上图中的那条白色直线就是股票的指示线,用来跟随手指精确确定股票的时间 ...
- android:怎样用一天时间,写出“飞机大战”这种游戏!(无框架-SurfaceView绘制)
序言作为一个android开发人员,时常想开发一个小游戏娱乐一下大家,今天就说说,我是怎么样一天写出一个简单的"飞机大战"的. 体验地址:http://www.wandoujia. ...
- SurfaceView 绘制分形图
之前一直做的是应用类,这次抽时间,参考网上资料实践了下SurfaceView.目标是在页面上画一个科赫曲线的分形图. 代码如下: package com.example.fredric.demo02; ...
- android SurfaceView绘制 重新学习--控制动画移动
直接上demo,图是自己切的,将就用吧.点击左右两边分别向左右移动. public class MySurfaceView extends SurfaceView implements Callbac ...
随机推荐
- ECNU 2018 10月月赛 E 盖房子 (bitset + 倍增)
题目链接 ECNU Monthly 2018.10 Problem E 从开场写到结束…… 显然要把三角形分成上下两部分. 把每一部分分成三部分,以上部分为例. 上面和右边,以及左下角的正方形. 也 ...
- 洛谷——P1869 愚蠢的组合数
P1869 愚蠢的组合数 题目描述 最近老师教了狗狗怎么算组合数,狗狗又想到了一个问题... 狗狗定义C(N,K)表示从N个元素中不重复地选取K个元素的方案数. 狗狗想知道的是C(N,K)的奇偶性. ...
- Linux命令之head
head [选项] [文件] head命令输出文件开头部分,默认情况下显示文件的头10行.如果指定多个文件,每个文件前都有一个标题,给出文件名.如果没有指定文件,或当文件为-时,读取标准输入. (1) ...
- 1、编译安装Nginx
1.1 如何选择web服务器 在实际工作中,我们需要根据业务需求来选择合适的业务服务软件,有关web服务,选择建议如下: 静态业务:若是高并发场景,尽量采用nginx或lighttpd,二者首选ngi ...
- 【Tempest】openstack自动化测试组件tempest及自动化测试工具nose
宝宝心里苦,但是宝宝只能在这穷乡僻壤说,下周又要开组会必须得干点事了.这次是做论文中的实验部分,主要利用到了openstack中的自动化测试组件Tempest,具体原因不细说了. 安装 分两种安装方法 ...
- hdu1006 Tick and Tick (数学题 借鉴了大神的博客)
先缩短一半的时间:早上的12个小时和下午的12小时对时钟是一样的,因为时钟12小时与0小时的三针位置相同.接着就是了解到每次所有的针从有重合到再次有重合至多有一段连续的段符合三针分离度大于n.所以只要 ...
- [BZOJ2007][NOI2010]海拔(对偶图最短路)
首先确定所有点的海拔非0即1,问题转化成裸的平面图最小割问题,进而转化成对偶图最短路(同BZOJ1002). 这题的边是有向的,所以所有边顺时针旋转90度即可. 如下图(S和T的位置是反的). #in ...
- JZYZOJ1530 [haoi2013]开关控制 状压 dfs 折半搜索
http://172.20.6.3/Problem_Show.asp?id=1530 元宵节快要到了,某城市人民公园将举办一次灯展.Dr.Kong准备设计出一个奇妙的展品,他计划将编号为1到N的N(1 ...
- BZOJ 1711:[Usaco2007 Open]Dining吃饭(最大流)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1711 [题目大意] 每头牛都有一些喜欢的饮料和食物, 现在有一些食物和饮料,但是每样只 ...
- 【计算几何】【bitset】Gym - 101412G - Let There Be Light
三维空间中有一些(<=2000)气球,一些光源(<=15),给定一个目标点,问你在移除不超过K个气球的前提下,目标点所能接受到的最大光照. 枚举每个光源,预处理其若要照射到光源,需要移走哪 ...