转载请标明出处:http://blog.csdn.net/sctu_vroy/article/details/45871823

功能:加载本地SD卡中moveDsp文件夹中的音频文件(包括录音获取文件和MP3文件),播放实时FFT,绘制出信号的时域和频域波形。

设计步骤:
第一步:页面布局,编写录音工具类URecorder(设置录音属性)和接口IVoiceManager
  1. public class URecorder implements IVoiceManager{
  2. private static final String TAG = "URecorder";
  3. private Context context = null;
  4. private String path = null;
  5. private MediaRecorder mRecorder = null;
  6. public URecorder(Context context, String path) {
  7. this.context = context;
  8. this.path = path;
  9. mRecorder = new MediaRecorder();
  10. }
  11. @Override
  12. public boolean start() {
  13. //设置音源为Micphone
  14. mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
  15. //设置封装格式
  16. mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
  17. mRecorder.setOutputFile(path);
  18. //设置编码格式
  19. mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
  20. try {
  21. mRecorder.prepare();
  22. } catch (IOException e) {
  23. Log.e(TAG, "prepare() failed");
  24. }
  25. //录音
  26. mRecorder.start();
  27. return false;
  28. }
  29. @Override
  30. public boolean stop() {
  31. mRecorder.stop();
  32. mRecorder.release();
  33. mRecorder = null;
  34. return false;
  35. }
  36. }
  1. public interface IVoiceManager {
  2. public boolean start();
  3. public boolean stop();
  4. }

第二步:继承父类View,编写自定义绘图类VisualizerView(显示时域波形)和VisualizerFFTView(显示频域波形(频谱)),重写onDraw()方法

时域波形显示类:
  1. public class VisualizerView extends View {
  2. private byte[] mBytes;
  3. private float[] mPoints;
  4. private Rect mRect = new Rect();
  5. private Paint mForePaint = new Paint();
  6. public VisualizerView(Context context) {
  7. super(context);
  8. init();
  9. }
  10. /**
  11. * 初始化
  12. */
  13. private void init() {
  14. mBytes = null;
  15. mForePaint.setStrokeWidth(1f);
  16. mForePaint.setAntiAlias(true);
  17. mForePaint.setColor(Color.GREEN);
  18. }
  19. public void updateVisualizer(byte[] waveForm)
  20. {
  21. mBytes = waveForm;
  22. invalidate();
  23. }
  24. @Override
  25. protected void onDraw(Canvas canvas) {
  26. super.onDraw(canvas);
  27. if (mBytes == null)
  28. {
  29. return;
  30. }
  31. if (mPoints == null || mPoints.length < mBytes.length * 4)
  32. {
  33. mPoints = new float[mBytes.length * 4];
  34. }
  35. mRect.set(0, 0, getWidth(), getHeight());
  36. //绘制波形
  37. for (int i = 0; i < mBytes.length - 1; i++) {
  38. mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1);
  39. mPoints[i * 4 + 1] = mRect.height() / 2
  40. + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2) / 128;
  41. mPoints[i * 4 + 2] = mRect.width() * (i + 1) / (mBytes.length - 1);
  42. mPoints[i * 4 + 3] = mRect.height() / 2
  43. + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2) / 128;
  44. }
  45. canvas.drawLines(mPoints, mForePaint);
  46. }
  47. }

