Android AudioEffect 音效方案

Posted ByteSaid

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android AudioEffect 音效方案相关的知识,希望对你有一定的参考价值。

1 基于 android 2.3 - Android 8.1的方案

Android2.3 增加了对音频混响的支持,通过 AudioEffect 可以方便地对 AudioTrack 和 MediaPlayer 播放的音乐进行音效控制。AudioEffect 是 Android 音频框架提供的用于控制音频效果的基类,开发者不应直接使用此类,应该使用它的派生类:

  • Equalizer 均衡器:增加或降低某一频率的声音响度来达到想要的效果。
  • Virtualizer 环绕音:让声音可以产生一种空间感,数值越大声音就距离耳机越远。
  • BassBoost 重低音控制器:增加低音的强度。
  • PresetReverb 预设混响(推荐用于音乐):使音乐通过声音在不同路径传播下造成的反射叠加产生的声音特效,比如流行,古典,爵士等。
  • EnvironmentalReverb 环境混响(推荐用于游戏):比如马路,走廊,室内,大厅等。
    以上音效包含在 android.media.audiofx 包中,可以参考官方文档
    为了使用音效处理需要在AndroidManifest.xml 添加相应的权限:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

1.1 Equalizer

Equalizer 的使用方法如下:

MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.test_cbr/*音频路径*/);
Equalizer equalizer = new Equalizer(0, mediaPlayer.getAudiosessionId());
equalizer.setEnabled(true);

//获取均衡器引擎支持的频段数
short bands = equalizer.getNumberOfBands();
        
//获取最大和最小增益
final short minEQLevel = equalizer.getBandLevelRange()[0];
final short maxEQLevel = equalizer.getBandLevelRange()[1];
for (short i = 0; i < bands; i++) 
    final short band = i;
    //获取当前频段的中心频率,分别为:60Hz,230Hz,910Hz,3600Hz,14000Hz
    int currentFreq = equalizer.getCenterFreq(band);
    //获取给定均衡器频段的增益
    short level = equalizer.getBandLevel(band);
    Log.d("Equalizer","currentFreq is: " + currentFreq + ", band level is: " + level);
    //为给定的均衡器频带设置增益值
    equalizer.setBandLevel(band,xx);

在构造函数 Equalizer(int priority, int audioSession) 中:

  • int priority:优先级,多个应用可以共享同一 Equalizer 引擎,该参数指出控制优先权,默认为0。
  • int audioSession:音频会话 ID,系统范围内唯一,Equalizer 将被附加在拥有相同音频会话 ID 的 MediaPlayer 或 AudioTrack 上生效。
    Android 系统预置了一些增益参数,可通过下面代码获取:
short presets= equalizer.getNumberOfPresets();
//获取系统预设的增益
for (short i = 0; i < presets; i++) 
  Log.d("presets",equalizer.getPresetName(i));

结果为:Normal、Classical、Dance、Flat、Folk、Heavy Metal、Hip hop、Jazz、Pop、Rock。
然后通过 equalizer.usePreset(); 使用系统预置参数。
销毁时:

if (equalizer != null) 
    equalizer.setEnabled(false);
    equalizer.release();
    equalizer = null;

1.2 Virtualizer

Virtualizer 的使用方法如下:

Virtualizer mVirtualizer= new Virtualizer (0, mMediaPlayer.getAudioSessionId()); //优先级为0
mVirtualizer.setEnabled(true);
if (mVirtualizer.getStrengthSupported())

    short strength = mVirtualizer.getRoundedStrength();
    mVirtualizer.setStrength((short)strength);

  • getRoundedStrength() :获取特效力度,特效力度值在0~1000间变化。
  • setStrength() :设置特效力度。
    如果指定的会话 ID 为0,则 Virtualizer 作用于主要的音频输出混音器(mix)。

1.3 BassBoost

低音增强,用于增强或放大声音的低频。 它与简单的均衡器相当,但仅限于低频范围内的一个频段放大。
主要使用方法为:

BassBoost bassBoost = new BassBoost(0,mediaPlayer.getAudioSessionId());
bassBoost.setEnabled(true);
if (bassBoost.getStrengthSupported())
    bassBoost.setStrength((short) 100);

其中,getStrengthSupported()指示是否支持设置强度。如果此方法返回 false,则仅支持一种强度,并且 setStrength() 方法始终舍入到该值。
setStrength() 设置效果的当前强度,强度的有效范围是[0, 1000],0 表示最温和的效果,1000 表示最强的效果。

1.4 PresetReverb

