在Android2.3中增加了对音频混响的支持,这些API包含在android.media.audiofx包中。
一、概述
AudioEffect是android audio framework(android 音频框架)提供的音频效果控制的基类。开发者不能直接使用此类,应该使用它的派生类。 下面列出它的派生类。
Equalizer
Virtualizer
BassBoost
PresetReverb
EnvironmentalReverb
当创建AudioEffect时,如果音频效果应用到一个具体的AudioTrack和MediaPlayer的实例,应用程序必须指定该实例的音频session ID,如果要应用Global音频输出混响的效果必须制定Session 0。
要创建音频输出混响(音频 Session 0)要求要有 MODIFY_AUDIO_SETTINGS权限。
如果要创建的效果在audio framework不存在,那么直接创建该效果,如果已经存在那么直接使用此效果。如果优先级高的对象要在低级别的对象使用该效果时,那么控制将转移到优先级高的对象上,否则继续停留在此对象上。在这种情况下,新的申请将被监听器通知。
二、嵌套类
1.AudioEffect.Descriptor:效果描述符包含在音频框架内实现某种特定的效果的信息。
2.AudioEffect.OnControlStatusChangeListener:此接口定义了当应用程序的音频效果的控制状态改变时由AudioEffect调用的方法。
3.AudioEffect.OnEnableStatusChangeListener:此接口定义了当应用程序的音频效果的启用状态改变时由AudioEffect调用的方法。
三、常量
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 | 操作成功。 |
四、公有方法
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.
|
五、应用(此应用来自于SDK包)
1.新建项目
你或许已经发现在2.3的项目中比2.2多一个配置文件proguard.cfg,包含
混淆所需的proguard脚本。
2.打开AndroidManifest.xml文件
添加权限“android.permission.RECORD_AUDIO”。
3.可视化工具类VisualizerView.java
效果图
4.Main.java
Main
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
;
}
}
}
VisualizerView
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
}