目录

·········前言

·········前期工作

·········编译工作

·········编码工作(AGC-JAVA)

·········编码工作(AGC-JNI)

·········编码工作(NS-JAVA)

········编码工作(NS-JNI)

·········总结

前言

    Android 单独抽取 WebRtc-AGC(音频增益) 模块

       Android 单独抽取 WebRtc-NS(音频降噪) 模块

本文章是基于某个博客主写的移植到Android NDK上编译(这边我没有用cMake)

https://www.cnblogs.com/mod109/p/5767867.html

WebRtc单独模块编译的资料网上很多,零零散散,但是看了很多程序大多数都是旧的版本模块源码进行编译的,并且都是在

C++/C  的基础上去运行,很少涉及到在NDK下的编译和JNI的使用。

后续会更新NS(降噪),aecm,aec(回音消除) 模块在NDK上编译JNI上使用的文章。

笔者也是今年才开始正式的接触WebRt,目前正在深入分析其源代码,主要研究的是 底层模块(非算法)、Android模块,希

望后续能把一些经验分享出来。

前期工作

1:Android Studio 开发工具 ,再次强调一下,这次没有使用 cMake 编译,使用的是传统的 ndk-build 编译,开发工具需要自行

配置一下,当然后面会提供源码  ,你完全可以 自己来配置cMake编译。

2:首先不认识WebRtc的请先百一下度,特别是模块的作用,如果是大佬的话请洗耳恭听。

3 : audacity2.3.exe  下载一下这个工具,干什么用 搜一下就知道。

长什么样,长这样  ↓

如何导入PCM数据:文件->导入->选择PCM数据->设置对应的采样率等参数

4:本章文重点讲解NDK、JNI等使用,需要有一定的基础适合看本文章

编译工作

后续会把Demo上传,建议想做这些工作但是没做过的可以自行另起一个项目来做。

首先观澜我随手一建的项目工程目录

⑴ 这个是要处理的原始音频文件,当然你也可以直接从SD卡读取,为了方便,这边直接从assets 读取。

⑵ AudioProcessJni 为native 加载的类

其余为测试类和工具类。

(里面代码写的比较乱没有整理,demo下载完后自行调整 )

⑶ agc文件夹是  NS和AGC编译所需的头文件和依赖文件,最开始是先调试AGC的,所以文件夹名称没有改,

ns_jni_wrapper.c 为JNI层实现的功能

这里贴一下Android.mk

这玩意不简单的 , 里面有一些编译错误的默认配置项

code:            

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
APP_ABI := armeabi x86
LOCAL_MODULE    := webrtc_audio
app_platform:=android-21
LOCAL_SRC_FILES := \agc/complex_bit_reverse.c \agc/complex_fft.c \agc/cross_correlation.c \agc/dot_product_with_scale.c \agc/downsample_fast.c \agc/energy.c \agc/fft4g.c \agc/get_scaling_square.c \agc/min_max_operations.c \agc/real_fft.c \agc/resample.c \agc/resample_48khz.c \agc/resample_by_2.c \agc/resample_by_2_internal.c \agc/resample_by_2_mips.c \agc/copy_set_operations.c \agc/division_operations.c \agc/spl_init.c \agc/spl_sqrt.c \agc/spl_sqrt_floor.c \agc/ring_buffer.c \agc/resample_fractional.c \agc/splitting_filter.c \agc/vector_scaling_operations.c \agc/analog_agc.c \agc/digital_agc.c \agc/ns_core.c \agc/nsx_core.c \agc/nsx_core_c.c \agc/nsx_core_neon_offsets.c \agc/noise_suppression.c \agc/noise_suppression_x.c \ns_jni_wrapper.c \#undefined reference to 错误问题解决办法
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
APP_SHORT_COMMANDS := true
LOCAL_LDLIBS    := -llog
APP_CPPFLAGS := -frtti -std=c++11
include $(BUILD_SHARED_LIBRARY)

  Application.mk

