日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

音频降噪在58直播中的研究与实现

發(fā)布時間:2025/7/25 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 音频降噪在58直播中的研究与实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

背景

在直播時主播經(jīng)常會受到一些外部環(huán)境音、噪音等影響,直播時音頻采集會一并采集所有音頻推流到觀眾設(shè)備上,從而影響觀眾收聽體驗。因此需要在直播主播端主動進(jìn)行降噪處理,提高觀眾收聽體驗。

58直播為了實現(xiàn)這個功能,通過綜合對比調(diào)研常見的開源降噪方案Speex、WebRTC、RNNoise,以及結(jié)合降噪之后的處理效果和58直播使用體驗,最終選擇WebRTC降噪方案。我們對其進(jìn)行了優(yōu)化兼容,將其移植應(yīng)用到58視頻直播中,提升直播效果和體驗。

降噪方案

常見的開源降噪方案

  • Speex

Speex是一套主要針對語音的開源免費,無專利保護的應(yīng)用集合,它不僅包括編解碼器,還包括VAD(語音檢測)、DTX(不連續(xù)傳輸)、AEC(回聲消除)、NS(去噪)等實用模塊。

  • WebRTC

WebRTC提供了視頻會議的核心技術(shù),包括音視頻的采集、編解碼、網(wǎng)絡(luò)傳輸、顯示等功能,并且還支持跨平臺:Windows、Linux、Mac、Android。我們這里使用的就是WebRTC的音頻處理模塊audio_processing。

  • RNNoise

RNNoise降噪算法是根據(jù)純語音以及噪聲通過GRU訓(xùn)練來做。包含特征點提取、預(yù)料等核心部分。

RNNoise降噪算法與傳統(tǒng)算法對比分析

傳統(tǒng)降噪算法大部分是估計噪聲+維納濾波,噪聲估計的準(zhǔn)確性是整個算法效果的核心。根據(jù)噪聲的不同大部分處理是針對平穩(wěn)噪聲以及瞬時噪聲來做。

RNNoise的優(yōu)點主要是一個算法通過訓(xùn)練可以解決所有噪聲場景以及可以優(yōu)化傳統(tǒng)噪聲估計的時延和收斂問題。

RNNoise的缺點是深度學(xué)習(xí)算法落地問題。因為相對大部分傳統(tǒng)算法,RNNoise訓(xùn)練要得到一個很好的效果,由于特征點個數(shù)、隱藏單元的個數(shù)以及神經(jīng)網(wǎng)絡(luò)層數(shù)的增加,導(dǎo)致模型增大,運行效率。

現(xiàn)在就WebRTC和RNNoise的降噪集成效果進(jìn)行對比驗證分析。

降噪音頻數(shù)據(jù)對比

音頻原始PCM數(shù)據(jù)可通過Audacity軟件進(jìn)行分析

下圖是58公司司慶直播時的截取一段音頻數(shù)據(jù),音頻為雙聲道、44100采樣率。分別用RNNoise和WebRTC進(jìn)行降噪處理得出效果對比圖如下:

下圖是網(wǎng)絡(luò)下載的一段帶有噪音的音頻數(shù)據(jù),音頻為單聲道、32000采樣率。分別用RNNoise和WebRTC進(jìn)行降噪處理得出效果對比圖如下:

綜合上面兩張效果圖可以結(jié)論出:

  • RNNoise處理之后的數(shù)據(jù)更干凈些,幾乎沒有電流音和雜音,但是受限于訓(xùn)練集、特征點問題,在處理一些數(shù)據(jù)時候會把正常的原聲數(shù)據(jù)一并錯誤處理掉。

  • WebRTC處理之后的數(shù)據(jù)也相對干凈,能更好的保持原有聲音的數(shù)據(jù),數(shù)據(jù)丟失較少。

降噪方案在直播實現(xiàn)

降噪方案調(diào)研過程

RNNoise過程

  • RNNoise的代碼是基于C開源的,集成到Android中需要使用NDK。

  • 開源項目提供的一個測試方法,但是該方法是針對文件處理的,可以把一個帶噪音的PCM文件處理成無噪音文件。直播SDK中的音頻數(shù)據(jù)是分段的byte數(shù)組數(shù)據(jù),所以中間需要添加一些接口來讓RNNoise來支持分段數(shù)據(jù)的降噪處理。

  • 根據(jù)RNNoise的降噪過程和業(yè)務(wù)接口流程,把接口定義成init、process、free三個接口。

  • 在process數(shù)據(jù)時發(fā)現(xiàn)RNNosie的處理窗口大小是480,所以傳入的數(shù)據(jù)也必須是480的正整數(shù)倍。如果不是的話處理之后會有明顯的新引入噪音。

