前言

本来我只是个纯写一般app的码农,奈何进了这家以音视频为主的公司。刚进公司时,还有好些牛人都没有离职(在
音视频方面浸淫以久的同事)。那关于音视频方面的什么传输库,h264,aac编解码等方面的事,有专门的人员来写和维
护,我作为移动端的开发人员,只需要用他们提供的传输库,使用他们在pc端的功能模块就可以了。我也可以快乐的
下班。可公司发展不景气(没有挣到钱),接下来你们懂的。(人员各种流失。。。)最后,移动原生开发的就剩下
两人(我也算在里面)。ios方面的开发,全走了。交接工作全给了我了,OMY,我对苹果开发可是啥也不会呀。只希
望公司能接下来招几个搞ios的人吧。。。可是过了很长的时间,都没有招人,慢慢的,我也了解到领导就没有再招人
的意思。。很明显,交接给你了(我),那么ios的工作就你来干了。。。。我真是。。。。好吧,说远了!回到主
题,因为有些android手机对回声消除不太好,老板就要求我能不能解决下这个问题。我去,我是谁,,我在哪。。我
不会呀。但总不能直接说不会吧。只能硬着头皮上了。。

上网

不会没有关系,网上有很多相关的文章,经过查找知道手机端可用方案大概有如下几种:

  • AcousticEchoCanceler(API4.1提供,部分手机不支持)
  • AudioSource.VOICE_COMMUNICATION(使用voip通道录音,不靠谱)
  • webrtc(业界有名的回声消除库,支持跨平台,难点是计算delay时间)
  • SpeexDsp

参考文章:

  • https://blog.csdn.net/badongdyc/article/details/73555007
  • https://www.pianshen.com/article/43212237/
  • https://blog.csdn.net/jiaoyangwuze/article/details/50618508

先谈speex

 说干就干,反正我自己现在(以后)也是没有能力写出回声消除算法的(那都是天才巨人干的事)。我能做的就是站在巨人的肩上。

下载speexdsp

speex现在分成两部分了,主要的speex包含编码解码 ,另外一部分声学处理就是speexdsp专门用来处理回声消除
下传送门:
在这里插入图片描述

解压源代码

在这里插入图片描述

将用红色箭头标识的两文件夹复制到android工程的cpp(jni)目录下的speexdsp目录中:

在这里插入图片描述

这里注意了

复制都完成了,就把libseexdsp中的几个test文件删除,不然会有些麻烦,不删除也没有问题哈。主要是这几个test文件都有main函数。

另外,还得在使用我另外编译iconv中使用的方式先configurate下,生成config.h文件。把这个config.h文件也复制到speexdsp目录的include目录中

export NDK=/Users/xxx/Library/Android/android-ndk-r20

export ANDROID_NDK=/Users/xxx/Library/Android/android-ndk-r20

export NDK=/Users/xxx/Library/Android/android-ndk-r20


export PATH=${PATH}:${ANDROID_NDK}

export PATH=${PATH}:${NDK}

export ANDROID_TOOLS=/Users/xxx/Library/Android/sdk/platform-tools

export ANDROID_HOME=/Users/xxx/Library/Android/sdk


export PATH=${PATH}:${ANDROID_HOME}

export ANDROID_TOOLS

export PATH=${PATH}:${ANDROID_TOOLS}

#PATH=/bin:/usr/bin:/usr/local/bin:${PATH} 

# darwin-x86_64 是Mac的路径,实际按目录结构修改
export PATH="$NDK:$NDK/shader-tools/darwin-x86_64:$NDK/build/tools:$PATH"
# 预设值,待会才生成的目录
export SYSROOT=~/my-android-toolchain/sysroot
export PATH=~/my-android-toolchain/bin:$PATH
export CC=arm-linux-androideabi-gcc   # or export CC=clang
export CXX=arm-linux-androideabi-g++  # or export CXX=clang++
export CXXFLAGS="-lstdc++"
export CPPFLAGS="-lstdc++"
export CC="arm-linux-androideabi-gcc --sysroot=$SYSROOT"
export LD="arm-linux-androideabi-ld"
export AR="arm-linux-androideabi-ar"
export RANLIB="arm-linux-androideabi-ranlib"
export STRIP="arm-linux-androideabi-strip"
export CPP="arm-linux-androideabi-gcc -E"

cd ~

mkdir my-android-toolchain

$NDK/build/tools/make-standalone-toolchain.sh  --arch=arm  --install-dir=~/my-android-toolchain --platform=android-21 --stl=gnustl 

cd ~/Downloads/speexdsp-1.2rc3

./configurate --host=arm-linux-eabi

接下来就是编写CMakeLists.txt及jni中间文件

  1. audio_echo.h
//
// Created by vnetoo on 2020/5/8.
//
#include  "echo_config.h"
#ifndef SPEEX_AUDIO_ECHO_H
#define SPEEX_AUDIO_ECHO_H


#define NN 128           //帧长      一般都是  80,160,320,
#define TAIL 1024        //尾长      一般都是  80*25 ,160*25 ,320*25

#define ECHO_ERROR -1
#define ECHO_OK 0

// defined speexEcho struct
typedef struct SpeexEcho{
  SpeexEchoState* st;
  SpeexPreprocessState* den;
  int aec_init_flag;
  pthread_mutex_t mutex;
  int enable_echo;
  int frame_size;
  int enable_vad;
}SpeexEcho;


/**
* 初始化回声消除
* @param context
* @param frameSize
* @param filterSize
* @param sampleRate
* @return
*/
int echo_init(SpeexEcho* context,const unsigned short frameSize ,const unsigned short filterSize,const unsigned short sampleRate);

/**
* 设置回声消除参数
* @param context
* @param key
* @param value
* @ 0/1
*/
int echo_set_ctl(SpeexEcho* context,const unsigned short key , int value);

/**
* 获取回声消除器的参数
* @param context
* @param key
* @param result
* @return
*/
int echo_get_ctl(SpeexEcho* context,const unsigned short key , void* result);

/***
* 设置噪声参数
* @param context
* @param key
* @param value
* @return
*/
int echo_set_preprocess_ctl(SpeexEcho* context,const unsigned short key , void* value);



int echo_get_preprocess_ctl(SpeexEcho* context,const unsigned short key , void* value);

/***
* 处理数据
* @param context
* @param near mic数据
* @param far  播放数据
* @param out  处理之后的数据
* @return
*/
int echo_sync_process(const SpeexEcho* context,unsigned short* near,unsigned short* far, unsigned short* out,size_t size);

int echo_denoise_only(const SpeexEcho* context,unsigned short* near);


int echo_asyn_process_near(const SpeexEcho* context,unsigned short* near,unsigned short *out);

int echo_asyn_process_far(const SpeexEcho* context,unsigned short* far);

/**
* 清除,释放
*/
void echo_distroy(SpeexEcho* context);


#endif //SPEEX_AUDIO_ECHO_H
  1. audio_echo.c
//
// Created by vnetoo on 2020/5/8.
//

#include "include/audio_echo.h"

int echo_init(SpeexEcho *context, const unsigned short frameSize, const unsigned short filterSize,
             const unsigned short sampleRate) {

   pthread_mutex_lock(&(context->mutex));

   unsigned short fs = frameSize;
   if (fs <= 0) {
       return ECHO_ERROR;
   }

   unsigned short fl = filterSize;

   if (fl <= 0) {
       return ECHO_ERROR;
   }

   int mSampleRate = sampleRate;

   if(mSampleRate<=0){
       return ECHO_ERROR;
   }

   context->st = speex_echo_state_init(fs, fl);
   ASSERT(context->st != NULL, "init echo error");
   if (context->st == NULL) {
       LOGE("init echo error with frameSize:%d,filterSize:%d", fs, fl);
       return ECHO_ERROR;
   }
   context->den = speex_preprocess_state_init(fs, sampleRate);
   ASSERT(context->den != NULL, "init preprocess with sampleRate: %d, error", mSampleRate);

   int flag = -1;

   flag = speex_echo_ctl(context->st, SPEEX_ECHO_SET_SAMPLING_RATE, &mSampleRate);

   ASSERT(flag == 0, "set sampleRate for echo error");

   flag = speex_preprocess_ctl(context->den, SPEEX_PREPROCESS_SET_ECHO_STATE, context->st);

   ASSERT(flag == 0, "set  aec proprocess to st error")

   context->aec_init_flag = flag == 0 ? 1 : 0;
   pthread_mutex_unlock(&(context->mutex));
   LOGI("init speex aec success");
   return ECHO_OK;

}


int echo_set_ctl(SpeexEcho *context, const unsigned short key, int value) {
   ASSERT(context != NULL, "speexEcho is NULL")
   if (context != NULL && context->aec_init_flag == 1) {
       int result = speex_echo_ctl(context->st, key, &value);
       if (result == 0) {
           return ECHO_OK;
       } else {
           LOGE("set speex_echo_ctl(%d,%d) unknow key", key, value);
       }

   }
   return ECHO_ERROR;
}

int echo_get_ctl(SpeexEcho *context, const unsigned short key, void *result) {
   ASSERT(context != NULL, "speexEcho is NULL")
   if (context != NULL && context->aec_init_flag == 1) {
       int result = speex_echo_ctl(context->st, key, &result);
       if (result == 0) {
           return ECHO_OK;
       } else {
           LOGE("get speex_echo_ctl(%d,%d) unknow key", key, result);
       }

   }
   return ECHO_ERROR;
}

int echo_set_preprocess_ctl(SpeexEcho *context, const unsigned short key, void *value) {
   ASSERT(context != NULL, "speexEcho is NULL")
   if (context != NULL && context->aec_init_flag == 1) {
       pthread_mutex_lock(&(context->mutex));
       int result = speex_preprocess_ctl(context->den, key, value);
       if(key == SPEEX_PREPROCESS_SET_NOISE_SUPPRESS){
           int* v = (int*)value;
           LOGI("SPEEX_PREPROCESS_SET_NOISE_SUPPRESS=>%d result:%d",*v,result);

       }else if(key == SPEEX_PREPROCESS_SET_DENOISE){
           int* v = (int*)value;
           LOGI("SPEEX_PREPROCESS_SET_DENOISE=>%d result:%d",*v,result);
       }
       else if(key == SPEEX_PREPROCESS_SET_AGC){
           int* v = (int*)value;
           LOGI("SPEEX_PREPROCESS_SET_AGC=>%d result:%d",*v,result);
       }else if(key == SPEEX_PREPROCESS_SET_DEREVERB){
           int* v = (int*)value;
           LOGI("SPEEX_PREPROCESS_SET_DEREVERB=>%d result:%d",*v,result);
       }else if(key == SPEEX_PREPROCESS_SET_VAD){
           int* v = (int*)value;
           &context->enable_vad == *v;
           LOGI("SPEEX_PREPROCESS_SET_VAD=>%d result:%d",*v,result);
       }else if(key == SPEEX_PREPROCESS_SET_NOISE_SUPPRESS){
           int* v = (int*)value;
           LOGI("SPEEX_PREPROCESS_SET_NOISE_SUPPRESS=>%d result:%d",*v,result);
       }else if(key == SPEEX_PREPROCESS_SET_AGC_LEVEL){
           int* v = (int*)value;
           LOGI("SPEEX_PREPROCESS_SET_AGC_LEVEL=>%d result:%d",*v,result);
       }
       pthread_mutex_unlock(&(context->mutex));
       if (result == 0) {
           return ECHO_OK;
       } else {
           LOGE("set echo_set_preprocess_ctl(%d) unknow key", key);
       }
   }
   return ECHO_ERROR;
}

int echo_get_preprocess_ctl(SpeexEcho *context, const unsigned short key, void *result) {
   ASSERT(context != NULL, "speexEcho is NULL")
   if (context != NULL && context->aec_init_flag == 1) {
       int ret = speex_preprocess_ctl(context->den, key, result);
       if (ret == 0) {
           return ECHO_OK;
       } else {
           LOGE("get echo_set_preprocess_ctl(%d) unknow key", key);
       }
   }
   return ECHO_ERROR;
}

int echo_sync_process(const SpeexEcho *context, unsigned short *near, unsigned short *far,
                     unsigned short *out,size_t size) {
   ASSERT(context != NULL, "speexEcho is NULL")
   ASSERT(near != NULL, "mic record data is NULL")
   ASSERT(far != NULL, "playData is NULL")
   ASSERT(out != NULL, "outputBuffer is NULL")

   if (context != NULL && context->aec_init_flag == 1) {
       pthread_mutex_lock(&(context->mutex));
       // speex_preprocess_run(context->den, far);
       speex_echo_cancellation(context->st, near, far, out);
       int result = speex_preprocess_run(context->den, out);
       pthread_mutex_unlock(&(context->mutex));
       return result;
   }
   return ECHO_ERROR;

}

int echo_denoise_only(const SpeexEcho* context,unsigned short* near){
   ASSERT(near != NULL, "near data is NULL")
   pthread_mutex_lock(&(context->mutex));
   int result = speex_preprocess_run(context->den, near);
   pthread_mutex_unlock(&(context->mutex));
   return result;
}

int echo_asyn_process_near(const SpeexEcho* context,unsigned short* near,unsigned short *out){
   if (context != NULL && context->aec_init_flag == 1) {
       pthread_mutex_lock(&(context->mutex));
       speex_echo_capture(context->st,near,out);
       pthread_mutex_unlock(&(context->mutex));
       return ECHO_OK;
   }
   return ECHO_ERROR;
}

int echo_asyn_process_far(const SpeexEcho* context,unsigned short* far){
   if (context != NULL && context->aec_init_flag == 1) {
       pthread_mutex_lock(&(context->mutex));
       speex_echo_playback(context->st,far);
       pthread_mutex_unlock(&(context->mutex));
       return ECHO_OK;
   }
   return ECHO_ERROR;
}

void echo_distroy(SpeexEcho *context) {
   if (context != NULL && context->aec_init_flag == 1) {
       pthread_mutex_lock(&(context->mutex));
       speex_preprocess_state_destroy(context->den);
       speex_echo_state_destroy(context->st);
       context->aec_init_flag = 0;
       pthread_mutex_unlock(&(context->mutex));
       LOGE("release speex echo");
   }
}

  1. echo_jni.c
//
// Created by vnetoo on 2020/5/8.
//

#include <stdlib.h>
#include <assert.h>
#include <jni.h>
#include <string.h>
#include <pthread.h>

#include "include/audio_echo.h"

#define ClassName "com/vnetoo/speex/echo/SpeexEcho"

#define EchoPk(method) Java_com_vnetoo_speex_echo_SpeexEcho_##method

static SpeexEcho echo;

static short *out;

int debug = 1;

inline void throwException(JNIEnv *env, const char *clasz, const char *errorMsg);

void throwException(JNIEnv *env, const char *clasz, const char *errorMsg) {
   jclass newExcCls;

   (*env)->ExceptionDescribe(env);

   (*env)->ExceptionClear(env);

   newExcCls = (*env)->FindClass(env, clasz);
   if (newExcCls != NULL) {
       (*env)->ThrowNew(env, newExcCls, errorMsg);
   }

}


JNIEXPORT jint JNICALL
EchoPk(aecInit)(JNIEnv *env, jclass jclasz, jint frameSize, jint filterSize, jint sampleRate) {

   int result = echo_init(&echo, frameSize, filterSize, sampleRate);

   if (result == ECHO_ERROR) {

       throwException(env, "java/lang/IllegalStateException", "init aec error");

   }

   echo.frame_size = frameSize;
   LOGI("speex jni init aec success");

   return result;
}

JNIEXPORT jint JNICALL
EchoPk(aec_1set_1feature)(JNIEnv *env, jclass jclasz, jint key, jint value) {
   if (echo.aec_init_flag) {
       return echo_set_ctl(&echo, key, value);
   }
   return ECHO_ERROR;
}


JNIEXPORT jint JNICALL
EchoPk(aec_1set_1other_1feature__II)(JNIEnv *env, jclass clazz,
                                    jint key, jint value) {
   if (echo.aec_init_flag) {
       int v = value;
       return echo_set_preprocess_ctl(&echo, key, &v);
   }
   return ECHO_ERROR;
}

JNIEXPORT jint JNICALL
EchoPk(aec_1set_1other_1feature__IF)(JNIEnv *env, jclass clazz,
                                    jint key, jfloat value) {
   if (echo.aec_init_flag) {
       float v = value;
       return echo_set_preprocess_ctl(&echo, key, &v);
   }
   return ECHO_ERROR;
}

JNIEXPORT jint JNICALL
EchoPk(aec_1process)(JNIEnv *env, jclass clazz, jbyteArray near,
                    jbyteArray far,jbyteArray out) {
   jbyteArray ret = NULL;
   jbyte *nearData = (*env)->GetByteArrayElements(env, near, JNI_FALSE);
   jbyte *fearData = (*env)->GetByteArrayElements(env, far, JNI_FALSE);
   jbyte *outData = (*env)->GetByteArrayElements(env, out, JNI_FALSE);
   int result = 0;
   bool needRet = false;
   if(echo.enable_echo == 1){
       result= echo_sync_process(&echo, nearData, fearData, outData, echo.frame_size);
       LOGI("echo canncel :%d", result );
   }
   else{
       result = echo_denoise_only(&echo,nearData);
       LOGI("echo_denoise_only:%d", result );
   }
   (*env)->ReleaseByteArrayElements(env, near, nearData, JNI_FALSE);
   (*env)->ReleaseByteArrayElements(env, far, fearData, JNI_FALSE);
   (*env)->ReleaseByteArrayElements(env, out, outData, JNI_FALSE);
   return result;
}

JNIEXPORT void JNICALL
EchoPk(destory)(JNIEnv *env, jclass clazz) {
   // pthread_mutex_lock(&(echo.mutex));
   echo_distroy(&echo);
   free(out);
   // pthread_mutex_unlock(&(echo.mutex));
   out = NULL;
}

JNIEXPORT jint JNICALL
EchoPk(aec_1set_1enable_1echo)(JNIEnv *env, jclass clazz,
                              jboolean enable) {
   pthread_mutex_lock(&(echo.mutex));
   echo.enable_echo = enable ? 1 : 0;
   LOGI("echo.enable_echo=%d", echo.enable_echo);
   pthread_mutex_unlock(&(echo.mutex));
   return ECHO_OK;
}

JNIEXPORT jfloat JNICALL
EchoPk(aec_1get_1other_1float_1feature)(JNIEnv *env, jclass clazz,

                                       jint key) {
   pthread_mutex_lock(&(echo.mutex));
   float value = 0;
   echo_get_preprocess_ctl(&(echo),key,&value);
   pthread_mutex_unlock(&(echo.mutex));
   return value;
}

JNIEXPORT jint JNICALL
EchoPk(aec_1get_1other_1int_1feature)(JNIEnv *env, jclass clazz,
                                     jint key) {
   pthread_mutex_lock(&(echo.mutex));
   int  value = 0;
   echo_get_preprocess_ctl(&(echo),key,&value);
   pthread_mutex_unlock(&(echo.mutex));
   return value;
}

JNIEXPORT jint JNICALL
Java_com_vnetoo_speex_echo_SpeexEcho_ProcessStream(JNIEnv *env, jclass clazz, jbyteArray data,
                                                  jint offset, jint len) {


   jbyte *nearData = (*env)->GetByteArrayElements(env, data, JNI_FALSE);
       short*  out = (short*)malloc(len);
       memset(out,0,len);
       echo_asyn_process_near(&echo,nearData,out);
       memcpy(nearData,out,len);
       free(out);

   (*env)->ReleaseByteArrayElements(env, data, nearData, JNI_FALSE);
   return 0;
}

JNIEXPORT jint JNICALL
Java_com_vnetoo_speex_echo_SpeexEcho_ProcessReverseStream(JNIEnv *env, jclass clazz,
                                                         jbyteArray far_end, jint offset,
                                                         jint len) {

   jbyte *nearData = (*env)->GetByteArrayElements(env, far_end, JNI_FALSE);

       echo_asyn_process_far(&echo,nearData);

   (*env)->ReleaseByteArrayElements(env, far_end, nearData, JNI_FALSE);
   return 0;
}
  1. SpeexEcho.java
package com.vnetoo.speex.echo;

class SpeexEcho {

   public static native int aecInit(int frame_size, int filter_length, int sampling_rate) throws IllegalStateException;

   public static native  int aec_set_enable_echo(boolean enable);

   public static native int aec_set_feature(int key,int value);

   public static native int aec_set_other_feature(int key,int  value);

   public static native int aec_set_other_feature(int key,float value);

   public static native float aec_get_other_float_feature(int key);

   public static native int  aec_get_other_int_feature(int key);

   public static native int aec_process(byte[] near, byte[] far,byte[] out);

   public static  native int ProcessStream(byte[] data,int offset, int len);

   public static  native int ProcessReverseStream(byte[] far_end,int offset, int len);




   public static native void destory();

   // public static native int aec_get_feature(int key);

   /**
    * Set preprocessor denoiser state
    */
   public static final int SPEEX_PREPROCESS_SET_DENOISE = 0;
   /**
    * Get preprocessor denoiser state
    */
   public static final int SPEEX_PREPROCESS_GET_DENOISE = 1;

   /**
    * Set preprocessor Automatic Gain Control state
    */
   public static final int SPEEX_PREPROCESS_SET_AGC = 2;
   /**
    * Get preprocessor Automatic Gain Control state
    */
   public static final int SPEEX_PREPROCESS_GET_AGC = 3;

   /**
    * Set preprocessor Voice Activity Detection state
    */
   public static final int SPEEX_PREPROCESS_SET_VAD = 4;
   /**
    * Get preprocessor Voice Activity Detection state
    */
   public static final int SPEEX_PREPROCESS_GET_VAD = 5;

   /**
    * Set preprocessor Automatic Gain Control level (float)
    */
   public static final int SPEEX_PREPROCESS_SET_AGC_LEVEL = 6;
   /**
    * Get preprocessor Automatic Gain Control level (float)
    */
   public static final int SPEEX_PREPROCESS_GET_AGC_LEVEL = 7;

   /**
    * Set preprocessor dereverb state
    */
   public static final int SPEEX_PREPROCESS_SET_DEREVERB = 8;
   /**
    * Get preprocessor dereverb state
    */
   public static final int SPEEX_PREPROCESS_GET_DEREVERB = 9;

   /**
    * Set preprocessor dereverb level
    */
   public static final int SPEEX_PREPROCESS_SET_DEREVERB_LEVEL = 10;
   /**
    * Get preprocessor dereverb level
    */
   public static final int SPEEX_PREPROCESS_GET_DEREVERB_LEVEL = 11;

   /**
    * Set preprocessor dereverb decay
    */
   public static final int SPEEX_PREPROCESS_SET_DEREVERB_DECAY = 12;
   /**
    * Get preprocessor dereverb decay
    */
   public static final int SPEEX_PREPROCESS_GET_DEREVERB_DECAY = 13;

   /**
    * Set probability required for the VAD = to go from silence to voice
    */
   public static final int SPEEX_PREPROCESS_SET_PROB_START = 14;
   /**
    * Get probability required for the VAD = to go from silence to voice
    */
   public static final int SPEEX_PREPROCESS_GET_PROB_START = 15;

   /**
    * Set probability required for the VAD = to stay in the voice state (integer percent)
    */
   public static final int SPEEX_PREPROCESS_SET_PROB_CONTINUE = 16;
   /**
    * Get probability required for the VAD = to stay in the voice state (integer percent)
    */
   public static final int SPEEX_PREPROCESS_GET_PROB_CONTINUE = 17;

   /**
    * Set maximum attenuation of the noise in dB = (negative number)
    */
   public static final int SPEEX_PREPROCESS_SET_NOISE_SUPPRESS = 18;
   /**
    * Get maximum attenuation of the noise in dB = (negative number)
    */
   public static final int SPEEX_PREPROCESS_GET_NOISE_SUPPRESS = 19;

   /**
    * Set maximum attenuation of the residual echo in dB = (negative number)
    */
   public static final int SPEEX_PREPROCESS_SET_ECHO_SUPPRESS = 20;
   /**
    * Get maximum attenuation of the residual echo in dB = (negative number)
    */
   public static final int SPEEX_PREPROCESS_GET_ECHO_SUPPRESS = 21;

   /**
    * Set maximum attenuation of the residual echo in dB = when near end is active (negative number)
    */
   public static final int SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE = 22;
   /**
    * Get maximum attenuation of the residual echo in dB = when near end is active (negative number)
    */
   public static final int SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE = 23;

   /**
    * Set the corresponding echo canceller state so that residual echo suppression can be performed (NULL = for no residual echo suppression)
    */
   public static final int SPEEX_PREPROCESS_SET_ECHO_STATE = 24;
   /**
    * Get the corresponding echo canceller state
    */
   public static final int SPEEX_PREPROCESS_GET_ECHO_STATE = 25;

   /**
    * Set maximal gain increase in dB/second (int32)
    */
   public static final int SPEEX_PREPROCESS_SET_AGC_INCREMENT = 26;

   /**
    * Get maximal gain increase in dB/second (int32)
    */
   public static final int SPEEX_PREPROCESS_GET_AGC_INCREMENT = 27;

   /**
    * Set maximal gain decrease in dB/second (int32)
    */
   public static final int SPEEX_PREPROCESS_SET_AGC_DECREMENT = 28;

