Android录音静音降噪
suwen8100 人气:0需求:
客户反馈产品的录音里面很多杂音(因为我们把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
加载全部内容