本实例来自于《疯狂Android讲义》,要实现具体的功能,需要了解以下API:
  • MediaPlayer  媒体播放器
  • Visualizer 频谱
  • Equalizer 均衡器
  • BassBoost 重低音控制器
  • PresetReverb 预设音场控制器
  • Paint 绘图
来看下效果示意图,如下所示
竖状波形图
块状波形图
曲线波形图
调节均衡器、重低音
选择音场
下面来看具体的实现代码    
MediaPlayerTest.java
[java] view plain copy print ?
  1. package com.oyp.media;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import android.app.Activity;
  5. import android.content.Context;
  6. import android.graphics.Canvas;
  7. import android.graphics.Color;
  8. import android.graphics.Paint;
  9. import android.graphics.Paint.Style;
  10. import android.graphics.Rect;
  11. import android.media.AudioManager;
  12. import android.media.MediaPlayer;
  13. import android.media.audiofx.BassBoost;
  14. import android.media.audiofx.Equalizer;
  15. import android.media.audiofx.PresetReverb;
  16. import android.media.audiofx.Visualizer;
  17. import android.os.Bundle;
  18. import android.view.Gravity;
  19. import android.view.MotionEvent;
  20. import android.view.View;
  21. import android.view.ViewGroup;
  22. import android.widget.AdapterView;
  23. import android.widget.ArrayAdapter;
  24. import android.widget.LinearLayout;
  25. import android.widget.SeekBar;
  26. import android.widget.Spinner;
  27. import android.widget.TextView;
  28. public class MediaPlayerTest extends Activity
  29. {
  30. // 定义播放声音的MediaPlayer
  31. private MediaPlayer mPlayer;
  32. // 定义系统的频谱
  33. private Visualizer mVisualizer;
  34. // 定义系统的均衡器
  35. private Equalizer mEqualizer;
  36. // 定义系统的重低音控制器
  37. private BassBoost mBass;
  38. // 定义系统的预设音场控制器
  39. private PresetReverb mPresetReverb;
  40. private LinearLayout layout;
  41. private List<Short> reverbNames = new ArrayList<Short>();
  42. private List<String> reverbVals = new ArrayList<String>();
  43. @Override
  44. public void onCreate(Bundle savedInstanceState)
  45. {
  46. super.onCreate(savedInstanceState);
  47. //设置音频流 - STREAM_MUSIC:音乐回放即媒体音量
  48. setVolumeControlStream(AudioManager.STREAM_MUSIC);
  49. layout = new LinearLayout(this);//代码创建布局
  50. layout.setOrientation(LinearLayout.VERTICAL);//设置为线性布局-上下排列
  51. setContentView(layout);//将布局添加到 Activity
  52. // 创建MediaPlayer对象,并添加音频
  53. // 音频路径为  res/raw/beautiful.mp3
  54. mPlayer = MediaPlayer.create(this, R.raw.beautiful);
  55. // 初始化示波器
  56. setupVisualizer();
  57. // 初始化均衡控制器
  58. setupEqualizer();
  59. // 初始化重低音控制器
  60. setupBassBoost();
  61. // 初始化预设音场控制器
  62. setupPresetReverb();
  63. // 开发播放音乐
  64. mPlayer.start();
  65. }
  66. /**
  67. * 初始化频谱
  68. */
  69. private void setupVisualizer()
  70. {
  71. // 创建MyVisualizerView组件,用于显示波形图
  72. final MyVisualizerView mVisualizerView =
  73. new MyVisualizerView(this);
  74. mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(
  75. ViewGroup.LayoutParams.MATCH_PARENT,
  76. (int) (120f * getResources().getDisplayMetrics().density)));
  77. // 将MyVisualizerView组件添加到layout容器中
  78. layout.addView(mVisualizerView);
  79. // 以MediaPlayer的AudioSessionId创建Visualizer
  80. // 相当于设置Visualizer负责显示该MediaPlayer的音频数据
  81. mVisualizer = new Visualizer(mPlayer.getAudioSessionId());
  82. //设置需要转换的音乐内容长度,专业的说这就是采样,该采样值一般为2的指数倍,如64,128,256,512,1024。
  83. mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
  84. // 为mVisualizer设置监听器
  85. /*
  86. * Visualizer.setDataCaptureListener(OnDataCaptureListener listener, int rate, boolean waveform, boolean fft
  87. *
  88. *      listener,表监听函数,匿名内部类实现该接口,该接口需要实现两个函数
  89. rate, 表示采样的周期,即隔多久采样一次,联系前文就是隔多久采样128个数据
  90. iswave,是波形信号
  91. isfft,是FFT信号,表示是获取波形信号还是频域信号
  92. */
  93. mVisualizer.setDataCaptureListener(
  94. new Visualizer.OnDataCaptureListener()
  95. {
  96. //这个回调应该采集的是快速傅里叶变换有关的数据
  97. @Override
  98. public void onFftDataCapture(Visualizer visualizer,
  99. byte[] fft, int samplingRate)
  100. {
  101. }
  102. //这个回调应该采集的是波形数据
  103. @Override
  104. public void onWaveFormDataCapture(Visualizer visualizer,
  105. byte[] waveform, int samplingRate)
  106. {
  107. // 用waveform波形数据更新mVisualizerView组件
  108. mVisualizerView.updateVisualizer(waveform);
  109. }
  110. }, Visualizer.getMaxCaptureRate() / 2, true, false);
  111. mVisualizer.setEnabled(true);
  112. }
  113. /**
  114. * 初始化均衡控制器
  115. */
  116. private void setupEqualizer()
  117. {
  118. // 以MediaPlayer的AudioSessionId创建Equalizer
  119. // 相当于设置Equalizer负责控制该MediaPlayer
  120. mEqualizer = new Equalizer(0, mPlayer.getAudioSessionId());
  121. // 启用均衡控制效果
  122. mEqualizer.setEnabled(true);
  123. TextView eqTitle = new TextView(this);
  124. eqTitle.setText(”均衡器:”);
  125. layout.addView(eqTitle);
  126. // 获取均衡控制器支持最小值和最大值
  127. final short minEQLevel = mEqualizer.getBandLevelRange()[0];//第一个下标为最低的限度范围
  128. short maxEQLevel = mEqualizer.getBandLevelRange()[1];  // 第二个下标为最高的限度范围
  129. // 获取均衡控制器支持的所有频率
  130. short brands = mEqualizer.getNumberOfBands();
  131. for (short i = 0; i < brands; i++)
  132. {
  133. TextView eqTextView = new TextView(this);
  134. // 创建一个TextView,用于显示频率
  135. eqTextView.setLayoutParams(new ViewGroup.LayoutParams(
  136. ViewGroup.LayoutParams.MATCH_PARENT,
  137. ViewGroup.LayoutParams.WRAP_CONTENT));
  138. eqTextView.setGravity(Gravity.CENTER_HORIZONTAL);
  139. // 设置该均衡控制器的频率
  140. eqTextView.setText((mEqualizer.getCenterFreq(i) / 1000)
  141. + ” Hz”);
  142. layout.addView(eqTextView);
  143. // 创建一个水平排列组件的LinearLayout
  144. LinearLayout tmpLayout = new LinearLayout(this);
  145. tmpLayout.setOrientation(LinearLayout.HORIZONTAL);
  146. // 创建显示均衡控制器最小值的TextView
  147. TextView minDbTextView = new TextView(this);
  148. minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
  149. ViewGroup.LayoutParams.WRAP_CONTENT,
  150. ViewGroup.LayoutParams.WRAP_CONTENT));
  151. // 显示均衡控制器的最小值
  152. minDbTextView.setText((minEQLevel / 100) + “ dB”);
  153. // 创建显示均衡控制器最大值的TextView
  154. TextView maxDbTextView = new TextView(this);
  155. maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
  156. ViewGroup.LayoutParams.WRAP_CONTENT,
  157. ViewGroup.LayoutParams.WRAP_CONTENT));
  158. // 显示均衡控制器的最大值
  159. maxDbTextView.setText((maxEQLevel / 100) + “ dB”);
  160. LinearLayout.LayoutParams layoutParams = new
  161. LinearLayout.LayoutParams(
  162. ViewGroup.LayoutParams.MATCH_PARENT,
  163. ViewGroup.LayoutParams.WRAP_CONTENT);
  164. layoutParams.weight = 1;
  165. // 定义SeekBar做为调整工具
  166. SeekBar bar = new SeekBar(this);
  167. bar.setLayoutParams(layoutParams);
  168. bar.setMax(maxEQLevel - minEQLevel);
  169. bar.setProgress(mEqualizer.getBandLevel(i));
  170. final short brand = i;
  171. // 为SeekBar的拖动事件设置事件监听器
  172. bar.setOnSeekBarChangeListener(new SeekBar
  173. .OnSeekBarChangeListener()
  174. {
  175. @Override
  176. public void onProgressChanged(SeekBar seekBar,
  177. int progress, boolean fromUser)
  178. {
  179. // 设置该频率的均衡值
  180. mEqualizer.setBandLevel(brand,
  181. (short) (progress + minEQLevel));
  182. }
  183. @Override
  184. public void onStartTrackingTouch(SeekBar seekBar)
  185. {
  186. }
  187. @Override
  188. public void onStopTrackingTouch(SeekBar seekBar)
  189. {
  190. }
  191. });
  192. // 使用水平排列组件的LinearLayout“盛装”3个组件
  193. tmpLayout.addView(minDbTextView);
  194. tmpLayout.addView(bar);
  195. tmpLayout.addView(maxDbTextView);
  196. // 将水平排列组件的LinearLayout添加到myLayout容器中
  197. layout.addView(tmpLayout);
  198. }
  199. }
  200. /**
  201. * 初始化重低音控制器
  202. */
  203. private void setupBassBoost()
  204. {
  205. // 以MediaPlayer的AudioSessionId创建BassBoost
  206. // 相当于设置BassBoost负责控制该MediaPlayer
  207. mBass = new BassBoost(0, mPlayer.getAudioSessionId());
  208. // 设置启用重低音效果
  209. mBass.setEnabled(true);
  210. TextView bbTitle = new TextView(this);
  211. bbTitle.setText(”重低音:”);
  212. layout.addView(bbTitle);
  213. // 使用SeekBar做为重低音的调整工具
  214. SeekBar bar = new SeekBar(this);
  215. // 重低音的范围为0~1000
  216. bar.setMax(1000);
  217. bar.setProgress(0);
  218. // 为SeekBar的拖动事件设置事件监听器
  219. bar.setOnSeekBarChangeListener(new SeekBar
  220. .OnSeekBarChangeListener()
  221. {
  222. @Override
  223. public void onProgressChanged(SeekBar seekBar
  224. , int progress, boolean fromUser)
  225. {
  226. // 设置重低音的强度
  227. mBass.setStrength((short) progress);
  228. }
  229. @Override
  230. public void onStartTrackingTouch(SeekBar seekBar)
  231. {
  232. }
  233. @Override
  234. public void onStopTrackingTouch(SeekBar seekBar)
  235. {
  236. }
  237. });
  238. layout.addView(bar);
  239. }
  240. /**
  241. * 初始化预设音场控制器
  242. */
  243. private void setupPresetReverb()
  244. {
  245. // 以MediaPlayer的AudioSessionId创建PresetReverb
  246. // 相当于设置PresetReverb负责控制该MediaPlayer
  247. mPresetReverb = new PresetReverb(0,
  248. mPlayer.getAudioSessionId());
  249. // 设置启用预设音场控制
  250. mPresetReverb.setEnabled(true);
  251. TextView prTitle = new TextView(this);
  252. prTitle.setText(”音场”);
  253. layout.addView(prTitle);
  254. // 获取系统支持的所有预设音场
  255. for (short i = 0; i < mEqualizer.getNumberOfPresets(); i++)
  256. {
  257. reverbNames.add(i);
  258. reverbVals.add(mEqualizer.getPresetName(i));
  259. }
  260. // 使用Spinner做为音场选择工具
  261. Spinner sp = new Spinner(this);
  262. sp.setAdapter(new ArrayAdapter<String>(MediaPlayerTest.this,
  263. android.R.layout.simple_spinner_item, reverbVals));
  264. // 为Spinner的列表项选中事件设置监听器
  265. sp.setOnItemSelectedListener(new Spinner
  266. .OnItemSelectedListener()
  267. {
  268. @Override
  269. public void onItemSelected(AdapterView<?> arg0
  270. , View arg1, int arg2, long arg3)
  271. {
  272. // 设定音场
  273. mPresetReverb.setPreset(reverbNames.get(arg2));
  274. }
  275. @Override
  276. public void onNothingSelected(AdapterView<?> arg0)
  277. {
  278. }
  279. });
  280. layout.addView(sp);
  281. }
  282. @Override
  283. protected void onPause()
  284. {
  285. super.onPause();
  286. if (isFinishing() && mPlayer != null)
  287. {
  288. // 释放所有对象
  289. mVisualizer.release();
  290. mEqualizer.release();
  291. mPresetReverb.release();
  292. mBass.release();
  293. mPlayer.release();
  294. mPlayer = null;
  295. }
  296. }
  297. /**
  298. * 根据Visualizer传来的数据动态绘制波形效果,分别为:
  299. * 块状波形、柱状波形、曲线波形
  300. */
  301. private static class MyVisualizerView extends View
  302. {
  303. // bytes数组保存了波形抽样点的值
  304. private byte[] bytes;
  305. private float[] points;
  306. private Paint paint = new Paint();
  307. private Rect rect = new Rect();
  308. private byte type = 0;
  309. public MyVisualizerView(Context context)
  310. {
  311. super(context);
  312. bytes = null;
  313. // 设置画笔的属性
  314. paint.setStrokeWidth(1f);
  315. paint.setAntiAlias(true);//抗锯齿
  316. paint.setColor(Color.YELLOW);//画笔颜色
  317. paint.setStyle(Style.FILL);
  318. }
  319. public void updateVisualizer(byte[] ftt)
  320. {
  321. bytes = ftt;
  322. // 通知该组件重绘自己。
  323. invalidate();
  324. }
  325. @Override
  326. public boolean onTouchEvent(MotionEvent me)
  327. {
  328. // 当用户触碰该组件时,切换波形类型
  329. if(me.getAction() != MotionEvent.ACTION_DOWN)
  330. {
  331. return false;
  332. }
  333. type ++;
  334. if(type >= 3)
  335. {
  336. type = 0;
  337. }
  338. return true;
  339. }
  340. @Override
  341. protected void onDraw(Canvas canvas)
  342. {
  343. super.onDraw(canvas);
  344. if (bytes == null)
  345. {
  346. return;
  347. }
  348. // 绘制白色背景
  349. canvas.drawColor(Color.WHITE);
  350. // 使用rect对象记录该组件的宽度和高度
  351. rect.set(0,0,getWidth(),getHeight());
  352. switch(type)
  353. {
  354. // ——-绘制块状的波形图——-
  355. case 0:
  356. for (int i = 0; i < bytes.length - 1; i++)
  357. {
  358. float left = getWidth() * i / (bytes.length - 1);
  359. // 根据波形值计算该矩形的高度
  360. float top = rect.height()-(byte)(bytes[i+1]+128)
  361. * rect.height() / 128;
  362. float right = left + 1;
  363. float bottom = rect.height();
  364. canvas.drawRect(left, top, right, bottom, paint);
  365. }
  366. break;
  367. // ——-绘制柱状的波形图(每隔18个抽样点绘制一个矩形)——-
  368. case 1:
  369. for (int i = 0; i < bytes.length - 1; i += 18)
  370. {
  371. float left = rect.width()*i/(bytes.length - 1);
  372. // 根据波形值计算该矩形的高度
  373. float top = rect.height()-(byte)(bytes[i+1]+128)
  374. * rect.height() / 128;
  375. float right = left + 6;
  376. float bottom = rect.height();
  377. canvas.drawRect(left, top, right, bottom, paint);
  378. }
  379. break;
  380. // ——-绘制曲线波形图——-
  381. case 2:
  382. // 如果point数组还未初始化
  383. if (points == null || points.length < bytes.length * 4)
  384. {
  385. points = new float[bytes.length * 4];
  386. }
  387. for (int i = 0; i < bytes.length - 1; i++)
  388. {
  389. // 计算第i个点的x坐标
  390. points[i * 4] = rect.width()*i/(bytes.length - 1);
  391. // 根据bytes[i]的值(波形点的值)计算第i个点的y坐标
  392. points[i * 4 + 1] = (rect.height() / 2)
  393. + ((byte) (bytes[i] + 128)) * 128
  394. / (rect.height() / 2);
  395. // 计算第i+1个点的x坐标
  396. points[i * 4 + 2] = rect.width() * (i + 1)
  397. / (bytes.length - 1);
  398. // 根据bytes[i+1]的值(波形点的值)计算第i+1个点的y坐标
  399. points[i * 4 + 3] = (rect.height() / 2)
  400. + ((byte) (bytes[i + 1] + 128)) * 128
  401. / (rect.height() / 2);
  402. }
  403. // 绘制波形曲线
  404. canvas.drawLines(points, paint);
  405. break;
  406. }
  407. }
  408. }
  409. }

