1. 程式人生 > >Speex 之回聲消除

Speex 之回聲消除

     m_bHasInit;
SpeexEchoState*   m_pState;
    SpeexPreprocessState* m_pPreprocessorState;
int      m_nFrameSize;
int      m_nFilterLen;
int      m_nSampleRate;
float*      m_pfNoise;
};

#endif

//fine name:speexEC.cpp
#include "SpeexEC.h"

CSpeexEC::CSpeexEC()
{
m_bHasInit   = false;
m_pState   = NULL;
m_pPreprocessorState  = NULL;
m_nFrameSize   = 160;
m_nFilterLen   = 160*8;
m_nSampleRate   = 8000;
m_pfNoise   = NULL;
}

CSpeexEC::~CSpeexEC()
{
Reset();
}

void CSpeexEC::Init(int frame_size, int filter_length, int sampling_rate)
{
Reset();

if (frame_size<=0 || filter_length<=0 || sampling_rate<=0)
{
  m_nFrameSize  =160;
  m_nFilterLen  = 160*8;
  m_nSampleRate = 8000;
}
else
{
  m_nFrameSize  =frame_size;
  m_nFilterLen  = filter_length;
  m_nSampleRate = sampling_rate;
}

m_pState = speex_echo_state_init(m_nFrameSize, m_nFilterLen);
m_pPreprocessorState = speex_preprocess_state_init(m_nFrameSize, m_nSampleRate);
m_pfNoise = new float[m_nFrameSize+1];
m_bHasInit = true;
}

void CSpeexEC::Reset()
{
if (m_pState != NULL)
{
  speex_echo_state_destroy(m_pState);
  m_pState = NULL;
}
if (m_pPreprocessorState != NULL)
{
  speex_preprocess_state_destroy(m_pPreprocessorState);
  m_pPreprocessorState = NULL;
}
if (m_pfNoise != NULL)
{
  delete []m_pfNoise;
  m_pfNoise = NULL;
}
m_bHasInit = false;
}

void CSpeexEC:DoAEC(short* mic, short* ref, short* out)
{
if (!m_bHasInit)
  return;

speex_echo_cancel(m_pState, mic, ref, out, m_pfNoise);
    speex_preprocess(m_pPreprocessorState, (__int16 *)out, m_pfNoise);
   
}

可以看出,這個回聲消除器類很簡單,只要初始化一下就可以呼叫了。但是,要注意的是,傳給回聲消除器的兩個聲音訊號,必須同步得非常的好,就是說,在B端,接收到A說的話以後,要把這些話音資料傳給回聲消除器做參考,然後再傳給音效卡,音效卡再放出來,這有一段延時,這時,B再採集,然後傳給回聲消除器,與那個參考資料比較,從採集到的資料中把頻域和參考資料相同的部分消除掉。如果傳給消除器的兩個訊號同步得不好,即兩個訊號找不到頻域相同的部分,就沒有辦法進行消除了。
測試程式:

#define NN 160
void main()
{
FILE* ref_fd, *mic_fd, *out_fd;
short ref[NN], mic[NN], out[NN];
ref_fd = fopen ("ref.pcm", "rb"); //開啟參考檔案,即要消除的聲音
mic_fd = fopen ("mic.pcm",  "rb");//開啟mic採集到的聲音檔案,包含回聲在裡面
out_fd = fopen ("echo.pcm", "wb");//消除了回聲以後的檔案

CSpeexEC ec;
ec.Init();

while (fread(mic, 1, NN*2, mic_fd))
   {
      fread(ref, 1, NN*2, ref_fd);  
      ec.DoAEC(mic, ref, out);
      fwrite(out, 1, NN*2, out_fd);
   }
 
   fclose(ref_fd);
   fclose(mic_fd);
   fclose(out_fd);
}

  以上的程式是用檔案來模擬回聲和MIC,但在實時流中是大不一樣的,在一般的VOIP軟體中,接收對方的聲音並傳到音效卡中播放是在一個執行緒中進行的,而採集本地的聲音並傳送到對方又是在另一個執行緒中進行的,而聲學回聲消除器在對採集到的聲音進行回聲消除的同時,還需要播放執行緒中的資料作為參考,而要同步這兩個執行緒中的資料是非常困難的,因為稍稍有些不同步,聲學回聲消除器中的自適應濾波器就會發散,不但消除不了回聲,還會破壞原始採集到的聲音,使被破壞的聲音難以分辨。我做過好多嘗試,始終無法用軟體來實現對這兩個執行緒中的資料進行同步,導致實現失敗,希望有經驗的網友們一起分享一下這方面的經驗。



示例程式碼:


Sample code

