简单地介绍了AudioRecord和AudioTrack的使用,这次就结合SurfaceView实现一个Android版的手机模拟信号示波器(PS:以前也讲过J2ME版的手机示波器)。最近物联网炒得很火,作为手机软件开发者,如何在不修改手机硬件电路的前提下实现与第三方传感器结合呢?麦克风就是一个很好的ADC接口,通过麦克风与第三方传感器结合,再在软件里对模拟信号做相应的处理,就可以提供更丰富的传感化应用。

先来看看本文程序运行的效果图(屏幕录像速度较慢,真机实际运行起来会更加流畅):

本文程序使用8000hz的采样率,对X轴方向绘图的实时性要求较高,如果不降低X轴的分辨率,程序的实时性较差,因此程序对X轴数据缩小区间为8倍~16倍。由于采用16位采样,因此Y轴数据的高度相对于手机屏幕来说也偏大,程序也对Y轴数据做缩小,区间为1倍~10倍。在SurfaceView的OnTouchListener方法里加入了波形基线的位置调节,直接在SurfaceView控件上触摸即可控制整体波形偏上或偏下显示。

main.xml源码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="fill_parent"android:layout_height="fill_parent"><LinearLayout android:id="@+id/LinearLayout01"android:layout_height="wrap_content" android:layout_width="fill_parent"android:orientation="horizontal"><Button android:layout_height="wrap_content" android:id="@+id/btnStart"android:text="开始" android:layout_width="80dip"></Button><Button android:layout_height="wrap_content" android:text="停止"android:id="@+id/btnExit" android:layout_width="80dip"></Button><ZoomControls android:layout_width="wrap_content"android:layout_height="wrap_content" android:id="@+id/zctlX"></ZoomControls><ZoomControls android:layout_width="wrap_content"android:layout_height="wrap_content" android:id="@+id/zctlY"></ZoomControls></LinearLayout><SurfaceView android:id="@+id/SurfaceView01"android:layout_height="fill_parent" android:layout_width="fill_parent"></SurfaceView>
</LinearLayout>

ClsOscilloscope.java是实现示波器的类库,包含AudioRecord操作线程和SurfaceView绘图线程的实现,两个线程同步操作,代码如下:

package com.testOscilloscope;
import java.util.ArrayList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.AudioRecord;
import android.view.SurfaceView;
public class ClsOscilloscope {private ArrayList<short[]> inBuf = new ArrayList<short[]>();private boolean isRecording = false;// 线程控制标记/*** X轴缩小的比例*/public int rateX = 4;/*** Y轴缩小的比例*/public int rateY = 4;/*** Y轴基线*/public int baseLine = 0;/*** 初始化*/public void initOscilloscope(int rateX, int rateY, int baseLine) {this.rateX = rateX;this.rateY = rateY;this.baseLine = baseLine;}/*** 开始* * @param recBufSize*            AudioRecord的MinBufferSize*/public void Start(AudioRecord audioRecord, int recBufSize, SurfaceView sfv,Paint mPaint) {isRecording = true;new RecordThread(audioRecord, recBufSize).start();// 开始录制线程new DrawThread(sfv, mPaint).start();// 开始绘制线程
    }/*** 停止*/public void Stop() {isRecording = false;inBuf.clear();// 清除
    }/*** 负责从MIC保存数据到inBuf* * @author GV* */class RecordThread extends Thread {private int recBufSize;private AudioRecord audioRecord;public RecordThread(AudioRecord audioRecord, int recBufSize) {this.audioRecord = audioRecord;this.recBufSize = recBufSize;}public void run() {try {short[] buffer = new short[recBufSize];audioRecord.startRecording();// 开始录制while (isRecording) {// 从MIC保存数据到缓冲区int bufferReadResult = audioRecord.read(buffer, 0,recBufSize);short[] tmpBuf = new short[bufferReadResult / rateX];for (int i = 0, ii = 0; i < tmpBuf.length; i++, ii = i* rateX) {tmpBuf[i] = buffer[ii];}synchronized (inBuf) {//
                        inBuf.add(tmpBuf);// 添加数据
                    }}audioRecord.stop();} catch (Throwable t) {}}};/*** 负责绘制inBuf中的数据* * @author GV* */class DrawThread extends Thread {private int oldX = 0;// 上次绘制的X坐标private int oldY = 0;// 上次绘制的Y坐标private SurfaceView sfv;// 画板private int X_index = 0;// 当前画图所在屏幕X轴的坐标private Paint mPaint;// 画笔public DrawThread(SurfaceView sfv, Paint mPaint) {this.sfv = sfv;this.mPaint = mPaint;}public void run() {while (isRecording) {ArrayList<short[]> buf = new ArrayList<short[]>();synchronized (inBuf) {if (inBuf.size() == 0)continue;buf = (ArrayList<short[]>) inBuf.clone();// 保存inBuf.clear();// 清除
                }for (int i = 0; i < buf.size(); i++) {short[] tmpBuf = buf.get(i);SimpleDraw(X_index, tmpBuf, rateY, baseLine);// 把缓冲区数据画出来X_index = X_index + tmpBuf.length;if (X_index > sfv.getWidth()) {X_index = 0;}}}}/*** 绘制指定区域* * @param start*            X轴开始的位置(全屏)* @param buffer*            缓冲区* @param rate*            Y轴数据缩小的比例* @param baseLine*            Y轴基线*/void SimpleDraw(int start, short[] buffer, int rate, int baseLine) {if (start == 0)oldX = 0;Canvas canvas = sfv.getHolder().lockCanvas(new Rect(start, 0, start + buffer.length, sfv.getHeight()));// 关键:获取画布canvas.drawColor(Color.BLACK);// 清除背景int y;for (int i = 0; i < buffer.length; i++) {// 有多少画多少int x = i + start;y = buffer[i] / rate + baseLine;// 调节缩小比例,调节基准线
                canvas.drawLine(oldX, oldY, x, y, mPaint);oldX = x;oldY = y;}sfv.getHolder().unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
        }}
}

