Android實(shí)現(xiàn)錄音靜音降噪
本文實(shí)例為大家分享了Android實(shí)現(xiàn)錄音靜音降噪的具體代碼,供大家參考,具體內(nèi)容如下
需求:
客戶反饋產(chǎn)品的錄音里面很多雜音(因?yàn)槲覀儼袰odec的錄音增益調(diào)至最大,且電路上沒(méi)有專(zhuān)用的音頻處理芯片、CPU直接接MIC(有包地))。在外殼、硬件不能修改的情況下,軟件得想想辦法嘗試解決問(wèn)題。
首先想到的是雙麥降噪,原理大概是:一個(gè)主麥克風(fēng)用來(lái)做通話,另一個(gè)收集環(huán)境噪音,對(duì)音頻波形分析和相位操作,疊加到主麥克風(fēng)的采樣波形上,形成相位抵消,就降噪了。缺點(diǎn)是,兩個(gè)麥克風(fēng)不能距離太近,并且兩個(gè)麥克風(fēng)距離說(shuō)話人的距離不能太遠(yuǎn),太遠(yuǎn)了角度就很小了,根本無(wú)法分辨出來(lái),另外,根據(jù)產(chǎn)品使用情況,上下麥克風(fēng)各自都有幾率稱(chēng)為主麥克風(fēng)。所以實(shí)驗(yàn)測(cè)試出來(lái)的結(jié)果并沒(méi)有很好。
考慮到錄音噪音在有“人聲”的時(shí)候是分辨不出來(lái)的、或者說(shuō)影響很小,而在靜音時(shí)有明顯的環(huán)境噪聲,因此想使用靜音降噪的方法來(lái)規(guī)避問(wèn)題。
本文只是簡(jiǎn)單的靜音降噪,原理如下:考慮到啟動(dòng)錄音時(shí),要等待一段時(shí)間(比如0.5s)才會(huì)有人聲,可根據(jù)這0.5s時(shí)間來(lái)預(yù)測(cè)噪聲的大?。ㄩ撝担缓笠源藶榛A(chǔ)來(lái)檢測(cè)“人聲”的起始點(diǎn)。在人聲到來(lái)前,把所有音頻數(shù)據(jù)設(shè)置為0,也就是做靜音處理,所以這里叫靜音降噪。而人聲到來(lái)時(shí),返回實(shí)際的音頻數(shù)據(jù)(包括里面的噪聲數(shù)據(jù))。計(jì)算閾值的方法只是簡(jiǎn)單的求和平均。
下面代碼在RK平臺(tái)上hardware/alsa_sound/AudioStreamInALSA.cpp實(shí)現(xiàn)。
#define MUTE_NOISE_REDUCTION #ifdef MUTE_NOISE_REDUCTION bool enable_reduction_noise = false;?? ?//由屬性sys.is.audiorecord.only控制 int threshold_def = 0x400;?? ?//默認(rèn)閾值 int threshold = 0;?? ?//自適應(yīng)噪聲閾值 int threshold_count = 0;?? ?//計(jì)數(shù),超過(guò)THRESHOLD_COUNT則使用threshold來(lái)檢測(cè)“人聲” #define THRESHOLD_COUNT 10 #define MUTE_DELAY_COUNT 15?? ??? ?//播放人聲后保留的音頻幀數(shù)、不靜音 #define AUDIO_BUFFER_NUM 4?? ??? ?//緩存音頻數(shù)據(jù)的幀數(shù) #define AUDIO_BUFFER_SIZE 1024?? ?//一幀的音頻數(shù)據(jù)大小 char *audio_buffer[AUDIO_BUFFER_NUM];?? ?//audio_buffer用于緩存音頻數(shù)據(jù) char *audio_buffer_temp;?? ?//用于交互音頻數(shù)據(jù) 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];?? ?//第二個(gè)字節(jié)為高位數(shù)據(jù) ? ? ? ? ? ? ? ? value = (value<<8)+buffer_temp[j];?? ?//獲得一個(gè)16bit的音頻數(shù)據(jù) ? ? ? ? ? ? ? ? data16 = value&0xFFFF; ? ? ? ? ? ? ? ? if( (data16 & 0x8000) == 0){//正數(shù) ?? ??? ??? ??? ??? ?total +=data16;?? ??? ?//思考:會(huì)不會(huì)溢出 ?? ??? ??? ??? ??? ?total_count++;?? ??? ?//計(jì)數(shù) ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ?? ??? ??? ?total_temp = total/total_count; ? ? ? ? ? ? if(total_temp > threshold_def){ ? ? ? ? ? ? ? ? is_voice++;?? ??? ?//檢測(cè)到人聲 ? ? ? ? ? ? }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要對(duì)產(chǎn)品實(shí)驗(yàn)來(lái)確定。 ?? ??? ??? ??? ??? ?threshold_count = THRESHOLD_COUNT;?? ?//此后一直用新閾值,直到停止錄音 ?? ??? ??? ??? ?} ?? ??? ??? ?} ?? ??? ??? ?//is_mute_delay_count的意義是,如果前面播放了人聲,那再停止說(shuō)話之后繼續(xù)保留MUTE_DELAY_COUNT的音頻數(shù)據(jù),這樣不會(huì)“戛然而止”。 ?? ? ? ? ? ?if( is_voice != 0 ){ ?? ? ? ? ? ? ? ?is_mute_delay_count=MUTE_DELAY_COUNT; ?? ? ? ? ? ?}else{ ?? ? ? ? ? ? ? ?if(is_mute_delay_count != 0) ?? ? ? ? ? ? ? ? ? ?is_mute_delay_count--; ?? ? ? ? ? ?} ?? ??? ??? ?//audio_buffer的意義:檢測(cè)到人聲,要返回說(shuō)話前的一小段音頻數(shù)據(jù),否則聲音從靜音到人聲有個(gè)POP聲的跳躍。 ?? ??? ??? ?//這里用audio_buffer來(lái)緩存AUDIO_BUFFER_NUM幀數(shù)據(jù)。 ?? ? ? ? ? ?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);?? ?//返回靜音數(shù)據(jù) ?? ? ? ? ? ?}else { ?? ? ? ? ? ? ? ?memcpy(audio_buffer_temp, (char *)buffer, bytes); ?? ? ? ? ? ? ? ?memcpy((char *)buffer, audio_buffer[audio_buffer_pos], bytes);?? ?//返回舊的音頻數(shù)據(jù) ?? ? ? ? ? ? ? ?memcpy(audio_buffer[audio_buffer_pos], (char *)audio_buffer_temp, bytes); ?? ?//保存新的音頻數(shù)據(jù) ?? ? ? ? ? ?} ?? ??? ??? ?audio_buffer_pos++; ?? ??? ??? ?if(audio_buffer_pos>=AUDIO_BUFFER_NUM) ? ? ? ? ? ? ? ? audio_buffer_pos=0; ? ? ? ? } ? ? } #endif
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android給通知channel靜音的方法實(shí)例
- Android實(shí)現(xiàn)靜音檢測(cè)功能
- Android 判斷網(wǎng)絡(luò)狀態(tài)對(duì)音頻靜音的實(shí)現(xiàn)方法
- Android實(shí)現(xiàn)定時(shí)自動(dòng)靜音小助手
- Android EasyPlayer聲音自動(dòng)停止、恢復(fù),一鍵靜音等功能
- android實(shí)現(xiàn)來(lái)電靜音示例(監(jiān)聽(tīng)來(lái)電)
- android系統(tǒng)在靜音模式下關(guān)閉camera拍照聲音的方法
- Android音頻錄制MediaRecorder之簡(jiǎn)易的錄音軟件實(shí)現(xiàn)代碼
- Android簡(jiǎn)單的利用MediaRecorder進(jìn)行錄音的實(shí)例代碼
- Android實(shí)現(xiàn)錄音功能實(shí)現(xiàn)實(shí)例(MediaRecorder)
相關(guān)文章
Android不顯示開(kāi)機(jī)向?qū)Ш烷_(kāi)機(jī)氣泡問(wèn)題
這篇文章主要介紹了Android不顯示開(kāi)機(jī)向?qū)Ш烷_(kāi)機(jī)氣泡問(wèn)題,需要的朋友可以參考下2019-05-05HandlerThread的使用場(chǎng)景和用法詳解
這篇文章主要介紹了HandlerThread的使用場(chǎng)景和用法詳解,HandlerThread是Android中的一個(gè)線程類(lèi),它是Thread的子類(lèi),并且內(nèi)部封裝了Looper和Handler,提供了更方便的消息處理和線程操作,需要的朋友可以參考下2023-07-07Android中實(shí)現(xiàn)布局背景模糊化處理的方法
這篇文章主要介紹了Android中實(shí)現(xiàn)布局背景模糊化處理的方法,需要的朋友可以參考下2015-04-04使用Kotlin開(kāi)發(fā)Android應(yīng)用教程
這篇文章主要為大家詳細(xì)介紹了使用Kotlin開(kāi)發(fā)Android應(yīng)用的教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Android CardView+ViewPager實(shí)現(xiàn)ViewPager翻頁(yè)動(dòng)畫(huà)的方法
本篇文章主要介紹了Android CardView+ViewPager實(shí)現(xiàn)ViewPager翻頁(yè)動(dòng)畫(huà)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Android自定義View實(shí)現(xiàn)彈性小球效果
前段時(shí)間看到一個(gè)功能,是一個(gè)小球沿著固定軌跡彈動(dòng)的效果,那么這篇文章小編給大家分享在Android中如何自定義View來(lái)實(shí)現(xiàn)彈性小球的效果,有需要的可以參考借鑒。2016-09-09