#define FRAME_SIZE_SHIFT 2#define FRAME_SIZE (120\u0026lt;\u0026lt;FRAME_SIZE_SHIFT)#define WINDOW_SIZE (2*FRAME_SIZE)

*通過測試發(fā)現(xiàn)這個窗口大小是可以進(jìn)行微調(diào)的,為了方便音頻數(shù)據(jù)的處理嘗試大小修改長512,雖然通過Audacity分析頻譜發(fā)現(xiàn)會有一些噪音波出現(xiàn),但是在實際感觀中效果還是可以接受的。這個方案可以臨時解決非480正整數(shù)倍數(shù)的問題。

//強制修改FRAME_SIZE大小#define FRAME_SIZE (128\u0026lt;\u0026lt;FRAME_SIZE_SHIFT)
  • 開源代碼中的rnn_data.c和rnn_data.h是通過機器學(xué)習(xí)訓(xùn)練出來的,不是通用的。在處理一個噪音數(shù)據(jù)時發(fā)現(xiàn)有些數(shù)據(jù)中的原聲也會一并處理掉,這個效果如果不通過新的數(shù)據(jù)集訓(xùn)練那么降噪之后的數(shù)據(jù)是不可用的。
/*This file is automatically generated from a Keras model*/#ifndef RNN_DATA_H#define RNN_DATA_H#include \u0026quot;rnn.h\u0026quot;#define INPUT_DENSE_SIZE 24extern const DenseLayer input_dense;#define VAD_GRU_SIZE 24extern const GRULayer vad_gru;#define NOISE_GRU_SIZE 48extern const GRULayer noise_gru;#define DENOISE_GRU_SIZE 96extern const GRULayer denoise_gru;#define DENOISE_OUTPUT_SIZE 22extern const DenseLayer denoise_output;#define VAD_OUTPUT_SIZE 1extern const DenseLayer vad_output; struct RNNState { float vad_gru_state[VAD_GRU_SIZE]; float noise_gru_state[NOISE_GRU_SIZE]; float denoise_gru_state[DENOISE_GRU_SIZE];};#endif
  • 機器學(xué)習(xí)和訓(xùn)練是RNNoise的靈魂,需要業(yè)務(wù)接入方根據(jù)自身的使用場景通過大量的數(shù)據(jù)集來找出最合適的處理集。

WebRTC過程

  • WebRTC的代碼是基于C++開源的,集成到Android中需要使用NDK。

  • WebRTC官方?jīng)]有提供降噪增益的測試代碼,需要查找相關(guān)資料找到其中的降噪、增益模塊,通過資料去熟悉其中的處理邏輯。

  • WebRTC只能處理特定的采樣率數(shù)據(jù),這個是其代碼內(nèi)部是寫死的,需要自己實現(xiàn)音頻重采樣來滿足WebRTC的降噪采樣率需求。音頻的重采樣算法有很多,在項目集成中都嘗試使用過,效果都是差不多的。

// WebRTC處理支持的采樣率// Initialization of struct.if (fs == 8000 || fs == 16000 || fs == 32000 || fs == 44100 || fs == 48000) { self-\u0026gt;fs = fs;} else { return -1;}
  • 根據(jù)WebRTC的降噪過程和業(yè)務(wù)接口流程,把接口定義成init、process、free三個接口。區(qū)別RNNoise的是需要在process中做增益處理,WebRTC降噪會降低數(shù)據(jù)的聲音大小,通過增益用來補充聲音大小。

  • 在process數(shù)據(jù)時發(fā)現(xiàn)WebRTC的處理窗口大小必須是160或是320個byte,根據(jù)采樣率不同窗口大小不同。測試發(fā)現(xiàn)這個和處理RNNoise是一致都只能傳正整數(shù)倍數(shù)據(jù),要不還是會新引入噪音數(shù)據(jù)。

if (fs == 8000) { self-\u0026gt;blockLen = 80; self-\u0026gt;anaLen = 128; self-\u0026gt;window = kBlocks80w128;} else { self-\u0026gt;blockLen = 160; self-\u0026gt;anaLen = 256; self-\u0026gt;window = kBlocks160w256;}
  • WebRTC在process時有兩種處理數(shù)據(jù)的方法:一種是需要把原始數(shù)據(jù)分成高頻數(shù)據(jù)和低頻數(shù)據(jù)給底層邏輯;一種是不用區(qū)分高低頻數(shù)據(jù)直接把數(shù)據(jù)給底層邏輯。資料上的解釋是32k以上需要分高低頻處理。但是在實際測試中發(fā)現(xiàn)分高低頻的處理效果不如不分高低頻的效果好。

  • WebRTC的降噪NS模塊和增益AGC模塊是獨立的,為了一次數(shù)據(jù)完成兩個過程需要組合數(shù)據(jù),邊降噪邊增益,減少處理耗時。

  • WebRTC_NS在處理數(shù)據(jù)時不應(yīng)該選擇高低頻分開采樣處理,應(yīng)直接把數(shù)據(jù)給你WebRTC_NS處理就可以。經(jīng)過測試發(fā)現(xiàn)通過高低頻處理之后的音頻降噪效果不如不區(qū)分高低頻的,高低頻處理之后會有明顯的人聲破音出現(xiàn),且處理的降噪效果不純凈。這個地方走了一些彎路,在發(fā)現(xiàn)降噪效果不理想時沒有懷疑是api使用的問題,這個高低頻操作是很多資料都推薦的使用方法,但是在運用到實際場景時發(fā)現(xiàn)效果不如不使用的。

兩種降噪方案集成優(yōu)缺點對比

  • 目前WebRTC最新代碼只支持采樣率為8000、16000、32000、44100、48000的音頻進(jìn)行降噪,針對其余的采樣率需要進(jìn)行數(shù)據(jù)重采樣到上述采樣率之后進(jìn)行降噪,處理完畢之后需要再次恢復(fù)原采樣率;RNNoise對采樣率沒有要求,可以適配常見的采樣率。

  • WebRTC在降噪之后還需要對數(shù)據(jù)進(jìn)行增益處理,但是增益會增大電流音,效果會稍差些。

  • WebRTC處理數(shù)據(jù)的buffer目前代碼是320的整數(shù)倍;RNNoise處理數(shù)據(jù)的buffer目前代碼是480的整數(shù)倍。輸入的buffer需是固定大小的,如果不是正整數(shù)倍,需要外部在傳入時處理下。

  • 從代碼復(fù)雜度看,WebRTC的代碼是多于RNNoise代碼的。RNNoise支持機器學(xué)習(xí),通過機器學(xué)習(xí)生成rnn_data.h和rnn_data.c文件來匹配不同的降噪效果。

  • 降噪耗時對比,RNNoise處理3840字節(jié)的buffer數(shù)據(jù)耗時大概在6ms左右,但在開始時耗時在30ms左右,遞減到6ms并穩(wěn)定;WebRTC處理3840字節(jié)的buffer數(shù)據(jù)耗時大概在2ms左右,但在開始時耗時在10ms左右,遞減到2ms并穩(wěn)定。對比發(fā)現(xiàn)WebRTC處理效率更好些。

  • 從處理流程上看都是需要init、process、free操作的,對接入方接入成本是一致的。

安卓端58直播SDK接入降噪方案

通過上章節(jié)的優(yōu)缺點對比以及58直播中已經(jīng)在使用了WebRTC相關(guān)代碼邏輯,綜合調(diào)研和處理結(jié)果驗證工作之后,最終選擇了WebRTC降噪方案。

APM模塊集成WebRTC降噪功能

  • 在58多媒體整體架構(gòu)上選擇把降噪模塊單獨解耦提取一個APM module,方便58視頻編輯、58直播等需要降噪業(yè)務(wù)統(tǒng)一調(diào)用。對外暴露工具類AudioNoiseHelp方便業(yè)務(wù)接入。APM module的規(guī)劃以后會接入更多的音頻處理模塊,現(xiàn)在已經(jīng)接入降噪、增益模塊。

  • 由于58直播SDK支持音頻采樣率種類大于WebRTC支持的種類,因此需要對數(shù)據(jù)進(jìn)行最優(yōu)音頻重采樣處理。

/** * 音頻重采樣 * * @param sourceData 原始數(shù)據(jù) * @param sampleRate 原始采樣率 * @param srcSize 原始數(shù)據(jù)長度 * @param destinationData 重采樣之后的數(shù)據(jù) * @param newSampleRate 重采樣之后的數(shù)據(jù)長度 */void resampleData(const int16_t *sourceData, int32_t sampleRate, uint32_t srcSize, int16_t *destinationData,int32_t newSampleRate){ if (sampleRate == newSampleRate) { memcpy(destinationData, sourceData, srcSize * sizeof(int16_t)); return; } uint32_t last_pos = srcSize - 1; uint32_t dstSize = (uint32_t) (srcSize * ((float) newSampleRate / sampleRate)); for (uint32_t idx = 0; idx \u0026lt; dstSize; idx++) { float index = ((float) idx * sampleRate) / (newSampleRate); uint32_t p1 = (uint32_t) index; float coef = index - p1; uint32_t p2 = (p1 == last_pos) ? last_pos : p1 + 1; destinationData[idx] = (int16_t) ((1.0f - coef) * sourceData[p1] + coef * sourceData[p2]); }}
  • 由于WebRTC只能處理320byte長度正整數(shù)倍的數(shù)據(jù),但是58直播的音頻采集數(shù)據(jù)在不同手機、不同采樣率上得到的音頻數(shù)據(jù)長度是不固定的,需要對數(shù)據(jù)進(jìn)行切分處理。錄音采集數(shù)據(jù)如果是byte格式的,假如長度是4096,那么直接把4096數(shù)據(jù)傳入到WebRTC_NS里處理會出現(xiàn)雜音出現(xiàn),所以在交給WebRTC_NS模塊之前需要用個緩沖區(qū)來處理下,一次最多可以傳入(4096/320)*320=3840長度數(shù)據(jù),并且在數(shù)據(jù)處理完畢之后還需要用另外一個緩沖區(qū)來保證處理之后的長度仍然是4096個。
