android音乐频谱解析


  1. 博客转载自:http://blog.csdn.net/com6339663/article/details/42681103

  2. package com.example.Code_Test;  
  3.   
  4. import android.content.Context;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Color;  
  7. import android.graphics.Paint;  
  8. import android.graphics.Paint.Cap;  
  9. import android.graphics.Paint.Join;  
  10. import android.media.audiofx.Visualizer;  
  11. import android.util.Log;  
  12. import android.view.View;  
  13.   
  14. public class VisualizerView extends View implements Visualizer.OnDataCaptureListener {  
  15.   
  16.     private static final int DN_W = 470;//view宽度与单个音频块占比 - 正常480 需微调  
  17.     private static final int DN_H = 360;//view高度与单个音频块占比  
  18.     private static final int DN_SL = 15;//单个音频块宽度  
  19.     private static final int DN_SW = 5;//单个音频块高度  
  20.   
  21.     private int hgap = 0;  
  22.     private int vgap = 0;  
  23.     private int levelStep = 0;  
  24.     private float strokeWidth = 0;  
  25.     private float strokeLength = 0;  
  26.   
  27.     protected final static int MAX_LEVEL = 30;//音量柱·音频块 - 最大个数  
  28.   
  29.     protected final static int CYLINDER_NUM = 26;//音量柱 - 最大个数  
  30.   
  31.     protected Visualizer mVisualizer = null;//频谱器  
  32.   
  33.     protected Paint mPaint = null;//画笔  
  34.   
  35.     protected byte[] mData = new byte[CYLINDER_NUM];//音量柱 数组  
  36.   
  37.     boolean mDataEn = true;  
  38.   
  39.     //构造函数初始化画笔  
  40.     public VisualizerView(Context context) {  
  41.         super(context);  
  42.   
  43.         mPaint = new Paint();//初始化画笔工具  
  44.         mPaint.setAntiAlias(true);//抗锯齿  
  45.         mPaint.setColor(Color.WHITE);//画笔颜色  
  46.   
  47.         mPaint.setStrokeJoin(Join.ROUND); //频块圆角  
  48.         mPaint.setStrokeCap(Cap.ROUND); //频块圆角  
  49.     }  
  50.   
  51.     //执行 Layout 操作  
  52.     @Override  
  53.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  54.         super.onLayout(changed, left, top, right, bottom);  
  55.   
  56.         float w, h, xr, yr;  
  57.   
  58.         w = right - left;  
  59.         h = bottom - top;  
  60.         xr = w / (float) DN_W;  
  61.         yr = h / (float) DN_H;  
  62.   
  63.         strokeWidth = DN_SW * yr;  
  64.         strokeLength = DN_SL * xr;  
  65.         hgap = (int) ((w - strokeLength * CYLINDER_NUM) / (CYLINDER_NUM + 1));  
  66.         vgap = (int) (h / (MAX_LEVEL + 2));//频谱块高度  
  67.   
  68.         mPaint.setStrokeWidth(strokeWidth); //设置频谱块宽度  
  69.     }  
  70.   
  71.     //绘制频谱块和倒影  
  72.     protected void drawCylinder(Canvas canvas, float x, byte value) {  
  73.         if (value == 0) {value = 1;}//最少有一个频谱块  
  74.         for (int i = 0; i < value; i++) { //每个能量柱绘制value个能量块  
  75.             float y = (getHeight()/2 - i * vgap - vgap);//计算y轴坐标  
  76.             float y1=(getHeight()/2+i * vgap + vgap);  
  77.             //绘制频谱块  
  78.             mPaint.setColor(Color.WHITE);//画笔颜色  
  79.             canvas.drawLine(x, y, (x + strokeLength), y, mPaint);//绘制频谱块  
  80.   
  81.             //绘制音量柱倒影  
  82.             if (i <= 6 && value > 0) {  
  83.                 mPaint.setColor(Color.WHITE);//画笔颜色  
  84.                 mPaint.setAlpha(100 - (100 / 6 * i));//倒影颜色  
  85.                 canvas.drawLine(x, y1, (x + strokeLength), y1, mPaint);//绘制频谱块  
  86.             }  
  87.         }  
  88.     }  
  89.   
  90.     @Override  
  91.     public void onDraw(Canvas canvas) {  
  92.         int j=-4;  
  93.         for (int i = 0; i < CYLINDER_NUM/2-4; i++) { //绘制25个能量柱  
  94.               
  95.             drawCylinder(canvas, strokeWidth / 2 + hgap + i * (hgap + strokeLength), mData[i]);  
  96.         }  
  97.         for(int i =CYLINDER_NUM; i>=CYLINDER_NUM/2-4; i--){  
  98.         j++;  
  99.             drawCylinder(canvas, strokeWidth / 2 + hgap + (CYLINDER_NUM/2+j-1 )* (hgap + strokeLength), mData[i-1]);  
  100.         }  
  101.     }  
  102.   
  103.     /** 
  104.      * It sets the visualizer of the view. DO set the viaulizer to null when exit the program. 
  105.      * 
  106.      * @parma visualizer It is the visualizer to set. 
  107.      */  
  108.     public void setVisualizer(Visualizer visualizer) {  
  109.         if (visualizer != null) {  
  110.             if (!visualizer.getEnabled()) {  
  111.                 visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[0]);  
  112.             }  
  113.             levelStep = 230 / MAX_LEVEL;  
  114.             visualizer.setDataCaptureListener(this, Visualizer.getMaxCaptureRate() / 2falsetrue);  
  115.   
  116.         } else {  
  117.   
  118.             if (mVisualizer != null) {  
  119.                 mVisualizer.setEnabled(false);  
  120.                 mVisualizer.release();  
  121.             }  
  122.         }  
  123.         mVisualizer = visualizer;  
  124.     }  
  125.   
  126.     //这个回调应该采集的是快速傅里叶变换有关的数据  
  127.     @Override  
  128.     public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {  
  129.         byte[] model = new byte[fft.length / 2 + 1];  
  130.         if (mDataEn) {  
  131.             model[0] = (byte) Math.abs(fft[1]);  
  132.             int j = 1;  
  133.             for (int i = 2; i < fft.length; ) {  
  134.                 model[j] = (byte) Math.hypot(fft[i], fft[i + 1]);  
  135.                 i += 2;  
  136.                 j++;  
  137.             }  
  138.         } else {  
  139.             for (int i = 0; i < CYLINDER_NUM; i++) {  
  140.                 model[i] = 0;  
  141.             }  
  142.         }  
  143.         for (int i = 0; i < CYLINDER_NUM; i++) {  
  144.             final byte a = (byte) (Math.abs(model[CYLINDER_NUM - i]) / levelStep);  
  145.   
  146.             final byte b = mData[i];  
  147.             if (a > b) {  
  148.                 mData[i] = a;  
  149.             } else {  
  150.                 if (b > 0) {  
  151.                     mData[i]--;  
  152.                 }  
  153.             }  
  154.         }  
  155.         postInvalidate();//刷新界面  
  156.     }  
  157.   
  158.     //这个回调应该采集的是波形数据  
  159.     @Override  
  160.     public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {  
  161.         // Do nothing...  
  162.     }  
  163. }  