频谱显示类:

  1. public class VisualizerFFTView extends View {
  2. private byte[] mBytes;
  3. private float[] mPoints;
  4. private Rect mRect = new Rect();
  5. private Paint mForePaint = new Paint();
  6. private int mSpectrumNum = 48;
  7. public VisualizerFFTView(Context context) {
  8. super(context);
  9. init();
  10. }
  11. /**
  12. * 初始化
  13. */
  14. private void init() {
  15. mBytes = null;
  16. mForePaint.setStrokeWidth(8f);
  17. mForePaint.setAntiAlias(true);
  18. mForePaint.setColor(Color.rgb(0, 128, 255));
  19. }
  20. public void updateVisualizer(byte[] fft)
  21. {
  22. byte[] model = new byte[fft.length / 2 + 1];
  23. model[0] = (byte) Math.abs(fft[0]);
  24. for (int i = 2, j = 1; j < mSpectrumNum;)
  25. {
  26. model[j] = (byte) Math.hypot(fft[i], fft[i + 1]);
  27. i += 2;
  28. j++;
  29. }
  30. mBytes = model;
  31. invalidate();
  32. }
  33. @Override
  34. protected void onDraw(Canvas canvas) {
  35. super.onDraw(canvas);
  36. if (mBytes == null)
  37. {
  38. return;
  39. }
  40. if (mPoints == null || mPoints.length < mBytes.length * 4)
  41. {
  42. mPoints = new float[mBytes.length * 4];
  43. }
  44. mRect.set(0, 0, getWidth(), getHeight());
  45. //绘制频谱
  46. final int baseX = mRect.width()/mSpectrumNum;
  47. final int height = mRect.height();
  48. for (int i = 0; i < mSpectrumNum ; i++)
  49. {
  50. if (mBytes[i] < 0)
  51. {
  52. mBytes[i] = 127;
  53. }
  54. final int xi = baseX*i + baseX/2;
  55. mPoints[i * 4] = xi;
  56. mPoints[i * 4 + 1] = height;
  57. mPoints[i * 4 + 2] = xi;
  58. mPoints[i * 4 + 3] = height - mBytes[i];
  59. }
  60. canvas.drawLines(mPoints, mForePaint);
  61. }
  62. }

第三步:通过URI新建一个MediaPlayer对象,其他方式当执行getAudioSessionId()时将为null

  1. //uri = Uri.parse(AudioPath);  // 解析录音文件路径到uri
  2. uri = Uri.parse(Mp3Path);  // 解析MP3文件路径到uri
  3. mMedia = MediaPlayer.create(this, uri);  // 实例化mMedia对象,并通过uri将资源文件加载到该对象

调用Android SDK 2.3以上版本中一个工具包android.media.audiofx.Visualizer,程序需要做的就是实例化一个Visualizer对象,通过获得一个实例化的音频媒体类对象的SessionId,并设置该对象的需要转换的音乐内容长度和采样率。最后为visualizer设置监听器setDataCaptureListener(OnDataCaptureListener listener, rate, iswave, isfft),当采样得到的数据长度达到之前设置的内容长度后,将会触发两个函数,在这两个函数中即可分别得到音频信号的时域信号数据以及经过快速傅里叶变换(FFT)处理的频域信号数据,均为字节数组形式(即:byte[])。

  1. mWaveView = new VisualizerView(this);   // 创建VisualizerView对象
  2. mFFtView = new VisualizerFFTView(this);   // 创建VisualizerFFTView对象
  3. final int maxCR = Visualizer.getMaxCaptureRate();   // 获取最大采样率
  4. mVisualizer = new Visualizer(mMedia.getAudioSessionId());   // 实例化mVisualizer
  5. mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);   // 设置内容长度为1024
  6. mVisualizer.setDataCaptureListener(
  7. new Visualizer.OnDataCaptureListener()
  8. {
  9. public void onWaveFormDataCapture(Visualizer visualizer,
  10. byte[] waveform, int samplingRate)
  11. {
  12. mWaveView.updateVisualizer(waveform);  // 更新时域波形数据
  13. }
  14. public void onFftDataCapture(Visualizer visualizer,
  15. byte[] fft, int samplingRate)
  16. {
  17. mFFtView.updateVisualizer(fft);  // 更新频域波形数据
  18. }
  19. }, maxCR / 2, true, true);   // 采样速率为512MHz,设置同时获取时域、频域波形数据

需要注意的是:停止播放时,除了release播放类对象外,还要释放Visualizer对象。
音频信号FFT效果图:


源码下载链接:

Android平台音频信号FFT的实现的更多相关文章

  1. [译]:Xamarin.Android平台功能——位置服务

    返回索引目录 原文链接:Location Services. 译文链接:Xamarin.Android平台功能--位置服务 本部分介绍位置服务以及与如何使用位置提供商服务 Location Servi ...

  2. Cocos2d-x 3.2 学习笔记(四)学习打包Android平台APK!

    从cocos2dx 3.2项目打包成apk安卓应用文件,搭建安卓环境的步骤有点繁琐,但搭建一次之后,以后就会非常快捷! (涉及到3.1.1版本的,请自动对应3.2版本,3.x版本的环境搭建都是一样的) ...

  3. (转)android平台phonegap框架实现原理

    (原文)http://blog.csdn.net/wuruixn/article/details/7405175 android平台phonegap框架实现原理 分类: Android2012-03- ...

  4. Android平台免Root无侵入AOP框架Dexposed使用详解

    Dexposed是基于久负盛名的开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架. Dexposed的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或者 ...

  5. Cocos2dx-3.0版本 从开发环境搭建(Win32)到项目移植Android平台过程详解

    作为重量级的跨平台开发的游戏引擎,Cocos2d-x在现今的手游开发领域占有重要地位.那么问题来了,作为Cocos2dx的学习者,它的可移植特性我们就需要掌握,要不然总觉得少一门技能.然而这个时候各种 ...

  6. 基于android平台的斗地主AI

    本软件是基于android平台的斗地主AI,我们在源代码的基础之上,旨在改进AI的算法,使玩家具有更丰富的体验感,让NPC可以更为智能. (一)玩法解析: (1)发牌和叫牌:一副扑克54张,先为每个人 ...

  7. 4412开发板Android教程——Android平台简介

    本文转自迅为开发板论坛:http://www.topeetboard.com Android和IOS Android的历史 Android公司 2005年Google收购成立22个月的Android公 ...

  8. Android平台的开发环境的发展演变

    因为之前学习java语言的时候安装过了eclipse,所以想在eclipse上搭建android平台,在参照知乎上大神们的意见,发现了AS强大的代码提示.实时预览和搜索匹配等出色功能,最后还是选择在A ...

  9. Cocos2d-x 3.0修改Android平台帧率fps - 解决游戏运行手机发热发烫问题

    使用Cocos2d-x 3.0开发游戏之后,发现游戏在android手机上发热非常严重,在魅族2上,几乎担心手机会爆炸了~~~采取的一个措施就是降低帧率,因为游戏对于帧率要求不是非常高. 做过coco ...

随机推荐

  1. 从txt中读入数据到数组中(fscanf)

    C-sources: #include<stdio.h> int main() { FILE* fp; //定义一个文件 fp = fopen("p5.txt",&qu ...

  2. OpenSuSE zypper OpenStack Icehouse repoAdd

    java for windows 浏览器(完整安装包离线版); http://www.java.com/zh_CN/download/windows_offline.jsp 配置OpenSuSE zy ...

  3. git 之别名配置

    在git操作中有很多命令我们自己可以起别名,以提高操作效率. 1. 配置方式 1)项目级别的配置,仅对当前项目生效(将写入到.git/config文件中)    $ git config --glob ...

  4. 关于smali插桩

    虽说是老生常谈的东西了,稍微记录一下. 我觉得最重要的就是寄存器的问题了,如果需要额外的寄存器,要在smali函数的最前面将寄存器数量增加到需要的数量. 在smali代码中,寄存器有两种表示方式,一种 ...

  5. ORA-03113 通信通道的文件结尾(ORA-19804 ORA-16038-归档空间满的处理方法)

    1.数据库启动报错SQL> startupORACLE 例程已经启动. Total System Global Area 1887350784 bytesFixed Size 2176848 b ...

  6. 【Tarjan,LCA】【3-21个人赛】【problemD】

    Problem D Time Limit : 6000/3000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other) Total Sub ...

  7. ADO.Net连接模式

    1.SqlConnection类 (1).通过构造函数创建一个SqlConnection对象,可以同时指定连接字符串 (2).通过SqlConnection对象的Open()方法打开数据库连接 (3) ...

  8. UVA 10603 Fill

    题意: 题目的意思是倒水,给出的四个数据是第一个水杯,第二个水杯,第三个水杯,和目标水量.一开始只有第三个水杯是满的,剩下的水杯是空的.倒水的时候只能把倒水出来的这个杯子倒空,或是倒水进去的杯子倒满. ...

  9. C#获取本周、上周、本月、上月、本季度、上季度、本年、上一年起始时间和结束时间

    /// 取得某月的第一天 /// </summary> /// <param name="datetime">要取得月份第一天的时间</param&g ...

  10. SQL Server数据恢复——日志备份

    太坑了,我把数据给删了 “大坑啊,数据被我误删了.”从事数据库相关工作的过程中,我想应该很多人会有过和我一样的遭遇吧?尤其是在进行update或者delete操作的时候,忘记了where条件.这些毁灭 ...