基于android的实时音频频谱仪

前一段实习,本来打算做c++,到了公司发现没啥项目,于是乎转行做了android,写的第一个程序竟然要我处理信号,咱可是一心搞计算机的,没接触过信号的东西,什么都没接触过,于是乎, 找各种朋友,各种熟人,现在想想,专注语言是不对的,语言就是一工具,关键还是业务,算法。好了,废话不多说,上程序,注释都很详细,应该能看懂。

分析声音,其实很简单,就是运用傅里叶变换,将声音信号由时域转化到频域(程序用的是快速傅里叶变换,比较简单),为啥要这样,好处多多,不细讲,公司里的用处是为了检测手机发出声音的信号所在的频率集中范围。

第一个类,复数的计算,用到加减乘,很简单。

[java] view plaincopy

  1. package com.mobao360.sunshine;
  2. //复数的加减乘运算
  3. public class Complex {
  4. public double real;
  5. public double image;
  6. //三个构造函数
  7. public Complex() {
  8. // TODO Auto-generated constructor stub
  9. this.real = 0;
  10. this.image = 0;
  11. }
  12. public Complex(double real, double image){
  13. this.real = real;
  14. this.image = image;
  15. }
  16. public Complex(int real, int image) {
  17. Integer integer = real;
  18. this.real = integer.floatValue();
  19. integer = image;
  20. this.image = integer.floatValue();
  21. }
  22. public Complex(double real) {
  23. this.real = real;
  24. this.image = 0;
  25. }
  26. //乘法
  27. public Complex cc(Complex complex) {
  28. Complex tmpComplex = new Complex();
  29. tmpComplex.real = this.real * complex.real - this.image * complex.image;
  30. tmpComplex.image = this.real * complex.image + this.image * complex.real;
  31. return tmpComplex;
  32. }
  33. //加法
  34. public Complex sum(Complex complex) {
  35. Complex tmpComplex = new Complex();
  36. tmpComplex.real = this.real + complex.real;
  37. tmpComplex.image = this.image + complex.image;
  38. return tmpComplex;
  39. }
  40. //减法
  41. public Complex cut(Complex complex) {
  42. Complex tmpComplex = new Complex();
  43. tmpComplex.real = this.real - complex.real;
  44. tmpComplex.image = this.image - complex.image;
  45. return tmpComplex;
  46. }
  47. //获得一个复数的值
  48. public int getIntValue(){
  49. int ret = 0;
  50. ret = (int) Math.round(Math.sqrt(this.real*this.real - this.image*this.image));
  51. return ret;
  52. }
  53. }

这个类是有三个功能,第一,采集数据;第二,进行快速傅里叶计算;第三,绘图。

采集数据用AudioRecord类,网上讲解这个类的蛮多的,搞清楚构造类的各个参数就可以。

绘图用的是SurfaceView Paint Canvas三个类,本人也是参考网络达人的代码

[java] view plaincopy

  1. package com.mobao360.sunshine;
  2. import java.util.ArrayList;
  3. import java.lang.Short;
  4. import android.content.Context;
  5. import android.graphics.Canvas;
  6. import android.graphics.Color;
  7. import android.graphics.DashPathEffect;
  8. import android.graphics.Paint;
  9. import android.graphics.Path;
  10. import android.graphics.PathEffect;
  11. import android.graphics.Rect;
  12. import android.media.AudioRecord;
  13. import android.util.Log;
  14. import android.view.SurfaceView;
  15. public class AudioProcess {
  16. public static final float pi= (float) 3.1415926;
  17. //应该把处理前后处理后的普线都显示出来
  18. private ArrayList<short[]> inBuf = new ArrayList<short[]>();//原始录入数据
  19. private ArrayList<int[]> outBuf = new ArrayList<int[]>();//处理后的数据
  20. private boolean isRecording = false;
  21. Context mContext;
  22. private int shift = 30;
  23. public int frequence = 0;
  24. private int length = 256;
  25. //y轴缩小的比例
  26. public int rateY = 21;
  27. //y轴基线
  28. public int baseLine = 0;
  29. //初始化画图的一些参数
  30. public void initDraw(int rateY, int baseLine,Context mContext, int frequence){
  31. this.mContext = mContext;
  32. this.rateY = rateY;
  33. this.baseLine = baseLine;
  34. this.frequence = frequence;
  35. }
  36. //启动程序
  37. public void start(AudioRecord audioRecord, int minBufferSize, SurfaceView sfvSurfaceView) {
  38. isRecording = true;
  39. new RecordThread(audioRecord, minBufferSize).start();
  40. new DrawThread(sfvSurfaceView).start();
  41. }
  42. //停止程序
  43. public void stop(SurfaceView sfvSurfaceView){
  44. isRecording = false;
  45. inBuf.clear();
  46. }
  47. //录音线程
  48. class RecordThread extends Thread{
  49. private AudioRecord audioRecord;
  50. private int minBufferSize;
  51. public RecordThread(AudioRecord audioRecord,int minBufferSize){
  52. this.audioRecord = audioRecord;
  53. this.minBufferSize = minBufferSize;
  54. }
  55. public void run(){
  56. try{
  57. short[] buffer = new short[minBufferSize];
  58. audioRecord.startRecording();
  59. while(isRecording){
  60. int res = audioRecord.read(buffer, 0, minBufferSize);
  61. synchronized (inBuf){
  62. inBuf.add(buffer);
  63. }
  64. //保证长度为2的幂次数
  65. length=up2int(res);
  66. short[]tmpBuf = new short[length];
  67. System.arraycopy(buffer, 0, tmpBuf, 0, length);
  68. Complex[]complexs = new Complex[length];
  69. int[]outInt = new int[length];
  70. for(int i=0;i < length; i++){
  71. Short short1 = tmpBuf[i];
  72. complexs[i] = new Complex(short1.doubleValue());
  73. }
  74. fft(complexs,length);
  75. for (int i = 0; i < length; i++) {
  76. outInt[i] = complexs[i].getIntValue();
  77. }
  78. synchronized (outBuf) {
  79. outBuf.add(outInt);
  80. }
  81. }
  82. audioRecord.stop();
  83. }catch (Exception e) {
  84. // TODO: handle exception
  85. Log.i("Rec E",e.toString());
  86. }
  87. }
  88. }
  89. //绘图线程
  90. class DrawThread extends Thread{
  91. //画板
  92. private SurfaceView sfvSurfaceView;
  93. //当前画图所在屏幕x轴的坐标
  94. //画笔
  95. private Paint mPaint;
  96. private Paint tPaint;
  97. private Paint dashPaint;
  98. public DrawThread(SurfaceView sfvSurfaceView) {
  99. this.sfvSurfaceView = sfvSurfaceView;
  100. //设置画笔属性
  101. mPaint = new Paint();
  102. mPaint.setColor(Color.BLUE);
  103. mPaint.setStrokeWidth(2);
  104. mPaint.setAntiAlias(true);
  105. tPaint = new Paint();
  106. tPaint.setColor(Color.YELLOW);
  107. tPaint.setStrokeWidth(1);
  108. tPaint.setAntiAlias(true);
  109. //画虚线
  110. dashPaint = new Paint();
  111. dashPaint.setStyle(Paint.Style.STROKE);
  112. dashPaint.setColor(Color.GRAY);
  113. Path path = new Path();
  114. path.moveTo(0, 10);
  115. path.lineTo(480,10);
  116. PathEffect effects = new DashPathEffect(new float[]{5,5,5,5},1);
  117. dashPaint.setPathEffect(effects);
  118. }
  119. @SuppressWarnings("unchecked")
  120. public void run() {
  121. while (isRecording) {
  122. ArrayList<int[]>buf = new ArrayList<int[]>();
  123. synchronized (outBuf) {
  124. if (outBuf.size() == 0) {
  125. continue;
  126. }
  127. buf = (ArrayList<int[]>)outBuf.clone();
  128. outBuf.clear();
  129. }
  130. //根据ArrayList中的short数组开始绘图
  131. for(int i = 0; i < buf.size(); i++){
  132. int[]tmpBuf = buf.get(i);
  133. SimpleDraw(tmpBuf, rateY, baseLine);
  134. }
  135. }
  136. }
  137. /**
  138. * 绘制指定区域
  139. *
  140. * @param start
  141. *            X 轴开始的位置(全屏)
  142. * @param buffer
  143. *             缓冲区
  144. * @param rate
  145. *            Y 轴数据缩小的比例
  146. * @param baseLine
  147. *            Y 轴基线
  148. */
  149. private void SimpleDraw(int[] buffer, int rate, int baseLine){
  150. Canvas canvas = sfvSurfaceView.getHolder().lockCanvas(
  151. new Rect(0, 0, buffer.length,sfvSurfaceView.getHeight()));
  152. canvas.drawColor(Color.BLACK);
  153. canvas.drawText("幅度值", 0, 3, 2, 15, tPaint);
  154. canvas.drawText("原点(0,0)", 0, 7, 5, baseLine + 15, tPaint);
  155. canvas.drawText("频率(HZ)", 0, 6, sfvSurfaceView.getWidth() - 50, baseLine + 30, tPaint);
  156. canvas.drawLine(shift, 20, shift, baseLine, tPaint);
  157. canvas.drawLine(shift, baseLine, sfvSurfaceView.getWidth(), baseLine, tPaint);
  158. canvas.save();
  159. canvas.rotate(30, shift, 20);
  160. canvas.drawLine(shift, 20, shift, 30, tPaint);
  161. canvas.rotate(-60, shift, 20);
  162. canvas.drawLine(shift, 20, shift, 30, tPaint);
  163. canvas.rotate(30, shift, 20);
  164. canvas.rotate(30, sfvSurfaceView.getWidth()-1, baseLine);
  165. canvas.drawLine(sfvSurfaceView.getWidth() - 1, baseLine, sfvSurfaceView.getWidth() - 11, baseLine, tPaint);
  166. canvas.rotate(-60, sfvSurfaceView.getWidth()-1, baseLine);
  167. canvas.drawLine(sfvSurfaceView.getWidth() - 1, baseLine, sfvSurfaceView.getWidth() - 11, baseLine, tPaint);
  168. canvas.restore();
  169. //tPaint.setStyle(Style.STROKE);
  170. for(int index = 64; index <= 512; index = index + 64){
  171. canvas.drawLine(shift + index, baseLine, shift + index, 40, dashPaint);
  172. String str = String.valueOf(frequence / 1024 * index);
  173. canvas.drawText( str, 0, str.length(), shift + index - 15, baseLine + 15, tPaint);
  174. }
  175. int y;
  176. for(int i = 0; i < buffer.length; i = i + 1){
  177. y = baseLine - buffer[i] / rateY ;
  178. canvas.drawLine(2*i + shift, baseLine, 2*i +shift, y, mPaint);
  179. }
  180. sfvSurfaceView.getHolder().unlockCanvasAndPost(canvas);
  181. }
  182. }
  183. /**
  184. * 向上取最接近iint的2的幂次数.比如iint=320时,返回256
  185. * @param iint
  186. * @return
  187. */
  188. private int up2int(int iint) {
  189. int ret = 1;
  190. while (ret<=iint) {
  191. ret = ret << 1;
  192. }
  193. return ret>>1;
  194. }
  195. //快速傅里叶变换
  196. public void fft(Complex[] xin,int N)
  197. {
  198. int f,m,N2,nm,i,k,j,L;//L:运算级数
  199. float p;
  200. int e2,le,B,ip;
  201. Complex w = new Complex();
  202. Complex t = new Complex();
  203. N2 = N / 2;//每一级中蝶形的个数,同时也代表m位二进制数最高位的十进制权值
  204. f = N;//f是为了求流程的级数而设立的
  205. for(m = 1; (f = f / 2) != 1; m++);                             //得到流程图的共几级
  206. nm = N - 2;
  207. j = N2;
  208. /******倒序运算——雷德算法******/
  209. for(i = 1; i <= nm; i++)
  210. {
  211. if(i < j)//防止重复交换
  212. {
  213. t = xin[j];
  214. xin[j] = xin[i];
  215. xin[i] = t;
  216. }
  217. k = N2;
  218. while(j >= k)
  219. {
  220. j = j - k;
  221. k = k / 2;
  222. }
  223. j = j + k;
  224. }
  225. /******蝶形图计算部分******/
  226. for(L=1; L<=m; L++)                                    //从第1级到第m级
  227. {
  228. e2 = (int) Math.pow(2, L);
  229. //e2=(int)2.pow(L);
  230. le=e2+1;
  231. B=e2/2;
  232. for(j=0;j<B;j++)                                    //j从0到2^(L-1)-1
  233. {
  234. p=2*pi/e2;
  235. w.real = Math.cos(p * j);
  236. //w.real=Math.cos((double)p*j);                                   //系数W
  237. w.image = Math.sin(p*j) * -1;
  238. //w.imag = -sin(p*j);
  239. for(i=j;i<N;i=i+e2)                                //计算具有相同系数的数据
  240. {
  241. ip=i+B;                                           //对应蝶形的数据间隔为2^(L-1)
  242. t=xin[ip].cc(w);
  243. xin[ip] = xin[i].cut(t);
  244. xin[i] = xin[i].sum(t);
  245. }
  246. }
  247. }
  248. }
  249. }

