Skip to content

Commit

Permalink
实现语音识别混合开发Android部分
Browse files Browse the repository at this point in the history
  • Loading branch information
xwh817 committed Nov 21, 2019
1 parent 1f74720 commit 8ba7104
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 1 deletion.
15 changes: 15 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ android {
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

// 选择打包时选取的so文件,x86时模拟上的环境
ndk {
abiFilters "armeabi-v7a","arm64-v8a","x86_64","x86"
}
}

buildTypes {
Expand All @@ -65,6 +70,14 @@ android {
signingConfig signingConfigs.config
}
}

// 插件和app都依赖了libflutter.so文件,这里指定一下
packagingOptions {
pickFirst 'lib/x86_64/libflutter.so'
pickFirst 'lib/x86/libflutter.so'
pickFirst 'lib/arm64-v8a/libflutter.so'
pickFirst 'lib/armeabi-v7a/libflutter.so'
}
}

flutter {
Expand All @@ -76,4 +89,6 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

implementation project(':speech')
}
9 changes: 9 additions & 0 deletions android/app/src/main/kotlin/xwh/flutter/music/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ import android.os.Bundle

import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
import xwh.lib.speech.AsrPlugin

class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)

this.registerMyPlugins()
}


// 注册自定义的插件
private fun registerMyPlugins() {
AsrPlugin.registerWith(registrarFor("xwh.lib.speech.AsrPlugin"))
}
}
2 changes: 1 addition & 1 deletion android/settings.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
include ':app'
include ':app', ':speech'

def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()

Expand Down
1 change: 1 addition & 0 deletions android/speech/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
53 changes: 53 additions & 0 deletions android/speech/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
apply plugin: 'com.android.library'


// 添加对Flutter的依赖
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"


android {
compileSdkVersion 28


defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

}

// 配置flutter的源目录
flutter {
source '../..'
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'androidx.appcompat:appcompat:1.0.2'
}
Binary file not shown.
21 changes: 21 additions & 0 deletions android/speech/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
22 changes: 22 additions & 0 deletions android/speech/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="xwh.lib.speech">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>
<meta-data
android:name="com.baidu.speech.APP_ID"
android:value="17747740"/>
<meta-data
android:name="com.baidu.speech.API_KEY"
android:value="CBWHCTTwCnWASBHCCtaP5Rr6"/>
<meta-data
android:name="com.baidu.speech.SECRET_KEY"
android:value="cpCKDMzp7uDIwX8CpfFEfs4U6qSuegzx"/>

<service
android:name="com.baidu.speech.VoiceRecognitionService"
android:exported="false"/>
</application>
</manifest>
196 changes: 196 additions & 0 deletions android/speech/src/main/java/xwh/lib/speech/AsrManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package xwh.lib.speech;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;

import com.baidu.speech.EventListener;
import com.baidu.speech.EventManager;
import com.baidu.speech.EventManagerFactory;
import com.baidu.speech.asr.SpeechConstant;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;


/**
* Created by xwh on 2019/11/18.
*/
public class AsrManager {

private static volatile AsrManager instance;
private AsrManager(){}

public static AsrManager getInstance() {
if (instance == null) {
instance = new AsrManager();
}
return instance;
}

private EventManager asr;

public void init(Activity context) {
initPermission(context);
initListener(context);
}

/**
* android 6.0 以上需要动态申请权限
*/
public void initPermission(Activity context) {
String permissions[] = {Manifest.permission.RECORD_AUDIO,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.INTERNET,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};

ArrayList<String> toApplyList = new ArrayList<String>();

for (String perm : permissions) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(context, perm)) {
toApplyList.add(perm);
//进入到这里代表没有权限.

printResult("Android 没有授权");
}
}
String tmpList[] = new String[toApplyList.size()];
if (!toApplyList.isEmpty()) {
ActivityCompat.requestPermissions(context, toApplyList.toArray(tmpList), 123);
}

}


public void initListener(Context context) {
asr = EventManagerFactory.create(context, "asr");
asr.registerListener(new EventListener() {
@Override
public void onEvent(String name, String params, byte[] data, int offset, int length) {
String result = null;
if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_READY)) {
result = "引擎准备就绪,可以开始说话";
} else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_BEGIN)) {
result = "检测到用户的已经开始说话";
startSpeakTime = System.currentTimeMillis();
} else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_END)) {
result = "检测到用户的已经停止说话"+ params;
stopSpeakTime = System.currentTimeMillis();
} else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) {
// 临时识别结果, 长语音模式需要从此消息中取出结果

try {
JSONObject jsonObject = new JSONObject(params);
String resultType = jsonObject.getString("result_type");

if ("final_result".equals(resultType)) {
String best_result = jsonObject.getString("best_result");
result = "最终识别结果:" + best_result;


if (mSpeechListener != null) {
mSpeechListener.onResult(best_result);
}

//tvParseResult.append("解析结果:" + best_result+"\n");

} else if ("nlu_result".equals(resultType)) {
String nlu_result = new String(data, offset, length);
result = "语义解析结果:" + nlu_result;
} else {
String best_result = jsonObject.getString("best_result");
result = "临时识别结果:" + best_result;
}

} catch (JSONException e) {
e.printStackTrace();
}


} else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_FINISH)) {
// 识别结束, 最终识别结果或可能的错误
result = "识别结束" ;

if (mSpeechListener != null) {
mSpeechListener.onEnd();
}
} else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_ERROR)) {
// 识别结束, 最终识别结果或可能的错误
result = "识别结束" ;

if (mSpeechListener != null) {
mSpeechListener.onError(name);
}
} else {
result = "onEvent: " + name;
}

printResult(result);


}
}); // EventListener 中 onEvent方法
}

private SpeechListener mSpeechListener;
public void setSpeechListener(SpeechListener onSpeechResult) {
mSpeechListener = onSpeechResult;
}
public interface SpeechListener{
void onResult(String text);
void onError(String error);
void onEnd();
}

private void printResult(String text) {
//tvResult.append(text + "\n\n");
Log.d("Speech", text);
}

private long startSpeakTime;
private long stopSpeakTime;

public void start() {

String json = getAsrParams().toString(); // 这里可以替换成你需要测试的json
asr.send(SpeechConstant.ASR_START, json, null, 0, 0);
printResult("启动识别,输入参数:" + json);
}

protected JSONObject asrParams;
protected JSONObject getAsrParams() {
if (asrParams == null) {
try {
asrParams = new JSONObject();
asrParams.put(SpeechConstant.PID, 1536); // 默认1536, 语义15361,输入法模型1537
asrParams.put(SpeechConstant.DECODER, 0); // 纯在线(默认)
asrParams.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN); // 语音活动检测
asrParams.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 800); // 开启VAD尾点检测,即静音判断的毫秒数。建议设置800ms-3000ms
//asrParams.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // VAD_ENDPOINT_TIMEOUT=0 && 输入法模型 开启长语音。
asrParams.put(SpeechConstant.ACCEPT_AUDIO_DATA, false);// 是否需要语音音频数据回调
asrParams.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);// 是否需要语音音量数据回调
} catch (JSONException e) {
e.printStackTrace();
}
}
return asrParams;
}

public void stop() {
asr.send(SpeechConstant.ASR_STOP, null, null, 0, 0);
}


public void cancel() {
asr.send(SpeechConstant.ASR_CANCEL, null, null, 0, 0);
}

}
Loading

0 comments on commit 8ba7104

Please sign in to comment.