#APP_ABI := armeabi armeabi-v7a x86
APP_ABI := armeabi x86
APP_STL := stlport_static
APP_CPPFLAGS := -frtti -std=c++11
APP_SHORT_COMMANDS := true
LOCAL_LDFLAGS += -fuse-ld=bfd

编码前当然你要先把so 编译过来才能调试

编译过程....(省略)

         好啦,当我们已经把so库成功的编出来了,并且能正常  loadLibrary

编码工作(AGC-JAVA)

首先看下 AudioProcessJni文件中对Agc 操作的native  函数有哪些?

public class AudioProcessJni {static{System.loadLibrary("webrtc_audio");  //加载native code的动态库}//Audio agc音频增益接口public   native  static  void  AgcFree();public   native  static  int   AgcFun(ByteBuffer buffer1 , short[] sArr, int frameSize);public   native  static  long  AgcInit(long minLevel, long maxLevel,long fs);public   native  static  void  AgcProcess();  //agc test  model
}

AgcInit : 初始化agc模块功能

AgcFun:音频增益处理函数

AgcFree:模块释放销毁

AgcProcess: 这个是增益整个搓成完全在JNI层处理,不和java 通信。

其中AgcFun中的  ByteBuffer  是java和JNI 交互数据的重点,他是java和JNI共享的一个内存块,当然你也可以给个返回值,看个人的编码习惯。

那么我们先来看java的测试代码:

 

 public static final  String  AGC_OUT_FILE_PATCH_DICTORY="/storage/emulated/0/Pictures/agc";public static final  String  AGC_OUT_FILE_PATCH="/storage/emulated/0/Pictures/agc/byby_8K_1C_16bit_agc.pcm";

文件的输出目录以及输出的文件。

  public static    void  agc_audio_test(Activity  act)  {try {int nbread = 0;//读取Assets文件流InputStream is = act.getAssets().open("byby_8K_1C_16bit.pcm");//输出的文件目录File  file = new File(AGC_OUT_FILE_PATCH_DICTORY);if (!file.exists()){boolean mkdirs = file.mkdirs();if (mkdirs) {log("create dictroy success");} else {log("create dictroy file");return;}}//输出的文件file = new File(AGC_OUT_FILE_PATCH);//调用初始刷Agc模块long res = AudioProcessJni.AgcInit(0, 255, 8000);log(" AudioProcessJni.AgcInit  res = "+ res);log("sleep 2s ");Thread.sleep(2000);//初始化byte转换工具BytesTransUtil bytesTransUtil = BytesTransUtil.getInstance();// rData 为读取的缓冲区 分配160字节byte[] rData = new byte[160];ByteBuffer  outBuffer = ByteBuffer.allocateDirect(160);FileOutputStream  fos = new FileOutputStream(file);//--------------------开始读取---------------------------while((nbread=is.read(rData))>-1){short[] shorts = bytesTransUtil.Bytes2Shorts(rData);res = AudioProcessJni.AgcFun(outBuffer, shorts,80);for (int i = 0 ;i< 80 ;i++){shorts[i] = (short) (outBuffer.get(2*i) + ( outBuffer.get(2*i+1) << 8));}fos.write(bytesTransUtil.Shorts2Bytes(shorts),0,nbread);}log(" 结束Agc = " );if (fos!=null){fos.close();}} catch (IOException e) {e.printStackTrace();log("e:error -> "+e.toString());} catch (InterruptedException e) {e.printStackTrace();}finally {AudioProcessJni.AgcFree();}}

这个是java测试的流程代码,也比较清晰,重点来讲解下以下这段代码

 //--------------------开始读取---------------------------while((nbread=is.read(rData))>-1){short[] shorts = bytesTransUtil.Bytes2Shorts(rData);res = AudioProcessJni.AgcFun(outBuffer, shorts,80);for (int i = 0 ;i< 80 ;i++){shorts[i] = (short) (outBuffer.get(2*i) + ( outBuffer.get(2*i+1) << 8));}fos.write(bytesTransUtil.Shorts2Bytes(shorts),0,nbread);}log(" 结束Agc = " );

① 循环读取文件流中的数据,每次读取160个byte, 有点需要注意,底层的处理接口需要传入的是short[] 数组,文件流读取的

byte[]数组需要转换, 这边的话在java 层转换或者在JNI层用C转换都是可以的,建议在JNI上转换,不过需要注意的是有无

符号的问题,我这边为了方便直接在java层进行转换。

② Byte2Shorts 吧 byte数组转换成了short数组 并传入AgcFun [JNI层] 去处理, 返回值res < 0 是错误的 这个根据JNI层的逻辑

去调整,我这边为了代码简洁,没有做判断。

③ 最后处理完的音频数据会存放在outBuffer 这个ByteBuff 的变量里,那么在上层就可以去读取它了。

刚开始的时候以为传上来就可以用了,然后打印了数据的十六进制,发现值不一样,正负数处理溢出的问题,这时候才慢慢

意识到,java  基本变量都是有符号的,因为底层是定义成无符号的,为什么定义成无符号这个研究JNI的时候再来说。

那么通过

                 shorts[i] = (short) (outBuffer.get(2*i) + ( outBuffer.get(2*i+1) << 8)); 

这个处理将数据还原成正确的。注 : 写文件操作在JNI层是很方便实现的,AgcProcess读写都是JNI层实现的,这边放到上层

考虑到有些人 java这边可能需要用到 这个处理完的buffer。 现在只是读取文件,其实从MIC 读取的PCM文件也基本类似。

因此:将数据还原成了short[]之后 写入文件还要转到byte[]

不知道各位小伙伴是否有更好的解决方法,或者看了我的demo之后又什么更好的解决办法一定要通知我。值得是有无符号

转换这一块。

编码工作(AGC-JNI)

现在来看下JNI的处理,其实也很简单一共3个函数先贴代码:

 初始化函数:

JNIEXPORT jlong JNICALL Java_com_webrtc_ns_AudioProcessJni_AgcInit(JNIEnv *env, jclass cls, jlong minLevel , jlong maxLevel , jlong fs){minLevel = 0;maxLevel = 255;agc_samples  =fs;int agcMode  = kAgcModeFixedDigital;LOGE("Java_com_webrtc_ns_AudioProcessJni_AgcInit!  -> %d \n", sizeof(short));if ( (   WebRtcAgc_Create(&agcHandle) ) != 0) { //allocate dynamic memory on native heap for NS instance pointed by hNS.LOGE("Noise_Suppression WebRtcNs_Create err! \n");return  NS_ERROR;  //error occurs}LOGE("Java_com_webrtc_ns_AudioProcessJni_AgcCreate success! \n");if (0 !=  WebRtcAgc_Init(agcHandle, minLevel, maxLevel, agcMode, agc_samples) ){LOGE("WebRtcAgc_Init WebRtcNs_Init err! \n");return  NS_ERROR;  //error occurs}LOGE("Java_com_webrtc_ns_AudioProcessJni_AgcInit success! \n");WebRtcAgc_config_t agcConfig;agcConfig.compressionGaindB = 20; //在Fixed模式下,越大声音越大agcConfig.limiterEnable     = 1;agcConfig.targetLevelDbfs   = 3;  //dbfs表示相对于full scale的下降值,0表示full scale,越小声音越大WebRtcAgc_set_config(agcHandle, agcConfig);return NS_SUCCESS;
}

处理函数 & 销毁函数:

JNIEXPORT jint JNICALL Java_com_webrtc_ns_AudioProcessJni_AgcFun(JNIEnv *env, jclass type, jobject jdirectBuff,jshortArray sArr_, jint frameSize) {if(agc_buffer == NULL){LOGE("gc_buffer == NULL! \n");void* buffer = (*env)->GetDirectBufferAddress(env,jdirectBuff);agc_buffer = buffer;}uint8_t saturationWarning;int outMicLevel = 0;int micLevelOut = 0;int i =0 ;int inMicLevel  = micLevelOut;const short *pData    = NULL;short *pOutData    = NULL;pOutData = (short*)malloc(frameSize*sizeof(short));pData  =(*env)->GetShortArrayElements(env,sArr_,NULL);if(agcHandle == NULL){LOGE("agcHandle is null! \n");return  -3;}if(frameSize <= 0){return  -2;}int  agcProcessResult =  WebRtcAgc_Process(agcHandle,pData,NULL,frameSize,pOutData,NULL,inMicLevel,&outMicLevel,0,&saturationWarning);if (0 !=  agcProcessResult ){LOGE("failed in WebRtcAgc_Process!  agcProcessResult = %d \n" ,agcProcessResult);return  NS_ERROR ;  //error occurs}//memset(agc_buffer, 0,  160);shortTobyte(80,pOutData,agc_buffer);(*env)->ReleaseShortArrayElements(env, sArr_, pData, 0);return  AGC_SUCCESS;
}JNIEXPORT void JNICALL Java_com_webrtc_ns_AudioProcessJni_AgcFree(JNIEnv *env , jclass  cls){WebRtcAgc_Free(agcHandle);
}

① 初始化函数看起来很简单,确实很简单。

关于一下

 WebRtcAgc_config_t agcConfig;agcConfig.compressionGaindB = 20; //在Fixed模式下,越大声音越大agcConfig.limiterEnable     = 1;agcConfig.targetLevelDbfs   = 3;  //dbfs表示相对于full scale的下降值,0表示full scale,越小声音越大

可以自己编译调试, 用工具去比对效果。

②  void* buffer = (*env)->GetDirectBufferAddress(env,jdirectBuff);   获取java层分配的ByteBuff 实例,最后一个 char指针指向了

这个 实例,需要注意的是 这里

    unsigned  char* agc_buffer = NULL;   

是无符号的,如果有符号的话后面可能会有溢出问题,所以在底层char指针表示的我这边都用无符号。

WebRtcAgc_Process 函数处理数据最后得到 short * 的数据 , 最后通过 shortTobyte 函数将short 的值赋给 ByteBuff 的实

例,这样java层就能得到这个数据。

③释放工作比较简单    WebRtcAgc_Free  即可。

编码工作(NS-JAVA)

降噪模块基本流程和增益模块大同小异。

目前处理的是8K采样率音频, 如果处理32K采样率音频,那么可能需要传入高频,低频数据做处理。

音频降噪部分较为简单,用起来也就几个函数,除了需要传入的音频数据以外,需要调整的参数也就是音频采样率和降噪等级。另外这部分代码采用纯C语言语法编写,可以跨平台编译。整个算法也不算特别复杂,运行起来占CPU也不是很多,可以上至i7 8核CPU的高配电脑,下至380Mhz的小嵌入式系统以及一切版本的移动端产品中编译运行。另外效果也十分不错,通过本文附带的测试demo大家就可以感受一下。

降噪有两部分代码,一套是定点算法(noise_suppression_x.h),一套是浮点算法(noise_suppression.h)。相对来说浮点算法精度更高,但是耗系统资源更多,特别是浮点计算能力较弱的低端ARM CPU上。但是一般来说浮点和定点算法在实际效果上听不出区别出来,估计是我没遇到过必须必须要用浮点的情况。在使用上,浮点和定点也仅仅只是变量名和函数没中多出了一个x。

音频处理的时候webrtc一次仅能处理10ms数据,小于10ms的数据不要传入,因为即时是传入小于10ms的数据最后传入也是按照10ms的数据传出,此时会出现问题。另外支持采样率也只有8K,16K,32K三种,不论是降噪模块,或者是回声消除增益等等均是如此。对于8000采样率,16bit的音频数据,10ms的时间采样点就是80个,一个采样点16bit也就是两个字节,那么需要传入WebRtcNsx_Process的数据就是160字节。对于8000和16000采样率的音频数据在使用时可以不管高频部分,只需要传入低频数据即可,但是对于32K采样率的数据就必须通过滤波接口将数据分为高频和低频传入,传入降噪后再组合成音频数据。大于32K的音频文件就必须要通过重采样接口降频到对应的采样率再处理,在demo源码里面有对应的接口使用者可以去查。

降噪的开源代码包括定点降噪,浮点降噪,音频重采样,滤波接口,抠出来后修改了头文件包含位置使其都在同一目录下,如果是xcode下编译可能会有once函数编译不过的问题,此时修改一个宏即可,为了尽量避免修改问题就不改动这部分了。

示例代码给了32K采样率的测试demo,也有8K采样率实现。16K采样率只需要将8K内部的数据大小由80改成160即可,PCM音频文件可以由Audacity软件通过文件--导入--原始数据,选择好bit位数,声道,采样率即可显示波形,播放。

public class TestNs {public static final  String  AGC_OUT_FILE_PATCH_DICTORY="/storage/emulated/0/Pictures/ns";public static final  String  AGC_OUT_FILE_PATCH="/storage/emulated/0/Pictures/ns/byby_8K_1C_16bit_ns_android.pcm";public  static    void  ns_audio_test(Activity act)  {try {File file = null;InputStream is = act.getAssets().open("byby_8K_1C_16bit.pcm");FileOutputStream fos = null;file = new File(AGC_OUT_FILE_PATCH_DICTORY);if (!file.exists()){boolean mkdirs = file.mkdirs();if (mkdirs) {log("create dictroy success");} else {log("create dictroy file");return;}}file = new File(AGC_OUT_FILE_PATCH);fos = new FileOutputStream(file);log("sleep 2s ");Thread.sleep(2000);long res = AudioProcessJni.initiateNSInstance(8000,3);if (res == 7 ){//success+}BytesTransUtil bytesTransUtil = BytesTransUtil.getInstance();byte[] rData = new byte[160];ByteBuffer outBuffer  =  ByteBuffer.allocateDirect(160);int nbread = 0;long startTime = System.currentTimeMillis();while((nbread=is.read(rData))>-1){short[] shorts = bytesTransUtil.Bytes2Shorts(rData);// ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(data);res = AudioProcessJni.processNS(outBuffer, shorts, 80);for (int i = 0 ;i< 80 ;i++){shorts[i] = (short) ( (outBuffer.get(2*i)&0x00FF )  + ( outBuffer.get(2*i+1) << 8));}fos.write(bytesTransUtil.Shorts2Bytes(shorts),0,nbread);}long endTime = System.currentTimeMillis();log(" 结束 Ns = 程序运行时间:"+(endTime-startTime)+"ms");if (fos!=null){fos.close();}} catch (IOException e) {e.printStackTrace();log("e:error -> "+e.toString());} catch (InterruptedException e) {e.printStackTrace();}finally {}}

编码工作(NS-JNI)

初始化函数  Java_com_webrtc_ns_AudioProcessJni_initiateNSInstance

降噪处理函数   Java_com_webrtc_ns_AudioProcessJni_processNS

销毁函数  Java_com_webrtc_ns_AudioProcessJni_destoryNS

//初始化频率的降噪结构体参数
/** nSample:采集数据的频率*/
JNIEXPORT jint JNICALL Java_com_webrtc_ns_AudioProcessJni_initiateNSInstance(JNIEnv *env, jclass cls,  jlong nSample, jint nMode){//创建webrtc降噪处理句柄//NsHandle *hNS = NULL; //create a pointer to NsHandle on native stack.if ( (  WebRtcNs_Create(&pNS_inst) ) !=  0) { //allocate dynamic memory on native heap for NS instance pointed by hNS.LOGE("Noise_Suppression WebRtcNs_Create err! \n");return  NS_ERROR;  //error occurs}if (0 !=  WebRtcNs_Init(pNS_inst,nSample)){LOGE("Noise_Suppression WebRtcNs_Init err! \n");return  NS_ERROR;  //error occurs}if (0 !=  WebRtcNs_set_policy(pNS_inst,nMode)){printf("Noise_Suppression WebRtcNs_set_policy err! \n");return  NS_ERROR;  //error occurs}// return ((int) (NSinst_t *) pNS_inst); //returns the address of NS instance on native heap.return NS_SUCCESS;
}
JNIEXPORT jint JNICALL  Java_com_webrtc_ns_AudioProcessJni_processNS(JNIEnv *env, jclass type, jobject jdirectBuff,jshortArray outframe_,jint sf) {jshort *outframe = (*env)->GetShortArrayElements(env, outframe_, NULL);short *pOutData    = NULL;int frameSize =sf;if(pNS_inst == NULL){LOGE("nsHandle is null! \n");return  -3;}if(ns_buffer == NULL){LOGE("gc_buffer == NULL! \n");void* buffer = (*env)->GetDirectBufferAddress(env,jdirectBuff);ns_buffer = buffer;}pOutData = (short*)malloc(frameSize*sizeof(short));if ( WebRtcNs_Process(pNS_inst, outframe, NULL, pOutData,NULL) != 0){LOGE("failed in WebRtcAgc_Process! \n" );return  NS_ERROR ;  //error occurs}shortTobyte(80,pOutData,ns_buffer);(*env)->ReleaseShortArrayElements(env, outframe_, outframe, 0);return  AGC_SUCCESS;}
JNIEXPORT jint JNICALL Java_com_webrtc_ns_AudioProcessJni_destoryNS(JNIEnv *env, jclass cls){WebRtcNs_Free(pNS_inst);
}

处理32K语言

以下当 sf=32*1000 时,进入处理32K语言的过程;

此过程全程在JNI中执行,读取pcm文件进行降噪处理

/*JNI全处理*/
JNIEXPORT void JNICALL  Java_com_webrtc_ns_AudioProcessJni_NSProcess(JNIEnv *env, jclass type ,jint sf) {const char *sFile     = "/storage/emulated/0/Pictures/ns/lhydd_1C_16bit_32K.pcm";const char *dFile     ="/storage/emulated/0/Pictures/ns/lhydd_1C_16bit_32K_ns.pcm";const char *sFile_8k     = "/storage/emulated/0/Pictures/ns/byby_8K_1C_16bit.pcm";const char *dFile_8k     ="/storage/emulated/0/Pictures/ns/byby_8K_1C_16bit_ns.pcm";// TODO  test agc modelchar *pInBuffer = NULL;char *pOutBuffer = NULL;int i = 0;long nFileSize = 0;if(sf == 32*1000) {FILE * fpIn = fopen(sFile, "rb");FILE * fpOut = fopen(dFile, "wb");if(fpIn == NULL || fpOut == NULL){LOGE("file open filed");return;}LOGE("NS Java_com_webrtc_ns_AudioProcessJni_NSProcess  32K   start");do {fseek(fpIn, 0, SEEK_END);nFileSize = ftell(fpIn);printf("nFileSize = %d  \n ", nFileSize);fseek(fpIn, 0, SEEK_SET);pInBuffer = (char *) malloc(nFileSize);memset(pInBuffer, 0, nFileSize);fread(pInBuffer, sizeof(char), nFileSize, fpIn);pOutBuffer = (char *) malloc(nFileSize);memset(pOutBuffer, 0, nFileSize);int filter_state1[6], filter_state12[6];int Synthesis_state1[6], Synthesis_state12[6];memset(filter_state1, 0, sizeof(filter_state1));memset(filter_state12, 0, sizeof(filter_state12));memset(Synthesis_state1, 0, sizeof(Synthesis_state1));memset(Synthesis_state12, 0, sizeof(Synthesis_state12));for (i = 0; i < nFileSize; i += 640) {if (nFileSize - i >= 640) {short shBufferIn[320] = {0};short shInL[160], shInH[160];short shOutL[160] = {0}, shOutH[160] = {0};memcpy(shBufferIn, (char *) (pInBuffer + i), 320 * sizeof(short));//首先需要使用滤波函数将音频数据分高低频,以高频和低频的方式传入降噪函数内部WebRtcSpl_AnalysisQMF(shBufferIn, 320, shInL, shInH, filter_state1,filter_state12);//将需要降噪的数据以高频和低频传入对应接口,同时需要注意返回数据也是分高频和低频if (0 == WebRtcNs_Process(pNS_inst, shInL, shInH, shOutL, shOutH)) {short shBufferOut[320];//如果降噪成功,则根据降噪后高频和低频数据传入滤波接口,然后用将返回的数据写入文件WebRtcSpl_SynthesisQMF(shOutL, shOutH, 160, shBufferOut, Synthesis_state1,Synthesis_state12);memcpy(pOutBuffer + i, shBufferOut, 320 * sizeof(short));}}}fwrite(pOutBuffer, sizeof(char), nFileSize, fpOut);} while (0);LOGE("NS 结束");WebRtcNs_Free(pNS_inst);fclose(fpIn);fclose(fpOut);free(pInBuffer);free(pOutBuffer);} else{FILE * fpIn = fopen(sFile_8k, "rb");FILE * fpOut = fopen(dFile_8k, "wb");if(fpIn == NULL || fpOut == NULL){LOGE("file open filed");return;}LOGE("NS Java_com_webrtc_ns_AudioProcessJni_NSProcess  8K   start");int len;int frameSize =80;int i = 0 ;const  short *pData    = NULL;short *pOutData = NULL;pData    = (short*)malloc(frameSize*sizeof(short));pOutData = (short*)malloc(frameSize*sizeof(short)); //160len = frameSize*sizeof(short);while(1){memset(pData, 0, len);if (len > 0){len = fread(pData, 1, len, fpIn);int nSRet = WebRtcNs_Process(pNS_inst, pData, NULL, pOutData,NULL);if (nSRet != 0){LOGE("failed in WebRtcAgc_Process\n");break;}fwrite(pOutData, 1, len, fpOut);} else {break;}}LOGE("NS 结束");WebRtcNs_Free(pNS_inst);fclose(fpIn);fclose(fpOut);free(pInBuffer);free(pOutBuffer);}}

总结

总结一句话,给你们代码就是了,下载地址:

https://download.csdn.net/download/gjy_it/11771371

【WebRTC】在Android上用NDK编译WebRtc音频增益模块(AGC),降噪(NS),java调用JNI实现功能相关推荐

  1. Android studio使用NDK编译和调试(生成so文件)

    作者:燕歆波 导读:android如何检测应用本身被卸载了,统计卸载量! 1.首先检查自己的Android studio上有没有装ndk,右击项目根目录–>open module setting ...

  2. android 编译 c 程序,Android上通过gcc编译普通的C程序

    1.编译可执行程序 1.1 通过mk脚本编译 目录结构: mk_app jni main.c Android.mk Android.mk内容十分满简单: LOCAL_PATH := $(call my ...

  3. Android上运行gcc编译的exe文件

    编译c程序生成可执行文件 user@thundersoft:~/data/work/media-ctl-97077a7$ gcc  main.c  options.c  v4l2subdev.c me ...

  4. 在 Mac 上为 Android 编译 WebRTC

    在 Mac 上为 Android 编译 WebRTC 的基本流程和在任意平台上编译任何其它目标平台的 WebRTC 大体一致,但在 Mac 上为 Android 编译 WebRTC 不是 WebRTC ...

  5. 单独编译使用WebRTC的音频处理模块 - android

    前言 最近一直在捣腾如何在android和iOS上使用Google的WebRTC--一个无疑大力推动了互联网即时通信以及VoIP发展的开源项目. 虽然WebRTC主要目标是为互联网提供高质量的富媒体即 ...

  6. 音视频开发之旅(六) -----Android集成webrtc降噪和增益模块, ns_core函数简析

    1.前言 再上一章主要介绍了音频文件的相关操作,在录音的过程当中,由于android机型不同的型号,即使采样率设置成44100k,有一定的外接音或者一些噪音等印象,配音出来的结果并不是很好,肯能存在' ...

  7. 单独编译使用WebRTC的音频处理模块

    不推荐单独编译 WebRTC 中的各个模块出来使用. 昨天有幸在 Google 论坛里询问到 AECM 模块的延迟计算一事,Project member 说捣腾这个延迟实际上对 AECM 的效果没有帮 ...

  8. Webrtc 实现Android群控实时推流

    云手机同步效果 现在市面上云手机 基本都是基于arm服务器+docker容器技术来实现,其中最核心的是gpu虚拟化,然后就是 远程画面操作. 今天和大家分享下如何在Android上把视频画面以及音频通 ...

  9. Android项目中创建编译期的注解

    ==注解 生命周期为RetentionPolicy.RUNTIME,可在运行时通过反射获取. 生命周期为RetentionPolicy.CLASS, 编译期处理的注解,可以使用APT(Annotati ...

最新文章

  1. ecshop模板的原理分析
  2. [原创] 如何追踪每一笔记录的来龙去脉:一个完整的Audit Logging解决方案—Part I...
  3. 马斯克要办大学上了热搜
  4. 未定义标识符 stringc/c++(20)_20款奔驰G63AMG霸气颜值 运动造型
  5. JAVA:this的使用
  6. 基于 SpringBoot,来实现MySQL读写分离技术
  7. 位域 内存 字节序_C语言中的位域、字节序、比特序、大小端(转)
  8. linux制作成后台服务,把dotnetcore 控制台app设置成linux后台服务
  9. LeetCode 1312. 让字符串成为回文串的最少插入次数(区间DP)
  10. Java 小记 — Spring Boot 的实践与思考,互联网 面试官 如何面试
  11. java进阶案例下载_登录案例java实现 ---- Java进阶篇
  12. Latex 书写 IEEE 会议论文
  13. 【1】星空派GD32F303开发板介绍
  14. xtwpro2编程器_最近喜欢上折腾路由器了,买的XTW100编程器给大家看看
  15. 电气版CAD学习笔记
  16. 论文翻译器:pdf 英文论文一键转换成中英文对照的文档
  17. Hyper-V下Centos7连接不上外网
  18. 软件测试需求文档案例,软件测试如何进行测试需求分析?
  19. python查找文件中某些字符串_python - Python无法从2个文件的字符串搜索中提取数据吗? - 堆栈内存溢出...
  20. 【雷达通信】合成孔径雷达(SAR)的点目标仿真matlab源码

热门文章

  1. 华为手机怎么用计算机玩隐藏空间,玩法 | 华为手机这五个隐藏功能,用过的都说好!...
  2. 智能小车交通标志识别功能的实现(python,ubuntu)
  3. clock synthesis
  4. 互联网晚报| 5月20日 星期五 |小鹏汽车被曝毁约20余名应届生;茅台冰淇淋39元一份 ;字节回应游戏发行业务线裁员80%传闻...
  5. python virtualenv_Python 基础之 Virtualenv详解
  6. 考研近代史 知识点 做题技巧 同类比较 重要会议 1800易错题 思维导图整理
  7. QT软件开发-基于FFMPEG设计视频播放器-GPU硬解、OpenGL渲染、解码音频完整例子(六)
  8. Observable的创建
  9. python 弹窗 模拟熊猫烧香病毒 适合老师开学特别的自我介绍
  10. java水印_Java实现给图片添加图片水印,文字水印及马赛克的方法示例