主程序

[java] view plaincopy

  1. package com.mobao360.sunshine;
  2. import java.util.ArrayList;
  3. import android.app.Activity;
  4. import android.app.AlertDialog;
  5. import android.content.Context;
  6. import android.content.DialogInterface;
  7. import android.os.Bundle;
  8. import android.util.Log;
  9. import android.view.SurfaceView;
  10. import android.view.View;
  11. import android.widget.AdapterView;
  12. import android.widget.ArrayAdapter;
  13. import android.widget.Button;
  14. import android.widget.Spinner;
  15. import android.widget.TextView;
  16. import android.widget.Toast;
  17. import android.widget.ZoomControls;
  18. import android.media.AudioFormat;
  19. import android.media.AudioRecord;
  20. import android.media.MediaRecorder;
  21. public class AudioMaker extends Activity {
  22. /** Called when the activity is first created. */
  23. static  int frequency = 8000;//分辨率
  24. static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
  25. static final int audioEncodeing = AudioFormat.ENCODING_PCM_16BIT;
  26. static final int yMax = 50;//Y轴缩小比例最大值
  27. static final int yMin = 1;//Y轴缩小比例最小值
  28. int minBufferSize;//采集数据需要的缓冲区大小
  29. AudioRecord audioRecord;//录音
  30. AudioProcess audioProcess = new AudioProcess();//处理
  31. Button btnStart,btnExit;  //开始停止按钮
  32. SurfaceView sfv;  //绘图所用
  33. ZoomControls zctlX,zctlY;//频谱图缩放
  34. Spinner spinner;//下拉菜单
  35. ArrayList<String> list=new ArrayList<String>();
  36. ArrayAdapter<String>adapter;//下拉菜单适配器
  37. TextView tView;
  38. @Override
  39. public void onCreate(Bundle savedInstanceState) {
  40. super.onCreate(savedInstanceState);
  41. setContentView(R.layout.main);
  42. initControl();
  43. }
  44. @Override
  45. protected void onDestroy(){
  46. super.onDestroy();
  47. android.os.Process.killProcess(android.os.Process.myPid());
  48. }
  49. //初始化控件信息
  50. private void initControl() {
  51. //获取采样率
  52. tView = (TextView)this.findViewById(R.id.tvSpinner);
  53. spinner = (Spinner)this.findViewById(R.id.spinnerFre);
  54. String []ls =getResources().getStringArray(R.array.action);
  55. for(int i=0;i<ls.length;i++){
  56. list.add(ls[i]);
  57. }
  58. adapter=new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,list);
  59. adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  60. spinner.setAdapter(adapter);
  61. spinner.setPrompt("请选择采样率");
  62. spinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener(){
  63. @SuppressWarnings("unchecked")
  64. public void onItemSelected(AdapterView arg0,View agr1,int arg2,long arg3){
  65. frequency = Integer.parseInt(adapter.getItem(arg2));
  66. tView.setText("您选择的是:"+adapter.getItem(arg2)+"HZ");
  67. Log.i("sunshine",String.valueOf(minBufferSize));
  68. arg0.setVisibility(View.VISIBLE);
  69. }
  70. @SuppressWarnings("unchecked")
  71. public void onNothingSelected(AdapterView arg0){
  72. arg0.setVisibility(View.VISIBLE);
  73. }
  74. });
  75. Context mContext = getApplicationContext();
  76. //按键
  77. btnStart = (Button)this.findViewById(R.id.btnStart);
  78. btnExit = (Button)this.findViewById(R.id.btnExit);
  79. //按键事件处理
  80. btnStart.setOnClickListener(new ClickEvent());
  81. btnExit.setOnClickListener(new ClickEvent());
  82. //画笔和画板
  83. sfv = (SurfaceView)this.findViewById(R.id.SurfaceView01);
  84. //初始化显示
  85. audioProcess.initDraw(yMax/2, sfv.getHeight(),mContext,frequency);
  86. //画板缩放
  87. zctlY = (ZoomControls)this.findViewById(R.id.zctlY);
  88. zctlY.setOnZoomInClickListener(new View.OnClickListener() {
  89. @Override
  90. public void onClick(View v) {
  91. if(audioProcess.rateY - 5>yMin){
  92. audioProcess.rateY = audioProcess.rateY - 5;
  93. setTitle("Y轴缩小"+String.valueOf(audioProcess.rateY)+"倍");
  94. }else{
  95. audioProcess.rateY = 1;
  96. setTitle("原始尺寸");
  97. }
  98. }
  99. });
  100. zctlY.setOnZoomOutClickListener(new View.OnClickListener() {
  101. @Override
  102. public void onClick(View v) {
  103. if(audioProcess.rateY<yMax){
  104. audioProcess.rateY = audioProcess.rateY + 5;
  105. setTitle("Y轴缩小"+String.valueOf(audioProcess.rateY)+"倍");
  106. }else {
  107. setTitle("Y轴已经不能再缩小");
  108. }
  109. }
  110. });
  111. }
  112. /**
  113. * 按键事件处理
  114. */
  115. class ClickEvent implements View.OnClickListener{
  116. @Override
  117. public void onClick(View v){
  118. Button button = (Button)v;
  119. if(button == btnStart){
  120. if(button.getText().toString().equals("Start")){
  121. try {
  122. //录音
  123. minBufferSize = AudioRecord.getMinBufferSize(frequency,
  124. channelConfiguration,
  125. audioEncodeing);
  126. //minBufferSize = 2 * minBufferSize;
  127. audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,frequency,
  128. channelConfiguration,
  129. audioEncodeing,
  130. minBufferSize);
  131. audioProcess.baseLine = sfv.getHeight()-100;
  132. audioProcess.frequence = frequency;
  133. audioProcess.start(audioRecord, minBufferSize, sfv);
  134. Toast.makeText(AudioMaker.this,
  135. "当前设备支持您所选择的采样率:"+String.valueOf(frequency),
  136. Toast.LENGTH_SHORT).show();
  137. btnStart.setText(R.string.btn_exit);
  138. spinner.setEnabled(false);
  139. } catch (Exception e) {
  140. // TODO: handle exception
  141. Toast.makeText(AudioMaker.this,
  142. "当前设备不支持你所选择的采样率"+String.valueOf(frequency)+",请重新选择",
  143. Toast.LENGTH_SHORT).show();
  144. }
  145. }else if (button.getText().equals("Stop")) {
  146. spinner.setEnabled(true);
  147. btnStart.setText(R.string.btn_start);
  148. audioProcess.stop(sfv);
  149. }
  150. }
  151. else {
  152. new AlertDialog.Builder(AudioMaker.this)
  153. .setTitle("提示")
  154. .setMessage("确定退出?")
  155. .setPositiveButton("确定", new DialogInterface.OnClickListener() {
  156. public void onClick(DialogInterface dialog, int whichButton) {
  157. setResult(RESULT_OK);//确定按钮事件
  158. AudioMaker.this.finish();
  159. finish();
  160. }
  161. })
  162. .setNegativeButton("取消", new DialogInterface.OnClickListener() {
  163. public void onClick(DialogInterface dialog, int whichButton) {
  164. //取消按钮事件
  165. }
  166. })
  167. .show();
  168. }
  169. }
  170. }
  171. }

