上次简单地介绍了AudioRecord和AudioTrack的使用,这次就结合SurfaceVIEw实现一个AndroID版的手机模拟信号示波器。最近物联网炒得很火,作为手机软件开发者,如何在不修改手机硬件电路的前提下实现与第三方传感器结合呢?麦克风就是一个很好的ADC接口,通过麦克风与第三方传感器结合,再在软件里对模拟信号做相应的处理,就可以提供更丰富的传感化应用。
先来看看本文程序运行的效果图(屏幕录像速度较慢,真机实际运行起来会更加流畅):
本文程序使用8000hz的采样率,对X轴方向绘图的实时性要求较高,如果不降低X轴的分辨率,程序的实时性较差,因此程序对X轴数据缩小区间为8倍~16倍。由于采用16位采样,因此Y轴数据的高度相对于手机屏幕来说也偏大,程序也对Y轴数据做缩小,区间为1倍~10倍。在SurfaceVIEw的OntouchListener方法里加入了波形基线的位置调节,直接在SurfaceVIEw控件上触摸即可控制整体波形偏上或偏下显示。
main.xml源码如下:
XML/HTML代码
<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 androID:layout_height="wrap_content" androID:text="停止" androID:ID="@+ID/btnExit" androID:layout_wIDth="80dip"> <zoomcontrols androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:ID="@+ID/zctlX"> <zoomcontrols androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:ID="@+ID/zctlY"> <surfacevIEw androID:ID="@+ID/SurfaceVIEw01" androID:layout_height="fill_parent" androID:layout_wIDth="fill_parent">
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 inBuf = new ArrayList(); 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,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 buf = new ArrayList(); synchronized (inBuf) { if (inBuf.size() == 0) continue; buf = (ArrayList) 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,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; @OverrIDe public voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.main); //录音组件 recBufSize = AudioRecord.getMinBufferSize(frequency,channelConfiguration,audioEnCoding); audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,frequency,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() { @OverrIDe public 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() { @OverrIDe public 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() { @OverrIDe public 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() { @OverrIDe public voID onClick(VIEw v) { if(clsOscilloscope.rateY<ymax) clsOscilloscope.rateY++; setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍" +","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍"); } }); } @OverrIDe protected voID onDestroy() { super.onDestroy(); androID.os.Process.killProcess(androID.os.Process.myPID()); } /** * 按键事件处理 * @author GV * */ class ClickEvent implements VIEw.OnClickListener { @OverrIDe public 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{ @OverrIDe public boolean ontouch(VIEw v,MotionEvent event) { clsOscilloscope.baseline=(int)event.getY(); return true; } } }
以上就是对AndroID 实现模拟系信号示波器的示例详解,后续继续补充相关知识,谢谢大家对本站 的支持!
总结以上是内存溢出为你收集整理的Android 模拟信号示波器示例代码全部内容,希望文章能够帮你解决Android 模拟信号示波器示例代码所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)