AndroidManifest.xml

[html] view plain copy print ?
  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <manifest
  3. xmlns:android=“http://schemas.android.com/apk/res/android”
  4. package=“com.oyp.media”
  5. android:versionCode=“1”
  6. android:versionName=“1.0”>
  7. <uses-sdk android:minSdkVersion=“10”
  8. android:targetSdkVersion=“17”/>
  9. <!– 使用音场效果必要的权限 –>
  10. <uses-permission android:name=“android.permission.RECORD_AUDIO” />
  11. <uses-permission android:name=“android.permission.MODIFY_AUDIO_SETTINGS”/>
  12. <application
  13. android:icon=“@drawable/ic_launcher”
  14. android:label=“@string/app_name”>
  15. <activity
  16. android:name=“.MediaPlayerTest”
  17. android:label=“@string/app_name”>
  18. <intent-filter>
  19. <action android:name=“android.intent.action.MAIN” />
  20. <category android:name=“android.intent.category.LAUNCHER” />
  21. </intent-filter>
  22. </activity>
  23. </application>
  24. </manifest>

PS:请在真机环境下运行此程序,如果在模拟器下运行,可能会报错:

[java] view plain copy print ?
  1. java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -4

转载自:http://blog.csdn.net/ouyang_peng/article/details/46841893

Android实现音乐示波器、均衡器、重低音和音场功能相关推荐

  1. 我的Android进阶之旅------gt;Android实现音乐示波器、均衡器、重低音和音场功能...

    本实例来自于<疯狂Android讲义>.要实现详细的功能,须要了解下面API: MediaPlayer  媒体播放器 Visualizer 频谱 Equalizer 均衡器 BassBoo ...

  2. Android移动开发-音乐的示波器、均衡、重低音和音场的实现

    本Demo无须界面布局文件,使用一个LinearLayout容器来盛装一个示波器View组件,该示波器View组件将负责绘制Visualizer传过来的数据:LinearLayout添加多个SeekB ...

  3. Android逆向之旅---微信封了抖音分享功能,而我要把短视频分享到朋友圈!

    一.前言 本文写的目的很简单,因为微信封了抖音短视频分享功能,但是这个对于用户来说都是不好的体验,当初3Q大战,损害的是用户利益,现在也是用户最后都是最受伤的一个.而这两次都和企鹅有关.先看看企鹅为了 ...

  4. Android 自定义音乐播放器实现

    Android自定义音乐播放器 一:首先介绍用了哪些Android的知识点: 1 MediaPlayer工具来播放音乐 2 Handle.因为存在定时任务(歌词切换,动画,歌词进度条变换等)需要由Ha ...

  5. Android:音乐特效控制

    音乐特效控制 标签(空格分隔): android 作者:陈小默 水平有限,如果错误恳请批评指正 音乐特效控制 一AcousticEchoCanceler回声消除器 二AutomaticGainCont ...

  6. 三款Android平台音乐播放器多方位对比

    音乐播放器作为重要的娱乐工具,在PC端异常重要,在移动端同样如此.移动端的音乐播放器很多,巨头们纷纷在这个领域布局,腾讯百度等均拥有各自的移动端音乐播放器.爱好音乐的机友们也面临较多的选择. 小编在这 ...

  7. 索尼XB950N1 震撼人心的重低音

    虽然题目是震撼人心的重低音,但是低音可以通过app调节,所以我们可以用这个耳机听各种类型的歌曲. 索尼XB950N1与XB950B1非常相似,但索尼XB950N1提供了主动降噪,续航稍长一些.从蓝牙3 ...

  8. Android简易音乐重构MVVM Java版 -搭建项目(八)

    Android简易音乐重构MVVM Java版 -搭建项目(八) 关于 新版本配置 网易云音乐api版本更新 重构代码 新建app类继承Application 项目结构 定义BaseActivity. ...

  9. 【android】音乐播放器之service服务设计

    学习Android有一个多月,看完了<第一行代码>以及mars老师的第一期视频通过音乐播放器小项目加深对知识点的理解.从本文开始,将详细的介绍简单仿多米音乐播放器的实现,以及网络解析数据获 ...