testOscilloscope.java是主程序,控制UI和ClsOscilloscope,代码如下:

package com.testOscilloscope;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ZoomControls;
public class testOscilloscope extends Activity {/** Called when the activity is first created. */Button btnStart,btnExit;SurfaceView sfv;ZoomControls zctlX,zctlY;ClsOscilloscope clsOscilloscope=new ClsOscilloscope();static final int frequency = 8000;//分辨率static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;static final int xMax = 16;//X轴缩小比例最大值,X轴数据量巨大,容易产生刷新延时static final int xMin = 8;//X轴缩小比例最小值static final int yMax = 10;//Y轴缩小比例最大值static final int yMin = 1;//Y轴缩小比例最小值int recBufSize;//录音最小buffer大小
    AudioRecord audioRecord;Paint mPaint;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);//录音组件recBufSize = AudioRecord.getMinBufferSize(frequency,channelConfiguration, audioEncoding);audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency,channelConfiguration, audioEncoding, recBufSize);//按键btnStart = (Button) this.findViewById(R.id.btnStart);btnStart.setOnClickListener(new ClickEvent());btnExit = (Button) this.findViewById(R.id.btnExit);btnExit.setOnClickListener(new ClickEvent());//画板和画笔sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01); sfv.setOnTouchListener(new TouchEvent());mPaint = new Paint();  mPaint.setColor(Color.GREEN);// 画笔为绿色  mPaint.setStrokeWidth(1);// 设置画笔粗细 //示波器类库clsOscilloscope.initOscilloscope(xMax/2, yMax/2, sfv.getHeight()/2);//缩放控件,X轴的数据缩小的比率高些zctlX = (ZoomControls)this.findViewById(R.id.zctlX);zctlX.setOnZoomInClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(clsOscilloscope.rateX>xMin)clsOscilloscope.rateX--;setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");}});zctlX.setOnZoomOutClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(clsOscilloscope.rateX<xMax)clsOscilloscope.rateX++;    setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");}});zctlY = (ZoomControls)this.findViewById(R.id.zctlY);zctlY.setOnZoomInClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(clsOscilloscope.rateY>yMin)clsOscilloscope.rateY--;setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");}});zctlY.setOnZoomOutClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(clsOscilloscope.rateY<yMax)clsOscilloscope.rateY++;    setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");}});}@Overrideprotected void onDestroy() {super.onDestroy();android.os.Process.killProcess(android.os.Process.myPid());}/*** 按键事件处理* @author GV**/class ClickEvent implements View.OnClickListener {@Overridepublic void onClick(View v) {if (v == btnStart) {clsOscilloscope.baseLine=sfv.getHeight()/2;clsOscilloscope.Start(audioRecord,recBufSize,sfv,mPaint);} else if (v == btnExit) {clsOscilloscope.Stop();}}}/*** 触摸屏动态设置波形图基线* @author GV**/class TouchEvent implements OnTouchListener{@Overridepublic boolean onTouch(View v, MotionEvent event) {clsOscilloscope.baseLine=(int)event.getY();return true;}}
}

http://blog.csdn.net/hellogv/article/details/6032046

