Android 回音消除(AcousticEchoCanceler)的使用

回音消除Google 开发文档原文:

/**
 * Acoustic Echo Canceler (AEC).
 * <p>Acoustic Echo Canceler (AEC) is an audio pre-processor which removes the contribution of the
 * signal received from the remote party from the captured audio signal.
 * <p>AEC is used by voice communication applications (voice chat, video conferencing, SIP calls)
 * where the presence of echo with significant delay in the signal received from the remote party
 * is highly disturbing. AEC is often used in conjunction with noise suppression (NS).
 * <p>An application creates an AcousticEchoCanceler object to instantiate and control an AEC
 * engine in the audio capture path.
 * <p>To attach the AcousticEchoCanceler to a particular {@link android.media.AudioRecord},
 * specify the audio session ID of this AudioRecord when creating the AcousticEchoCanceler.
 * The audio session is retrieved by calling
 * {@link android.media.AudioRecord#getAudioSessionId()} on the AudioRecord instance.
 * <p>On some devices, an AEC can be inserted by default in the capture path by the platform
 * according to the {@link android.media.MediaRecorder.AudioSource} used. The application should
 * call AcousticEchoCanceler.getEnable() after creating the AEC to check the default AEC activation
 * state on a particular AudioRecord session.
 * <p>See {@link android.media.audiofx.AudioEffect} class for more details on
 * controlling audio effects.
 */

场景就是在手机播放声音和声音录制同时进行,但是手机播放的声音不会被本机录制,达到了消除的效果。微信对讲的最适合不过了,但是微信的回音消除好像是不是自己弄的。

文档大致意思:创建android.media.AudioRecord 的对象的时候,可以通过这个对象获取到一个audio session 的ID(获取的方法:getAudioSessionId()),这个ID的话在创建AcousticEchoCanceler的时候用到(创建对象:AcousticEchoCanceler.create(audioSessionId)),最后播放音频的时候(这里是用AudioTrack播放)传入这个ID就行了。

首先创建AudioRecord。这里就不啰嗦里面的参数是干嘛的了,可以自己查资料知道什么是采样率,声道,位数。


    private AudioRecord mRecorder;
    private byte[] pcm;
    private int mRecorderBufferSize;


    /**
     * 初始化录音
     */
    public void initRecorder() {
        mRecorderBufferSize = AudioRecord.getMinBufferSize(8000,
                AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        pcm = new byte[320];
        mRecorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
                mRecorderBufferSize);
    }

初始化AudioTrack(注意下传入的audioSessionId)。

private AudioTrack mAudioTrack;
private int audioSessionId = -1;
private void initAudioTrack() {
        if (mAudioTrack == null) {
            if (audioSessionId == -1) {
                mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, mRecorderBufferSize * 2
                        , AudioTrack.MODE_STREAM);
            } else {
                mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, mRecorderBufferSize * 2
                        , AudioTrack.MODE_STREAM, audioSessionId);
            }
        }
}

最后创建AcousticEchoCanceler

    private AcousticEchoCanceler acousticEchoCanceler;

    private void initAEC() {
        if (AcousticEchoCanceler.isAvailable()) {
            if (acousticEchoCanceler == null) {
                acousticEchoCanceler = AcousticEchoCanceler.create(audioSessionId);
                Log.d(TAG, "initAEC: ---->" + acousticEchoCanceler + "\t" + audioSessionId);
                if (acousticEchoCanceler == null) {
                    Log.e(TAG, "initAEC: ----->AcousticEchoCanceler create fail.");
                } else {
                    acousticEchoCanceler.setEnabled(true);
                }
            }
        }
    }

最后播放的时候就可以了(验证是ok的)


    public void write(byte[] data) {
        if (mAudioTrack != null && mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
            mAudioTrack.write(data, 0, data.length);
        }
    }

推荐阅读更多精彩内容