当前位置:  互联网>综合
本页文章导读:
    ▪Speex Acoustic Echo Cancellation (AEC) 回声消除模块的使用      背景:回声与啸叫的产生  http://blog.csdn.net/u011202336/article/details/9238397 参考资料:  http://www.speex.org/docs/manual    从代码分析,下边是Speex test demo #include <stdio.h> #include <stdl.........
    ▪Acoustic Echo Cancellation (AEC) 回音消除技术探索               回声产生的原因: 本地产生的音频信息通过网络传输到远端, 远端音频信号通过反射再由远端麦克采集到远端系统,再通过IP网络传输本地,本地播放后,在由本地麦.........
    ▪笔试面试常考数据结构-单链表常用操作编程实现      单链表是笔试以及面试手写代码中常考的数据结构之一。下面实现了单链表的常见操作:创建单链表、删除节点、打印单链表(包括正向打印以及逆向打印)、反转单链表、找出单链表的倒数.........

[1]Speex Acoustic Echo Cancellation (AEC) 回声消除模块的使用
    来源: 互联网  发布时间: 2013-10-26

背景:回声与啸叫的产生  http://blog.csdn.net/u011202336/article/details/9238397

参考资料:  http://www.speex.org/docs/manual   

从代码分析,下边是Speex test demo


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "speex/speex_echo.h"
#include "speex/speex_preprocess.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");
   // Step1: 初始化结构
     st = speex_echo_state_init(NN, TAIL);
   den = speex_preprocess_state_init(NN, sampleRate);

   //Step2: 设置相关参数
   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);
      
      //Step3: 调用Api回声消除,ref_buf是麦克采集到的数据
      // echo_buf:是从speaker处获取到的数据
      // e_buf: 是回声消除后的数据
      speex_echo_cancellation(st, ref_buf, echo_buf, e_buf);
      speex_preprocess_run(den, e_buf);
      fwrite(e_buf, sizeof(short), NN, e_fd);
   }

   //Step4: 销毁结构 释放资源
   speex_echo_state_destroy(st);
   speex_preprocess_state_destroy(den);
   fclose(e_fd);
   fclose(echo_fd);
   fclose(ref_fd);
   return 0;
}

Speex 源码中附带的这个例子,只适合于串行的链式媒体流,当媒体播放、媒体采集、媒体网络数据接口分属在不同现成时,就会存在同步问题,异步线程会导致信号延迟加大,回声消除收敛效果不好。其中Speex 回声消除必须按照建议的流程:

write_to_soundcard(echo_frame, frame_size);     //播放音频数据,并从声卡获得播放的数据echo_frame.
read_from_soundcard(input_frame, frame_size);   //在数据播放后,从声卡麦克获取采集到的数据input_frame.
speex_echo_cancellation(echo_state, input_frame, echo_frame, output_frame); //调用Api消除噪声,输入input_frame,echo_frame,输出out_frame

在典型的VOIP类型应用中:

echo_frame: 从RTP接收的数据包解码后,送入声卡播放,获取的数据。

input_frame: 本地麦克采集到的数据

output_frame: 回声消除后的数据,送入encodec,并构造rtp数据包,传输到远端。


典型的应用模式: 

Thread A:  接收audio rtp -> decodec -----> sound card 

                                                                    |__> echo_frame queue


Thread B: 获取麦克数据input_frame --> speex_echo_cancellation( speex_state, input_frame, echo_frame,out_frame ) -> rtp packet -> network 

也可以将rtp packet 与network 传输放到另外一个线程。


作者:u011202336 发表于2013-7-3 23:51:22 原文链接
阅读:21 评论:0 查看评论

    
[2]Acoustic Echo Cancellation (AEC) 回音消除技术探索
    来源: 互联网  发布时间: 2013-10-26


        回声产生的原因: 本地产生的音频信息通过网络传输到远端, 远端音频信号通过反射再由远端麦克采集到远端系统,再通过IP网络传输本地,本地播放后,在由本地麦克采集到,这就构成了类似闭环正反馈,当延时较小时,信号不断加强,就会导致啸叫现象产生,当延时较大,就会产生回音。 

回音消除的基本原理是用等幅等频率但反相的信号与采集的信号相叠加,当通过网络或者物理反射获得音频信号与等频率反相的信号相叠加,就可以出去回声。问题的关键就是获取这个反相的信号,回声信号的产生主要由: 1.本地环境的反射  2.通过网络在远端播放后,音频反射后由远端麦克采集到并传输到本地扬声器外放后,再由本地麦克采集到,从而形成闭环正反馈。

       本地物理反射: 声学信号在本地物理多路径反射,从信号上讲类似一个衰减延时的滤波器;
       网络反射:  网络传输到远端,远端播放后,再进过远端物理反射后,再进入远端麦克后通过网络传输到本地扬声器,在进入本地麦克,在正反馈,也是一个衰减延时网络。

      回声的产生由这两种因素产生,两种效果叠加后,就是一个衰减延时滤波器,我们需要利用本地播放音频信号与远端回传信号,来设计滤波器。回音消除的关键就是这个自适应滤波器的设计。 
                                          
