我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能
- MediaPlayer 媒体播放器
- Visualizer 频谱
- Equalizer 均衡器
- BassBoost 重低音控制器
- PresetReverb 预设音场控制器
- Paint 绘图
- 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;
- }
- }
- }
- }
AndroidManifest.xml
- <?xml version="1.0" encoding="utf-8"?>
- <manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.oyp.media"
- android:versionCode="1"
- android:versionName="1.0">
- <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"/>
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name">
- <activity
- android:name=".MediaPlayerTest"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
- java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -4
====================================================================================
作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:http://blog.csdn.net/ouyang_peng
====================================================================================
我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能的更多相关文章
- 我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能
本实例来自于<疯狂Android讲义>.要实现详细的功能,须要了解下面API: MediaPlayer 媒体播放器 Visualizer 频谱 Equalizer 均衡器 BassBoo ...
- 我的Android进阶之旅------>关于android:layout_weight属性的详细解析
关于androidlayout_weight属性的详细解析 效果一 效果二 图3的布局代码 图4的布局代码 效果三 图7代码 图8代码 效果四 效果五 版权声明:本文为[欧阳鹏]原创文章,欢迎转载,转 ...
- 我的Android进阶之旅------>关于android:layout_weight属性的一个面试题
最近碰到一个面试题,按照下图,由Button和EditText组成的界面下厨布局代码,解决这题目需要使用android:layout_weight的知识. 首先分析上图所示的界面可以看成一下3个部分. ...
- 我的Android进阶之旅------> Android在TextView中显示图片方法
面试题:请说出Android SDK支持哪些方式显示富文本信息(不同颜色.大小.并包括图像的文本信息).并简要说明实现方法. 答案:Android SDK支持例如以下显示富文本信息的方式. 1.使用T ...
- 我的Android进阶之旅------>Android字符串资源中的单引號问题error: Apostrophe not preceded by 的解决的方法
刚刚在string字符串资源文件里,写了一个单引號.报错了,错误代码例如以下 error: Apostrophe not preceded by \ (in OuyangPeng's blog ) 资 ...
- 我的Android进阶之旅------> Android为TextView组件中显示的文本加入背景色
通过上一篇文章 我的Android进阶之旅------> Android在TextView中显示图片方法 (地址:http://blog.csdn.net/ouyang_peng/article ...
- 我的Android进阶之旅------>Android系统设置默认来电铃声、闹钟铃声、通知铃声
首先了解Android系统本身提供的默认铃声文件,这些文件都放在 /system/media/audio 文件夹下. /system/media/audio/ringtones 系统来电铃声 ...
- 【我的Android进阶之旅】Android 混淆文件资源分类整理
之前将所有的混淆都配置在一个 proguard-rules.pro 这个Android Studio新建项目时自动生成的文件里面,而随着项目功能迭代越来越多,代码量越来越多,引用的第二方库.第三方库都 ...
- 我的Android进阶之旅------>Android关于Activity管理的一个简单封装
怎样管理当前的执行Activity栈,怎样彻底退出程序.本文封装了一个Activity管理类,能够方便随时退出程序. import java.util.Stack; import android.ap ...
随机推荐
- 【转】java8中谨慎使用实数作为HashMap的key!
java8中谨慎使用实数作为HashMap的key! java8中一个hashCode()函数引发的血案java8中一个hashCode()函数引发的血案1.起因2.实数的hashCode()3.总结 ...
- 【mob】Android短信验证+源码
在很多的应用当中,都涉及到了短信验证的功能,比如在注册或者找回密码的时候,那么我们如何通过第三方的平台来完成这个功能呢? 本面博文就实现短信验证,来做一个小的栗子. 第一步-下载开发包 第二步-将SD ...
- Python操作sqlite数据库小节
学习了Python操作sqlite数据库,做一个小结,以备后用. import sqlite3import os# 进行数据库操作时,主要是参数如何传输try:# 链接数据库conn=sqlite3. ...
- Linux 设备驱动模型
Linux系统将设备和驱动归一到设备驱动模型中了来管理 设备驱动程序功能: 1,对硬件设备初始化和释放 2,对设备进行管理,包括实参设置,以及提供对设备的统一操作接口 3,读取应用程序传递给设备文件的 ...
- fiddler使用心得记录
fiddler是一款非常好用的软件,通过监听8888端口来修改http,https等请求和响应,是抓包神器. 最近正在学习如何使用,现在记录下学习的一些技巧 如何支持https 点击菜单项tools, ...
- MFC中的CDC详细教程
参考: MFC中的CDC详细教程1,2,3 StretchDIBits用法
- [转]MySQL的简单使用和JDBC示例
MySql简单操作 //启动mysql net start mysql //登陆 mysql -u root -p //创建建数据库 create database mydb; create data ...
- 转:java工程师成神之路
转自: http://www.hollischuang.com/archives/489 一.基础篇 1.1 JVM 1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 htt ...
- Linux下快速安装Mysql及使用
1.安装 查看有没有安装过: yum list installed mysql* rpm -qa | grep mysql* 查看有没有安装包: yum list mysql* 安装mysql客户端: ...
- 《linux 内核全然剖析》 mktime.c
tm结构体的定义在time.h里面 struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_y ...