PresetReverb 使用预设混响来配置全局混响,适合于音乐。预置的常见混响场景有:

  • PresetReverb.PRESET_LARGEHALL(适合整个管弦乐队的大型大厅);
  • PresetReverb.PRESET_LARGEROOM(适合现场表演的大型房间的混响预设)。
    使用方法为:
PresetReverb presetReverb = new PresetReverb(0,mediaPlayer.getAudioSessionId());
presetReverb.setEnabled(true);
presetReverb.setPreset(PresetReverb.PRESET_LARGEROOM);

1.5 EnvironmentalReverb

允许应用程序控制全局混响环境中的每个混响引擎属性,更适合游戏。下面介绍下该类常用方法:

  • setDecayHFRatio:设置高频到中频衰减比率。范围是[100, 2000] ,如果设为1000,则全部衰减相同。
  • setDecayTime:中频混响衰减时间。[100, 20000]
  • setDensity:在后期混响衰减,控制模态密度的值。[0, 1000]
  • setDiffusion:在后期混响衰减,控制回声密度的值。 [0, 1000]
  • setReflectionsDelay:初始反射延迟时间。[0, 300]
  • setReflectionsLevel:对于环境效果的早期反射等级。[-9000, 1000]
  • setReverbDelay:先对于初始反射的后期混响延迟时间。 [0, 100]
  • setReverbLevel:相对于环境效果的后期混响等级。[-9000, 2000]
  • setRoomHFLevel:相对于高频环境效果等级。 [-9000, 0]
  • setRoomLevel:相对于低频环境效果等级。[-9000, 0]

2 基于 Android 9.0的方案

Android 9.0新增了用于 DynamicsProcessing 的 AudioEffect API,使用此类,我们可以构建基于通道的音频效果,包括各种类型的多个阶段,包括均衡,多频段压缩和限制。频段和活动阶段的数量是可配置的,并且大多数参数可以实时控制,例如增益、启动/释放时间、阈值等。

2.1 基本原理

效果由通道实例化和控制。每个通道具有相同的基本架构,但它们的所有参数都独立于其他通道。
基本通道配置为:


    Channel 0          Channel 1       ....       Channel N-1
      Input              Input                       Input
        |                  |                           |
   +----v----+        +----v----+                 +----v----+
   |inputGain|        |inputGain|                 |inputGain|
   +---------+        +---------+                 +---------+
        |                  |                           |
  +-----v-----+      +-----v-----+               +-----v-----+
  |   PreEQ   |      |   PreEQ   |               |   PreEQ   |
  +-----------+      +-----------+               +-----------+
        |                  |                           |
  +-----v-----+      +-----v-----+               +-----v-----+
  |    MBC    |      |    MBC    |               |    MBC    |
  +-----------+      +-----------+               +-----------+
        |                  |                           |
  +-----v-----+      +-----v-----+               +-----v-----+
  |  PostEQ   |      |  PostEQ   |               |  PostEQ   |
  +-----------+      +-----------+               +-----------+
        |                  |                           |
  +-----v-----+      +-----v-----+               +-----v-----+
  |  Limiter  |      |  Limiter  |               |  Limiter  |
  +-----------+      +-----------+               +-----------+
        |                  |                           |
     Output             Output                      Output
 

其中,

  • inputGain:输入增益因子,以分贝 (dB) 为单位。0 dB 表示电平没有变化。
  • PreEQ:多频段均衡器。
  • MBC:多频段压缩器 。
  • PostEQ:多频段均衡器。
  • Limiter:单频段压缩器/限制器,通常用于保护信号免于过载和失真。

2.1.1 均衡器

可以理解为单独控制每个频率的音量,调整各频段信号的增益量,衰减多余频率的同时塑造音色。
详细介绍:EQ 均衡器介绍

2.1.2 压缩器

是用来控制电平的效果器,控制电平的同时会影响音色,不单单是人声,混音当中,所有的元素都可以需要压缩器。
常见参数:

  • Thress hoid(阈值):阈值表示声音经过压缩器是,多大的电平才能触发压缩器进行压缩工作,当声音超过你设定的阈值时他开始对音频进行压缩,声音低于阈值压缩器不工作。
  • Ratio(压缩比):压缩器表示当声音超过阈值压缩器开始工作时,压缩器会以多大的比例对声音进行压缩。
  • Attack(启动时间):表示声音超过阈值后需要多长时间才平稳到达使压缩器压缩到规定的压缩比。
  • release(释放时间):表示当压缩器已经开始工作后,当声音电平低于阈值时,压缩器要多长时间平稳的恢复到压缩前的电平(目前电平)。

2.1.3 限制器

用来控制峰值的效果器,限制信号不过载的同时提高响度。
常见参数:

  • Thress hold (阈值):限制器工作时的触发值。
  • output(输出上限):能被输出的最高电平。
  • release(释放时间):指限制器恢复到不限制所需要的时间。
  • Dither(抖动):作用是给声音添加一层白底燥。

应用程序创建一个 DynamicsProcessing 对象以在音频框架中实例化和控制此音频效果。如果需要,可以使用 DynamicsProcessor.Config 和 DynamicsProcessor.Config.Builder 来帮助配置多个阶段和每个频段参数。
如果在创建过程中未指定任何配置,则选择默认配置。
下面介绍 DynamicsProcessing 的使用方法。

2.2 初始化

在使用的时候需要先判断 Build.VERSION.SDK_INT >= 28

2.2.1 创建 DynamicsProcessing 对象

要将 DynamicsProcessing 附加到特定的 AudioTrack 或 MediaPlayer,在实例化时指定此 AudioTrack 或 MediaPlayer 的音频会话 ID。

MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.test_cbr/*音频路径*/);
int audioSessionId = mediaPlayer.getAudioSessionId();
DynamicsProcessing.Config.Builder builder = new DynamicsProcessing.Config.Builder(
                        0,//variant
                        1,//channelCount
                        true,//preEqInUse
                        10,//preEqBandCount
                        true,//mbcInUse
                        10,//mbcBandCount
                        true,//postEqInUse
                        10,//postEqBandCount
                        true//limiterInUse
);
DynamicsProcessing mDynamicsProcessing = new DynamicsProcessing(0, audioSessionId, builder.build());
mDynamicsProcessing.setEnabled(true);

2.2.2 创建 DynamicsProcessing.Eq (均衡器)对象

//创建用于调节的10个频段
private static final int[] bandVal = 31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000;
private static final int maxBandCount = bandVal.length;

DynamicsProcessing.Eq mEq = new DynamicsProcessing.Eq(true, true, maxBandCount);
mEq.setEnabled(true);

for (int i = 0; i < maxBandCount; i++) 
    mEq.getBand(i).setCutoffFrequency(bandVal[i]);//设置此频段将处理的最高频率数(以 Hz 为单位)

//设置压缩前的均衡器给全频道
mDynamicsProcessing.setPreEqAllChannelsTo(mEq);
//设置压缩前的均衡器给指定频道
//public static final int CHANNEL_1 = 0;
//public static final int CHANNEL_2 = 1;
//mDynamicsProcessing.setPreEqByChannelIndex(CHANNEL_1, mEq);

//设置压缩后的均衡器给全频道
//mDynamicsProcessing.setPostEqAllChannelsTo(mEq);
//设置压缩后的均衡器给指定频道
//public static final int CHANNEL_1 = 0;
//public static final int CHANNEL_2 = 1;
//mDynamicsProcessing.setPostEqByChannelIndex(CHANNEL_1, mEq);

2.2.3 创建 DynamicsProcessing.Mbc(多频段压缩器)对象

DynamicsProcessing.Mbc mDynamicsProcessingMbc = new DynamicsProcessing.Mbc(true, true, maxBandCount);
mDynamicsProcessingMbc.setEnabled(true);
for (int i = 0; i < maxBandCount; i++) 
    mDynamicsProcessingMbc.getBand(i).setCutoffFrequency(bandVal[i]);//设置此频段将处理的最高频率数(以 Hz 为单位)


//设置多频段压缩器给全频道
mDynamicsProcessing.setMbcAllChannelsTo(mDynamicsProcessingMbc);
//设置多频段压缩器给指定频道
//mDynamicsProcessing.getMbcBandByChannelIndex(CHANNEL_1, mDynamicsProcessingMbc);

2.2.4 创建 DynamicsProcessing.Limiter(限制器)对象

