Android实现录音静音降噪

 更新时间:2022年08月09日 14:49:35   作者:suwen8100  
这篇文章主要为大家详细介绍了Android实现录音静音降噪,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Android编程中沉浸式状态栏的三种实现方式详解

    Android编程中沉浸式状态栏的三种实现方式详解

    这篇文章主要介绍了Android编程中沉浸式状态栏的三种实现方式,简单描述了沉浸式状态栏的概念、功能并结合实例形式详细分析了Android实现沉浸式状态栏的三种操作技巧与注意事项,需要的朋友可以参考下
    2018-02-02
  • Android nonTransitiveRClass资源冲突问题浅析

    Android nonTransitiveRClass资源冲突问题浅析

    这篇文章主要介绍了Android nonTransitiveRClass资源冲突问题,别搞错了,nonTransitiveRClass不能解决资源冲突,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • Android openGl 绘制简单图形的实现示例

    Android openGl 绘制简单图形的实现示例

    这篇文章主要介绍了Android openGl 绘制简单图形的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Android的Glide库加载图片的用法及其与Picasso的对比

    Android的Glide库加载图片的用法及其与Picasso的对比

    这篇文章主要介绍了Android的Glide库加载图片的用法及其与Picasso的对比,Glide的加载gif图片的功能和性能受到了很多开发者的青睐,需要的朋友可以参考下
    2016-04-04
  • ImageView简单加载网络图片实例代码

    ImageView简单加载网络图片实例代码

    使用ImageView实现简单加载网络图片的功能,示例代码如下,感兴趣的朋友可以参考下哈,希望对大家有所帮助
    2013-06-06
  • Android事件分发机制(下) View的事件处理

    Android事件分发机制(下) View的事件处理

    这篇文章主要介绍了Android事件分发机制下篇, View的事件处理的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Android组件化、插件化详细讲解

    Android组件化、插件化详细讲解

    这篇文章主要介绍了Android组件化、插件化详细讲解,这些单独⼆次封装的功能模块apk,就称作插件,文章围绕主题展开详细的内容介绍,需要的朋友可以参考一下
    2022-07-07
  • Android PopupMenu弹出菜单的实现

    Android PopupMenu弹出菜单的实现

    这篇文章主要介绍了 Android PopupMenu弹出菜单的实现的相关资料,希望通过本文能帮助到大家,实现这样的功能,需要的朋友可以参考下
    2017-10-10
  • 如何使用Flutter开发一款电影APP详解

    如何使用Flutter开发一款电影APP详解

    这篇文章主要给大家介绍了关于如何使用Flutter开发一款电影APP的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Flutter具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-07-07
  • android如何设置小区广播默认信道(50与60并支持双卡)

    android如何设置小区广播默认信道(50与60并支持双卡)

    置小区广播默认信道50与60,并支持双卡主要是印度市场,具体的实现如下,感兴趣的朋友可以参考下哈
    2013-06-06

最新评论