美文网首页
android 用speex做回音消除

android 用speex做回音消除

作者: 老祝读书 | 来源:发表于2021-03-18 11:06 被阅读0次

Android回音消除困扰了我将近一个星期终于解决了。

项目一边是使用Java FX的PC端,一边是Android设备进行实时语言通话。

废话不多说直接上代码。
speex_jni.cpp

#include <jni.h>
#include <string.h>
#include <unistd.h>

#include <speex/speex.h>
#include <speex/speex_preprocess.h>
#include <speex/speex_echo.h>
#include<Android/log.h>

SpeexEchoState *st;
SpeexPreprocessState *den;
extern "C"
JNIEXPORT void JNICALL Java_com_e_voice_audio_Speex_open
        (JNIEnv *env, jobject jObj, jint jSampleRate, jint jBufSize, jint jTotalSize)
{
    int sampleRate=jSampleRate;


    st = speex_echo_state_init(jBufSize, jTotalSize);
    den = speex_preprocess_state_init(jBufSize, sampleRate);
    speex_echo_ctl(st, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);

    speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_ECHO_STATE, st);
    speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_DENOISE, st);
    speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_DEREVERB, st);


}
extern "C"
JNIEXPORT jshortArray JNICALL Java_com_e_voice_audio_Speex_process
        (JNIEnv * env, jobject jObj, jshortArray input_frame, jshortArray echo_frame)
{

    //create native shorts from java shorts
    jshort *native_input_frame = env->GetShortArrayElements(input_frame, 0);
    jshort *native_echo_frame = env->GetShortArrayElements(echo_frame, 0);

    //allocate memory for output data
    jint length = env->GetArrayLength(input_frame);
    jshortArray temp = env->NewShortArray(length);
    jshort *native_output_frame = env->GetShortArrayElements(temp, 0);

    //call echo cancellation
    speex_echo_cancellation(st, native_input_frame, native_echo_frame, native_output_frame);
    //preprocess output frame
    speex_preprocess_run(den, native_output_frame);

    //convert native output to java layer output
    jshortArray output_shorts = env->NewShortArray(length);
    env->SetShortArrayRegion(output_shorts, 0, length, native_output_frame);

    //cleanup and return
    env->ReleaseShortArrayElements(input_frame, native_input_frame, 0);
    env->ReleaseShortArrayElements(echo_frame, native_echo_frame, 0);
    env->ReleaseShortArrayElements(temp, native_output_frame, 0);

    return output_shorts;
}

extern "C"
JNIEXPORT void JNICALL Java_com_e_voice_audio_Speex_playback
        (JNIEnv *env, jobject jObj, jshortArray echo_frame)
{
    jshort *native_echo_frame = env->GetShortArrayElements(echo_frame, 0);
    speex_echo_playback(st, native_echo_frame);
    env->ReleaseShortArrayElements(echo_frame, native_echo_frame, 0);
}
extern "C"
JNIEXPORT jshortArray JNICALL Java_com_e_voice_audio_Speex_capture
        (JNIEnv *env, jobject jObj, jshortArray input_frame)
{
    env->MonitorEnter(jObj);
    jshort *native_input_frame = env->GetShortArrayElements(input_frame, 0);

    jint length = env->GetArrayLength(input_frame);
    jshortArray temp = env->NewShortArray(length);
    jshort *native_output_frame = env->GetShortArrayElements(temp, 0);

    speex_echo_capture(st, native_input_frame, native_output_frame);
    speex_preprocess_run(den, native_output_frame);

    jshortArray output_shorts = env->NewShortArray(length);
    env->SetShortArrayRegion(output_shorts, 0, length, native_output_frame);

    env->ReleaseShortArrayElements(input_frame, native_input_frame, 0);
    env->ReleaseShortArrayElements(temp, native_output_frame, 0);
    env->MonitorExit(jObj);
    return output_shorts;


}
extern "C"
JNIEXPORT void JNICALL Java_com_e_voice_audio_Speex_reset(JNIEnv *env, jobject jObj) {
    speex_echo_state_reset(st);
}
extern "C"
JNIEXPORT void JNICALL Java_com_e_voice_audio_Speex_close
        (JNIEnv *env, jobject jObj)
{
    speex_echo_state_destroy(st);
    speex_preprocess_state_destroy(den);
    st = 0;
    den = 0;
}

Speex.java



import com.e.voice.util.Constants;

/**
 * 回音消除
 */

public class Speex {

