本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处!
早就有人通过PC声卡的输入(麦克风孔)来做模拟示波器,但是用手机来实现的比较少。用J2ME的MMAPI实现模拟示波器,具体效果稍逊于智能机,因为智能机可以实时读取麦克风输入流,而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();
- }
- }