实践linux, alsa下的speex 回声消除

前几天搞过win32下的speex AEC (http://blog.csdn.net/sunkwei/archive/2011/05/18/6429096.aspx), 很复杂, 今天尝试搞搞 alsa 下的, 发现很简单啊!!! 照例先贴上 audacity 的效果图:

 

实践linux, alsa下的speex 回声消除_第1张图片

 

代码很简单, 而且效果似乎比win32下好些.

 

因为代码很简单, 直接贴在这里得了

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <alloca.h> #include <math.h> #include <alsa/asoundlib.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> #include <assert.h> #include <speex/speex_echo.h> #include <speex/speex_preprocess.h> #include "util_cirbuf.h" #define SAVEFILE 1 #define LISTEN_PORT 7777 #define PERIOD_NUM 3 #define FRAMESIZE 160 static snd_pcm_uframes_t _period_size = 160; static unsigned int _period_bytes = 320; // sock static int _sock_listen = -1, _sock_sender = -1; static struct sockaddr_in _target; static char *_target_ip; // argv[1] // speex obj static SpeexEchoState *_speex_aec = 0; static SpeexPreprocessState *_speex_preprocess = 0; // snd obj static snd_pcm_t *_snd_capture = 0, *_snd_playback = 0; // fifo, 接收缓冲,录音缓冲,回放缓冲 static struct tea_cirbuf_t *_cbuf_recv = 0, *_cbuf_capture = 0, *_cbuf_playback = 0; // recv thread static void *_thread_recv (void *p) { /** recv udp data. and save into _cbuf_recv */ assert(_sock_listen != -1); assert(_cbuf_recv); while (1) { char buf[4096]; // 每个pcm包不可能超过 struct sockaddr_in from; socklen_t fromlen = sizeof(from); int rc = recvfrom(_sock_listen, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); if (rc < 0) { fprintf(stderr, "%s: recvfrom err/n", __func__); exit(-1); } // chk _cbuf_recv if (util_cbuf_space(_cbuf_recv) < rc) { // TODO: overflow, ERR, 其实可以清空处理 fprintf(stderr, "%s: recv overflow!!!!!!/n", __func__); exit(-1); } util_cbuf_save(_cbuf_recv, buf, rc); fprintf(stderr, "."); } return 0; } static int start_listener() { _sock_listen = socket(AF_INET, SOCK_DGRAM, 0); if (_sock_listen == -1) { fprintf(stderr, "%s: open listen sock err/n", __func__); exit(-1); return -1; } struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(LISTEN_PORT); sin.sin_addr.s_addr = INADDR_ANY; if (bind(_sock_listen, (struct sockaddr*)&sin, sizeof(sin)) < 0) { fprintf(stderr, "%s: bind listen port %d err/n", __func__, LISTEN_PORT); exit(-1); return -1; } // start recv thread pthread_t th; if (pthread_create(&th, 0, _thread_recv, 0)) { fprintf(stderr, "%s: create recv thread err/n", __func__); exit(-1); return -1; } return 1; } static int start_sender () { _sock_sender = socket(AF_INET, SOCK_DGRAM, 0); if (_sock_sender == -1) { fprintf(stderr, "%s: create sender sock err/n", __func__); exit(-1); return -1; } _target.sin_family = AF_INET; _target.sin_port = htons(LISTEN_PORT); _target.sin_addr.s_addr = inet_addr(_target_ip); return 1; } static int sendpcm(void *pcm, size_t len) { return sendto(_sock_sender, pcm, len, 0, (struct sockaddr*)&_target, sizeof(_target)); } static void pop_data (struct tea_cirbuf_t *cbuf, char *buf, size_t bufsize) { assert(util_cbuf_data(cbuf) >= bufsize); char *p; size_t cs = util_cbuf_get_cdata(cbuf, &p); if (cs >= bufsize) { memcpy(buf, p, bufsize); util_cbuf_consume(cbuf, bufsize); } else { memcpy(buf, p, cs); util_cbuf_consume(cbuf, cs); buf += cs; bufsize -= cs; util_cbuf_get_cdata(cbuf, &p); memcpy(buf, p, bufsize); util_cbuf_consume(cbuf, bufsize); } } static double now () { struct timeval tv; gettimeofday(&tv, 0); return tv.tv_sec + 0.000001*tv.tv_usec; } #define PI 3.1415927 /** 制作 1khz的正弦波 对于 8k 单声道, 每个采样变化为 pi/4 */ static void make_sine (short *sample_buf, int samples) { double radian = 0; for (int i = 0; i < samples; i++) { sample_buf[i] = 65535 * 0.7 * sin(radian); radian += PI/4; } } /** 工作线程, 使用阻塞模式, write(), read() .... */ static void *_thread_run (void *p) { assert(_snd_capture && _snd_playback); unsigned char *silence = (unsigned char *)malloc(_period_bytes); memset(silence, 0, _period_bytes); // make_sine(silence, _period_size); unsigned char *playback_data = (unsigned char *)malloc(_period_bytes); unsigned char *capture_data = (unsigned char *)malloc(_period_bytes); unsigned char *outbuf = (unsigned char*)malloc(_period_bytes); // aec output buf // start if (snd_pcm_start(_snd_capture) < 0) { fprintf(stderr, "%s: snd_pcm_start for capture ERR/n", __func__); exit(-1); } if (snd_pcm_start(_snd_playback) < 0) { fprintf(stderr, "%s: snd_pcm_start for playback err/n", __func__); exit(-1); } snd_pcm_prepare(_snd_playback); snd_pcm_prepare(_snd_capture); double curr = now(); while (1) { // p 为需要写入声卡的数据 unsigned char *p = silence; if (util_cbuf_data(_cbuf_recv) >= _period_bytes) { pop_data(_cbuf_recv, playback_data, _period_bytes); p = playback_data; } else { fprintf(stderr, "S"); } int rc = snd_pcm_writei(_snd_playback, p, _period_size); if (rc == _period_size) { } else if (rc == -EPIPE) { fprintf(stderr, "%s: snd_pcm_writei xrun!/n", __func__); exit(-1); } else { fprintf(stderr, "%s: snd_pcm_writei ret %d/n", __func__, rc); exit(-1); } // read from capture rc = snd_pcm_readi(_snd_capture, capture_data, _period_size); if (rc == _period_size) { } else if (rc == -EPIPE) { fprintf(stderr, "%s: snd_pcm_readi xrun/n", __func__); exit(-1); } else { fprintf(stderr, "%s: snd_pcm_readi ERR, code=%d/n", __func__, rc); exit(-1); } // AEC speex_echo_cancellation(_speex_aec, (short*)capture_data, (short*)playback_data, (short*)outbuf); speex_preprocess_run(_speex_preprocess, outbuf); // send to target sendpcm(outbuf, _period_size); #if SAVEFILE FILE *fp_c = fopen("capture.pcm", "ab"); FILE *fp_p = fopen("playback.pcm", "ab"); FILE *fp_o = fopen("aecd.pcm", "ab"); fwrite(capture_data, 1, _period_bytes, fp_c); fwrite(playback_data, 1, _period_bytes, fp_p); fwrite(outbuf, 1, _period_bytes, fp_o); fclose(fp_c); fclose(fp_p); fclose(fp_o); #endif // save file } } /** 设置参数, mono, 16bits, 8k */ static int set_params (snd_pcm_t *snd) { int err; snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); if (snd_pcm_hw_params_any(snd, hwparams) < 0) { fprintf(stderr, "%s: snd_pcm_hw_params_any err/n", __func__); exit(-1); return -1; } if (snd_pcm_hw_params_set_access(snd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { fprintf(stderr, "%s: set_access err/n", __func__); exit(-1); } if (snd_pcm_hw_params_set_channels(snd, hwparams, 1)) { fprintf(stderr, "%s: set_channels err/n", __func__); exit(-1); } if (snd_pcm_hw_params_set_format(snd, hwparams, SND_PCM_FORMAT_S16_LE)) { fprintf(stderr, "%s: set_format err/n", __func__); exit(-1); } if (snd_pcm_hw_params_set_rate(snd, hwparams, 8000, 1)) { fprintf(stderr, "%s: set_rate err/n", __func__); exit(-1); } // set period size int dir; if (snd_pcm_hw_params_set_period_size_near(snd, hwparams, &_period_size, &dir) < 0) { fprintf(stderr, "%s: snd_pcm_hw_params_set_period_size_near ERR/n", __func__); exit(-1); } fprintf(stderr, "set period size=%u/n", _period_size); _period_bytes = _period_size*2; // set buffer size if (snd_pcm_hw_params_set_buffer_size(snd, hwparams, PERIOD_NUM*_period_bytes) < 0) { fprintf(stderr, "%s: snd_pcm_hw_params_set_buffer_size err/n", __func__); exit(-1); } if (snd_pcm_hw_params(snd, hwparams) < 0) { fprintf(stderr, "%s: snd_pcm_hw_params err/n", __func__); exit(-1); } // set software params snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); return 1; } /** open alsa plughw:0,0 for capture */ static int open_snd_capture() { int err = snd_pcm_open(&_snd_capture, "default", SND_PCM_STREAM_CAPTURE, 0); if (err < 0) { fprintf(stderr, "%s: snd_pcm_open err/n", __func__); exit(-1); return -1; } set_params(_snd_capture); return 1; } // open plughw:0,0 for playback static int open_snd_playback () { int err = snd_pcm_open(&_snd_playback, "default", SND_PCM_STREAM_PLAYBACK, 0); if (err < 0) { fprintf(stderr, "%s: snd_pcm_open err/n", __func__); exit(-1); return -1; } set_params(_snd_playback); return 1; } int main (int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s <target ip>/n", argv[0]); return -1; } _target_ip = strdup(argv[1]); // speex state int sample_rate = 8000; _speex_aec = speex_echo_state_init(FRAMESIZE, 2500); _speex_preprocess = speex_preprocess_state_init(FRAMESIZE, 8000); speex_echo_ctl(_speex_aec, SPEEX_ECHO_SET_SAMPLING_RATE, &sample_rate); speex_preprocess_ctl(_speex_preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, _speex_aec); int dn = 1; speex_preprocess_ctl(_speex_preprocess, SPEEX_PREPROCESS_SET_DENOISE, &dn); // prepare cbufs _cbuf_recv = util_cbuf_create(64*1024); _cbuf_capture = util_cbuf_create(4*1024); _cbuf_playback = util_cbuf_create(4*1024); // start local listener start_listener(); // start sender sock start_sender(); // open playback open_snd_playback(); open_snd_capture(); // work thread pthread_t th; pthread_create(&th, 0, _thread_run, 0); while (1) { if (getchar() == 'q') { break; } } return 0; }  

 

还需要那个 util_cirbuf.c util_cirbuf.h, 这里下载 http://download.csdn.net/source/3290182

 

你可能感兴趣的:(实践linux, alsa下的speex 回声消除)