Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 我的Android進階之旅------)Android實現音樂示波器、均衡器、重低音和音場功能

我的Android進階之旅------)Android實現音樂示波器、均衡器、重低音和音場功能

編輯:關於Android編程

\ 本實例來自於《瘋狂Android講義》,要實現具體的功能,需要了解以下API: MediaPlayer 媒體播放器Visualizer 頻譜Equalizer 均衡器BassBoost 重低音控制器PresetReverb 預設音場控制器Paint 繪圖

來看下效果示意圖,如下所示
豎狀波形圖 \
塊狀波形圖
\
曲線波形圖
\

調節均衡器、重低音 \
選擇音場
\

下面來看具體的實現代碼 MediaPlayerTest.java
package com.oyp.media;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.audiofx.BassBoost;
import android.media.audiofx.Equalizer;
import android.media.audiofx.PresetReverb;
import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;

public class MediaPlayerTest extends Activity
{
	// 定義播放聲音的MediaPlayer
	private MediaPlayer mPlayer;
	// 定義系統的頻譜
	private Visualizer mVisualizer;	
	// 定義系統的均衡器
	private Equalizer mEqualizer;
	// 定義系統的重低音控制器
	private BassBoost mBass;
	// 定義系統的預設音場控制器
	private PresetReverb mPresetReverb;
	private LinearLayout layout;
	private List reverbNames = new ArrayList();
	private List reverbVals = new ArrayList();

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		//設置音頻流 - STREAM_MUSIC:音樂回放即媒體音量
		setVolumeControlStream(AudioManager.STREAM_MUSIC);
		layout = new LinearLayout(this);//代碼創建布局
		layout.setOrientation(LinearLayout.VERTICAL);//設置為線性布局-上下排列
		setContentView(layout);//將布局添加到 Activity
		// 創建MediaPlayer對象,並添加音頻
		// 音頻路徑為  res/raw/beautiful.mp3
		mPlayer = MediaPlayer.create(this, R.raw.beautiful);
		// 初始化示波器
		setupVisualizer();
		// 初始化均衡控制器
		setupEqualizer();
		// 初始化重低音控制器
		setupBassBoost();
		// 初始化預設音場控制器
		setupPresetReverb();
		// 開發播放音樂
		mPlayer.start();
	}
	/**
	 * 初始化頻譜
	 */
	private void setupVisualizer()
	{
		// 創建MyVisualizerView組件,用於顯示波形圖
		final MyVisualizerView mVisualizerView =
			new MyVisualizerView(this);
		mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(
			ViewGroup.LayoutParams.MATCH_PARENT,
			(int) (120f * getResources().getDisplayMetrics().density)));
		// 將MyVisualizerView組件添加到layout容器中
		layout.addView(mVisualizerView);
		// 以MediaPlayer的AudioSessionId創建Visualizer
		// 相當於設置Visualizer負責顯示該MediaPlayer的音頻數據
		mVisualizer = new Visualizer(mPlayer.getAudioSessionId());
		//設置需要轉換的音樂內容長度,專業的說這就是采樣,該采樣值一般為2的指數倍,如64,128,256,512,1024。
		mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
		// 為mVisualizer設置監聽器
		/*
		 * Visualizer.setDataCaptureListener(OnDataCaptureListener listener, int rate, boolean waveform, boolean fft
		 * 	
		 * 		listener,表監聽函數,匿名內部類實現該接口,該接口需要實現兩個函數	
		 		rate, 表示采樣的周期,即隔多久采樣一次,聯系前文就是隔多久采樣128個數據
				iswave,是波形信號
				isfft,是FFT信號,表示是獲取波形信號還是頻域信號
			
		 */
		mVisualizer.setDataCaptureListener(
			new Visualizer.OnDataCaptureListener()
			{
				//這個回調應該采集的是快速傅裡葉變換有關的數據
				@Override
				public void onFftDataCapture(Visualizer visualizer,
					byte[] fft, int samplingRate)
				{
				}
				 //這個回調應該采集的是波形數據
				@Override
				public void onWaveFormDataCapture(Visualizer visualizer,
					byte[] waveform, int samplingRate)
				{
					// 用waveform波形數據更新mVisualizerView組件
					mVisualizerView.updateVisualizer(waveform);
				}
			}, Visualizer.getMaxCaptureRate() / 2, true, false);
		mVisualizer.setEnabled(true);
	}
	
	/**
	 * 初始化均衡控制器
	 */
	private void setupEqualizer()
	{
		// 以MediaPlayer的AudioSessionId創建Equalizer
		// 相當於設置Equalizer負責控制該MediaPlayer
		mEqualizer = new Equalizer(0, mPlayer.getAudioSessionId());
		// 啟用均衡控制效果
		mEqualizer.setEnabled(true);
		TextView eqTitle = new TextView(this);
		eqTitle.setText(均衡器:);
		layout.addView(eqTitle);
		// 獲取均衡控制器支持最小值和最大值
		final short minEQLevel = mEqualizer.getBandLevelRange()[0];//第一個下標為最低的限度范圍
		short maxEQLevel = mEqualizer.getBandLevelRange()[1];  // 第二個下標為最高的限度范圍
		// 獲取均衡控制器支持的所有頻率
		short brands = mEqualizer.getNumberOfBands();
		for (short i = 0; i < brands; i++)
		{
			TextView eqTextView = new TextView(this);
			// 創建一個TextView,用於顯示頻率
			eqTextView.setLayoutParams(new ViewGroup.LayoutParams(
				ViewGroup.LayoutParams.MATCH_PARENT,
				ViewGroup.LayoutParams.WRAP_CONTENT));
			eqTextView.setGravity(Gravity.CENTER_HORIZONTAL);
			// 設置該均衡控制器的頻率
			eqTextView.setText((mEqualizer.getCenterFreq(i) / 1000)
				+  Hz);
			layout.addView(eqTextView);
			// 創建一個水平排列組件的LinearLayout
			LinearLayout tmpLayout = new LinearLayout(this);
			tmpLayout.setOrientation(LinearLayout.HORIZONTAL);
			// 創建顯示均衡控制器最小值的TextView
			TextView minDbTextView = new TextView(this);
			minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
				ViewGroup.LayoutParams.WRAP_CONTENT,
				ViewGroup.LayoutParams.WRAP_CONTENT));
			// 顯示均衡控制器的最小值
			minDbTextView.setText((minEQLevel / 100) +  dB);
			// 創建顯示均衡控制器最大值的TextView
			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.MATCH_PARENT,
				ViewGroup.LayoutParams.WRAP_CONTENT);
			layoutParams.weight = 1;
			// 定義SeekBar做為調整工具
			SeekBar bar = new SeekBar(this);
			bar.setLayoutParams(layoutParams);
			bar.setMax(maxEQLevel - minEQLevel);
			bar.setProgress(mEqualizer.getBandLevel(i));
			final short brand = i;
			// 為SeekBar的拖動事件設置事件監聽器
			bar.setOnSeekBarChangeListener(new SeekBar
				.OnSeekBarChangeListener()
			{
				@Override
				public void onProgressChanged(SeekBar seekBar,
					int progress, boolean fromUser)
				{
					// 設置該頻率的均衡值
					mEqualizer.setBandLevel(brand,
						(short) (progress + minEQLevel));
				}
				@Override
				public void onStartTrackingTouch(SeekBar seekBar)
				{
				}
				@Override
				public void onStopTrackingTouch(SeekBar seekBar)
				{
				}
			});
			// 使用水平排列組件的LinearLayout“盛裝”3個組件
			tmpLayout.addView(minDbTextView);
			tmpLayout.addView(bar);
			tmpLayout.addView(maxDbTextView);
			// 將水平排列組件的LinearLayout添加到myLayout容器中
			layout.addView(tmpLayout);
		}
	}

	/**
	 * 初始化重低音控制器
	 */
	private void setupBassBoost()
	{
		// 以MediaPlayer的AudioSessionId創建BassBoost
		// 相當於設置BassBoost負責控制該MediaPlayer
		mBass = new BassBoost(0, mPlayer.getAudioSessionId());
		// 設置啟用重低音效果
		mBass.setEnabled(true);
		TextView bbTitle = new TextView(this);
		bbTitle.setText(重低音:);
		layout.addView(bbTitle);
		// 使用SeekBar做為重低音的調整工具 
		SeekBar bar = new SeekBar(this);
		// 重低音的范圍為0~1000
		bar.setMax(1000);
		bar.setProgress(0);
		// 為SeekBar的拖動事件設置事件監聽器
		bar.setOnSeekBarChangeListener(new SeekBar
			.OnSeekBarChangeListener()
		{
			@Override
			public void onProgressChanged(SeekBar seekBar
				, int progress, boolean fromUser)
			{
				// 設置重低音的強度
				mBass.setStrength((short) progress);
			}
			@Override
			public void onStartTrackingTouch(SeekBar seekBar)
			{
			}
			@Override
			public void onStopTrackingTouch(SeekBar seekBar)
			{
			}
		});
		layout.addView(bar);
	}

	/**
	 * 初始化預設音場控制器
	 */
	private void setupPresetReverb()
	{
		// 以MediaPlayer的AudioSessionId創建PresetReverb
		// 相當於設置PresetReverb負責控制該MediaPlayer
		mPresetReverb = new PresetReverb(0,
			mPlayer.getAudioSessionId());
		// 設置啟用預設音場控制
		mPresetReverb.setEnabled(true);
		TextView prTitle = new TextView(this);
		prTitle.setText(音場);
		layout.addView(prTitle);
		// 獲取系統支持的所有預設音場
		for (short i = 0; i < mEqualizer.getNumberOfPresets(); i++)
		{
			reverbNames.add(i);
			reverbVals.add(mEqualizer.getPresetName(i));
		}
		// 使用Spinner做為音場選擇工具
		Spinner sp = new Spinner(this);
		sp.setAdapter(new ArrayAdapter(MediaPlayerTest.this,
			android.R.layout.simple_spinner_item, reverbVals));
		// 為Spinner的列表項選中事件設置監聽器
		sp.setOnItemSelectedListener(new Spinner
			.OnItemSelectedListener()
		{
			@Override
			public void onItemSelected(AdapterView arg0
				, View arg1, int arg2, long arg3)
			{
				// 設定音場
				mPresetReverb.setPreset(reverbNames.get(arg2));
			}

			@Override
			public void onNothingSelected(AdapterView arg0)
			{
			}
		});
		layout.addView(sp);
	}

	@Override
	protected void onPause()
	{
		super.onPause();
		if (isFinishing() && mPlayer != null)
		{
			// 釋放所有對象
			mVisualizer.release();
			mEqualizer.release();
			mPresetReverb.release();
			mBass.release();
			mPlayer.release();
			mPlayer = null;
		}
	}
	/**
	 * 根據Visualizer傳來的數據動態繪制波形效果,分別為:
	 * 塊狀波形、柱狀波形、曲線波形
	 */
	private static class MyVisualizerView extends View
	{
		// bytes數組保存了波形抽樣點的值
		private byte[] bytes;
		private float[] points;
		private Paint paint = new Paint();
		private Rect rect = new Rect();
		private byte type = 0;
		public MyVisualizerView(Context context)
		{
			super(context);
			bytes = null;
			// 設置畫筆的屬性
			paint.setStrokeWidth(1f);
			paint.setAntiAlias(true);//抗鋸齒
			paint.setColor(Color.YELLOW);//畫筆顏色
			paint.setStyle(Style.FILL);
		}

		public void updateVisualizer(byte[] ftt)
		{
			bytes = ftt;
			// 通知該組件重繪自己。
			invalidate();
		}
		
		@Override
		public boolean onTouchEvent(MotionEvent me)
		{
			// 當用戶觸碰該組件時,切換波形類型
			if(me.getAction() != MotionEvent.ACTION_DOWN)
			{
				return false;
			}
			type ++;
			if(type >= 3)
			{
				type = 0;
			}
			return true;
		}

		@Override
		protected void onDraw(Canvas canvas)
		{
			super.onDraw(canvas);
			if (bytes == null)
			{
				return;
			}
			// 繪制白色背景
			canvas.drawColor(Color.WHITE);			
			// 使用rect對象記錄該組件的寬度和高度
			rect.set(0,0,getWidth(),getHeight());
			switch(type)
			{
				// -------繪制塊狀的波形圖-------
				case 0:	
					for (int i = 0; i < bytes.length - 1; i++)
					{
						float left = getWidth() * i / (bytes.length - 1);
						// 根據波形值計算該矩形的高度		
						float top = rect.height()-(byte)(bytes[i+1]+128)
							* rect.height() / 128;
						float right = left + 1;
						float bottom = rect.height();
						canvas.drawRect(left, top, right, bottom, paint);
					}
					break;
				// -------繪制柱狀的波形圖(每隔18個抽樣點繪制一個矩形)-------
				case 1:
					for (int i = 0; i < bytes.length - 1; i += 18)
					{
						float left = rect.width()*i/(bytes.length - 1);
						// 根據波形值計算該矩形的高度
						float top = rect.height()-(byte)(bytes[i+1]+128)
							* rect.height() / 128;
						float right = left + 6;
						float bottom = rect.height();
						canvas.drawRect(left, top, right, bottom, paint);
					}
					break;
				// -------繪制曲線波形圖-------
				case 2:
					// 如果point數組還未初始化
					if (points == null || points.length < bytes.length * 4)
					{
						points = new float[bytes.length * 4];
					}
					for (int i = 0; i < bytes.length - 1; i++)
					{
						// 計算第i個點的x坐標
						points[i * 4] = rect.width()*i/(bytes.length - 1);
						// 根據bytes[i]的值(波形點的值)計算第i個點的y坐標
						points[i * 4 + 1] = (rect.height() / 2)
							+ ((byte) (bytes[i] + 128)) * 128
							/ (rect.height() / 2);
						// 計算第i+1個點的x坐標
						points[i * 4 + 2] = rect.width() * (i + 1)
							/ (bytes.length - 1);
						// 根據bytes[i+1]的值(波形點的值)計算第i+1個點的y坐標
						points[i * 4 + 3] = (rect.height() / 2)
							+ ((byte) (bytes[i + 1] + 128)) * 128
							/ (rect.height() / 2);
					}
					// 繪制波形曲線
					canvas.drawLines(points, paint);
					break;
			}
		}
	}	
}

AndroidManifest.xml


	
	
	
	 
	
	
		
			
				
				
			
		
	

PS:請在真機環境下運行此程序,如果在模擬器下運行,可能會報錯:
java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -4


 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved