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

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

本实例来自于《疯狂Android讲义》,要实现具体的功能,需要了解以下API:
  • MediaPlayer  媒体播放器
  • Visualizer 频谱
  • Equalizer 均衡器
  • BassBoost 重低音控制器
  • PresetReverb 预设音场控制器
  • Paint 绘图

来看下效果示意图,如下所示

竖状波形图
我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能_第1张图片
块状波形图
我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能_第2张图片
曲线波形图
我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能_第3张图片

调节均衡器、重低音
我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能_第4张图片
选择音场
我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能_第5张图片

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

AndroidManifest.xml
[html]  view plain copy
  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.       
  13.     <application  
  14.         android:icon="@drawable/ic_launcher"  
  15.         android:label="@string/app_name">  
  16.         <activity  
  17.             android:name=".MediaPlayerTest"  
  18.             android:label="@string/app_name">  
  19.             <intent-filter>  
  20.                 <action android:name="android.intent.action.MAIN" />  
  21.                 <category android:name="android.intent.category.LAUNCHER" />  
  22.             intent-filter>  
  23.         activity>  
  24.     application>  
  25. manifest>  

PS:请在真机环境下运行此程序,如果在模拟器下运行,可能会报错:
[java]  view plain copy
  1. java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -4  

你可能感兴趣的:(音乐)