    static {
        try {
            System.loadLibrary("speex");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private static Speex speex = null;

    private Speex() {
        init();
    }

    public static Speex getInstance() {
        if (speex == null) {
            synchronized (Speex.class) {
                if (speex == null) {
                    speex = new Speex();
                }
            }
        }
        return speex;
    }


    public void init() {
       //8000,620,160*25
        open(8000,620,160*25);
    }

    public void closeS(){
        speex.reset();
        speex.close();
        speex = null;
    }

    public native void close();
    public native void open(int jSampleRate, int jBufSize, int jTotalSize );
    public native short[] process(short[] recordArray, short[] playArray);
    public native void reset();
    public native void  playback(short[] playArray);
    public native short[]  capture(short[] recordArray);


}

播放录音




/**
 * AudioTrack音频播放,录音
 *
 * @author Robbie
 */
public class Tracker extends JobHandler {

    private static AudioTrack audioTrack;


    private AudioRecord audioRecord;
    // 音频大小
    private int inAudioBufferSize;
    // 音频大小
    private static int outAudioBufferSize;
    // 播放标志
    private boolean isPlaying = true;

    public Tracker(Handler handler) {
        super(handler);
        outAudioBufferSize = 1280;
        inAudioBufferSize = 1280;
        this.init();
        initAudioTrace(AudioManager.AUDIO_SESSION_ID_GENERATE);
    }

    public static void initAudioTrace(int sessionId) {
        audioTrack = new AudioTrack((new AudioAttributes.Builder())
                .setLegacyStreamType(Constants.streamType)
                .build(),
                (new AudioFormat.Builder())
                        .setChannelMask(Constants.outputChannelConfig)
                        .setEncoding(Constants.audioFormat)
                        .setSampleRate(Constants.sampleRateInHz)
                        .build(),
                outAudioBufferSize,
                Constants.trackMode, sessionId);


        audioTrack.setVolume(1.0f);
    }

    public boolean isPlaying() {
        return isPlaying;
    }

    public void setPlaying(boolean playing) {
        isPlaying = playing;
    }

    public void init() {
        //inAudioBufferSize =AudioRecord.getMinBufferSize(
        //Constants.sampleRateInHz, Constants.inputChannelConfig, Constants.audioFormat)*2;
        // 初始化音频录制
        audioRecord = new AudioRecord(Constants.audioSource,
                Constants.sampleRateInHz, Constants.inputChannelConfig, Constants.audioFormat, inAudioBufferSize);


    }


    @Override
    public void run() {
        AudioData currentAudioData;

        while ((currentAudioData = MessageQueue.getInstance(MessageQueue.TRACKER_DATA_QUEUE).take()) != null) {

            if (!IntercomService.connecting) {
                break;
            }
            
            if (audioTrack == null) {
                continue;
            }
            try {
                if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
                    audioTrack.play();
                }
                short[] bytesPkg = currentAudioData.getRawData();
                if (bytesPkg != null) {
                    AudioDataUtil.playback(bytesPkg);
                    audioTrack.write(bytesPkg, 0, bytesPkg.length);
                }
                this.reacord(bytesPkg);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    private void reacord(short[] bytesPkg) {
        if(audioRecord == null){
            this.init();
        }
        if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
            audioRecord.startRecording();
        }

        AudioData audioData = new AudioData();
        // 实例化音频数据缓冲
        short[] rawData = new short[inAudioBufferSize / 2];

        audioRecord.read(rawData, 0, inAudioBufferSize / 2);

        if (bytesPkg != null) {

            short[] recordData = AudioDataUtil.capture(rawData);

            audioData.setRawData(recordData);
        } else {
            audioData.setRawData(rawData);
        }

        MessageQueue.getInstance(MessageQueue.ENCODER_DATA_QUEUE).put(audioData);
    }

    @Override
    public void free() {
        if (audioTrack != null) {
            audioTrack.flush();
            audioTrack.stop();
            audioTrack.release();
            audioTrack = null;
        }


        if (audioRecord != null) {
            audioRecord.stop();
            audioRecord.release();
            AcousticEchoCancelerUtil.getInstance().release();
            audioRecord = null;
        }

    }
}


相关文章

  • android 用speex做回音消除

    Android回音消除困扰了我将近一个星期终于解决了。 项目一边是使用Java FX的PC端,一边是Android...

  • android 用WebRTC做回音消除

    之前使用speex做回音消除,不知道是没有用对还是其他什么原因,导致回音消除的效果非常差。 好在在GitHub找到...

  • Android 回音消除(AcousticEchoCancele

    回音消除Google 开发文档原文: 场景就是在手机播放声音和声音录制同时进行,但是手机播放的声音不会被本机录制,...

  • [转]speex

    speex官方文档 编解码器列表 speex官方下载 Speex 同时适用于 Ios 与 Android 系列 音...

  • Android中使用Speex

    Speex 这是一个Android Studio工程,集成了speex库,可以将音频编码成speex格式,也可以将...

  • Speex 双声道回声消除中frame_size的含义

    Speex中的回声消除默认是按单声道处理的。那么Speex支持直接对双声道的数据进行回声消除吗?答案是肯定的,10...

  • Speex回声消除

    1. 概述 Speex AEC是开源的回声消除代码。 回声消除框架图如下 AEC一般的方法使用自适应FIR滤波器设...

  • 回音消除技术概述

    一、 回音消除技术的基础概念 回音消除已经替代了早期的回音抑制,回音抑制最早始于20世纪50年代,在卫星通讯环境中...

  • 语音开源库有哪些

    1.iLBC http://code.google.com/p/android-ilbc/ 2.speex htt...

  • 音视频笔记1

    关于 Speex Speex官网:http://speex.org/由于语音对话编解码需要一个免费的开源软件,所以...

网友评论

      本文标题:android 用speex做回音消除

      本文链接:https://www.haomeiwen.com/subject/sdyucltx.html