在Android2.3中增長了對音頻混響的支持,這些API包含在android.media.audiofx包中。 java
1、概述 android
AudioEffect是android audio framework(android 音頻框架)提供的音頻效果控制的基類。開發者不能直接使用此類,應該使用它的派生類。 下面列出它的派生類。 canvas
Equalizer
Virtualizer
BassBoost
PresetReverb
EnvironmentalReverb session
當建立AudioEffect時,若是音頻效果應用到一個具體的AudioTrack和MediaPlayer的實例,應用程序必須指定該實例的音頻session ID,若是要應用Global音頻輸出混響的效果必須制定Session 0。 app
要建立音頻輸出混響(音頻 Session 0)要求要有 MODIFY_AUDIO_SETTINGS權限。 框架
若是要建立的效果在audio framework不存在,那麼直接建立該效果,若是已經存在那麼直接使用此效果。若是優先級高的對象要在低級別的對象使用該效果時,那麼控制將轉移到優先級高的對象上,不然繼續停留在此對象上。在這種狀況下,新的申請將被監聽器通知。 ide
2、嵌套類 工具
1.AudioEffect.Descriptor:效果描述符包含在音頻框架內實現某種特定的效果的信息。
2.AudioEffect.OnControlStatusChangeListener:此接口定義了當應用程序的音頻效果的控制狀態改變時由AudioEffect調用的方法。 this
3.AudioEffect.OnEnableStatusChangeListener:此接口定義了當應用程序的音頻效果的啓用狀態改變時由AudioEffect調用的方法。 spa
3、常量
String | ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION | 關閉音頻效果 |
String | ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL | 啓動一個音頻效果控制面板UI。 |
String | ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION | 打開音頻效果。 |
int | ALREADY_EXISTS | 內部操做狀態。 |
int | CONTENT_TYPE_GAME | 當播放內容的類型是遊戲音頻時EXTRA_CONTENT_TYPE的值。 |
int | CONTENT_TYPE_MOVIE | 當播放內容的類型是電影時EXTRA_CONTENT_TYPE的值。 |
int | CONTENT_TYPE_MUSIC | 當播放內容的類型是音樂時EXTRA_CONTENT_TYPE的值。 |
int | CONTENT_TYPE_VOICE | 當播放內容的類型是話音時EXTRA_CONTENT_TYPE的值。 |
String | EFFECT_AUXILIARY | Effect connection mode 是auxiliary |
String | EFFECT_INSERT | Effect connection mode 是insert. |
int | ERROR | 指示操做錯誤。 |
int | ERROR_BAD_VALUE | 指示因爲錯誤的參數致使的操做失敗。 |
int | ERROR_DEAD_OBJECT | 指示因爲已關閉的遠程對象致使的操做失敗。 |
int | ERROR_INVALID_OPERATION | 指示因爲錯誤的請求狀態致使的操做失敗。 |
int | ERROR_NO_INIT | 指示因爲錯誤的對象初始化致使的操做失敗。 |
int | ERROR_NO_MEMORY | 指示因爲內存不足致使的操做失敗。 |
String | EXTRA_AUDIO_SESSION | 包含使用效果的音頻會話ID。 |
String | EXTRA_CONTENT_TYPE | 指示應用程序播放內容的類型。 |
String | EXTRA_PACKAGE_NAME | 包含調用應用程序的包名。 |
int | SUCCESS | 操做成功。 |
4、公有方法
AudioEffect.Descriptor | getDescriptor()
獲取效果描述符。
|
boolean | getEnabled()
返回效果的啓用狀態。
|
int | getId()
返回效果的標識符
|
boolean | hasControl()
檢查該AudioEffect 對象是否擁有效果引擎的控制。若是有,則返回true。
|
static Descriptor[] | queryEffects()
查詢平臺上的全部有效的音頻效果。
|
void | release()
釋放本地AudioEffect資源。
|
void | setControlStatusListener(AudioEffect.OnControlStatusChangeListener listener)
註冊音頻效果的控制狀態監聽器.當控制狀態改變時AudioEffect發出通知。
|
void | setEnableStatusListener(AudioEffect.OnEnableStatusChangeListener listener)
設置音頻效果的啓用狀態監聽器。當啓用狀態改變時AudioEffect發出通知。
|
int | setEnabled(boolean enabled)
Enable or disable the effect.
|
5、應用(此應用來自於SDK包)
1.新建項目
你或許已經發如今2.3的項目中比2.2多一個配置文件proguard.cfg,包含混淆所需的proguard腳本。
2.打開AndroidManifest.xml文件
添加權限「android.permission.RECORD_AUDIO」。
3.可視化工具類VisualizerView.java
效果圖
4.Main.java
package com.wjq.audiofx; import android.app.Activity; 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.ViewGroup; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; public class Main extends Activity { private static final String TAG = " AudioFxDemo " ; private static final float VISUALIZER_HEIGHT_DIP = 50f; private MediaPlayer mMediaPlayer; private Visualizer mVisualizer; private Equalizer mEqualizer; private LinearLayout mLinearLayout; private VisualizerView mVisualizerView; private TextView mStatusTextView; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setVolumeControlStream(AudioManager.STREAM_MUSIC); 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.test_cbr); 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 ); } }); mMediaPlayer.start(); mStatusTextView.setText( " Playing audio... " ); } 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( " Equalizer: " ); 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() { // Create a VisualizerView (defined below), which will render the simplified audio // wave form to a Canvas. mVisualizerView = new VisualizerView( this ); mVisualizerView.setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ( int )(VISUALIZER_HEIGHT_DIP * getResources().getDisplayMetrics().density))); mLinearLayout.addView(mVisualizerView); // Create the Visualizer object and attach it to our media player. mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId()); mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[ 1 ]); mVisualizer.setDataCaptureListener( new Visualizer.OnDataCaptureListener() { public void onWaveFormDataCapture(Visualizer visualizer, byte [] bytes, int samplingRate) { mVisualizerView.updateVisualizer(bytes); } public void onFftDataCapture(Visualizer visualizer, byte [] bytes, int samplingRate) {} }, Visualizer.getMaxCaptureRate() / 2 , true , false ); } @Override protected void onPause() { super.onPause(); if (isFinishing() && mMediaPlayer != null ) { mVisualizer.release(); mEqualizer.release(); mMediaPlayer.release(); mMediaPlayer = null ; } } }
1 package com.wjq.audiofx; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import android.graphics.Color; 6 import android.graphics.Paint; 7 import android.graphics.Rect; 8 import android.view.View; 9 10 public class VisualizerView extends View { 11 12 private byte [] mBytes; 13 private float [] mPoints; 14 private Rect mRect = new Rect(); 15 16 private Paint mForePaint = new Paint(); 17 18 public VisualizerView(Context context) { 19 super (context); 20 init(); 21 } 22 23 private void init() { 24 mBytes = null ; 25 26 mForePaint.setStrokeWidth(1f); 27 mForePaint.setAntiAlias( true ); 28 mForePaint.setColor(Color.rgb( 0 , 128 , 255 )); 29 } 30 31 public void updateVisualizer( byte [] bytes) { 32 mBytes = bytes; 33 invalidate(); 34 } 35 36 @Override 37 protected void onDraw(Canvas canvas) { 38 super .onDraw(canvas); 39 40 if (mBytes == null ) { 41 return ; 42 } 43 44 if (mPoints == null || mPoints.length < mBytes.length * 4 ) { 45 mPoints = new float [mBytes.length * 4 ]; 46 } 47 48 mRect.set( 0 , 0 , getWidth(), getHeight()); 49 50 for ( int i = 0 ; i < mBytes.length - 1 ; i ++ ) { 51 mPoints[i * 4 ] = mRect.width() * i / (mBytes.length - 1 ); 52 mPoints[i * 4 + 1 ] = mRect.height() / 2 53 + (( byte ) (mBytes[i] + 128 )) * (mRect.height() / 2 ) / 128 ; 54 mPoints[i * 4 + 2 ] = mRect.width() * (i + 1 ) / (mBytes.length - 1 ); 55 mPoints[i * 4 + 3 ] = mRect.height() / 2 56 + (( byte ) (mBytes[i + 1 ] + 128 )) * (mRect.height() / 2 ) / 128 ; 57 } 58 59 canvas.drawLines(mPoints, mForePaint); 60 } 61 62 }