//部分邏輯代碼如下所示://這個數(shù)據(jù)拆分和緩沖區(qū)數(shù)據(jù)邏輯可以由業(yè)務(wù)方自行出/** * 降噪處理方法,數(shù)據(jù)異步處理之后通過回調(diào)方法通知給調(diào)用方。 * * @param bytes 音頻數(shù)據(jù) * @param nsProcessListener 異步方法回調(diào) */public void webRtcNsProcess(byte[] bytes, INsProcessListener nsProcessListener) { if (isAudioNsInitOk) { synchronized (TAG) { if (null == bytes || bytes.length == 0) { return; } ... int byteLen = bytes.length; if (inByteLen != byteLen) { inByteLen = byteLen; webRtcNsInit(byteLen); } int frames = byteLen / FRAME_SIZE; int lastFrame = byteLen % FRAME_SIZE; int frameBufferLen = frames * FRAME_SIZE; byte[] buf = new byte[frameBufferLen]; Log.d(TAG, \u0026quot;webRtcNsProcess inBufferSize:\u0026quot; + inBufferSize); if (inBufferSize \u0026gt;= frameBufferLen) { Log.d(TAG, \u0026quot;webRtcNsProcess mInByteBuffer full\u0026quot;); nsProcessInner(buf, nsProcessListener); } ... nsProcessInner(buf, nsProcessListener); } } else { if (null != nsProcessListener) { nsProcessListener.onProcess(bytes); } }}private void nsProcessInner(byte[] buf, INsProcessListener nsProcessListener) { mInByteBuffer.rewind(); mInByteBuffer.get(buf, 0, buf.length); byte[] inBufferLeft = new byte[inBufferSize - mInByteBuffer.position()]; ... byte[] nsProcessData = AudioNoiseUtils.webRtcNsProcess(buf); byte[] outBuf = new byte[inByteLen]; ... if (outBufferSize \u0026gt;= inByteLen) { ... byte[] outBufferLeft = new byte[outBufferSize - mOutByteBuffer.position()]; ... mOutByteBuffer.put(outBufferLeft); outBufferSize += outBufferLeft.length; if (null != nsProcessListener) { nsProcessListener.onProcess(outBuf); } }}

58直播接入降噪之后的效果對比

  • 時域?qū)Ρ?/li>

下圖中藍(lán)色部分是58直播時截取的一段未開啟降噪邏輯的音頻波形dB圖,綠色部分是58直播時截取的一段開啟降噪邏輯的音頻波形dB圖。從時域波形圖對比上可以看到開啟降噪邏輯之后波形更加清晰了,降噪效果比較明顯。

  • 頻譜圖對比

下圖中上半部分是58直播時截取的一段未開啟降噪邏輯音頻的頻譜圖,下半部分是58直播時截取的一段開啟降噪邏輯音頻的頻譜圖。從頻譜圖對比上可以看到開啟降噪邏輯之后噪音的頻譜被去除掉,音頻數(shù)據(jù)的原始數(shù)據(jù)更加清晰突出。

  • 主觀感覺對比

在同樣的噪音環(huán)境下通過開啟和關(guān)閉降噪功能,在觀眾端體驗收聽效果。未開啟降噪功能時觀眾端可以明顯的聽到沙沙的雜音,開啟降噪功能之后沙沙聲音明顯減少或沒有,對應(yīng)的主播的聲音凸顯出來。

總結(jié)

本文分享了58直播在降噪方面所做的一些調(diào)研實踐經(jīng)驗,重點闡述了其中的一些痛點和難點問題以及我們的解決方案。由于RNNoise降噪方案的優(yōu)勢是存在的,在后續(xù)研究中會對RNNoise的深度學(xué)習(xí)繼續(xù)進(jìn)行深入了解,期望能更好的解決噪音問題,更好的提升直播體驗。也希望能有更多朋友一起來探討更優(yōu)的解決方案。

本文作者:金開龍,來自58集團TEG多媒體部,安卓高級工程師,專注音視頻開發(fā)。

總結(jié)

以上是生活随笔為你收集整理的音频降噪在58直播中的研究与实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。