作者:u011202336 发表于2013-7-3 23:11:30 原文链接
阅读:16 评论:0 查看评论

    
[3]笔试面试常考数据结构-单链表常用操作编程实现
    来源: 互联网  发布时间: 2013-10-26

单链表是笔试以及面试手写代码中常考的数据结构之一。下面实现了单链表的常见操作:创建单链表、删除节点、打印单链表(包括正向打印以及逆向打印)、反转单链表、找出单链表的倒数第K个节点、合并两个有序单链表等操作。


代码(C++):

//笔试面试单链表常用操作编程实现
#include <iostream>
#include <stack>
#include <cstdlib>

using namespace std;

//单链表节点数据结构定义
typedef struct link_node_s{
	int m_val;
	struct link_node_s *next;
}link_node_t,*link_list_t;

//函数:创建单链表(头插法)
link_list_t create_linklist(int *a,int n);

//函数:打印单链表(从头到尾)
void print_linklist(link_list_t head);

//函数:打印单链表(从尾到头)
void print_linklist_reverse(link_list_t head);

//函数:新建链表节点
link_list_t creart_linknode(int val);

//函数:删除链表中的某一个节点(前提条件:该节点一定存在)
//性能要求:在O(1)时间复杂度内实现
void delete_node_exist(link_list_t *head,link_list_t node_deleted);

//函数:删除链表中数据值等于给定值的节点
void delete_node(link_list_t *head,int val);

//函数:获得链表中的倒数第K个节点
link_list_t get_kth_node(link_list_t head,int k);

//函数:反转链表
link_list_t reverse_linklist(link_list_t head);

//函数:合并两个已排序的链表(递归方法实现)
link_list_t merge_linklist_recursive(link_list_t head1,link_list_t head2);

int main(){
	const int num1 = 8;
	const int num2 = 10;
	int *a = new int[num1];
	int *b = new int[num2];
	int *a_sorted = new int[num1];
	int *b_sorted = new int[num2];

	srand(1);
	for(int i = 0;i < num1;++i){
		*(a + i) = rand() % 100;
		*(a_sorted + i) = 50 - i * 2 + 8;
	}
	
	for(int i = 0;i < num2;++i){
		*(b + i) = rand() % 200;
		*(b_sorted + i) = 50 - i * 4 + 1;
	}

	cout << "**********创建链表测试**********" << endl;
	link_list_t list1 = create_linklist(a,num1);
	link_list_t list2 = create_linklist(b,num2);
	link_list_t list_sorted1 = create_linklist(a_sorted,num1);
	link_list_t list_sorted2 = create_linklist(b_sorted,num2);

	cout << "**********输出链表测试(正向输出)**********" << endl;
	cout << "链表1:" << endl;
	print_linklist(list1);
	cout << "链表1(已序):" << endl;
	print_linklist(list_sorted1);
	cout << "链表2(已序):" << endl;
	print_linklist(list_sorted2);

	cout << "**********输出链表测试(逆向输出)**********" << endl;
	print_linklist_reverse(list1);


	cout << "**********获取链表的倒数第K个节点测试**********" << endl;
	int k = 3;
	link_list_t kth_node = get_kth_node(list1,k);
	if(NULL == kth_node)
		cout << "链表中倒数第" << k << "个节点不存在" << endl;
	else
		cout << "链表中倒数第" << k <<"个节点是: " <<kth_node->m_val << endl; 

	k = 8;
	kth_node = get_kth_node(list1,k);
	if(NULL == kth_node)
		cout << "链表中倒数第" << k << "个节点不存在" << endl;
	else
		cout << "链表中倒数第" << k <<"个节点是: " <<kth_node->m_val << endl; 

	k = 11;
	kth_node = get_kth_node(list1,k);
	if(NULL == kth_node)
		cout << "链表中倒数第" << k << "个节点不存在" << endl;
	else
		cout << "链表中倒数第" << k <<"个节点是: " <<kth_node->m_val << endl; 

	cout << "**********删除链表中一定存在的节点测试(输入参数是要删除的节点指针)**********" << endl;
	link_list_t node_deleted = list1;
	while(node_deleted->m_val != *(a + 4))
		node_deleted = node_deleted->next;

	cout << "删除节点" << *(a + 4) << "之后的单链表:" << endl;
	delete_node_exist(&list1,node_deleted);
	print_linklist(list1);
	
	node_deleted = list1;
	while(node_deleted->m_val != *(a + 6))
		node_deleted = node_deleted->next;

	cout << "删除节点" << *(a + 6) << "之后的单链表:" << endl;
	delete_node_exist(&list1,node_deleted);
	print_linklist(list1);

	cout << "**********删除链表中值等于给定值的节点测试(不一定存在,输入参数是int型值)**********" << endl;
	const int val_deleted = 22;
	delete_node(&list1,val_deleted);
	cout << "删除值等于" << val_deleted << "之后的链表:" << endl;
	print_linklist(list1);

	cout << "**********合并链表测试**********" << endl;
	link_list_t merge_list_head = merge_linklist_recursive(list_sorted1,list_sorted2);
	print_linklist(merge_list_head);

	
	cout << "**********逆转链表测试**********" << endl;
	link_list_t head_reverse = reverse_linklist(merge_list_head);
	cout << "逆转之后的链表:" << endl;
	cout << "头节点:" << head_reverse->m_val << endl;
	print_linklist(head_reverse);
	
	return 0;
}

//函数:创建单链表(头插法)
link_list_t create_linklist(int *a,int n){
	link_list_t head = NULL;

	if(NULL == a || 0 == n)
		return NULL;

	for(int i = 0;i < n;++i){
		link_list_t new_node = creart_linknode(*(a + i));

		if(NULL == head){
			head = new_node;
		}
		else{
			new_node->next = head;
			head = new_node;
		}
	}

	return head;
}

//函数:新建链表节点
link_list_t creart_linknode(int val){
	link_list_t node = new link_node_t;
	node->m_val = val;
	node->next = NULL;

	return node;
}

//函数:打印单链表
void print_linklist(link_list_t head){
	link_list_t node = head;

	cout << "正向输出单链表" << endl;
	while(node != NULL){
		cout << node->m_val << " ";
		node = node->next;
	}
	cout << endl;

	return;
}

//函数:打印单链表(从尾到头)
void print_linklist_reverse(link_list_t head){
	stack<int> node_stack;
	link_list_t node = head;
	while(node != NULL){
		node_stack.push(node->m_val);
		node = node->next;
	}

	cout << "逆向输出单链表" << endl;
	while(!node_stack.empty()){
		cout << node_stack.top() << " ";
		node_stack.pop();
	}
	cout << endl;

	return;
}


//函数:删除链表中的某一个节点(前提条件:该节点一定存在)
//性能要求:在O(1)时间复杂度内实现
void delete_node_exist(link_list_t *head,link_list_t node_deleted){
	//算法思想:
	//通过拷贝要删除节点的后继节点的内容覆盖要删除节点的内容,然后删除要删除节点的后继节点即可
	//要考虑的特殊情况是:要删除的节点是链表尾部节点,仍然需要遍历链表
	if(NULL == head || NULL == node_deleted)
		return;

	//要删除的节点不是尾节点
	if(node_deleted->next != NULL){
		link_list_t next_node = node_deleted->next;
		node_deleted->m_val = next_node->m_val;
		node_deleted->next = next_node->next;

		delete next_node;
		next_node = NULL;
	}
	//链表中只有一个节点
	else if(*head == node_deleted){
		delete node_deleted;
		node_deleted = NULL;
		*head = NULL;
	}
	//要删除的节点是尾节点
	else{
		link_list_t node = *head;
		while(node->next != node_deleted)
			node = node->next;

		node->next = node_deleted->next;
		delete node_deleted;
		node_deleted = NULL;
	}

	return;
}

//函数:获得链表中的倒数第K个节点
link_list_t get_kth_node(link_list_t head,int k){
	//性能:只需遍历链表一遍即可
	//算法思想:设置两个指针,一个指向链表头部,一个指向第k个节点,然后两个指针同时向后移动,当第二个指针指向链表的尾节点时,第一个指针指向的节点便是倒数第K个节点
	//注意代码的鲁棒性,防止程序的崩溃
	if(NULL == head || k <= 0)
		return NULL;

	//设置两个指针
	link_list_t p1 = head,p2 = head;
	int i = 0;
	//第二个指针向前走k-1步
	while(i < k - 1 && p2->next != NULL){
		p2 = p2->next;
		++i;
	}
	//注意链表中总节点数小于K的情况
	if(i != k - 1 && NULL == p2->next)
		return NULL;

	//两个指针同时向后前进
	while(p2->next != NULL){
		p1 = p1->next;
		p2 = p2->next;
	}

	return p1;
}


//函数:反转链表
//返回值:反转之后的链表头节点
link_list_t reverse_linklist(link_list_t head){
	//链表为空或者只有一个节点
	if(NULL == head || NULL == head->next)
		return head;

	link_list_t prev_node = NULL,next_node,cur_node = head,head_reverse;
	while(cur_node != NULL){
		next_node = cur_node->next;
		if(NULL == next_node)
			head_reverse = cur_node;//原链表尾节点即逆转后链表的头节点
		cur_node->next = prev_node;
		prev_node = cur_node;
		cur_node = next_node;
	}

	return head_reverse;
}

