Android使用webrtc实现检测用户是否在说话
原文博客:Doi技术团队
链接地址:https://blog.doiduoyi.com/authors/1584446358138
初心:记录优秀的Doi技术团队学习经历
本文链接:Android使用webrtc实现检测用户是否在说话
前言
我们在Android应用做语音识别的时候,一般是用户唤醒之后开始说话。当用户超过一定的时候没有说话,就停止录音,并把录音发送到语音识别服务器,获取语音识别结果。本教程就是解决如何检测用户是否停止说话,我们使用的是WebRTC架构的源代码中的vad代码实现的。
VAD算法全称是Voice Activity Detection,该算法的作用是检测是否是人的语音,使用范围极广,降噪,语音识别等领域都需要有vad检测。webrtc的vad检测原理是根据人声的频谱范围,把输入的频谱分成六个子带:80Hz——250Hz,250Hz——500Hz,500Hz——1K,1K——2K,2K——3K,3K——4K。分别计算这六个子带的能量。然后使用高斯模型的概率密度函数做运算,得出一个对数似然比函数。对数似然比分为全局和局部,全局是六个子带之加权之和,而局部是指每一个子带则是局部,所以语音判决会先判断子带,子带判断没有时会判断全局,只要有一个通过认为是语音。
创建Android项目
现在我们就来使用webrtc的vad源码开发检测是否有语音的Android项目。
首先我们创建一个Android项目,修改local.properties
中的配置信息,添加NDK的路径,例如笔者的如下:
ndk.dir=D\:\\Android\\android-ndk-r15csdk.dir=D\:\\Android\\sdk
接着在app
目录下创建CMakeLists.txt
文件,并添加以下代码:
cmake_minimum_required(VERSION 3.4.1)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic")aux_source_directory(src/main/cpp/vad_src/ DIR_LIB_SRCS)add_definitions(-DWEBRTC_POSIX)add_definitions(-DWEBRTC_ANDROID)add_library( native-lib SHARED src/main/cpp/native-lib.cpp ${DIR_LIB_SRCS})include_directories(src/main/cpp/vad_src/)find_library( log-lib log )target_link_libraries( native-lib ${log-lib} )
然后修改app
目录下的build.gradle
文件,修改如下:
# 在defaultConfig添加externalNativeBuild { cmake { arguments = ['-DANDROID_STL=c++_static'] cppFlags "" }}# 在android下添加buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } externalNativeBuild { cmake { path "CMakeLists.txt" } }
使用webrtc
接下来就开始克隆webrtc源码
git clone https://android.googlesource.com/platform/external/webrtc
我们所需的源码主要存放webrtc/webrtc/common_audio/vad
目录中,我们把里面的源码文件都复制到我们的Android项目main/cpp/vad_src
目录下,主要: 有很多的依赖代码并不在这个目录中,我们需要更加每个文件的导入库查看依赖库所在的位置,并吧他们都复制到main/cpp/vad_src
目录下。在鼻子提供的源码中,已经提取好了,可以下载:
在main/cpp
目录下创建native-lib.cpp
文件,为Java调用vad提供接口,代码如下:
#include #include #include #include "vad_src/webrtc_vad.h"#include "vad_src/vad_core.h"extern "C"JNIEXPORT jboolean JNICALLJava_com_yeyupiaoling_testvad_MainActivity_webRtcVad_1Process(JNIEnv *env, jobject instance, jshortArray audioData_, jint offsetInshort, jint readSize) { VadInst *handle = WebRtcVad_Create(); WebRtcVad_Init(handle); WebRtcVad_set_mode(handle, 2); int index = readSize / 160; jshort *pcm_data = env->GetShortArrayElements(audioData_, JNI_FALSE); bool b = JNI_FALSE; for (int i = 0; i < index; ++i) { int vad = WebRtcVad_Process(handle, 16000, pcm_data + offsetInshort + i * 160, 160); if (vad == 1) { b = JNI_TRUE; } else{ b=JNI_FALSE; } } env->ReleaseShortArrayElements(audioData_, pcm_data, JNI_ABORT); WebRtcVad_Free(handle); return static_cast<jboolean>(b);}
其对应的Java方法如下:
public native boolean webRtcVad_Process(short[] audioData, int offsetInshort, int readSize);
最后在我们的Android这样子调用,可以检测到用户是否在说话。
int mMinBufferSize = AudioRecord.getMinBufferSize(16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);AudioRecord mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, mMinBufferSize * 2);mMinBufferSize = 320;short[] audioData = new short[mMinBufferSize];if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) { stopRecord(); return;}mRecorder.startRecording();while (mIsRecording) { if (null != mRecorder) { readSize = mRecorder.read(audioData, 0, mMinBufferSize); if (readSize == AudioRecord.ERROR_INVALID_OPERATION || readSize == AudioRecord.ERROR_BAD_VALUE) { continue; } if (readSize != 0 && readSize != -1) { // 语音活动检测 mSpeaking = webRtcVad_Process(audioData, 0, readSize); if (mSpeaking) { Log.d(TAG, ">>>>>正在讲话"); } else { Log.d(TAG, "=====当前无声音"); } } else { break; } }}
最后别忘了添加录音权限RECORD_AUDIO
,如果是Android 6.0以上的,还需要动态申请权限。
if (!hasPermission()){ requestPermission();}// check had permissionprivate boolean hasPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED; } else { return true; }}// request permissionprivate void requestPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 1); }}
本项目下载地址:https://resource.doiduoyi.com/#qoe3sse
更多相关文章
- APK签名
- Android(安卓)事件分发
- Android消息机制源码解读
- Android实现语音识别 && 讯飞平台语音识别语音播报
- 如何编译运行Android系统自带桌面Launcher2源码
- 从源码角度分析Activity、Window和DecorView的关系
- Android(安卓)源码学习资源
- 从源码的角度分析Android中的Handler机制的工作原理
- android 自定义View之SubmitView