程序源码下载地址:http://download.csdn.net/detail/sunshine_okey/3790484

详细的看代码吧,有什么写的详细的可以留言

第一次写技术文章,写的不好,大家不要怪罪,将就着看把

时间: 2024-10-10 18:29:58

基于android的实时音频频谱仪的相关文章

基于Android平台的i-jetty网站智能农业监控系统

基于android平台i-jetty网站的智能农业监控系统 摘要:传统的监控系统,一般是基于PC的有线通信传输,其有很多不足之处,如功耗较高.布线成本高.难度大,适应性差,可扩展性不强,增加新的通信线路需要再次布线施工,而且维护起来也比较麻烦,一旦线路出问题,需要繁琐的检查.而嵌入式Web监控系统是基于物联网技术,其无线通信技术具有成本低廉.适应性强.扩展性强.信息安全.使用维护简单等优点. 智能农业中,种植大棚是通过大棚内安装温湿度以及光照传感器,来对农作物的环境参数进行实时采集,由Web监控

Android Camera 实时滤镜(三)

一.基于Android平台基本滤镜算法的实现 1.Android提供了改变图像数值的方法ColorMatrix,通过ColorMatrix方法可以实现基本滤镜,如黑白.灰色.泛黄等效果. 2.通过ColorMatrix改变图像数值,生成变换矩阵,利用矩阵相乘,来改变每个点的像素值. Matrix => r1 r2 r3 r4 r5 g1 g2 g3 g4 g5 b1 b2 b3 b4 b5 a1 a2 a3 a4 a5 变化以后 R'  =  R * r1  +  G * r2  +  B * 

