Android实现录音静音降噪
作者:suwen8100 时间:2023-01-06 23:46:25
本文实例为大家分享了Android实现录音静音降噪的具体代码,供大家参考,具体内容如下
需求:
客户反馈产品的录音里面很多杂音(因为我们把Codec的录音增益调至最大,且电路上没有专用的音频处理芯片、CPU直接接MIC(有包地))。在外壳、硬件不能修改的情况下,软件得想想办法尝试解决问题。
首先想到的是双麦降噪,原理大概是:一个主麦克风用来做通话,另一个收集环境噪音,对音频波形分析和相位操作,叠加到主麦克风的采样波形上,形成相位抵消,就降噪了。缺点是,两个麦克风不能距离太近,并且两个麦克风距离说话人的距离不能太远,太远了角度就很小了,根本无法分辨出来,另外,根据产品使用情况,上下麦克风各自都有几率称为主麦克风。所以实验测试出来的结果并没有很好。
考虑到录音噪音在有“人声”的时候是分辨不出来的、或者说影响很小,而在静音时有明显的环境噪声,因此想使用静音降噪的方法来规避问题。
本文只是简单的静音降噪,原理如下:考虑到启动录音时,要等待一段时间(比如0.5s)才会有人声,可根据这0.5s时间来预测噪声的大小(阈值),然后以此为基础来检测“人声”的起始点。在人声到来前,把所有音频数据设置为0,也就是做静音处理,所以这里叫静音降噪。而人声到来时,返回实际的音频数据(包括里面的噪声数据)。计算阈值的方法只是简单的求和平均。
下面代码在RK平台上hardware/alsa_sound/AudioStreamInALSA.cpp实现。
#define MUTE_NOISE_REDUCTION
#ifdef MUTE_NOISE_REDUCTION
bool enable_reduction_noise = false;?? ?//由属性sys.is.audiorecord.only控制
int threshold_def = 0x400;?? ?//默认阈值
int threshold = 0;?? ?//自适应噪声阈值
int threshold_count = 0;?? ?//计数,超过THRESHOLD_COUNT则使用threshold来检测“人声”
#define THRESHOLD_COUNT 10
#define MUTE_DELAY_COUNT 15?? ??? ?//播放人声后保留的音频帧数、不静音
#define AUDIO_BUFFER_NUM 4?? ??? ?//缓存音频数据的帧数
#define AUDIO_BUFFER_SIZE 1024?? ?//一帧的音频数据大小
char *audio_buffer[AUDIO_BUFFER_NUM];?? ?//audio_buffer用于缓存音频数据
char *audio_buffer_temp;?? ?//用于交互音频数据
int audio_buffer_pos=0;
#endif
#ifdef MUTE_NOISE_REDUCTION
? ? {
? ? ? ? unsigned int value = 0;
? ? ? ? int is_voice = 0;
? ? ? ? static int is_mute_delay_count;
? ? ? ? //ALOGE("in_begin_swip_num:%d in_begin_narrow_num=%d",in_begin_swip_num,in_begin_narrow_num);?? ??? ?
? ? ? ? ?if(enable_reduction_noise && bytes > AUDIO_BUFFER_SIZE){
? ? ? ? ? ? bytes = AUDIO_BUFFER_SIZE;
? ? ? ? }
? ? ? ? if(enable_reduction_noise){
? ? ? ? ? ? unsigned char * buffer_temp=(unsigned char *)buffer;
? ? ? ? ? ? unsigned int total = 0;
? ? ? ? ? ? unsigned int total_count=0;
?? ??? ??? ?unsigned int total_temp = 0;
? ? ? ? ? ? short data16;
? ? ? ? ? ? int j = 0;
? ? ? ? ? ? for(j=0; j<bytes; j=j+2){
? ? ? ? ? ? ? ? value = buffer_temp[j+1];?? ?//第二个字节为高位数据
? ? ? ? ? ? ? ? value = (value<<8)+buffer_temp[j];?? ?//获得一个16bit的音频数据
? ? ? ? ? ? ? ? data16 = value&0xFFFF;
? ? ? ? ? ? ? ? if( (data16 & 0x8000) == 0){//正数
?? ??? ??? ??? ??? ?total +=data16;?? ??? ?//思考:会不会溢出
?? ??? ??? ??? ??? ?total_count++;?? ??? ?//计数
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
?? ??? ??? ?total_temp = total/total_count;
? ? ? ? ? ? if(total_temp > threshold_def){
? ? ? ? ? ? ? ? is_voice++;?? ??? ?//检测到人声
? ? ? ? ? ? }else {?? ?//is noise
?? ??? ??? ??? ?if(threshold_count == 0){
?? ??? ??? ??? ??? ?threshold = total_temp;
?? ??? ??? ??? ?}else{
?? ??? ??? ??? ??? ?threshold = (threshold+total_temp)/2;
?? ??? ??? ??? ?}
?? ??? ??? ??? ?threshold_count++;
?? ??? ??? ??? ?if(threshold_count >= THRESHOLD_COUNT){
?? ??? ??? ??? ??? ?threshold_def = threshold*2;?? ?//更新阈值,这里的2要对产品实验来确定。
?? ??? ??? ??? ??? ?threshold_count = THRESHOLD_COUNT;?? ?//此后一直用新阈值,直到停止录音
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ??? ?//is_mute_delay_count的意义是,如果前面播放了人声,那再停止说话之后继续保留MUTE_DELAY_COUNT的音频数据,这样不会“戛然而止”。
?? ? ? ? ? ?if( is_voice != 0 ){
?? ? ? ? ? ? ? ?is_mute_delay_count=MUTE_DELAY_COUNT;
?? ? ? ? ? ?}else{
?? ? ? ? ? ? ? ?if(is_mute_delay_count != 0)
?? ? ? ? ? ? ? ? ? ?is_mute_delay_count--;
?? ? ? ? ? ?}
?? ??? ??? ?//audio_buffer的意义:检测到人声,要返回说话前的一小段音频数据,否则声音从静音到人声有个POP声的跳跃。
?? ??? ??? ?//这里用audio_buffer来缓存AUDIO_BUFFER_NUM帧数据。
?? ? ? ? ? ?if(is_mute_delay_count == 0){//Mute in order to remove noise
?? ? ? ? ? ? ? ?memcpy(audio_buffer[audio_buffer_pos], (char *)buffer, bytes);?? ?//缓存音频
?? ? ? ? ? ? ? ?memset(buffer, 0, bytes);?? ?//返回静音数据
?? ? ? ? ? ?}else {
?? ? ? ? ? ? ? ?memcpy(audio_buffer_temp, (char *)buffer, bytes);
?? ? ? ? ? ? ? ?memcpy((char *)buffer, audio_buffer[audio_buffer_pos], bytes);?? ?//返回旧的音频数据
?? ? ? ? ? ? ? ?memcpy(audio_buffer[audio_buffer_pos], (char *)audio_buffer_temp, bytes); ?? ?//保存新的音频数据
?? ? ? ? ? ?}
?? ??? ??? ?audio_buffer_pos++;
?? ??? ??? ?if(audio_buffer_pos>=AUDIO_BUFFER_NUM)
? ? ? ? ? ? ? ? audio_buffer_pos=0;
? ? ? ? }
? ? }
#endif
来源:https://blog.csdn.net/suwen8100/article/details/117068805
标签:Android,录音,静音
0
投稿
猜你喜欢
java获取和设置系统变量问题(环境变量)
2023-11-19 07:34:07
Android利用OpenGLES绘制天空盒实例教程
2023-11-19 16:07:27
idea创建maven父子工程导致子工程无法导入父工程依赖
2021-09-17 09:34:08
JAVA Integer类常用方法解析
2021-09-01 06:51:08
使用JAVA实现邮件发送功能的图文教程
2021-09-04 17:29:36
C#短时间内产生大量不重复的随机数
2023-11-28 00:15:53
C#中using关键字的使用方法示例
2023-07-30 14:17:02
基于mybatis plus实现数据源动态添加、删除、切换,自定义数据源的示例代码
2021-08-20 20:07:46
Java中将File转化为MultipartFile的操作
2021-07-05 21:25:32
Android中 自定义数据绑定适配器BaseAdapter的方法
2022-02-02 21:12:50
Flutter 分页功能表格控件详细解析
2023-09-22 20:02:45
Java中的Set、List、Map的用法与区别介绍
2022-10-03 04:11:48
Android实现网络图片浏览功能
2023-10-17 14:05:38
C#开发的人脸左右相似度计算软件源码分析
2023-08-26 05:18:41
unity shader实现较完整光照效果
2023-04-27 07:43:29
Android表格自定义控件使用详解
2023-12-23 23:35:36
Android使用Matrix旋转图片模拟碟片加载过程
2022-08-22 21:07:32
springmvc拦截器登录验证示例
2022-11-28 12:15:24
Java的静态类型检查示例代码详解
2023-08-09 10:42:13
C#窗体编程不显示最小化、最大化、关闭按钮的方法
2023-03-03 00:04:29