Android语音信号波形显示相关推荐

  1. matlab语音波形,MATLAB程序原始语音信号波形与加噪信号波形(最新整理)

    <MATLAB程序原始语音信号波形与加噪信号波形(最新整理)>由会员分享,可在线阅读,更多相关<MATLAB程序原始语音信号波形与加噪信号波形(最新整理)(3页珍藏版)>请在人 ...

  2. python求语音信号短时能量、短时过零率、语谱图

    python语音信号处理(二) 一.短时能量 短时能量主要用于区分浊音段和清音段,因为浊音时E(i)值比清音时大得多:区分声母与韵母的分界和无话段与有话段分界. 计算第i帧语音信号yi(n)的短时能量 ...

  3. 语音信号处理-语音信号的预处理

    实验二 语音信号的预处理 一.实验目的 (1)了解 MATLAB 采集语音信号的原理及常用命令: (2)熟练掌握基于 MATLAB 的语音文件的创建.读写等基本操作: (3)学会使用 Plot 命令来 ...

  4. 判断清浊音 matlab,基于MATLAB的语音信号的清浊音分析.doc

    基于MATLAB的语音信号的清浊音分析 目录 1 语音信号概述1 1.1 语音信号的基本组成1 1.2 语音信号的"短时谱"1 1.3 基音周期2 1.4 短时分析技术2 2 语音 ...

  5. 《MATLAB语音信号分析与合成(第二版)》:第10章 语音信号的合成算法

    <MATLAB语音信号分析与合成(第二版)>:第10章 语音信号的合成算法 前言 1. 数据与函数路径设置 2. MATLAB仿真一:重叠相加法语音合成 3. MATLAB仿真二:重叠存储 ...

  6. 基于matlab的语音信号滤波处理

    基于matlab的语音信号滤波处理 摘要:本课程设计的主要目的是在MATLAB环境下,使用窗口设计法设计一个滤波器,并对语音信号进行滤波去噪.开发平台为MATLAB,设计方法为窗口设计法.用麦克风采集 ...

  7. 语音信号中的特征提取

    一.语音的产生简介 1.1   发音器官 人体的语音是由人体的发音器官在大脑的控制下做生理运动产生的.人体发音器官由三部分组成:肺和气管.喉.声道. 肺是语音产生的能源所在.气管连接着肺和喉,是肺与声 ...

  8. 通过Matlab分析语音信号的时频特性

    Matlab为我们提供了音频文件读取函数wavread, 以这个函数为基础可实现语音音频时域波形的绘制. 一开始我直接将.m4a文件改为.wav文件, 虽然语音文件能够通过播放器播放, 但Matlab ...

  9. 二维数组信号 显示波形_LabVIEW编程:如何将数据存为电子表格文件,并读取进行波形显示...

    问题引出 在使用LabVIEW软件编写大型测试程序时,很多时候需要将原始采样数据或者分析处理后的数据在硬盘上存储为文件,而存储的格式可以是直观的普通的文本文件,也可以是占用空间小的二进制文件,除了这些 ...

最新文章

  1. 百度离职员工吐槽:整天除了工作还要演好戏,拍马屁,心太累!
  2. 抽象工厂模式java_面试官:说一下静态工厂模式,工厂方法模式,抽象工厂的区别吧...
  3. 如何规划自己的博士五年生活?
  4. 全国计算机水平考试技巧,全国计算机等级考试上机考试应试技巧
  5. 关于智能推荐的几点思考
  6. 【系统架构】ER图的画图规范和优化点
  7. 邻接矩阵转换为邻接表;邻接表转换为邻接矩阵
  8. 微信小程序tab切换,(scroll-view + swiper)可滑动切换,导航栏跟随滚动实现
  9. Redis 增加互斥锁
  10. 手机背景图片被删除怎么恢复
  11. 国内期市前7个月成交超去年全年
  12. 腐烂的橘子(广度优先搜索)(考虑同时搜索)
  13. 【数据结构】认识赫夫曼树与赫夫曼编码 上手实现压缩文件和解压
  14. 计算机视觉--KNN算法和稠密SIFT实现图像识别(手势识别)
  15. MySQL之连接原理
  16. hook:实现简单的键盘记录器
  17. 装载M1芯片的Mac安装“AE”时,出现错误代码“501”怎么办?
  18. 西电李航 操作系统课程笔记 day10 IO hardware principles
  19. 基于手机支付方式的电子商务网站
  20. python的pandas库第二关

热门文章

  1. undefined reference to 'pthread_create'
  2. HTMLButton控件下的Confirm()
  3. Redis队列的应用
  4. Redis源码解析——内存管理
  5. c语言编译开头,#includestdio.h,为什么C语言代码开头都有这一行?
  6. mysql 应用前景_图数据库在企业应用中前景如何,相比关系型数据库有哪些优势?...
  7. java的关键字和保留字_「Java」详解常见的53个关键字
  8. bpython ipython_安装ipython后命令找不到ipython bpython -bash: *python: command not found
  9. Java学习总结:23
  10. 指定服务器无效,安装sqlserver2008r2 服务器配置,服务帐户配置出错,提示Sql server服务指定的凭据无效...