Android在应用层提供丰富的多媒体接口,本文主要介绍音效处理:均衡器风格、预设混响、重低音调节、音量增强、可视化动态频谱,这些音效都使用audioSessionId进行绑定。让用户可选择/设置自己喜欢的风格,身临其境地感受不同歌曲的跳动旋律。方便开发者进行二次开发,打造出酷炫的音乐播放器。

第一篇文章探讨:MediaPlayer、MediaCodec、AudioTrack、MediaMuxer、MediaExtractor。

第二篇文章探讨:MediaRecorder、AudioRecord、MediaMetadataRetriever、MediaProjection、AudioManager。

第三篇文章探讨:CameraX,即对Camera的封装,提供预览、拍照、录像功能。

目录

一、均衡器风格

二、预设混响

三、重低音调节

四、音量增强

五、可视化频谱

1、申请权限

2、初始化

3、数据的获取与显示

4、释放资源


我们先看下均衡器的各种风格以及其它音效,如下图所示:

Android音频跳动 android 音效_均衡器

一、均衡器风格

均衡器Equalizer,Android源码地址:Equalizer。根据官方介绍,均衡器用于调节音乐源或输出混音的频率增益,频率范围从低到高包括:超低频、低频、中频、高频、超高频。我们可以通过mEqualizer?.bandLevelRange[0]获取频率带宽最小值,通过mEqualizer?.bandLevelRange[1]获取频率带宽最大值,然后获取band个数mEqualizer?.numberOfBands,最终根据最小值、最大值和band个数计算出所有频率带宽。具体实现如下:

val equalizerList = ArrayList<Pair<*, *>>()
        mEqualizer = Equalizer(0, mPlayer!!.audioSessionId)
        mEqualizer!!.enabled = enableEqualizer
        minEQLevel = mEqualizer!!.bandLevelRange[0]
        val maxEQLevel = mEqualizer!!.bandLevelRange[1]
        bands = mEqualizer!!.numberOfBands
        for (i in 0 until bands) {
            val centerFreq = (mEqualizer!!.getCenterFreq(i.toShort()) / 1000).toString() + " Hz"
            val pair = Pair.create(centerFreq, mEqualizer!!.getBandLevel(i.toShort()) - minEQLevel)
            equalizerList.add(pair)
        }

关于均衡器风格,我们可以使用mEqualizer?.getPresetName(i)来遍历获取,包括古典、舞蹈、民俗、重金属、嘻哈、爵士、流行、摇滚等,如下列表所示:

Normal
    Classical
    Dance
    Flat
    Folk
    Heavy Metal
    Hip Hop
    Jazz
    Pop
    Rock

二、预设混响

预设混响PresetReverb,源码地址:PresetReverb。根据官方介绍,一个房间内产生的声音可以往多个方向传播,当声波经历越来越多的反射到达后,聆听者听到的是随时间衰减的连续混响。它可以在音乐应用程序用来模拟各种环境中播放音乐,或者在游戏中让听众沉浸在游戏环境里。

混响模型有Schroeder模型和Moorer模型。其中Schroeder模型由4个梳状滤波器和2个全通滤波器组成,梳状滤波器提供较长延时,全通滤波器提供较短延时。而Moorer模型在Schroeder基础上提出改进:引入低通滤波器,用于高频衰减;增加前级反馈,用于早反射。

混响参数包括:房间大小、高频衰减系数、延时大小、干湿比、立体声宽度等。

预设混响包括小房间、中等房间、大房间、中等大厅、宽敞大厅等,如下列表所示:

SmallRoom
    MediumRoom
    LargeRoom
    MediaHall
    LargeHall
    Plate

三、重低音调节

重低音BassBoost,源码地址:BassBoost。重低音又称为低音增强,重低音是一种放大或增强低频声音的音频效果,它类似于简单的均衡器,只是限于低频范围内的频带放大。调用mBass?.setStrength(progress.toShort())来调节放大等级。

四、音量增强

音量增强LoudnessEnhancer,一种用于增强声音响度的音乐效果,通过设置目标增益值来决定声音信号放大系数。把LoudnessEnhancer关联到特定的AudioTrack或者MediaPlayer,然后指定从AudioTrack或MediaPlayer获取的audio session id。声音信号的目标增益值默认单位是mB,其中100mB=1dB,当增益值为0mB时,默认不进行信号放大。Android官方提供的源码地址:LoudnessEnhancer

五、可视化频谱

可视化频谱Visualizer,用于播放音乐时产生可视化数据,让用户享受跳动的旋律。由PCM数据从时域到频域变换,即FFT变换得到频谱。源码地址:Visualizer

1、申请权限

为了保护用户声音数据的隐私安全,需要申请录音操作权限:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

另外,在输出混音(audio session id为0)时,需要申请修改录音的权限:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

如果没有录音权限,直接初始化会报错,因为底层初始化时会检测录音权限,抛出RuntimeException。底层源码如下:

public Visualizer(int audioSession)
    throws UnsupportedOperationException, RuntimeException {
        int[] id = new int[1];

        synchronized (mStateLock) {
            mState = STATE_UNINITIALIZED;
            // native initialization
            int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id,
                    ActivityThread.currentOpPackageName());
            if (result != SUCCESS && result != ALREADY_EXISTS) {
                Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
                switch (result) {
                case ERROR_INVALID_OPERATION:
                    throw (new UnsupportedOperationException("Effect library not loaded"));
                default:
                    throw (new RuntimeException("Cannot initialize Visualizer engine, error: "
                            +result));
                }
            }
            mId = id[0];
            if (native_getEnabled()) {
                mState = STATE_ENABLED;
            } else {
                mState = STATE_INITIALIZED;
            }
        }
    }

2、初始化

在初始化时,需要设置captureSize每次采样的数据量,通过Visualizer.getCaptureSizeRange()[1]来获取。另外,获取采样率Visualizer.getMaxCaptureRate() / 2,注册数据监听接口回调,然后调用visualizer.setEnabled(true)来开启音频数据的采集处理。具体接口如下:

open fun setDataCaptureListener(
    listener: Visualizer.OnDataCaptureListener!, 
    rate: Int, 
    waveform: Boolean, 
    fft: Boolean
): Int

3、数据的获取与显示

音频内容的展示有两种捕获类型:

方波数据:连续8位(无符号)单声道采样,调用getWaveform(byte[] waveform)方法。

频率数据:8位幅度的FFT傅立叶变换,调用getFFT(byte[] fft)方法。

4、释放资源

在音乐播放结束时,需要调用释放可视化频谱的相关资源。先关闭可视化频谱器,注销监听接口,最后调用release来释放资源。需要注意的是,我们在release之前,先注销监听接口,不然有些机型(比如vivo的一些特定机型)会报错。

int captureRate = Visualizer.getMaxCaptureRate() / 2;
    visualizer.setEnabled(false);
    visualizer.setDataCaptureListener(null, captureRate, false, false);
    visualizer.release();

至此,关于音效处理的均衡器、混响、重低音、音量增强、可视化频谱介绍完毕。