主界面activity:

[java]  view plain  copy
  1. import android.app.Activity;  
  2. import android.graphics.Color;  
  3. import android.media.AudioManager;  
  4. import android.media.MediaPlayer;  
  5. import android.media.audiofx.Equalizer;  
  6. import android.media.audiofx.Visualizer;  
  7. import android.os.Bundle;  
  8. import android.view.*;  
  9. import android.widget.*;  
  10.   
  11. public class MyActivity extends Activity {  
  12.   
  13.     private static final float VISUALIZER_HEIGHT_DIP = 150f;//频谱View高度  
  14.   
  15.     private MediaPlayer mMediaPlayer;//音频  
  16.     private Visualizer mVisualizer;//频谱器  
  17.     private Equalizer mEqualizer; //均衡器  
  18.   
  19.     private LinearLayout mLayout;//代码布局  
  20.     VisualizerView mBaseVisualizerView;  
  21.   
  22.     ImageButton play;  
  23.   
  24.     public void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.   
  27.         this.requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏  
  28.         this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//去掉信息栏  
  29.   
  30.         setVolumeControlStream(AudioManager.STREAM_MUSIC);//设置音频流 - STREAM_MUSIC:音乐回放即媒体音量  
  31.   
  32.   
  33.         mLayout = new LinearLayout(this);//代码创建布局  
  34.         mLayout.setOrientation(LinearLayout.VERTICAL);//设置为线性布局-上下排列  
  35. //        mLayout.setBackgroundResource(R.drawable.ac88o);//设置界面背景  
  36.         mLayout.setGravity(Gravity.CENTER);  
  37.         setContentView(mLayout);//将布局添加到 Activity  
  38.   
  39.         mMediaPlayer = MediaPlayer.create(this, R.raw.aaaass);//实例化 MediaPlayer 并添加音频  
  40.   
  41.         setupVisualizerFxAndUi();//添加频谱到界面  
  42.         setupEqualizeFxAndUi();//添加均衡器到界面  
  43.         setupPlayButton();//添加按钮到界面  
  44.   
  45.   
  46.         mVisualizer.setEnabled(true);//false 则不显示  
  47.         mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {  
  48.   
  49.             @Override  
  50.             public void onCompletion(MediaPlayer mp) {  
  51.             }  
  52.         });  
  53.   
  54.         mMediaPlayer.start();//开始播放  
  55.         mMediaPlayer.setLooping(true);//循环播放  
  56.   
  57.     }  
  58.   
  59.     /** 
  60.      * 通过mMediaPlayer返回的AudioSessionId创建一个优先级为0均衡器对象 并且通过频谱生成相应的UI和对应的事件 
  61.      */  
  62.     private void setupEqualizeFxAndUi() {  
  63.   
  64.         TextView kongge = new TextView(this);  
  65.         kongge.setText("");  
  66.         kongge.setTextSize(10);  
  67.         mLayout.addView(kongge);  
  68.   
  69.         mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());  
  70.         mEqualizer.setEnabled(true);// 启用均衡器  
  71.   
  72.         // 通过均衡器得到其支持的频谱引擎  
  73.         short bands = mEqualizer.getNumberOfBands();  
  74.   
  75.         // getBandLevelRange 是一个数组,返回一组频谱等级数组,  
  76.         // 第一个下标为最低的限度范围  
  77.         // 第二个下标为最大的上限,依次取出  
  78.         final short minEqualizer = mEqualizer.getBandLevelRange()[0];  
  79.         final short maxEqualizer = mEqualizer.getBandLevelRange()[1];  
  80.   
  81.         for (short i = 0; i < bands; i++) {  
  82.             final short band = i;  
  83.   
  84.             TextView freqTextView = new TextView(this);  
  85.             freqTextView.setTextColor(Color.WHITE);  
  86.             freqTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));  
  87.             freqTextView.setGravity(Gravity.CENTER_HORIZONTAL);  
  88.   
  89.             // 取出中心频率  
  90.             freqTextView.setText((mEqualizer.getCenterFreq(band) / 1000) + "HZ");  
  91.             mLayout.addView(freqTextView);  
  92.   
  93.             LinearLayout row = new LinearLayout(this);  
  94.             row.setOrientation(LinearLayout.HORIZONTAL);  
  95.   
  96.             TextView minDbTextView = new TextView(this);  
  97.             minDbTextView.setTextColor(Color.WHITE);  
  98.             minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));  
  99.             minDbTextView.setText((minEqualizer / 100) + " dB");  
  100.   
  101.             TextView maxDbTextView = new TextView(this);  
  102.             maxDbTextView.setTextColor(Color.WHITE);  
  103.             maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));  
  104.             maxDbTextView.setText((maxEqualizer / 100) + " dB");  
  105.   
  106.             LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, 40);  
  107.   
  108.   
  109.             layoutParams.weight = 1;  
  110.             SeekBar seekbar = new SeekBar(this);  
  111.             seekbar.setLayoutParams(layoutParams);  
  112.             seekbar.setPadding(150150);  
  113.             seekbar.setThumb(getResources().getDrawable(R.drawable.seek_bar_dian_selector));  
  114.             seekbar.setThumbOffset(20);  
  115.             seekbar.setMax(maxEqualizer - minEqualizer);  
  116.             seekbar.setProgress(mEqualizer.getBandLevel(band));  
  117.   
  118.   
  119.             seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {  
  120.   
  121.                 @Override  
  122.                 public void onStopTrackingTouch(SeekBar seekBar) {  
  123.                 }  
  124.   
  125.                 @Override  
  126.                 public void onStartTrackingTouch(SeekBar seekBar) {  
  127.                 }  
  128.   
  129.                 @Override  
  130.                 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {  
  131.                     mEqualizer.setBandLevel(band, (short) (progress + minEqualizer));  
  132.                 }  
  133.             });  
  134.             row.addView(minDbTextView);  
  135.             row.addView(seekbar);  
  136.             row.addView(maxDbTextView);  
  137.   
  138.             mLayout.addView(row);  
  139.   
  140.         }  
  141.         TextView eqTextView = new TextView(this);  
  142.         eqTextView.setTextColor(Color.WHITE);  
  143.         eqTextView.setText("均衡器");  
  144.         eqTextView.setGravity(Gravity.CENTER_HORIZONTAL);  
  145.         eqTextView.setTextSize(20);  
  146.   
  147.         mLayout.addView(eqTextView);  
  148.   
  149.     }  
  150.   
  151.     /** 
  152.      * 生成一个VisualizerView对象,使音频频谱的波段能够反映到 VisualizerView上 
  153.      */  
  154.     private void setupVisualizerFxAndUi() {  
  155.         mBaseVisualizerView = new VisualizerView(this);  
  156.   
  157.         mBaseVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(  
  158.                 ViewGroup.LayoutParams.FILL_PARENT,//宽度  
  159.                 (int) (VISUALIZER_HEIGHT_DIP * getResources().getDisplayMetrics().density)//高度  
  160.         ));  
  161.         //将频谱View添加到布局  
  162.         mLayout.addView(mBaseVisualizerView);  
  163.         //实例化Visualizer,参数SessionId可以通过MediaPlayer的对象获得  
  164.         mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId());  
  165.         //采样 - 参数内必须是2的位数 - 如64,128,256,512,1024  
  166.         mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);  
  167.         //设置允许波形表示,并且捕获它  
  168.         mBaseVisualizerView.setVisualizer(mVisualizer);  
  169.     }  
  170.   
  171.     //播放按钮  
  172.     private void setupPlayButton() {  
  173.         play = new ImageButton(this);  
  174.         play.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));  
  175.         play.setBackgroundResource(R.drawable.new_main_activity_stop_up);  
  176.         play.setOnClickListener(new View.OnClickListener() {  
  177.             @Override  
  178.             public void onClick(View view) {  
  179.                 if (mMediaPlayer.isPlaying()) {  
  180.                     play.setBackgroundResource(R.drawable.new_main_activity_play_up);  
  181.                     mMediaPlayer.pause();  
  182.                 } else {  
  183.                     play.setBackgroundResource(R.drawable.new_main_activity_stop_up);  
  184.                     mMediaPlayer.start();  
  185.                 }  
  186.             }  
  187.         });  
  188.   
  189.         mLayout.addView(play);  
  190.     }  
  191.   
  192.     @Override  
  193.     protected void onPause() {  
  194.         super.onPause();  
  195.         if (isFinishing() && mMediaPlayer != null) {  
  196.             mVisualizer.release();  
  197.             mMediaPlayer.release();  
  198.             mEqualizer.release();  
  199.             mMediaPlayer = null;  
  200.         }  
  201.     }  
  202.   
  203. }  

图示效果:



 
[java]  view plain  copy
  1.   

demo下载点击打开链接


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值