android音乐柱状频谱实现


原文地址:http://blog.csdn.net/caryee89/article/details/6935237


注意android2.3以后才可用,主要用到这个类Visualizer,这个源码其实是apiDemos中一个例子,但例子中实现的是两种中的波形显示,而不是频谱显示,

原文博主实现了另一种频谱显示,并分享出来,精神可嘉。我做了些修改,使稍微好看了些,继续分享。


官方文档解释:

public int getFft (byte[] fft)

Since: API Level 9

Returns a frequency capture of currently playing audio content.

This method must be called when the Visualizer is enabled.

The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of the sampling rate returned by getSamplingRate(). The capture returns the real and imaginary parts of a number of frequency points equal to half of the capture size plus one.

Note: only the real part is returned for the first point (DC) and the last point (sampling frequency / 2).

The layout in the returned byte array is as follows:

  • n is the capture size returned by getCaptureSize()

  • Rfk, Ifk are respectively the real and imaginary parts of the kth frequency component

  • If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is: (k*Fs)/(n/2)

Index 0 1 2 3 4 5 ... n - 2 n - 1
Data Rf0 Rf(n/2) Rf1 If1 Rf2 If2 ... Rf(n-1)/2 If(n-1)/2
Parameters
fft array of bytes where the FFT should be returned
Returns
  • SUCCESS in case of success, ERROR_NO_MEMORYERROR_INVALID_OPERATION or ERROR_DEAD_OBJECT in case of failure.

Throws
IllegalStateException

实部和虚部的平方和就是振幅的平方,因为是byte类型,所以最大值是127。


对原文的代码做了一些修改,使更好看一些,代码中用到的歌曲谁要用到,自己重新放一首就行,代码如下:

package com.test.music.AudioFx;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.audiofx.Equalizer;
import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import com.example.oplayer2.R;


@SuppressLint("NewApi")
public class AudioFxActivity extends Activity {
 private static final String TAG = "AudioFxActivity"; 
  
     private static final float VISUALIZER_HEIGHT_DIP = 160f; 
  
     private MediaPlayer mMediaPlayer; 
     private Visualizer mVisualizer; 
     private Equalizer mEqualizer; 
  
    private LinearLayout mLinearLayout; 
    private VisualizerView mVisualizerView; 
    private TextView mStatusTextView; 
       private TextView mInfoView; 
 
     @SuppressLint("NewApi")
  @Override 
    public void onCreate(Bundle icicle) 
     { 
         super.onCreate(icicle); 
          
        mStatusTextView = new TextView(this); 
 
        mLinearLayout = new LinearLayout(this); 
        mLinearLayout.setOrientation(LinearLayout.VERTICAL); 
         mLinearLayout.addView(mStatusTextView); 
  
        setContentView(mLinearLayout); 
  
         // Create the MediaPlayer 
        mMediaPlayer = MediaPlayer.create(this, R.raw.my_life); 
        Log.d(TAG, 
                  "MediaPlayer audio session ID: " 
                          + mMediaPlayer.getAudioSessionId()); 
   
        setupVisualizerFxAndUI(); 
         setupEqualizerFxAndUI(); 
   
          // Make sure the visualizer is enabled only when you actually want to 
         // receive data, and 
         // when it makes sense to receive data. 
         mVisualizer.setEnabled(true); 
  
         // When the stream ends, we don't need to collect any more data. We 
          // don't do this in 
        // setupVisualizerFxAndUI because we likely want to have more, 
        // non-Visualizer related code 
        // in this callback. 
         mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() 
                 { 
                    public void onCompletion(MediaPlayer mediaPlayer) 
                    { 
                         mVisualizer.setEnabled(false); 
                          getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 
                         setVolumeControlStream(AudioManager.STREAM_SYSTEM); 
                         mStatusTextView.setText("音乐播放完毕"); 
                     } 
                 }); 
   
          getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 
         setVolumeControlStream(AudioManager.STREAM_MUSIC); 
          mMediaPlayer.start(); 
          mStatusTextView.setText("播放音乐中...."); 
      } 
  
     @SuppressLint("NewApi")
  private void setupEqualizerFxAndUI() 
      { 
          // Create the Equalizer object (an AudioEffect subclass) and attach it 
          // to our media player, 
          // with a default priority (0). 
          mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId()); 
          mEqualizer.setEnabled(true); 
   
          TextView eqTextView = new TextView(this); 
         eqTextView.setText("均衡器:"); 
          mLinearLayout.addView(eqTextView); 
  
         short bands = mEqualizer.getNumberOfBands(); 
  
         final short minEQLevel = mEqualizer.getBandLevelRange()[0]; 
         final short maxEQLevel = mEqualizer.getBandLevelRange()[1]; 
  
          for (short i = 0; i < bands; i++) 
        { 
             final short band = i; 
   
             TextView freqTextView = new TextView(this); 
            freqTextView.setLayoutParams(new ViewGroup.LayoutParams( 
                    ViewGroup.LayoutParams.FILL_PARENT, 
                     ViewGroup.LayoutParams.WRAP_CONTENT)); 
             freqTextView.setGravity(Gravity.CENTER_HORIZONTAL); 
             freqTextView.setText((mEqualizer.getCenterFreq(band) / 1000) 
                      + " Hz"); 
              mLinearLayout.addView(freqTextView); 
  
            LinearLayout row = new LinearLayout(this); 
             row.setOrientation(LinearLayout.HORIZONTAL); 
   
             TextView minDbTextView = new TextView(this); 
             minDbTextView.setLayoutParams(new ViewGroup.LayoutParams( 
                     ViewGroup.LayoutParams.WRAP_CONTENT, 
                      ViewGroup.LayoutParams.WRAP_CONTENT)); 
             minDbTextView.setText((minEQLevel / 100) + " dB"); 
   
              TextView maxDbTextView = new TextView(this); 
            maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams( 
                   ViewGroup.LayoutParams.WRAP_CONTENT, 
                     ViewGroup.LayoutParams.WRAP_CONTENT)); 
             maxDbTextView.setText((maxEQLevel / 100) + " dB"); 
  
             LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( 
                     ViewGroup.LayoutParams.FILL_PARENT, 
                    ViewGroup.LayoutParams.WRAP_CONTENT); 
              layoutParams.weight = 1; 
            SeekBar bar = new SeekBar(this); 
             bar.setLayoutParams(layoutParams); 
             bar.setMax(maxEQLevel - minEQLevel); 
              bar.setProgress(mEqualizer.getBandLevel(band)); 
   
              bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() 
              { 
                 public void onProgressChanged(SeekBar seekBar, int progress, 
                         boolean fromUser) 
                  { 
                      mEqualizer.setBandLevel(band, (short) (progress + minEQLevel)); 
                 } 
  
                 public void onStartTrackingTouch(SeekBar seekBar) 
                 { 
                  } 
  
                 public void onStopTrackingTouch(SeekBar seekBar) 
                { 
                 } 
             }); 
  
             row.addView(minDbTextView); 
             row.addView(bar); 
              row.addView(maxDbTextView); 
  
              mLinearLayout.addView(row); 
         } 
      } 
   
      @SuppressLint("NewApi")
  private void setupVisualizerFxAndUI() 
      { 
          mVisualizerView = new VisualizerView(this); 
          mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams( 
                  ViewGroup.LayoutParams.FILL_PARENT, 
                  (int) (VISUALIZER_HEIGHT_DIP * getResources() 
                         .getDisplayMetrics().density))); 
         mLinearLayout.addView(mVisualizerView); 
  
          mInfoView = new TextView(this); 
          String infoStr = ""; 
           
          int[] csr = Visualizer.getCaptureSizeRange(); 
         if(csr != null) 
        { 
            String csrStr = "CaptureSizeRange: "; 
             for(int i = 0; i < csr.length; i ++) 
             { 
                 csrStr += csr[i]; 
                  csrStr +=" "; 
              } 
              infoStr += csrStr; 
        } 
          
         final int maxCR = Visualizer.getMaxCaptureRate(); 
          
         infoStr = infoStr + "\nMaxCaptureRate: " + maxCR; 
           
         mInfoView.setText(infoStr); 
         mLinearLayout.addView(mInfoView); 
          
         mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId()); 
         mVisualizer.setCaptureSize(256); 
         mVisualizer.setDataCaptureListener( 
                  new Visualizer.OnDataCaptureListener() 
                { 
                    public void onWaveFormDataCapture(Visualizer visualizer, 
                             byte[] bytes, int samplingRate) 
                      { 
                          mVisualizerView.updateVisualizer(bytes); 
                     } 
 
                    public void onFftDataCapture(Visualizer visualizer, 
                             byte[] fft, int samplingRate) 
                     { 
                         mVisualizerView.updateVisualizer(fft); 
                     } 
                }, maxCR / 2, false, true); 
      } 
   
      @Override 
     protected void onPause() 
    { 
         super.onPause(); 
 
         if (isFinishing() && mMediaPlayer != null) 
        { 
             mVisualizer.release();
             mEqualizer.release();
             mMediaPlayer.release(); 
             mMediaPlayer = null; 
         } 
     } 
      
      /**
      * A simple class that draws waveform data received from a
      * {@link  Visualizer.OnDataCaptureListener#onWaveFormDataCapture }
       */ 
    class VisualizerView extends View 
      { 
         private byte[] mBytes; 
          private float[] mPoints; 
         private Rect mRect = new Rect(); 
  
         private Paint mForePaint = new Paint(); 
          private int mSpectrumNum = 48; 
          private boolean mFirst = true; 
   
         public VisualizerView(Context context) 
         { 
            super(context); 
              init(); 
          } 
  
         private void init() 
          { 
             mBytes = null; 
   
             mForePaint.setStrokeWidth(8f); 
              mForePaint.setAntiAlias(true); 
              mForePaint.setColor(Color.rgb(0, 128, 255)); 
          } 
  
          public void updateVisualizer(byte[] fft) 
         { 
             if(mFirst ) 
            { 
                  mInfoView.setText(mInfoView.getText().toString() + "\nCaptureSize: " + fft.length); 
                 mFirst = false; 
            } 
              
              
             byte[] model = new byte[fft.length / 2 + 1]; 
 
            model[0] = (byte) Math.abs(fft[0]); 
              for (int i = 2, j = 1; j < mSpectrumNum;) 
             { 
                 model[j] = (byte) Math.hypot(fft[i], fft[i + 1]); 
                i += 2; 
                 j++; 
             } 
             mBytes = model; 
             invalidate(); 
        } 
  
          @Override 
          protected void onDraw(Canvas canvas) 
         { 
           super.onDraw(canvas); 
  
              if (mBytes == null) 
              { 
                 return; 
           } 
   
            if (mPoints == null || mPoints.length < mBytes.length * 4) 
              { 
                 mPoints = new float[mBytes.length * 4]; 
             } 
  
             mRect.set(0, 0, getWidth(), getHeight()); 
   
             //绘制波形 
              // for (int i = 0; i < mBytes.length - 1; i++) { 
              // mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1); 
              // mPoints[i * 4 + 1] = mRect.height() / 2 
              // + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2) / 128; 
             // mPoints[i * 4 + 2] = mRect.width() * (i + 1) / (mBytes.length - 1); 
             // mPoints[i * 4 + 3] = mRect.height() / 2 
             // + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2) / 128; 
              // } 
              
            //绘制频谱 
             final int baseX = mRect.width()/mSpectrumNum; 
             final int height = mRect.height(); 
  
            for (int i = 0; i < mSpectrumNum ; i++) 
             { 
                 if (mBytes[i] < 0) 
                 { 
                     mBytes[i] = 127; 
                  } 
                  
                 final int xi = baseX*i + baseX/2; 
                  
                 mPoints[i * 4] = xi; 
                 mPoints[i * 4 + 1] = height; 
                  
                  mPoints[i * 4 + 2] = xi; 
                 mPoints[i * 4 + 3] = height - mBytes[i]; 
            } 
  
             canvas.drawLines(mPoints, mForePaint); 
         } 
     } 

}

你可能感兴趣的:(android音乐柱状频谱实现)