This section shows sample code for encoding and decoding speech using the Speex API. The commands can be used to encode and decode a file by calling:
% sampleenc in_file.sw | sampledec out_file.sw
where both files are raw (no header) files encoded at 16 bits per sample (in the machine natural endianness).

sampleenc.c

sampleenc takes a raw 16 bits/sample file, encodes it and outputs a Speex stream to stdout. Note that the packing used is NOT compatible with that of speexenc/speexdec.


#include <speex/speex.h>
#include <stdio.h>

#define FRAME_SIZE 160
int main(int argc, char **argv)
{
char *inFile;
FILE *fin;
short in[FRAME_SIZE];
float input[FRAME_SIZE];
char cbits[200];
int nbBytes;

void *state;

SpeexBits bits;
int i, tmp;

state = speex_encoder_init(&speex_nb_mode);

tmp=8;
speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp);
inFile = argv[1];
fin = fopen(inFile, "r");

speex_bits_init(&bits);
while (1)
{

fread(in, sizeof(short), FRAME_SIZE, fin);
if (feof(fin))
break;

for (i=0;i<FRAME_SIZE;i++)
input[i]=in[i];

speex_bits_reset(&bits);

speex_encode(state, input, &bits);

nbBytes = speex_bits_write(&bits, cbits, 200);

fwrite(&nbBytes, sizeof(int), 1, stdout);

fwrite(cbits, 1, nbBytes, stdout);
}

speex_encoder_destroy(state);

speex_bits_destroy(&bits);
fclose(fin);
return 0;
}

sampledec.c

sampledec reads a Speex stream from stdin, decodes it and outputs it to a raw 16 bits/sample file. Note that the packing used is NOT compatible with that of speexenc/speexdec.


#include <speex/speex.h>
#include <stdio.h>

#define FRAME_SIZE 160
int main(int argc, char **argv)
{
char *outFile;
FILE *fout;

short out[FRAME_SIZE];

float output[FRAME_SIZE];
char cbits[200];
int nbBytes;

void *state;

SpeexBits bits;
int i, tmp;

state = speex_decoder_init(&speex_nb_mode);

tmp=1;
speex_decoder_ctl(state, SPEEX_SET_ENH, &tmp);
outFile = argv[1];
fout = fopen(outFile, "w");

speex_bits_init(&bits);
while (1)
{

fread(&nbBytes, sizeof(int), 1, stdin);
fprintf (stderr, "nbBytes: %d\n", nbBytes);
if (feof(stdin))
break;

fread(cbits, 1, nbBytes, stdin);

speex_bits_read_from(&bits, cbits, nbBytes);

speex_decode(state, &bits, output);

for (i=0;i<FRAME_SIZE;i++)
out[i]=output[i];

fwrite(out, sizeof(short), FRAME_SIZE, fout);
}

speex_decoder_destroy(state);

speex_bits_destroy(&bits);
fclose(fout);
return 0;
}




 


 




開源 H323 協議中封裝的使用參考程式碼:




#include <ptlib.h>

#ifdef __GNUC__
#pragma implementation "speexcodec.h"
#endif

#include "speexcodec.h"

#include "h323caps.h"
#include "h245.h"
#include "rtp.h"

extern "C" {
#include "speex/libspeex/speex.h"
};


#define new PNEW

#define XIPH_COUNTRY_CODE       0xB5  // (181) Country code for United States
#define XIPH_T35EXTENSION       0
#define XIPH_MANUFACTURER_CODE  0x0026 // Allocated by Delta Inc

#define EQUIVALENCE_COUNTRY_CODE       9  // Country code for Australia
#define EQUIVALENCE_T35EXTENSION       0
#define EQUIVALENCE_MANUFACTURER_CODE  61 // Allocated by Australian Communications Authority, Oct 2000

#define SAMPLES_PER_FRAME        160

#define SPEEX_BASE_NAME "Speex"

#define SPEEX_NARROW2_H323_NAME    SPEEX_BASE_NAME "Narrow-5.95k{sw}"
#define SPEEX_NARROW3_H323_NAME    SPEEX_BASE_NAME "Narrow-8k{sw}"
#define SPEEX_NARROW4_H323_NAME    SPEEX_BASE_NAME "Narrow-11k{sw}"
#define SPEEX_NARROW5_H323_NAME    SPEEX_BASE_NAME "Narrow-15k{sw}"
#define SPEEX_NARROW6_H323_NAME    SPEEX_BASE_NAME "Narrow-18.2k{sw}"

H323_REGISTER_CAPABILITY(SpeexNarrow2AudioCapability, SPEEX_NARROW2_H323_NAME);
H323_REGISTER_CAPABILITY(SpeexNarrow3AudioCapability, SPEEX_NARROW3_H323_NAME);
H323_REGISTER_CAPABILITY(SpeexNarrow4AudioCapability, SPEEX_NARROW4_H323_NAME);
H323_REGISTER_CAPABILITY(SpeexNarrow5AudioCapability, SPEEX_NARROW5_H323_NAME);
H323_REGISTER_CAPABILITY(SpeexNarrow6AudioCapability, SPEEX_NARROW6_H323_NAME);

