通过J2ME的录音功能实现简易示波器

本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处!

早就有人通过PC声卡的输入(麦克风孔)来做模拟示波器,但是用手机来实现的比较少。用J2ME的MMAPI实现模拟示波器,具体效果稍逊于智能机,因为智能机可以实时读取麦克风输入流,而J2ME还需要有短暂的缓冲构成了阻塞,不过,实现出来玩一下还是足够了。

先贴出效果图:

通过J2ME的录音功能实现简易示波器

左图是程序在WTK运行的结果,右图是Audition读取音频输入口的波形,信号源是一个经过信号放大的压力传感器。

程序使用NetBeans + LWUIT类库,接下来贴出全部代码:

import com.sun.lwuit.Command; import com.sun.lwuit.Display; import com.sun.lwuit.Form; import com.sun.lwuit.events.ActionEvent; import com.sun.lwuit.events.ActionListener; import com.sun.lwuit.layouts.BorderLayout; import java.io.ByteArrayOutputStream; import javax.microedition.media.Manager; import javax.microedition.media.Player; import javax.microedition.media.control.RecordControl; /** * @author 张国威 */ public class Frm_MainMenu extends javax.microedition.midlet.MIDlet implements ActionListener { public Form form ; private Command cmdExit = new Command("退出", 1); private ThreadReceive threadReceive =new ThreadReceive();//接收数据线程 private Cmp_Wave cmp_HeartWave=null; private Player capturePlayer = null; private RecordControl recordControl = null; private ByteArrayOutputStream bos = new ByteArrayOutputStream(); public void startApp() { Display.init(this); form = new Form();//达到全屏的效果 cmp_HeartWave=new Cmp_Wave(form.getHeight(),form.getWidth()); form.getStyle().setBgImage(null);//本窗体不需要背景 form.addCommand(cmdExit); form.setCommandListener(this); form.setLayout(new BorderLayout()); //设置画板控件 form.addComponent(BorderLayout.CENTER,cmp_HeartWave);//添加控件 form.show(); try { capturePlayer = Manager.createPlayer("capture://audio?rate=8000&bits=8&channels=1");//PCM,8位,8kH if (capturePlayer != null) { capturePlayer.realize(); recordControl = (RecordControl) capturePlayer .getControl("javax.microedition.media.control.RecordControl"); if (recordControl == null) throw new Exception("No RecordControl available"); recordControl.setRecordStream(bos); } else { throw new Exception("Capture Audio Player is not available"); } } catch (Exception e) {} threadReceive.start();//开始启动线程 } /* * byte转为int的函数,因为JAVA的byte范围从-127~127 */ public static int unsignedByteToInt(byte b) { return (int) b & 0xFF; } class ThreadReceive extends Thread { private boolean isRuning=true;//默认线程内部while循环可以执行 public void StopThread() { isRuning=false; } public void run(){ //************************************************************* //绘制波形数据 //************************************************************* try { capturePlayer.start(); while(isRuning) { recordControl = (RecordControl) capturePlayer.getControl("javax.microedition.media.control.RecordControl"); recordControl.setRecordStream(bos); recordControl.startRecord(); Thread.sleep(25);//停顿25ms录音 recordControl.stopRecord(); recordControl.commit(); //由于采集频率太高,手机不能完全显示,所以需要通过均值滤波来降低分辨率 int Zoom_out=200;//缩小200倍 int[] bits=new int[bos.toByteArray().length/Zoom_out]; for(int i=0,total=0,index=0;i<bos.toByteArray().length;i++) { total=total+unsignedByteToInt(bos.toByteArray()[i]); if(i%Zoom_out==0 && i!=0) { bits[index]=total/Zoom_out; total=0; index++; } } cmp_HeartWave.UpdateVerticalWave(bits); bos.reset(); } capturePlayer.stop(); capturePlayer.close(); } catch (Exception e) {} } } public void actionPerformed(ActionEvent arg0) { Command command=arg0.getCommand(); if(command==cmdExit)//退出程序 notifyDestroyed(); } protected void pauseApp() {} protected void destroyApp(boolean arg0) {} }

以下代码为画波形图的代码:

/* * To change this template, choose Tools | Templates * and open the template in the editor. */ import com.sun.lwuit.Component; import com.sun.lwuit.Container; import com.sun.lwuit.Graphics; import com.sun.lwuit.Image; import com.sun.lwuit.layouts.BorderLayout; /** * * @author Administrator */ public class Cmp_Wave extends Container{ private Image imgWaveCanvas;//波形图画板,图形画在图像上,再不断地贴图到屏幕 private int height; //自己定义的画板高度 getHeight() private int width; //自己定义的画板宽度 getWidth() private int old_xPos=-1;//位置初始化 private int old_yPos=-1;//位置初始化 private Component cmpWaveCanvas;//显示波形控件 private int[] point=null;//每次收到的point数组 public Cmp_Wave(int canvasHeight,int canvasWidth) { super.setLayout(new BorderLayout()); super.addComponent(BorderLayout.CENTER,WaveCanvas(canvasHeight,canvasWidth));//波形显示在中间 } private Component WaveCanvas(int canvasHeight,int canvasWidth) { height=canvasHeight; width=canvasWidth; //-------初始化波形画板图像 imgWaveCanvas=Image.createImage(width, height); //-------初始化波形画板控件,在控件上绘制波形画板图像 cmpWaveCanvas=new Component(){ public void paint(Graphics g) { //在内存中绘制波形图 Graphics canvas=imgWaveCanvas.getGraphics(); if(point==null)//如果point还没初始化 return;//不执行下面语句 for(int i=0;i<point.length;i++) { if(old_xPos>=width //如果xPos超过屏幕宽度 || old_xPos==-1) //如果PaintVerticalWave是第一次运行 { old_xPos=0;//归零画图 old_yPos=height/2;//中心点画图 canvas.setColor(0x444444);//设置背景色 canvas.fillRect(0, 0, width, height); } int new_yPosPoint=point[i] ; canvas.setColor(0xCCCCCC);//线条色 canvas.drawLine(old_xPos,old_yPos,old_xPos+1,new_yPosPoint); old_xPos++; old_yPos=new_yPosPoint; } //把内存中的波形图画到控件上 g.drawImage(imgWaveCanvas, 0, 0); } }; cmpWaveCanvas.setFocusPainted(false); cmpWaveCanvas.setFocusable(false); return cmpWaveCanvas; } public void UpdateVerticalWave(int []yPos) { //更新绘图数组 int []tmpPos=new int[yPos.length];//临时数组,只取原数组的waveN分之一 for(int i=0,ii=0;i<tmpPos.length;i++,ii+=1) tmpPos[i]=yPos[ii]; point=tmpPos; //重新绘图 cmpWaveCanvas.repaint(); } }