一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - Android提高之模擬信號示波器的實現

Android提高之模擬信號示波器的實現

2021-03-06 23:38Android開發網 Android

這篇文章主要介紹了Android模擬信號示波器的實現方法,在Android項目開發中有一定的實用價值,需要的朋友可以參考下

前面簡單地介紹了android程序開發中audiorecord和audiotrack的使用,這次再結合surfaceview實現一個android版的手機模擬信號示波器。最近物聯網炒得很火,作為手機軟件開發者,如何在不修改手機硬件電路的前提下實現與第三方傳感器結合呢?麥克風就是一個很好的adc接口,通過麥克風與第三方傳感器結合,再在軟件里對模擬信號做相應的處理,就可以提供更豐富的傳感化應用。

先來看看本文程序運行的效果圖(屏幕錄像截圖的速度較慢,真機實際運行起來會很流暢):

Android提高之模擬信號示波器的實現

本文程序使用8000hz的采樣率,對x軸方向繪圖的實時性要求較高,如果不降低x軸的分辨率,程序的實時性較差,因此程序對x軸數據縮小區間為8倍~16倍。由于采用16位采樣,因此y軸數據的高度相對于手機屏幕來說也偏大,程序也對y軸數據做縮小,區間為1倍~10倍。在surfaceview的ontouchlistener方法里加入了波形基線的位置調節,直接在surfaceview控件上觸摸即可控制整體波形偏上或偏下顯示。

main.xml源碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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繪圖線程的實現,兩個線程同步操作,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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,
  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() {
  @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項目開發能起到一定的借鑒與幫助作用。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日产乱码卡一卡2卡三卡四福利 | 爸爸干女儿小说 | 车上小婕子系列辣文小说 | 校花在公车上被内射好舒服 | 隔壁老王国产在线精品 | 操丝袜秘书 | 99精品国产高清自在线看超 | 久久午夜夜伦痒痒想咳嗽P 久久无码AV亚洲精品色午夜麻豆 | 国产精品成人麻豆专区 | 攻插受 | 国产精品不卡 | 久久永久影院免费 | 深夜国产在线 | 日韩免费视频播放 | 娇妻被又大又粗又长又硬好爽 | 精品国产成人a区在线观看 精品国产91久久久久久久 | 亚洲人和日本人hd | 欧美一区二区三区高清不卡tv | 日韩精品国产自在欧美 | 国产精品高清一区二区三区不卡 | 亚洲国产精品一区二区首页 | 欧美在线看片a免费观看 | 青青热久麻豆精品视频在线观看 | 日韩一级片在线观看 | 美女一级ba大片免色 | 久久福利影院 | 日韩一区视频在线 | 久久AV国产麻豆HD真实乱 | 久草草在线视视频 | 亚洲高清一区二区三区久久 | 射逼网站 | 欧美同志video 在线观看 | 国产成人99精品免费观看 | 青青草高清视频 | 男人狂躁女人下面的视频免费 | 亚洲欧美天堂 | 亚洲免费一 | 亚洲高清视频网站 | 波多野结衣教师未删减版 | 天天天天天干 | 亚欧毛片基地国产毛片基地 |