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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android音频降噪webrtc

發布時間:2024/1/8 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android音频降噪webrtc 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在音頻處理的開源項目中,webrtc是一個很不錯的例子。它包含降噪,去回聲,增益,均衡等音頻處理。這里我講講我所使用到的如何使用降噪方式。當然,具體它是如何降噪的,大家可以細看源碼處理了。好了,線上源碼。

以下是java 層MainActivity.java:

package com.test.jni;import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.AudioTrack; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.SeekBar;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream;public class MainActivity extends AppCompatActivity implements View.OnClickListener{SeekBar skbVolume;//調節音量boolean isProcessing = true;//是否錄放的標記boolean isRecording = false;//是否錄放的標記static final int frequency = 8000;static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;int recBufSize, playBufSize;AudioRecord audioRecord;AudioTrack audioTrack;private String outFilePath;private OutputStream mOutputStream;private static final int FLAG_RECORD_START = 1;private static final int FLAG_RECORDING = 2;private static final int FLAG_RECORD_FINISH = 3;private WebrtcProcessor mProcessor;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//獲得合適的錄音緩存大小recBufSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);Log.e("", "recBufSize:" + recBufSize);//獲得合適的播放緩存大小playBufSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);//創建錄音和播放實例audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, recBufSize);audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency, channelConfiguration, audioEncoding, playBufSize, AudioTrack.MODE_STREAM);findViewById(R.id.btnRecord).setOnClickListener(this);findViewById(R.id.btnStop).setOnClickListener(this);skbVolume = (SeekBar) this.findViewById(R.id.skbVolume);skbVolume.setMax(100);//音量調節的極限skbVolume.setProgress(50);//設置seekbar的位置值audioTrack.setStereoVolume(0.7f, 0.7f);//設置當前音量大小skbVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {float vol = (float) (seekBar.getProgress()) / (float) (seekBar.getMax());audioTrack.setStereoVolume(vol, vol);//設置音量}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}});((CheckBox) findViewById(R.id.cb_ap)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton view, boolean checked) {isProcessing = checked;}});initProccesor();}@Overrideprotected void onDestroy() {releaseProcessor();android.os.Process.killProcess(android.os.Process.myPid());super.onDestroy();}@Overridepublic void onClick(View v) {if (v.getId() == R.id.btnRecord) {isRecording = true;//啟動線程,開始錄音和一邊播放new RecordPlayThread().start();} else if (v.getId() == R.id.btnStop) {isRecording = false;}}class RecordPlayThread extends Thread {public void run() {try {short[] buffer = new short[recBufSize/2];audioRecord.startRecording();//開始錄制audioTrack.play();//開始播放saveToFile(FLAG_RECORD_START, null);while (isRecording) {//setp 1 從MIC保存數據到緩沖區int bufferReadResult = audioRecord.read(buffer, 0, recBufSize/2);short[] tmpBuf_src = new short[bufferReadResult];System.arraycopy(buffer, 0, tmpBuf_src, 0, bufferReadResult);//setp 2 進行處理if (isProcessing) {processData(tmpBuf_src);} else {}//寫入數據即播放audioTrack.write(tmpBuf_src, 0, tmpBuf_src.length);//saveToFile(FLAG_RECORDING, tmpBuf_src);}saveToFile(FLAG_RECORD_FINISH, null);audioTrack.stop();audioRecord.stop();} catch (Exception t) {t.printStackTrace();}}};class RecordPlayThread2 extends Thread {public void run() {try {byte[] buffer = new byte[recBufSize];audioRecord.startRecording();//開始錄制audioTrack.play();//開始播放saveToFile(FLAG_RECORD_START, null);while (isRecording) {//setp 1 從MIC保存數據到緩沖區int bufferReadResult = audioRecord.read(buffer, 0, recBufSize);byte[] tmpBuf_src = new byte[bufferReadResult];System.arraycopy(buffer, 0, tmpBuf_src, 0, bufferReadResult);//setp 2 進行處理if (isProcessing) {processData(tmpBuf_src);} else {}//寫入數據即播放audioTrack.write(tmpBuf_src, 0, tmpBuf_src.length);saveToFile(FLAG_RECORDING, tmpBuf_src);}saveToFile(FLAG_RECORD_FINISH, null);audioTrack.stop();audioRecord.stop();} catch (Exception t) {t.printStackTrace();}}};/*** 保存錄音數據到本地wav文件* @param flag* @param data*/private void saveToFile(int flag, byte[] data){switch (flag){case FLAG_RECORD_START:String pcmPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/record/record.pcm";try {mOutputStream = new FileOutputStream(pcmPath);} catch (FileNotFoundException e) {e.printStackTrace();}break;case FLAG_RECORDING:if(mOutputStream != null){try {mOutputStream.write(data);} catch (IOException e) {e.printStackTrace();}}break;case FLAG_RECORD_FINISH:try {if(mOutputStream != null){mOutputStream.close();}} catch (IOException e) {e.printStackTrace();}pcmPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/record/record.pcm";String wavePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/record/record.wav";AudioEncodeUtil.convertPcm2Wav(pcmPath, wavePath);break;}}/*** 初始化降噪*/private void initProccesor(){mProcessor = new WebrtcProcessor();mProcessor.init(frequency);}/*** 釋放降噪資源*/private void releaseProcessor(){if(mProcessor != null){mProcessor.release();}}/*** 處理需要降噪的音頻數據* @param data*/private void processData(byte[] data){if(mProcessor != null){mProcessor.processNoise(data);}}/*** 處理需要降噪的音頻數據* @param data */private void processData(short[] data){if(mProcessor != null){mProcessor.processNoise(data);}}}

以上代碼主要是實現一邊錄音,一邊播放錄音的聲音,類似ktv,在其中對每次獲取的錄音數據tmpBuf_src交給WebrtcProcessor處理,這里可以讀取為byte[]或者short[] 數據,但是交給底層webrtc處理時,都是需要轉換為short[] 數據的。然后這邊采樣率我采用8000,采樣編碼位16,單聲道。

接下來看看WebrtcProcessor.java的處理:

package com.test.jni;import android.util.Log;/*** 音頻降噪處理*/ public class WebrtcProcessor {static {try {//加載降噪庫System.loadLibrary("webrtc");} catch (UnsatisfiedLinkError e) {Log.e("TAG", "Couldn't load lib: - " + e.getMessage());}}/*** 處理降噪* @param data*/public void processNoise(byte[] data){if(data == null) return;int newDataLength = data.length/2;if(data.length % 2 == 1){newDataLength += 1;}//此處是將字節數據轉換為short數據short[] newData = new short[newDataLength];for(int i=0; i<newDataLength; i++){byte low = 0;byte high = 0;if(2*i < data.length){low = data[2*i];}if((2*i+1) < data.length){high = data[2*i+1];}newData[i] = (short) (((high << 8) & 0xff00) | (low & 0x00ff));}// 交給底層處理processNoise(newData);//處理完之后, 又將short數據轉換為字節數據for(int i=0; i<newDataLength; i++){if(2*i < data.length){data[2*i] = (byte) (newData[i] & 0xff);}if((2*i+1) < data.length){data[2*i+1] = (byte) ((newData[i] >> 8) & 0xff);}}}/*** 初始化降噪設置* @param sampleRate 采樣率* @return 是否初始化成功*/public native boolean init(int sampleRate);/*** 處理降噪* @param data* @return*/public native boolean processNoise(short[] data);/*** 釋放降噪資源*/public native void release();}

此處你可能需要將字節數據轉換為short數據,要特別小心,如果不小心轉錯了,你的音頻數據就亂碼了,來的后果是,聽到的聲音基本都是沙沙聲,我之前就是在這里踩了坑,底層調試了很久也沒解決,后面才意識到可能上層出錯了,調試之后發現是這里。正常呢,調試時看short數據時,如果它們的數值不是很大,那應該是沒問題的,如果大部分都是4000以上,或者-4000以下的,那很可能是轉換的時候出問題了,一般來說數值都是幾十,幾百的樣子。

好了,現在看看底層大概是如何實現的:

#include <jni.h> #include "audio_ns.h" #include "noise_suppression.h"//此處是為了里面的底層方法能被java層識別 extern "C" {//降噪的實例,句柄 NsHandle* handle = NULL;//降噪處理 void innerProcess(short in_sample[], short out_sample[], int length){int curPosition = 0;//此處以160為單位, 依次調用audio_ns_process處理數據,因為這個方法一次只能處理160個short音頻數據while(curPosition < length){audio_ns_process((int) handle, in_sample + curPosition, out_sample + curPosition);curPosition += 160;}}JNIEXPORT jboolean JNICALL Java_com_test_jni_WebrtcProcessor_init(JNIEnv *env, jobject instance, jint sample_rate) {//初始化降噪實例handle = (NsHandle *) audio_ns_init(sample_rate);return false; }JNIEXPORT jboolean JNICALL Java_com_test_jni_WebrtcProcessor_processNoise(JNIEnv *env, jobject instance, jshortArray sample) {if(!handle)return false;//獲取數據長度jsize length = env->GetArrayLength(sample);//轉換為jshort數組jshort *sam = env->GetShortArrayElements(sample, 0);//將sam的數據全部復制給新的in_sampleshort in_sample[length];for(int i=0; i<length; i++){in_sample[i] = sam[i];}//傳入in_sample作為需要處理音頻數據, 處理之后的數據返回到sam中innerProcess(in_sample, sam, length);//將sam中的數據,再轉換回sample中env->ReleaseShortArrayElements(sample, sam, 0);return true; }JNIEXPORT void JNICALL Java_com_test_jni_WebrtcProcessor_release(JNIEnv *env, jobject instance) {// 釋放降噪資源if(handle){audio_ns_destroy((int) handle);}}}

上面代碼描述的比較清晰了,就是實際上webrtc降噪一次性只處理了80個short數據,在8000采樣率中是這樣的,意思就是說webrtc每次只能處理10毫秒,0.01秒的數據。那么依次類推,針對44100采樣率的數據處理的話,每次能處理的數據長度就應該是441個short數據了,有不同采樣率需求的朋友,可以自行修改測試。接下來看看webrtc的降噪是如何初始化和處理的:

#include "audio_ns.h" #include "noise_suppression.h"#include <stdio.h>int audio_ns_init(int sample_rate){NsHandle* NS_instance;int ret;//創建WebRtcNs實例if ((ret = WebRtcNs_Create(&NS_instance) )) {printf("WebRtcNs_Create failed with error code = %d", ret);return ret;}//初始化WebRtcNs實例,此處需要指定采樣,告訴它一次可以處理多少個short音頻數據,//如果是8000, 則一次可以處理80,如果是44100, 則一次可以處理441個//也就是說,一次性可以處理10ms時間的數據if ((ret = WebRtcNs_Init(NS_instance, sample_rate) )) {printf("WebRtcNs_Init failed with error code = %d", ret);return ret;}//設置降噪的力度,0,1,2, 0最弱,2最強if ( ( ret = WebRtcNs_set_policy(NS_instance, 2) ) ){printf("WebRtcNs_set_policy failed with error code = %d", ret);return ret;}return (int)NS_instance; }int audio_ns_process(int ns_handle , short *src_audio_data ,short *dest_audio_data){//get handleNsHandle* NS_instance = (NsHandle* )ns_handle;//noise suppressionif(//此處這么做,是因為,真正的WebRtcNs_Process,一次只能處理80個shorts音頻數據WebRtcNs_Process(NS_instance ,src_audio_data ,NULL ,dest_audio_data , NULL) ||WebRtcNs_Process(NS_instance ,&src_audio_data[80] ,NULL ,&dest_audio_data[80] , NULL) ){printf("WebRtcNs_Process failed with error code = " );return -1;}return 0; }void audio_ns_destroy(int ns_handle){//釋放WebRtcNs資源WebRtcNs_Free((NsHandle *) ns_handle); }

以上是調用真正的webrtc代碼處理降噪了,注釋也比較詳細,大家自己看。

那么真正底層的webrtc處理降噪是怎么樣的呢?這個,呵呵,我覺得吧,淺嘗則止,這個不是一般能看懂的,我是看不懂,核心大部分是算法,如果不熟悉降噪的算法和各個數據的意義的話,那看著簡直是看天書啊。當然啦,大家想看的或者想要源碼進行測試的話,我后面會提供項目源碼下載的。

你以為這樣就完了嗎?那好像還不夠豐富啊,因此還有一點我想分享給大家的,就是音頻處理中,還有最開始我所說過的各種音頻處理,絕不僅僅只有降噪,在當前網上開放的android音頻處理項目源碼如此稀缺的環境中(我想說,真是百度了好久的android音頻處理,卻找不出幾個可以運行測試的android項目源碼,實在香菇),該如何進行其它的音頻處理呢。幸運的是,webrtc這個項目里,提供了很多音頻處理的模塊,大家可以去網上把它下載下來,找到對應的模塊,比如增益,在webrtc/modules/audio_processing/agc目錄下,把里面的文件拷到自己項目中編譯,當然可能還會設計到其它目錄的文件,找到拷過來,后面應該就可以編譯了。至于怎么編譯,找到我項目中的CMakeList.txt文件,依葫蘆畫瓢,替換修改就是了。

可是好像還有一個問題,那就是編譯之后我該怎么用啊??這個,才是重點啊!是啊,我當時也是一頭霧水。好吧,本著助人為樂的精神(嘿嘿),我就傳授一個從不外傳的絕技吧(好像好高級,好期待啊),那就是搜索github(程序員都該知道的超牛逼網站),比如我想找webrtc降噪,那么我就搜WebRtcNs_Process, 找到一些合適的項目,看人家是如何調用實現的,這樣就可以實現啦。

好了,就說到這里,等著吃飯了。下面是項目下載地址。

http://download.csdn.net/detail/hesong1120/9687830

總結

以上是生活随笔為你收集整理的android音频降噪webrtc的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。