当前位置: 首页 > 编程日记 > 正文

Android语音信号波形显示

简单地介绍了AudioRecord和AudioTrack的使用,这次就结合SurfaceView实现一个Android版的手机模拟信号示波器(PS:以前也讲过J2ME版的手机示波器)。最近物联网炒得很火,作为手机软件开发者,如何在不修改手机硬件电路的前提下实现与第三方传感器结合呢?麦克风就是一个很好的ADC接口,通过麦克风与第三方传感器结合,再在软件里对模拟信号做相应的处理,就可以提供更丰富的传感化应用。

先来看看本文程序运行的效果图(屏幕录像速度较慢,真机实际运行起来会更加流畅):

本文程序使用8000hz的采样率,对X轴方向绘图的实时性要求较高,如果不降低X轴的分辨率,程序的实时性较差,因此程序对X轴数据缩小区间为8倍~16倍。由于采用16位采样,因此Y轴数据的高度相对于手机屏幕来说也偏大,程序也对Y轴数据做缩小,区间为1倍~10倍。在SurfaceView的OnTouchListener方法里加入了波形基线的位置调节,直接在SurfaceView控件上触摸即可控制整体波形偏上或偏下显示。

main.xml源码如下:

<?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绘图线程的实现,两个线程同步操作,代码如下:

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,代码如下:

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;@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onClick(View v) {if(clsOscilloscope.rateY<yMax)clsOscilloscope.rateY++;    setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");}});}@Overrideprotected void onDestroy() {super.onDestroy();android.os.Process.killProcess(android.os.Process.myPid());}/*** 按键事件处理* @author GV**/class ClickEvent implements View.OnClickListener {@Overridepublic 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{@Overridepublic boolean onTouch(View v, MotionEvent event) {clsOscilloscope.baseLine=(int)event.getY();return true;}}
}

http://blog.csdn.net/hellogv/article/details/6032046

相关文章:

科研费4年翻3倍,全球科研队伍突破8000人,滴滴致力打造出行领域核心技术

日前&#xff0c;十三届全国人大四次会议表决通过了《国民经济和社会发展第十四个五年规划和2035年远景目标纲要》&#xff08;下称《规划》&#xff09;。《规划》强调要坚持创新在我国现代化建设全局中的核心地位&#xff0c;把科技自立自强作为国家发展的战略支撑。 《规划…

c++ 继承访问控制初步

访问控制方式这里有篇很好的文章,其实内容也是总结cprimer上的内容 现在就按照这篇的文章举例进行学习. 思路 不同继承方式的影响主要体现在&#xff1a; 1、派生类成员对基类成员的访问控制。 2、派生类对象对基类成员的访问控制 三种继承方式 公有继承(public) 所有public和p…

Excel在.Net 环境下Web方式下驻留内存问题的解决

这段时间在VS 2003 的WebForm 方式下对Excel 进行操作&#xff0c;遇到一个最为头疼的问题就是对Excel操作完毕后Excel不能够正常关闭&#xff0c;系统退出后&#xff0c;Excel总是驻留在内存中。但是这段代码放到WinForm的程序中又没有问题。在网上进行了查找也没有找到有效可…

2.8 FSM之Moore和Mealy part3

来看看我们的Mealy机的设计吧~~。Mealy机的想法起源于&#xff1a;这里我们有输入&#xff0c;并且根据相应的输入我们的字符识别机能做出相应的应答也就是输出。所以我们为何不把输入和输出同时表达出来呢&#xff1f;这样我们就能把输出和抽象的状态分离出来。好处第一就是我…

​对标GPT-3、AlphaFold,智源研究院发布超大规模智能模型系统“悟道1.0”

出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;3月20日&#xff0c;北京智源人工智能研究院发布我国首个超大规模智能模型系统“悟道1.0”。“悟道1.0”由智源研究院学术副院长、清华大学唐杰教授领衔&#xff0c;带领来自北京大学、清华大学、中国人民大学、中国科…

TCP Cluster for mqtt 技术实施方案

最前沿的网络技术&#xff0c;为你的网站带来国际化的用户体验和易用性&#xff0c;这一切只有Witmart.com能做到。

两台SQL Server数据同步解决方案

复制的概念复制是将一组数据从一个数据源拷贝到多个数据源的技术&#xff0c;是将一份数据发布到多个存储站点上的有效方式。使用复制技术&#xff0c;用户可以将一份数据发布到多台服务器上&#xff0c;从而使不同的服务器用户都可以在权限的许可的范围内共享这份数据。复制技…

一个用微软官方的OpenXml读写Excel 目前网上不太普及的方法。

新版本的xlsx是使用新的存储格式&#xff0c;貌似是处理过的XML。 传统的excel处理方法&#xff0c;我真的感觉像屎。用Oldeb不方便&#xff0c;用com组件要实际调用excel打开关闭&#xff0c;很容易出现死。 对于OpenXML我网上搜了一下&#xff0c;很多人没有介绍。所以我就这…

分析6千万条GitHub帖子,发现你的工作状态与表情符号强相关

作者 | 凌霄出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;新冠疫情使得远程办公的人数大幅度增加&#xff0c;然而&#xff0c;当越来越多的人远程工作时&#xff0c;人们的情绪和心理健康状态也难以通过日常面对面的交流来观察&#xff0c;雇主们也就无法获得员工…

软件定义网络 对我们有多重要?

软件定义网络(简称SDN)属于网络流量控制的下一个步骤。Tech Pro Research发布的调查报告正是以此为中心&#xff0c;旨在为我们展示企业如何使用SDN方案。 过去几年以来&#xff0c;以更为高效方式管理环境的需求正快速普及&#xff0c;这也使得网络领域的更高灵活性与控制手段…

SQL Server数据库六种数据移动方法

1. 通过工具DTS的设计器进行导入或导出DTS的设计器功能强大&#xff0c;支持多任务&#xff0c;也是可视化界面&#xff0c;容易操作&#xff0c;但知道的人一般不多&#xff0c;如果只是进行SQL Server数据库中部分表的移动&#xff0c;用这种方法最好&#xff0c;当然&#x…

[企业化NET]Window Server 2008 R2[3]-SVN 服务端 和 客户端 基本使用

1. 服务器基本安装即问题解决记录 √ 2. SVN环境搭建和客户端使用 2.1 服务端 和 客户端 安装 √ 2.2 项目建立与基本使用 √ 2.3 基本冲突解决,并版&#xff0c;tags 3. 数据库安装 4. 邮件服务器搭建 5. JIRA环境搭建和使用 6. CC.NET项目持续发布工具…

又一个Jupyter神器,操作Excel自动生成Python代码

来源 | Python数据科学&#xff08;ID: PyDataScience&#xff09;不得不说&#xff0c;Jupyter对于表的处理真的是越来越方便了&#xff0c;很多库可以直接实现可视化操作&#xff0c;无需写代码。但是这还不够&#xff0c;最近看到一个神器叫Mito&#xff0c;它真的是做到了无…

CIR:2020年全球数据中心应用AOC市场达$42亿

未来十年&#xff0c;QSFP和CXP将占有源光缆销售收入的大部分。到2020年&#xff0c;QSFP和QSFP28销售收入将分别达到7.27亿美元和7.41亿美元。 根据CIR(CommunicationsIndustryResearchers)的最新报告(《2015有源光缆市场&#xff1a;数据中心和高性能计算市场》)&#xff0c;…

Visual C#创建资源文件

资源文件顾名思义就是存放资源的文件。资源文件在程序设计中有着自身独特的优势&#xff0c;他独立于源程序&#xff0c;这样资源文件就可以被多个程序使用。同时在程序设计的时候&#xff0c;有时出于安全或者其他方面因素的考虑&#xff0c;把重要东西存放在资源文件中&#…

给IIS添加CA证书以支持https

一、在IIS中生成Certificate Signing Request (CSR) 个人理解&#xff1a;生成CSR就是生成“私钥/公钥对”之后从中提取出公钥。 1. 打开IIS Manager&#xff0c;在根节点中选择Server Certificates&#xff08;服务器证书&#xff09;&#xff0c;点击右侧的Create Certificat…

MathWorks的AI之路:面向工业场景,打通开发到部署的全链路

作者 | 阿司匹林 AI正在快速发展&#xff0c;并在更多的领域落地。对于MATLAB和Simulink的开发商MathWorks来说&#xff0c;把握AI的机会&#xff0c;显得尤为重要。 不少人对MATLAB等的印象依然停留在学校期间学习的高级线性代数解题器的阶段。然而&#xff0c;MATLAB在几年前…

《Android应用开发攻略》——1.3 从命令行创建 “Hello, World”应用程序

1.3 从命令行创建 “Hello, World”应用程序 Ian Darwin1.3.1 问题你想在不使用Eclipse ADT插件的情况下创建新的Android项目。1.3.2 解决方案使用Android开发工具包&#xff08;Android Development Kit&#xff0c;ADK&#xff09;中的android工具&#xff0c;利用creat proj…

将Excel文件数据库导入SQL Server

将Excel文件数据库导入SQL Server的三种方案//方案一&#xff1a; 通过OleDB方式获取Excel文件的数据&#xff0c;然后通过DataSet中转到SQL Server openFileDialog new OpenFileDialog();openFileDialog.Filter "Excel files(*.xls)|*.xls"; if(openFileDialog.…

Android----PopupWindow

Android的对话框有两种&#xff1a;PopupWindow和AlertDialog。它们的不同点在于&#xff1a;  AlertDialog的位置固定&#xff0c;而PopupWindow的位置可以随意  AlertDialog是非阻塞线程的&#xff0c;而PopupWindow是阻塞线程的 PopupWindow的位置按照有无偏移分&#x…

GitLab 在中国成立公司极狐,GitHub 还会远吗?

作者 | 宋慧 责编 | 苏宓出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;开源的种子已在中国落地开花。今天&#xff0c;中国的开源圈再次迎来一大盛事&#xff1a;全球第二大开源代码托管和项目管理平台 GitLab与红杉宽带等基金正式宣布成立中国合资公司极狐信…

消除危害 让BYOD策略更安全的几个秘诀

自带设备办公(BYOD)已经不是什么新鲜的事情&#xff0c;在近些年&#xff0c;随着移动设备的发展&#xff0c;员工利用自带设备办公已经成为一件非常平常的事情。 但是由于出于安全问题的考虑&#xff0c;一些企业禁止员工通过自带设备连接到公司网络中进行办公。他们不允许个人…

图神经网络如何对知识图谱建模? | 赠书

几乎所有早期的知识图谱嵌入的经典方法都是在对每个三元组打分&#xff0c;在实体和关系的表示中并没有完全考虑到整幅图的结构。早期&#xff0c;图神经网络的方法在知识图谱嵌入中并没有被重视&#xff0c;主要由于&#xff1a;早期的图神经网络更多是具有同种类型节点和边的…

浅析ado.net获取数据库元数据信息

写这个文章源于早先对ADO.Net获取数据库元数据上的认识&#xff0c;去年我在阅读ADO.Net Core Reference的时候曾经注意过DataSet的FillSchema的这个方法。这方面&#xff0c;在我之前的随笔中提到过Typed DataSet&#xff0c;而FillSchem与WriteXmlSchema的结合使用可以获得数…

账户密码策略修改

由于是虚拟测试环境所以AD 和 Exchange安装在同一台虚拟机上&#xff0c;所以修改账户密码策略已达到降低密码是设置的复杂度。 1.打开 --开始--管理工具--组策略管理 2.选择--Default Domain Policy 右键编辑 3.选择计算机设置--策略--安全设置--账户策略--密码策略 4.调整--密…

什么是新IP的四层网络技术

新IP技术共有四层&#xff1a;物理底层、网络服务、控制服务和编排。通过这四个层级实现网络虚拟化和软件定义网络&#xff0c;下面就来详细说一说什么是新IP的四层网络技术。 物理底层 新IP技术依然是一种基于硬件的技术&#xff0c;脱离不了物理底层&#xff0c;所有的虚拟软…

按拼音模糊匹配查询条件的生成类

转载了好几个地方&#xff0c;很难确定最早的出处。将源码贴出来先。1usingSystem; 2usingSystem.Text; 3usingSystem.IO; 45namespacets6{ 7 class test 8 { 9 private static string[] startChars {"啊", "芭", "擦","搭…

Java架构技术文档:并发编程+设计模式+常用框架+JVM+精选视频

本篇文章是我们整理的一份架构师的成长路线&#xff0c;包括了并发编程、设计模式、常用框架、中间件、微服务与分布式、常用工具、JVM、MySQL、数据结构与算法&#xff0c;还有架构师精选视频、架构师成长路线高清大图。又是新的一年&#xff0c;每一年都会有人在成为架构师的…

linux tune2fs简解(每日一令之五)

1:命令简介Linux下的文件检测命令&#xff0c;且可以自行定义自检周期2&#xff1a;用法[rootuyhd000225 ~]# tune2fs --help tune2fs 1.39 (29-May-2006) tune2fs&#xff1a;无效选项 -- - Usage: tune2fs [-c max_mounts_count] [-e errors_behavior] [-g group][-i interva…

物联网技术正颠覆传统医疗行业

如果你或你的爱人最近正在接受治疗&#xff0c;你可能会觉察到一些很酷的新设备可以辅助诊断和治疗。然而&#xff0c;你可能没有意识到&#xff0c;一部分这些设备已连接到互联网&#xff0c;成为物联网生态系统的一个重要组成部分。 到底如何连接核磁共振成像仪、CT扫描仪或实…