原文地址:http://blog.csdn.net/caryee89/article/details/6935237
注意android2.3以后才可用,主要用到这个类Visualizer,这个源码其实是apiDemos中一个例子,但例子中实现的是两种中的波形显示,而不是频谱显示,
原文博主实现了另一种频谱显示,并分享出来,精神可嘉。我做了些修改,使稍微好看了些,继续分享。
官方文档解释:
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 |
fft | array of bytes where the FFT should be returned |
---|
SUCCESS
in case of success, ERROR_NO_MEMORY
, ERROR_INVALID_OPERATION
or ERROR_DEAD_OBJECT
in case of failure.
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);
}
}
}