Android提供了用于音乐播放时的音效控制器,比如均衡器、重低音以及显示音乐波形等。这些功能被定义在AudioEffect的子类中完成1:
AcousticEchoCanceler:回声消除器
AutomaticGainControl:自动增强控制器
NoiseSuppressor:噪音抑制器
BassBoost:重低音调节器
Equalizer:均衡器
PresetReverb:预设音场控制器
Visualizer:示波器
代码实现:
com/example/administrator/Permission/PermissionManager.java
package com.example.administrator.Permission;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import java.util.concurrent.ConcurrentHashMap;
/**
* @time 2017/2/20
* @fuction 动态权限管理封装类
* @use 1. 在BaseActivity和BaseFragment重写onRequestPermissionsResult ,并实现如下代码PermissionManager.onRequestResult(requestCode, permissions, grantResults);
* 2. 然后需要请求权限的时候调用requestPermission方法 即可
*/
public class PermissionManager {
//维护的每个Activity的申请权限的监听
//便于清除释放
private static ConcurrentHashMap mListenerMap = new ConcurrentHashMap<>();
/**
* 权限申请
*
* @param context Activity
* @param desc 再次申请权限的提示语
* @param requestCode
* @param permissionsListener
* @param permissions
*/
public static void requestPermission(Activity context,
String desc,
int requestCode,
PermissionsResultListener permissionsListener,
String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mListenerMap.put(requestCode, permissionsListener);
if (checkEachSelfPermission(context, permissions)) {
requestEachPermission(context, desc, permissions, requestCode);
} else {
mListenerMap.get(requestCode).onPermissionGranted(requestCode);
}
}
}
/**
* 权限申请
*
* @param context Fragment
* @param desc 再次申请权限的提示语
* @param requestCode
* @param permissionsListener
* @param permissions
*/
public static void requestPermission(Fragment context,
String desc,
int requestCode,
PermissionsResultListener permissionsListener,
String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mListenerMap.put(requestCode, permissionsListener);
if (checkEachSelfPermission(context.getActivity(), permissions)) {
requestEachPermission(context, desc, permissions, requestCode);
} else {
mListenerMap.get(requestCode).onPermissionGranted(requestCode);
}
}
}
/**
* 权限请求
*
* @param context Activity
* @param desc 再次申请权限的提示语
* @param permissions
* @param requestCode
*/
private static void requestEachPermission(final Activity context, String desc, final String[] permissions, final int requestCode) {
if (shouldShowRequestPermissionRationale(context, permissions)) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("权限申请")
.setMessage(desc)
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityCompat.requestPermissions(context, permissions, requestCode);
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setCancelable(false)
.show();
} else {
ActivityCompat.requestPermissions(context, permissions, requestCode);
}
}
/**
* 权限请求
*
* @param context Fragment
* @param desc 再次申请权限的提示语
* @param permissions
* @param requestCode
*/
private static void requestEachPermission(final Fragment context, String desc, final String[] permissions, final int requestCode) {
if (shouldShowRequestPermissionRationale(context, permissions)) {
AlertDialog.Builder builder = new AlertDialog.Builder(context.getActivity());
builder.setTitle("权限申请")
.setMessage(desc)
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
context.requestPermissions(permissions, requestCode);
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setCancelable(false)
.show();
} else {
context.requestPermissions(permissions, requestCode);
}
}
/**
* 再次申请权限时,是否需要声明
*
* @param context Activity
* @param permissions
* @return
*/
private static boolean shouldShowRequestPermissionRationale(Activity context, String[] permissions) {
for (String permission : permissions) {
if (ActivityCompat.shouldShowRequestPermissionRationale(context, permission)) {
return true;
}
}
return false;
}
/**
* 再次申请权限时,是否需要声明
*
* @param context Fragment
* @param permissions
* @return
*/
private static boolean shouldShowRequestPermissionRationale(Fragment context, String[] permissions) {
for (String permission : permissions) {
if (context.shouldShowRequestPermissionRationale(permission)) {
return true;
}
}
return false;
}
/**
* 检察每个权限是否申请
*
* @param permissions
* @return true 需要申请权限,false 已申请权限
*/
private static boolean checkEachSelfPermission(Context context, String[] permissions) {
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return true;
}
}
return false;
}
/**
* 权限申请处理回调
* 写在Activity或者Fragment的onRequestPermissionsResult 方法内
*
* @param requestCode
* @param permissions
* @param grantResults
*/
public static void onRequestResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
PermissionsResultListener permissionsResultListener = mListenerMap.get(requestCode);
if (permissionsResultListener == null) {
return;
}
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permissionsResultListener.onPermissionGranted(requestCode);
} else {
permissionsResultListener.onPermissionDenied(requestCode);
}
}
/**
* 清除掉已用完的Listner
*/
public static void clearListner(int requestCode) {
if (mListenerMap.get(requestCode) != null) {
mListenerMap.remove(requestCode);
}
}
public interface PermissionsResultListener {
/**
* 权限申请成功回调
*/
void onPermissionGranted(int requestCode);
/**
* 权限申请失败回调
*/
void onPermissionDenied(int requestCode);
}
/**
* 危险权限 授权一个就等于同组都授权了
* Manifest.permission.XXX
*
* group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
*/
}
com/example/administrator/MainActivity.java
package com.example.administrator;
import java.util.ArrayList;
import java.util.List;
import android.Manifest;
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;
import android.widget.Toast;
import com.example.administrator.Permission.PermissionManager;
public class MainActivity extends Activity implements PermissionManager.PermissionsResultListener{
// 定义播放声音的MediaPlayer
private MediaPlayer mPlayer;
// 定义系统的频谱
private Visualizer mVisualizer;
// 定义系统的均衡器
private Equalizer mEqualizer;
// 定义系统的重低音控制器
private BassBoost mBass;
// 定义系统的预设音场控制器
private PresetReverb mPresetReverb;
private LinearLayout layout;
private List reverbNames = new ArrayList();
private List reverbVals = new ArrayList();
@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
PermissionManager.requestPermission(this, "音场效果必要的权限", 1, this, Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS);
// 创建MediaPlayer对象,并添加音频
// 音频路径为 res/raw/beautiful.mp3
mPlayer = MediaPlayer.create(this, R.raw.bomb);
// 初始化示波器
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(MainActivity.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;
}
}
}
@Override
public void onPermissionGranted(int requestCode) {
Toast.makeText(this, "申请权限成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionDenied(int requestCode) {
Toast.makeText(this, "申请权限失败", Toast.LENGTH_SHORT).show();
}
}
添加权限: