Speex echo cancellation configuration

前端 未结 2 1744
情书的邮戳
情书的邮戳 2020-12-30 13:30

I am making an Android-to-Android VoIP (loudspeaker) app using its AudioRecord and AudioTrack class, along with Speex via NDK to do echo cancellation. I was able to successf

相关标签:
2条回答
  • 2020-12-30 13:34

    Your code is right but missing something in native codes, I modified init method and added speex preprocess after echo cancellation, then your code worked well (I tried in windows) Here is Native Code

    #include <jni.h>
    #include "speex/speex_echo.h"
    #include "speex/speex_preprocess.h"
    #include "EchoCanceller_jniHeader.h"
    SpeexEchoState *st;
    SpeexPreprocessState *den;
    
    JNIEXPORT void JNICALL Java_speex_EchoCanceller_open
      (JNIEnv *env, jobject jObj, jint jSampleRate, jint jBufSize, jint jTotalSize)
    {
         //init
         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);
    }
    
    JNIEXPORT jshortArray JNICALL Java_speex_EchoCanceller_process
      (JNIEnv * env, jobject jObj, jshortArray input_frame, jshortArray echo_frame)
    {
      //create native shorts from java shorts
      jshort *native_input_frame = (*env)->GetShortArrayElements(env, input_frame, NULL);
      jshort *native_echo_frame = (*env)->GetShortArrayElements(env, echo_frame, NULL);
    
      //allocate memory for output data
      jint length = (*env)->GetArrayLength(env, input_frame);
      jshortArray temp = (*env)->NewShortArray(env, length);
      jshort *native_output_frame = (*env)->GetShortArrayElements(env, 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(env, length);
      (*env)->SetShortArrayRegion(env, output_shorts, 0, length, native_output_frame);
    
      //cleanup and return
      (*env)->ReleaseShortArrayElements(env, input_frame, native_input_frame, 0);
      (*env)->ReleaseShortArrayElements(env, echo_frame, native_echo_frame, 0);
      (*env)->ReleaseShortArrayElements(env, temp, native_output_frame, 0);
    
      return output_shorts;   
    }
    
    JNIEXPORT void JNICALL Java_speex_EchoCanceller_close
      (JNIEnv *env, jobject jObj)
    {
         //close
         speex_echo_state_destroy(st);
         speex_preprocess_state_destroy(den);
    }
    

    You can find useful samples such as Encoding, Decoding, Echo Cancellation in speex library's source (http://www.speex.org/downloads/)

    0 讨论(0)
  • 2020-12-30 13:41

    Are you properly aligning the far-end signal (what you call recv) and near end signal (what you call record)? There is always some playback/record latency which needs to be accounted for. This generally requires buffering of the far-end signal in a ring buffer for some specified period of time. On PCs this is usually about 50 - 120ms. On Android I suspect it's much higher. Probably in the range of 150 - 400ms. I would recommend using a 100ms taillength with speex and adjusting the size of your far-end buffer until the AEC converges. These changes should allow the AEC to converge, independently of the inclusion of the preprocessor, which is not required here.

    0 讨论(0)
提交回复
热议问题