原文博客: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

更多相关文章

  1. APK签名
  2. Android(安卓)事件分发
  3. Android消息机制源码解读
  4. Android实现语音识别 && 讯飞平台语音识别语音播报
  5. 如何编译运行Android系统自带桌面Launcher2源码
  6. 从源码角度分析Activity、Window和DecorView的关系
  7. Android(安卓)源码学习资源
  8. 从源码的角度分析Android中的Handler机制的工作原理
  9. android 自定义View之SubmitView

随机推荐

  1. 定制你自己的Andoird桌面Launcher
  2. android在EditText中插入表情图片
  3. Android中Uri的使用
  4. 8步教你打开Android之门 NDK入门教程
  5. DRM in Android
  6. 构建 Android 手机 RSS 阅读器
  7. 30+关于如何成为更好Android开发者的短小
  8. Android中Content Provide
  9. Android音乐播放器开发
  10. Android程序库