首頁 > 軟體

Android實現錄音靜音降噪

2022-08-09 18:00:41

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com