#define XIPH_SPEEX_NARROW2_H323_NAME    SPEEX_BASE_NAME "Narrow-5.95k(Xiph){sw}"
#define XIPH_SPEEX_NARROW3_H323_NAME    SPEEX_BASE_NAME "Narrow-8k(Xiph){sw}"
#define XIPH_SPEEX_NARROW4_H323_NAME    SPEEX_BASE_NAME "Narrow-11k(Xiph){sw}"
#define XIPH_SPEEX_NARROW5_H323_NAME    SPEEX_BASE_NAME "Narrow-15k(Xiph){sw}"
#define XIPH_SPEEX_NARROW6_H323_NAME    SPEEX_BASE_NAME "Narrow-18.2k(Xiph){sw}"

H323_REGISTER_CAPABILITY(XiphSpeexNarrow2AudioCapability, XIPH_SPEEX_NARROW2_H323_NAME);
H323_REGISTER_CAPABILITY(XiphSpeexNarrow3AudioCapability, XIPH_SPEEX_NARROW3_H323_NAME);
H323_REGISTER_CAPABILITY(XiphSpeexNarrow4AudioCapability, XIPH_SPEEX_NARROW4_H323_NAME);
H323_REGISTER_CAPABILITY(XiphSpeexNarrow5AudioCapability, XIPH_SPEEX_NARROW5_H323_NAME);
H323_REGISTER_CAPABILITY(XiphSpeexNarrow6AudioCapability, XIPH_SPEEX_NARROW6_H323_NAME);

/////////////////////////////////////////////////////////////////////////

static int Speex_Bits_Per_Second(int mode) {
    void *tmp_coder_state;
    int bitrate;
    tmp_coder_state = speex_encoder_init(&speex_nb_mode);
    speex_encoder_ctl(tmp_coder_state, SPEEX_SET_QUALITY, &mode);
    speex_encoder_ctl(tmp_coder_state, SPEEX_GET_BITRATE, &bitrate);
    speex_encoder_destroy(tmp_coder_state);
    return bitrate;
}

static int Speex_Bytes_Per_Frame(int mode) {
    int bits_per_frame = Speex_Bits_Per_Second(mode) / 50; // (20ms frame size)
    return ((bits_per_frame+7)/8); // round up
}