最新文章

  1. autotype安全 fastjson_Fastjson 安全更新,建议升级到 1.2.28 或更新版本
  2. XML文件中的CDATA的使用.
  3. java书籍_Java学习必备书籍(快来收藏)
  4. 网络原理 | TCP/IP中的连接管理机制 重要协议与核心机制
  5. H5 通过Ajax方式上传文件,使用FormData进行Ajax请求
  6. 计算机二级公共基础知识笔记
  7. kjb文件 解析_十四期全国BIM一级考试真题及解析
  8. 个人随笔/小白应该如何学习Linux,我的一些心得分享.
  9. 什么叫嵌入式开发 嵌入式开发的要求
  10. 橙色云再度入选中国科学院发布的《互联网周刊》
  11. 全球光纤接头闭合器(FOSC)收入预计2028年达到42.159亿美元
  12. 麻理工MIT的脑计划eyewire (顺便学习一下医学影像知识)
  13. Less变量动态修改
  14. 计算机五个盘,我的电脑分区分了5个盘。怎么办?
  15. CF115B Lawnmower
  16. 2019-2020年催化类专业期刊的影响因子
  17. python足球数据可视化_欧洲足球,5大联赛!Python爬虫数据可视化带你解析经典赛事...
  18. 是时候来了解android7了:shortcuts(快捷方式)
  19. Windows将鼠标单击转换为双击的原理
  20. yjp-9.5.6使用

热门文章

  1. 容器编排之战(三)连载
  2. 使用muJava进行变异测试
  3. 黑暗中归来(4-7)
  4. mysql SELECT/UPDATE command denied to user 'root'@'localhost' for table 'XXX'
  5. on-line gradient descent
  6. 深信服android面试题,2017年深信服Android开发岗位笔试面试总结
  7. 【CSS】设置文字不能被选中解除限制
  8. SpringBoot实战:整合Redis、mybatis,封装RedisUtils工具类等
  9. [转载] 在进行数据分析的时候,什么情况下需要对数据进行标准化处理?
  10. 技术分享 | 跨平台API对接(Java)