激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

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

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項目開發能起到一定的借鑒與幫助作用。

延伸 · 閱讀

精彩推薦
1240
主站蜘蛛池模板: 国产精品亚洲一区二区三区久久 | 91成人免费看片 | 国产黄色毛片 | 91看片成人| 国产精品一区二区三区在线播放 | 激情亚洲一区二区 | 欧洲精品久久久 | 看黄在线 | 一区二区久久电影 | 美女av在线免费观看 | 国产毛片毛片 | 成人午夜在线播放 | v天堂在线视频 | 中文字幕激情视频 | 国产精品一 | www.国产一区.com | 黄色大片免费看 | 国产1区2区3区中文字幕 | 水卜樱一区二区av | 亚洲成人久久精品 | 日本一道aⅴ不卡免费播放 视屏一区 | 成人在线观看免费爱爱 | 国产一级免费在线视频 | 欧美一级黄色片在线观看 | 国产精品视频成人 | 97se亚洲综合在线韩国专区福利 | 操操操操操| 精品国产一二区 | 国产精品99久久久久久久 | 青青草最新网址 | 热re91久久精品国产99热 | 欧美老外a级毛片 | 妇子乱av一区二区三区 | 高潮激情aaaaa免费看 | 爱爱视频天天干 | 毛片在哪里看 | 欧美特级黄色 | 久久久久成人网 | 免费欧美一级视频 | av懂色 | 一区二区三区欧美在线 |