我的Android进阶之旅------)Android实现音乐示波器、均衡器、重低音和音场功能
来源:互联网 发布:sql 时间转换成秒 编辑:程序博客网 时间:2024/04/29 10:13
http://www.2cto.com/kf/201507/417299.html
本实例来自于《疯狂Android讲义》,要实现具体的功能,需要了解以下API: MediaPlayer 媒体播放器Visualizer 频谱Equalizer 均衡器BassBoost 重低音控制器PresetReverb 预设音场控制器Paint 绘图
来看下效果示意图,如下所示
竖状波形图
块状波形图
曲线波形图
调节均衡器、重低音
选择音场
下面来看具体的实现代码 MediaPlayerTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
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<
short
> reverbNames =
new
ArrayList<
short
>();
private
List<string> reverbVals =
new
ArrayList<string>();
@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<string>(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
;
}
}
}
}</string></string></string></
short
></
short
>
AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--?xml version=
1.0
encoding=utf-
8
?-->
<manifest android:versioncode=
"1"
android:versionname=
"1.0"
package
=
"com.oyp.media"
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<uses-sdk android:minsdkversion=
"10"
android:targetsdkversion=
"17/"
>
<!-- 使用音场效果必要的权限 -->
<uses-permission android:name=
"android.permission.RECORD_AUDIO"
>
<uses-permission android:name=
"android.permission.MODIFY_AUDIO_SETTINGS/"
>
<intent-filter>
<category android:name=
"android.intent.category.LAUNCHER"
>
</category></action></intent-filter>
</activity>
</application>
</uses-permission></uses-permission></uses-sdk></manifest>
PS:请在真机环境下运行此程序,如果在模拟器下运行,可能会报错:
0 0
- 我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能
- 我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能
- 我的Android进阶之旅------)Android实现音乐示波器、均衡器、重低音和音场功能
- Android实现音乐示波器、均衡器、重低音和音场功能
- Android移动开发-音乐的示波器、均衡、重低音和音场的实现
- 我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(三)Android客户端功能实现
- 我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(二)Android客户端功能展示
- 我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(一)PC服务器端
- 我的Android进阶之旅------>Android之进度条(ProgressBar)的功能和用法
- 我的Android进阶之旅------>Android之选项卡(TabHost)的功能和用法
- 我的Android进阶之旅------>Android利用Sensor(传感器)实现水平仪功能的小例
- 我的Android进阶之旅------>Android利用Sensor(传感器)实现指南针功能
- 我的Android进阶之旅------>Android之拖动条(SeekBar和RatingBar)的功能和用法
- android音乐播放器之在线播放功能的实现
- 我的Android进阶之旅------>Android百度地图定位SDK功能学习
- 【我的Android进阶之旅】解决MediaPlayer播放音乐的时候报错: Should have subtitle controller already set
- 我的Android进阶之旅------>Android之ListView实现下拉回弹刷新
- 我的Android进阶之旅------>Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能
- 区分copy构造、copy赋值
- 无废话WCF入门教程一[什么是WCF]
- HTML5、JavaScript 3D游戏引擎和框架
- android动画解析 之补间动画和帧动画
- 前端需要掌握的20个正则表达式
- 我的Android进阶之旅------)Android实现音乐示波器、均衡器、重低音和音场功能
- Java设计模式之简单工厂模式
- android studio 导入工程篇(2)
- UVa 11729 - Commando War(贪心)
- quick动态控制动画的播放速度
- 将json字符串转化为java对象
- 如何在纯HTML页面中进行值传递
- SLF4J: Class path contains multiple SLF4J bindings.
- Solr管理界面以及标准接口说明