//函数:删除链表中数据值等于给定值的节点
void delete_node(link_list_t *head,int val){
	if(NULL == head){
		cout << "Delete node failed :The node to be delete not exist!" << endl;
		return;
	}

	if(val == (*head)->m_val){
		link_list_t node = *head;
		*head = (*head)->next;
		delete node;
		return;      
    
最新技术文章:
▪用户及权限基础 2---- Linux权限    ▪用户及权限基础 3---- Linux扩展权限    ▪git 简明教程(1) --创建及提交
▪背包 代码    ▪json对象的封装与解析    ▪01背包,完全背包,多重背包 ,模板代码
▪apache安装详解    ▪HDU 4668 Finding string (解析字符串 + KMP)    ▪《TCP-IP详解 卷1:协议》学习笔记(二)
▪《TCP-IP详解 卷1:协议》学习笔记(持续更新...    ▪windows下使用swig    ▪gensim试用
▪Linux Shell脚本编程--nc命令使用详解    ▪solr对跨服务器表联合查询的配置    ▪递归和非递归实现链表反转
▪Linux磁盘及文件系统管理 1---- 磁盘基本概念    ▪Cholesky Decomposition    ▪HTTP协议学习
▪用C语言写CGI入门教程    ▪用hdfs存储海量的视频数据的设计思路    ▪java多线程下载的实现示例
▪【原创】eAccelerator 一个锁bug问题跟踪    ▪hadoop学习之ZooKeeper    ▪使用cuzysdk web API 实现购物导航类网站
▪二维数组中的最长递减子序列    ▪内嵌W5100的网络模块WIZ812MJ--数据手册    ▪xss 跨站脚本攻击
▪《TCP-IP详解 卷1:协议》学习笔记(持续更新...    ▪windows下使用swig    ▪gensim试用 iis7站长之家
▪Logtype    ▪关于端口号你知道多少!    ▪Linux基本操作 1-----命令行BASH的基本操作
▪CI8.7--硬币组合问题    ▪Ruby on Rails 学习(五)    ▪如何使用W5300实现ADSL连接(二)
▪不允许启动新事务,因为有其他线程正在该会...    ▪getting start with storm 翻译 第六章 part-3    ▪递归求排列和组合(无重复和有重复)
▪工具类之二:RegexpUtils    ▪Coding Interview 8.2    ▪Coding Interview 8.5
▪素因子分解 Prime factorization    ▪C# DllImport的用法    ▪图的相关算法
▪Softmax算法:逻辑回归的扩展    ▪最小生成树---Kruskal算法---挑战程序设计竞赛...    ▪J2EE struts2 登录验证
▪任意两点间的最短路径---floyd_warshall算法    ▪Sqoop实现关系型数据库到hive的数据传输    ▪FFMPEG采集摄像头数据并切片为iPhone的HTTP Stream...
▪Ubuntu 13.04 – Install Jetty 9    ▪TCP/IP笔记之多播与广播    ▪keytool+tomcat配置HTTPS双向证书认证
▪安装phantomjs    ▪Page Redirect Speed Test    ▪windows media player 中播放pls的方法
▪sre_constants.error: unbalanced parenthesis    ▪http headers    ▪Google MapReduce中文版
▪The TCP three-way handshake (connect)/four wave (closed)    ▪网站反爬虫    ▪Log4j实现对Java日志的配置全攻略
▪Bit Map解析    ▪Notepad 快捷键 大全    ▪Eclipse 快捷键技巧 + 重构
▪win7 打开防火墙端口    ▪Linux Shell脚本入门--awk命令详解    ▪Linux Shell脚本入门--Uniq命令
▪Linux(Android NDK)如何避免僵死进程    ▪http Content-Type一览表    ▪Redis实战之征服 Redis + Jedis + Spring (二)
▪Tomcat7.0.40 基于DataSourceRealm的和JDBCRealm的资源...    ▪利用SQOOP将ORACLE到HDFS    ▪django输出 hello world
▪python re    ▪unity3D与网页的交互    ▪内存共享基本演示
▪python join    ▪不再为无限级树结构烦恼,且看此篇    ▪python实现变参
▪打开文件数限制功能不断地制造问题    ▪Arduino Due, Maple and Teensy3.0 的 W5200性能测试    ▪Selenium实例----12306网站测试
▪基于协同过滤的推荐引擎    ▪C4.5决策树    ▪C#HTTP代理的实现之注册表实现
▪nosql和关系型数据库比较?    ▪如何快速比较这两个字符串是否相等?    ▪hdoj 1863 畅通工程 最小生成树---prime算法
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3