您的位置:首页 > 运维架构 > Linux

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

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



代码很简单, 而且效果似乎比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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: