您的位置:首页 > 移动开发 > Android开发

android音乐柱状频谱实现

2015-08-20 19:15 417 查看
原文地址:/article/1858335.html

注意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
fftarray of bytes where the FFT should be returned
Returns

SUCCESS
in case of success,
ERROR_NO_MEMORY
,
ERROR_INVALID_OPERATION
or
ERROR_DEAD_OBJECT
in case of failure.

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

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

[java] view plaincopy

/*

* Copyright (C) 2010 The Android Open Source Project

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0
*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package com.AudioFx;

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;

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;

@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("播放音乐中....");

}

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);

}

}

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);

}

}

}

运行效果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: