我的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
packagecom.oyp.media;
 
importjava.util.ArrayList;
importjava.util.List;
 
importandroid.app.Activity;
importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.graphics.Paint.Style;
importandroid.graphics.Rect;
importandroid.media.AudioManager;
importandroid.media.MediaPlayer;
importandroid.media.audiofx.BassBoost;
importandroid.media.audiofx.Equalizer;
importandroid.media.audiofx.PresetReverb;
importandroid.media.audiofx.Visualizer;
importandroid.os.Bundle;
importandroid.view.Gravity;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.AdapterView;
importandroid.widget.ArrayAdapter;
importandroid.widget.LinearLayout;
importandroid.widget.SeekBar;
importandroid.widget.Spinner;
importandroid.widget.TextView;
 
publicclass MediaPlayerTest extendsActivity
{
    // 定义播放声音的MediaPlayer
    privateMediaPlayer mPlayer;
    // 定义系统的频谱
    privateVisualizer mVisualizer;
    // 定义系统的均衡器
    privateEqualizer mEqualizer;
    // 定义系统的重低音控制器
    privateBassBoost mBass;
    // 定义系统的预设音场控制器
    privatePresetReverb mPresetReverb;
    privateLinearLayout layout;
    privateList<short> reverbNames = newArrayList<short>();
    privateList<string> reverbVals = newArrayList<string>();
 
    @Override
    publicvoid onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        //设置音频流 - STREAM_MUSIC:音乐回放即媒体音量
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
        layout = newLinearLayout(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();
    }
    /**
     * 初始化频谱
     */
    privatevoid setupVisualizer()
    {
        // 创建MyVisualizerView组件,用于显示波形图
        finalMyVisualizerView mVisualizerView =
            newMyVisualizerView(this);
        mVisualizerView.setLayoutParams(newViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            (int) (120f * getResources().getDisplayMetrics().density)));
        // 将MyVisualizerView组件添加到layout容器中
        layout.addView(mVisualizerView);
        // 以MediaPlayer的AudioSessionId创建Visualizer
        // 相当于设置Visualizer负责显示该MediaPlayer的音频数据
        mVisualizer = newVisualizer(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(
            newVisualizer.OnDataCaptureListener()
            {
                //这个回调应该采集的是快速傅里叶变换有关的数据
                @Override
                publicvoid onFftDataCapture(Visualizer visualizer,
                    byte[] fft, intsamplingRate)
                {
                }
                 //这个回调应该采集的是波形数据
                @Override
                publicvoid onWaveFormDataCapture(Visualizer visualizer,
                    byte[] waveform, intsamplingRate)
                {
                    // 用waveform波形数据更新mVisualizerView组件
                    mVisualizerView.updateVisualizer(waveform);
                }
            }, Visualizer.getMaxCaptureRate() / 2,true,false);
        mVisualizer.setEnabled(true);
    }
     
    /**
     * 初始化均衡控制器
     */
    privatevoid setupEqualizer()
    {
        // 以MediaPlayer的AudioSessionId创建Equalizer
        // 相当于设置Equalizer负责控制该MediaPlayer
        mEqualizer = newEqualizer(0, mPlayer.getAudioSessionId());
        // 启用均衡控制效果
        mEqualizer.setEnabled(true);
        TextView eqTitle = newTextView(this);
        eqTitle.setText(均衡器:);
        layout.addView(eqTitle);
        // 获取均衡控制器支持最小值和最大值
        finalshort minEQLevel = mEqualizer.getBandLevelRange()[0];//第一个下标为最低的限度范围
        shortmaxEQLevel = mEqualizer.getBandLevelRange()[1]; // 第二个下标为最高的限度范围
        // 获取均衡控制器支持的所有频率
        shortbrands = mEqualizer.getNumberOfBands();
        for(shorti = 0; i < brands; i++)
        {
            TextView eqTextView = newTextView(this);
            // 创建一个TextView,用于显示频率
            eqTextView.setLayoutParams(newViewGroup.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 = newLinearLayout(this);
            tmpLayout.setOrientation(LinearLayout.HORIZONTAL);
            // 创建显示均衡控制器最小值的TextView
            TextView minDbTextView = newTextView(this);
            minDbTextView.setLayoutParams(newViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));
            // 显示均衡控制器的最小值
            minDbTextView.setText((minEQLevel / 100) +  dB);
            // 创建显示均衡控制器最大值的TextView
            TextView maxDbTextView = newTextView(this);
            maxDbTextView.setLayoutParams(newViewGroup.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 = newSeekBar(this);
            bar.setLayoutParams(layoutParams);
            bar.setMax(maxEQLevel - minEQLevel);
            bar.setProgress(mEqualizer.getBandLevel(i));
            finalshort brand = i;
            // 为SeekBar的拖动事件设置事件监听器
            bar.setOnSeekBarChangeListener(newSeekBar
                .OnSeekBarChangeListener()
            {
                @Override
                publicvoid onProgressChanged(SeekBar seekBar,
                    intprogress, booleanfromUser)
                {
                    // 设置该频率的均衡值
                    mEqualizer.setBandLevel(brand,
                        (short) (progress + minEQLevel));
                }
                @Override
                publicvoid onStartTrackingTouch(SeekBar seekBar)
                {
                }
                @Override
                publicvoid onStopTrackingTouch(SeekBar seekBar)
                {
                }
            });
            // 使用水平排列组件的LinearLayout“盛装”3个组件
            tmpLayout.addView(minDbTextView);
            tmpLayout.addView(bar);
            tmpLayout.addView(maxDbTextView);
            // 将水平排列组件的LinearLayout添加到myLayout容器中
            layout.addView(tmpLayout);
        }
    }
 
    /**
     * 初始化重低音控制器
     */
    privatevoid setupBassBoost()
    {
        // 以MediaPlayer的AudioSessionId创建BassBoost
        // 相当于设置BassBoost负责控制该MediaPlayer
        mBass = newBassBoost(0, mPlayer.getAudioSessionId());
        // 设置启用重低音效果
        mBass.setEnabled(true);
        TextView bbTitle = newTextView(this);
        bbTitle.setText(重低音:);
        layout.addView(bbTitle);
        // 使用SeekBar做为重低音的调整工具
        SeekBar bar = newSeekBar(this);
        // 重低音的范围为0~1000
        bar.setMax(1000);
        bar.setProgress(0);
        // 为SeekBar的拖动事件设置事件监听器
        bar.setOnSeekBarChangeListener(newSeekBar
            .OnSeekBarChangeListener()
        {
            @Override
            publicvoid onProgressChanged(SeekBar seekBar
                ,intprogress, booleanfromUser)
            {
                // 设置重低音的强度
                mBass.setStrength((short) progress);
            }
            @Override
            publicvoid onStartTrackingTouch(SeekBar seekBar)
            {
            }
            @Override
            publicvoid onStopTrackingTouch(SeekBar seekBar)
            {
            }
        });
        layout.addView(bar);
    }
 
    /**
     * 初始化预设音场控制器
     */
    privatevoid setupPresetReverb()
    {
        // 以MediaPlayer的AudioSessionId创建PresetReverb
        // 相当于设置PresetReverb负责控制该MediaPlayer
        mPresetReverb = newPresetReverb(0,
            mPlayer.getAudioSessionId());
        // 设置启用预设音场控制
        mPresetReverb.setEnabled(true);
        TextView prTitle = newTextView(this);
        prTitle.setText(音场);
        layout.addView(prTitle);
        // 获取系统支持的所有预设音场
        for(shorti = 0; i < mEqualizer.getNumberOfPresets(); i++)
        {
            reverbNames.add(i);
            reverbVals.add(mEqualizer.getPresetName(i));
        }
        // 使用Spinner做为音场选择工具
        Spinner sp = newSpinner(this);
        sp.setAdapter(newArrayAdapter<string>(MediaPlayerTest.this,
            android.R.layout.simple_spinner_item, reverbVals));
        // 为Spinner的列表项选中事件设置监听器
        sp.setOnItemSelectedListener(newSpinner
            .OnItemSelectedListener()
        {
            @Override
            publicvoid onItemSelected(AdapterView<!--?--> arg0
                , View arg1, intarg2, longarg3)
            {
                // 设定音场
                mPresetReverb.setPreset(reverbNames.get(arg2));
            }
 
            @Override
            publicvoid onNothingSelected(AdapterView<!--?--> arg0)
            {
            }
        });
        layout.addView(sp);
    }
 
    @Override
    protectedvoid onPause()
    {
        super.onPause();
        if(isFinishing() && mPlayer != null)
        {
            // 释放所有对象
            mVisualizer.release();
            mEqualizer.release();
            mPresetReverb.release();
            mBass.release();
            mPlayer.release();
            mPlayer = null;
        }
    }
    /**
     * 根据Visualizer传来的数据动态绘制波形效果,分别为:
     * 块状波形、柱状波形、曲线波形
     */
    privatestatic class MyVisualizerView extendsView
    {
        // bytes数组保存了波形抽样点的值
        privatebyte[] bytes;
        privatefloat[] points;
        privatePaint paint = newPaint();
        privateRect rect = newRect();
        privatebyte type = 0;
        publicMyVisualizerView(Context context)
        {
            super(context);
            bytes = null;
            // 设置画笔的属性
            paint.setStrokeWidth(1f);
            paint.setAntiAlias(true);//抗锯齿
            paint.setColor(Color.YELLOW);//画笔颜色
            paint.setStyle(Style.FILL);
        }
 
        publicvoid updateVisualizer(byte[] ftt)
        {
            bytes = ftt;
            // 通知该组件重绘自己。
            invalidate();
        }
         
        @Override
        publicboolean onTouchEvent(MotionEvent me)
        {
            // 当用户触碰该组件时,切换波形类型
            if(me.getAction() != MotionEvent.ACTION_DOWN)
            {
                returnfalse;
            }
            type ++;
            if(type >= 3)
            {
                type = 0;
            }
            returntrue;
        }
 
        @Override
        protectedvoid onDraw(Canvas canvas)
        {
            super.onDraw(canvas);
            if(bytes == null)
            {
                return;
            }
            // 绘制白色背景
            canvas.drawColor(Color.WHITE);         
            // 使用rect对象记录该组件的宽度和高度
            rect.set(0,0,getWidth(),getHeight());
            switch(type)
            {
                // -------绘制块状的波形图-------
                case0:
                    for(inti = 0; i < bytes.length - 1; i++)
                    {
                        floatleft = getWidth() * i / (bytes.length - 1);
                        // 根据波形值计算该矩形的高度       
                        floattop = rect.height()-(byte)(bytes[i+1]+128)
                            * rect.height() / 128;
                        floatright = left + 1;
                        floatbottom = rect.height();
                        canvas.drawRect(left, top, right, bottom, paint);
                    }
                    break;
                // -------绘制柱状的波形图(每隔18个抽样点绘制一个矩形)-------
                case1:
                    for(inti = 0; i < bytes.length - 1; i += 18)
                    {
                        floatleft = rect.width()*i/(bytes.length - 1);
                        // 根据波形值计算该矩形的高度
                        floattop = rect.height()-(byte)(bytes[i+1]+128)
                            * rect.height() / 128;
                        floatright = left + 6;
                        floatbottom = rect.height();
                        canvas.drawRect(left, top, right, bottom, paint);
                    }
                    break;
                // -------绘制曲线波形图-------
                case2:
                    // 如果point数组还未初始化
                    if(points == null|| points.length < bytes.length * 4)
                    {
                        points = newfloat[bytes.length * 4];
                    }
                    for(inti = 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.0encoding=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