基于android的天气预报的设计与实现

目录 应用开发技术及开发平台介绍 应用需求分析 应用功能设计及其描述 应用UI展示 ①开发技术: 本系统是采用面向对象的软件开发方法,基于Android studio开发平台,以Android作为本系统的开发语言实现音乐播放器预定的需求功能. ②平台介绍 硬件平台 CPU奔腾双核 (主频2.0GHz) 内存1G以上 64或32位PC机 500G硬盘 软件平台 操作系统:Windows XP \ Win7\Win8\win10 开发工具:Android Studio 本软件占用系统空间小,能满足用

【源代码】基于Android和蓝牙的单片机温度採集系统

如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:129518033 STC89C52单片机通过HC-06蓝牙模块与Android手机通信实例- 基于Android和蓝牙的单片机温度採集系统 整个project下载:http://download.csdn.net/detail/itas109/7539057 当中包含. 1.下位机电路原理图 2.下位机採集温度.控制发送.自己主动纠错代码 3.Android端接收温度并显示代码 文件截图 这个是我当年

基于 Android 的 3D 视频样本代码

作者:Mark Liu 下载样本代码 简介 在Android 中,创建一个能够播放视频剪辑的应用非常简单:创建一个采用 3D 图形平面的游戏应用也非常简单.但是,创建一个能够在 3D 图形对象上播放视频的应用却不容易.本文介绍了我为应对该挑战创建的应用.该应用可在 3D 平面上渲染视频,并支持用户以交互的方式在视频平面上播放. 该应用需要解决三大实施问题: 如何构建代码以支持用户在播放视频时变更 3D 平面? 虽然 Android 中默认的 MediaPlayer 配备了全面的播放操作,但是难以

Android开发自学笔记(基于Android Studio1.3.1)—1.环境搭建

一.引言    本套学习笔记的开发环境是Windows 10 专业版和Android Studio 的最新版1.3.1. Android Studio 是一个Android开发环境,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调试.    笔者没有怎么使用过Eclipse做Android学习,但是基于Android Studio类似于VS的项目架构,还是偏爱Android Studio这个IDE,没

基于 Android 的 3D 视频示例代码

笔者:Mark Liu 下载样本代码 简单介绍 在Android 中,创建一个可以播放视频剪辑的应用很easy:创建一个採用 3D 图形平面的游戏应用也很easy.可是,创建一个可以在 3D 图形对象上播放视频的应用却不easy. 本文介绍了我为应对该挑战创建的应用. 该应用可在 3D 平面上渲染视频,并支持用户以交互的方式在视频平面上播放. 该应用须要解决三大实施问题: 怎样构建代码以支持用户在播放视频时变更 3D 平面? 尽管 Android 中默认的 MediaPlayer 配备了全面的播

【源码】基于Android和蓝牙的单片机温度采集系统

如需转载请标明出处:http://blog.csdn.net/itas109 STC89C52单片机通过HC-06蓝牙模块与Android手机通信实例- 基于Android和蓝牙的单片机温度采集系统 整个工程下载:http://download.csdn.net/detail/itas109/7539057 其中包括, 1.下位机电路原理图 2.下位机采集温度.控制发送.自动纠错代码 3.Android端接收温度并显示代码 文件截图 这个是我当年毕业设计做的东西,虽然比较简单,但是还是有一定的参

Android端实时音视频开发指南

简介 yun2win-sdk-Android提供Android端实时音视频完整解决方案,方便客户快速集成实时音视频功能. SDK 提供的能力如下: 发起 加入 AVClient Channel AVMember yun2win官网:www.yun2win.com SDK下载地址:http://www.yun2win.com/h-col-107.html 开发准备 注册并创建应用 到 github下载yun2winSDK及demo 下载源码详解 app为主体显示Module uikit为公共服务M