//Limiter构造参数
private static final boolean LIMITER_DEFAULT_IN_USE = true;//如果将使用 MBC 阶段,则为 true,否则为 false。
private static final boolean LIMITER_DEFAULT_ENABLED = true;//如果启用/禁用 MBC 阶段,则为 true。这可以在效果运行时更改
private static final int LIMITER_DEFAULT_LINK_GROUP = 0;//分配给此限制器的组的索引。只有共享相同 linkGroup 索引的限制器才会一起做出反应。
private static final float LIMITER_DEFAULT_ATTACK_TIME = 1; // 限制器压缩器的启动时间,以毫秒 (ms) 为单位
private static final float LIMITER_DEFAULT_RELEASE_TIME = 60; //限制器压缩器的释放时间,以毫秒 (ms) 为单位
private static final float LIMITER_DEFAULT_RATIO = 10; // 限制器压缩比 (N:1)(输入:输出)
private static final float LIMITER_DEFAULT_THRESHOLD = -2; // 限幅压缩器阈值以分贝 (dB) 为单位,从 0 dB 满量程 (dBFS) 开始测量。
private static final float LIMITER_DEFAULT_POST_GAIN = 0; // 压缩后应用于信号的增益。

DynamicsProcessing.Limiter mDynamicsProcessingLimiter = new DynamicsProcessing.Limiter(LIMITER_DEFAULT_IN_USE, LIMITER_DEFAULT_ENABLED, LIMITER_DEFAULT_LINK_GROUP, LIMITER_DEFAULT_ATTACK_TIME, LIMITER_DEFAULT_RELEASE_TIME, LIMITER_DEFAULT_RATIO, LIMITER_DEFAULT_THRESHOLD, LIMITER_DEFAULT_POST_GAIN);
mDynamicsProcessingLimiter.setEnabled(true);

//设置限制器给全频道
mDynamicsProcessing.setLimiterAllChannelsTo(mDynamicsProcessingLimiter);
//设置限制器给指定频道
//mDynamicsProcessing.setLimiterByChannelIndex(CHANNEL_1, mDynamicsProcessingLimiter);

2.3 调节频段

2.3.1 调节输入增益

mDynamicsProcessing.setInputGainAllChannelsTo(value);

2.3.2 调节均衡器

//根据调节的频段在bandVal数组中的索引bandIndex和调整后的值gain来调节均衡器
mEq.getBand(bandIndex).setGain(gain);
//设置压缩前的均衡器频段增益给全频道
mDynamicsProcessing.setPreEqBandAllChannelsTo(bandIndex, mEq.getBand(bandIndex));
//设置压缩前的均衡器频段增益给指定频道
//mDynamicsProcessing.setPreEqBandByChannelIndex(CHANNEL_1, bandIndex, mEq.getBand(bandIndex));

//设置压缩后的均衡器频段增益给全频道
//mDynamicsProcessing.setPostEqBandAllChannelsTo(bandIndex, mEq.getBand(bandIndex));
//设置压缩后的均衡器频段增益给指定频道
//mDynamicsProcessing.setPostEqBandByChannelIndex(CHANNEL_1, bandIndex, mEq.getBand(bandIndex));

2.3.3 调节多频段压缩器

//设置在压缩之前应用于信号的增益,以分贝 (dB) 为单位测量,其中 0 dB 表示没有电平变化。
mDynamicsProcessingMbc.getBand(bandIndex).setPreGain(gain);
//设置给全频道
mDynamicsProcessing.setMbcBandAllChannelsTo(bandIndex, mDynamicsProcessingMbc.getBand(bandIndex));
//设置给指定频道
//mDynamicsProcessing.setMbcBandByChannelIndex(CHANNEL_1, bandIndex, mDynamicsProcessingMbc.getBand(bandIndex));

//设置在压缩之后应用于信号的增益,以分贝 (dB) 为单位测量,其中 0 dB 表示没有电平变化。
//mDynamicsProcessingMbc.getBand(bandIndex).setPostGain(gain);
//设置给全频道
//mDynamicsProcessing.setMbcBandAllChannelsTo(bandIndex, mDynamicsProcessingMbc.getBand(bandIndex));
//设置给指定频道
//mDynamicsProcessing.setMbcBandByChannelIndex(CHANNEL_1, bandIndex, mDynamicsProcessingMbc.getBand(bandIndex));

2.4 销毁

if (mMediaPlayer != null) 
    mMediaPlayer.pause();
    mMediaPlayer.release();

if (mDynamicsProcessing != null) 
    mDynamicsProcessing.setEnabled(false);
    mDynamicsProcessing.release();
    mDynamicsProcessing = null;

2.5 示例代码下载

dynamics-processing-sample

以上是关于Android AudioEffect 音效方案的主要内容,如果未能解决你的问题,请参考以下文章

AudioEffect构造流程跟踪 & 音效库实现(native侧)

AudioEffect与Equalizer解析(Java侧)

将 AudioEffect 附加到全局混音的首选方式?

AudioEffect中文API

Android:如何为我的应用播放的任何音乐文件创建淡入/淡出音效?

OPENGL相关 音效