   /**
    * Get maximal gain decrease in dB/second (int32)
    */
   public static final int SPEEX_PREPROCESS_GET_AGC_DECREMENT = 29;

   /**
    * Set maximal gain in dB = (int32)
    */
   public static final int SPEEX_PREPROCESS_SET_AGC_MAX_GAIN = 30;

   /**
    * Get maximal gain in dB = (int32)
    */
   public static final int SPEEX_PREPROCESS_GET_AGC_MAX_GAIN = 31;

   /*  Can't set loudness */
   /**
    * Get loudness
    */
   public static final int SPEEX_PREPROCESS_GET_AGC_LOUDNESS = 33;

   /*  Can't set gain */
   /**
    * Get current gain (int32 percent)
    */
   public static final int SPEEX_PREPROCESS_GET_AGC_GAIN = 35;

   /*  Can't set spectrum size */
   /**
    * Get spectrum size for power spectrum (int32)
    */
   public static final int SPEEX_PREPROCESS_GET_PSD_SIZE = 37;

   /*  Can't set power spectrum */
   /**
    * Get power spectrum (int32[] of squared values)
    */
   public static final int SPEEX_PREPROCESS_GET_PSD = 39;

   /*  Can't set noise size */
   /**
    * Get spectrum size for noise estimate (int32)
    */
   public static final int SPEEX_PREPROCESS_GET_NOISE_PSD_SIZE = 41;

   /*  Can't set noise estimate */
   /**
    * Get noise estimate (int32[] of squared values)
    */
   public static final int SPEEX_PREPROCESS_GET_NOISE_PSD = 43;

   /* Can't set speech probability */
   /**
    * Get speech probability in last frame (int32).
    */
   public static final int SPEEX_PREPROCESS_GET_PROB = 45;

   /**
    * Set preprocessor Automatic Gain Control level (int32)
    */
   public static final int SPEEX_PREPROCESS_SET_AGC_TARGET = 46;
   /**
    * Get preprocessor Automatic Gain Control level (int32)
    */
   public static final int SPEEX_PREPROCESS_GET_AGC_TARGET = 47;

}

使用及例子及所有源码

我已经将所有源代码上传于github,各位可以自行下载修改,所有speex支持的参数设置,我已经通过jni公开。只是有很多参数

我也是不得要领,不知道有什么作为!

传送门

speexdsp使用手册

官方例子:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "speex/speex_echo.h"
#include "speex/speex_preprocess.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#define NN 128
#define TAIL 1024

int main(int argc, char **argv)
{
  FILE *echo_fd, *ref_fd, *e_fd;
  short echo_buf[NN], ref_buf[NN], e_buf[NN];
  SpeexEchoState *st;
  SpeexPreprocessState *den;
  int sampleRate = 8000;

  if (argc != 4)
  {
     fprintf(stderr, "testecho mic_signal.sw speaker_signal.sw output.sw\n");
     exit(1);
  }
  echo_fd = fopen(argv[2], "rb");
  ref_fd  = fopen(argv[1],  "rb");
  e_fd    = fopen(argv[3], "wb");

  st = speex_echo_state_init(NN, TAIL);
  den = speex_preprocess_state_init(NN, sampleRate);
  speex_echo_ctl(st, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);
  speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_ECHO_STATE, st);

  while (!feof(ref_fd) && !feof(echo_fd))
  {
     fread(ref_buf, sizeof(short), NN, ref_fd);
     fread(echo_buf, sizeof(short), NN, echo_fd);
     speex_echo_cancellation(st, ref_buf, echo_buf, e_buf);
     speex_preprocess_run(den, e_buf);
     fwrite(e_buf, sizeof(short), NN, e_fd);
  }
  speex_echo_state_destroy(st);
  speex_preprocess_state_destroy(den);
  fclose(e_fd);
  fclose(echo_fd);
  fclose(ref_fd);
  return 0;
}

本地验证呢,这个nn,与tail是最难确认的了。

结束

有人使用speexdsp作了测试,有人说效果可以,实际环境中使用效果如何,有待验证。

  • Speex 回声消除 frame_size和filter_length
  • Speex 回声消除接口介绍
  • 关于speex及speexdsp详细介绍

兄弟点个赞!!

原文链接:https://blog.csdn.net/q384264619/article/details/106492784

最后修改日期:2020年6月4日