OpalMediaFormat const OpalSpeexNarrow_5k95(OPAL_SPEEX_NARROW_5k95,
                                           OpalMediaFormat::DefaultAudioSessionID,
               

相關推薦

Speex 回聲消除

     m_bHasInit; SpeexEchoState*   m_pState;     SpeexPreprocessState* m_pPreprocessorState; int      m_nFrameSize; int      m_nFilterLen; int      m_nSamp

speex aec回聲消除效果調研

speex aec回聲消除效果測試 本次測試以取樣率44100,幀長882,對應時長為20ms(speex推薦時長為20ms) 麥克風採集的PCM波形圖: 遠端傳輸來用來播放的PCM波形圖: 1.當聲音對齊錯位範圍在0.01s以內時,回聲消除處理效果如下波形所示

音訊處理回聲消除及除錯經驗

本文講的回聲(Echo)是指語音通訊時產生的回聲,即打電話時自己講的話又從對方傳回來被自己聽到。回聲在固話和手機上都有,小時還可以忍受,大時嚴重影響溝通交流,它是影響語音質量的重要因素之一。可能有的朋友要問了,為什麼我打電話時沒有聽見自己的回聲,那是因為市面上的成熟產品回聲都被消除掉了。回聲分為線路回聲(li

speex與webrtc回聲消除小結

回聲消除AEC包含:   延時估計對齊+線性自適應濾波器+NLP(雙講檢測、處理)+舒適噪聲CNG 一、speex aec 1、沒有NLP 2、只考慮實時DSP系統,即是沒有延時對齊等 3、自適應濾波(MDF)使用雙濾波器結構,自適應濾波器因子自動更新 二、webrtc ae

投稿 Speex回聲消除原理深度解析

                本文是音訊處理的朋友icoolmedia(QQ:314138065)的投稿。對音訊處理有興趣的朋友可以通過下面的方式與他交流:作者:icoolmedia QQ:314138065 音視訊演算法討論QQ群:374737122            

Speex回聲消除

speex是一個不錯的音訊編解碼和音訊處理的開源庫,可以跨平臺; 之前我轉過一篇文,主要講了speex的回聲消除; 之前做過測試,覺得效果不好,最近有時間研究了一下,效果還是不錯的; 以前是單機測試,

LMS、NLMS最優步長理論分析與Speex回聲消除可能的改進想法

一、回聲消除演算法模型   先來分析下自適應回聲消除的主要組成部分,大體上可以把回聲消除模型分為兩個部分 橫向濾波器結構濾波器係數自適應與步長控制  橫向濾波器用脈衝響應w(n)【有的地方也稱為回聲路徑】與遠端說話者訊號u(n)卷積得到回聲估計,並用y(n)表示該估計。麥克

speex 回聲消除的用法

speex的回聲訊息 就是speex_echo_cancellation函式的正確用法 回聲訊息的原理: 對參考聲音(解碼的對端原始語音包)做延遲(會有多個延遲,如麥克風直接採集到音箱的聲音,經牆壁反射後再次採集),衰減, 從聲卡里採集到的語音,做一個語音合成。 回聲產

[投稿] Speex回聲消除原理深度解析

姓名:雷霄驊 網名:leixiaohua1020 本科: 中國傳媒大學-廣播電視工程 碩士: 中國傳媒大學-數字電視技術 博士: 中國傳媒大學-數字視訊技術 Email: [email protected] QQ: 494085803 [注1:QQ訊息較多,難以一一回復,見諒]

實踐linux, alsa下的speex 回聲消除

程式碼很簡單, 而且效果似乎比win32下好些. 因為程式碼很簡單, 直接貼在這裡得了 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <

Speex回聲消除原理解析

這裡假設讀者具有自適應濾波器的基礎知識。Speex的AEC是以NLMS為基礎,用MDF頻域實現,最終推匯出最優步長估計:殘餘回聲與誤差之比。最優步長等於殘餘回聲方差與誤差訊號方差之比,這個結論可以記下,下面會用到的。   對於長度為N的NLMS濾波器,誤差訊號定義為期望訊號與估計訊號之差,表示如

Speex Acoustic Echo Cancellation (AEC) 回聲消除模組的使用

從程式碼分析,下邊是Speex test demo #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h>

speex回聲消除原始碼解讀

一、speex回聲消除aec小析: 頻域自適應演算法採用了分塊處理的思想,以減少高階自適應濾波器的計算複雜度,多延遲自適應濾波器(MDF)則更一般可以分M塊來處理,其中塊的長度比自適應濾波器的階數更小。         後置濾波器係數一直在更新,單講時前置濾波器用後置濾波

dubbo回聲測試

comment status sta 服務 comm 自動實現 cti 是否 mem 回聲測試 回聲測試用於檢測服務是否可用,回聲測試按照正常請求流程執行,能夠測試整個調用是否通暢,可用於監控。 所有服務自動實現 EchoService 接口,只需將任意服務引用強制轉型

WebRTC回聲消除(1)

通過 幹凈 過程 info ebr src 保存 語音 代碼 語音通話中回聲分為兩種: 1.電路回聲(已經被解決) 2.聲學回聲 WebRTC源代碼中設計了兩個回聲消除模塊: 1.AEC(Acoustic Echo Canceller):電腦端 2.AECM(Acousti

WebRTC回聲消除(2)

適應 時長 and span bsp 基於 最大的 方程 矩陣 WebRTC的回聲抵消算法(AEC,AECM)有以下幾個重要的模塊: 1.回聲延時估計 2.NLMS 3.NLP 4.CNG 5.雙端檢測(DT) 下面分別介紹: (1)回聲延時估計 回聲延時長短:基於相關的

解密回聲消除技術之一

一、前言 因為工作的關係,筆者從2004年開始接觸回聲消除(Echo Cancellation)技術,而後一直在某大型通訊企業從事與回聲消除技術相關的工作,對回聲消除這個看似神祕、高階和難以理解的技術領域可謂知之甚詳。 要了解回聲消除技術的來龍去脈,不得不提及作為現代通訊技術的理論基礎——數

Opencv背景消除建模(BSM)

Opencv--背景消除建模(BSM)  在opencv中有兩種方法可以進行背景消除: 其一、基於機器學習(Knn--K個最近鄰)背景消除建模 其一、基於影象分割(GMM,抗干擾影象分割)背景消除建模  BS ,Background Subtraction 相關API 

【轉載】基於WebRTC的Android數字樓宇對講系統回聲消除

本文轉載自部落格:http://www.qttaudio.com/webrtc-android-lydj.html ------------------------------------------------------------------------------------------

回聲消除

Webrtc回聲消除模組在linux上的單獨編譯 版權宣告:本文為博主原創文章,若需轉載請註明出處。 本文基於以下連結編寫 [1]: http://blog.51cto.com/silversand/166095 回聲消除原理 [2]: https://www.cnblogs.com