1.实验内容
掌握基于Android平台的程序设计技术,并能够编写完成语音识别系统。
2.实验过程
2.1 注册科大讯飞账户,在控制台创建应用
**2.2 下载语音识别的SDK **
2.3 打开SDK目录,找到相应资源放到工程的相应位置
2.4 如图,将两个java文件放到MainActivity同一目录下
2.5 添加相应的依赖
2.6 清单文件中加入相应的权限
2.7 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#519D9E"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"
tools:context=".MainActivity">
<!-- 标题文本 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" <!-- 使用layout_gravity代替layout_centerInParent -->
android:layout_margin="15dp"
android:text="讯飞听写示例"
android:textSize="30sp" />
<!-- 听写结果显示文本框 -->
<EditText
android:id="@+id/iat_text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top|left"
android:hint="听写结果显示"
android:paddingBottom="10dp"
android:textColorHint="@color/white"
android:textColor="@color/white"
android:textSize="20sp" />
<!-- 控制按钮组 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="2dp"
android:gravity="center_horizontal"
android:orientation="horizontal">
<!-- 开始按钮 -->
<Button
android:id="@+id/iat_recognize"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="开始"
android:textSize="20sp" />
<!-- 停止按钮 -->
<Button
android:id="@+id/iat_stop"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="停止"
android:textSize="20sp" />
<!-- 取消按钮 -->
<Button
android:id="@+id/iat_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="取消"
android:textSize="20sp" />
</LinearLayout>
<!-- 音频流识别按钮 -->
<Button
android:id="@+id/iat_recognize_stream"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="音频流识别"
android:textSize="20sp" />
</LinearLayout>
2.8 MainActivity
package com.gong20212411.xfrecognizeri
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Message
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.activity.ComponentActivity
import com.iflytek.cloud.ErrorCode
import com.iflytek.cloud.InitListener
import com.iflytek.cloud.RecognizerListener
import com.iflytek.cloud.RecognizerResult
import com.iflytek.cloud.SpeechConstant
import com.iflytek.cloud.SpeechError
import com.iflytek.cloud.SpeechRecognizer
import com.iflytek.cloud.SpeechUtility
import com.iflytek.cloud.ui.RecognizerDialog
import com.iflytek.cloud.ui.RecognizerDialogListener
import org.json.JSONException
import org.json.JSONObject
class MainActivity : ComponentActivity(), View.OnClickListener {
// 语音听写对象
private var mIat: SpeechRecognizer? = null
// 语音听写UI
private var mIatDialog: RecognizerDialog? = null
// 存储听写结果
private val mIatResults = LinkedHashMap<String?, String>()
private lateinit var mResultText: EditText
// 语言类型和结果格式
private val language = "zh_cn"
private val resultType = "json"
private const val cyclic = false // 音频流识别是否循环调用
// 用于拼接听写结果的字符串缓冲区
private val buffer = StringBuffer()
// Handler消息码
private const val handlerCode = 0x123
// 函数调用返回值
private var resultCode = 0
// 初始化SpeechUtility和控件
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
SpeechUtility.createUtility(this, "appid=6fe5435f") // 初始化语音服务
initializeViews() // 初始化视图和监听器
}
// 初始化视图和监听器
private fun initializeViews() {
// 初始化按钮点击事件
findViewById<Button>(R.id.iat_recognize).setOnClickListener(this)
findViewById<Button>(R.id.iat_recognize_stream).setOnClickListener(this)
findViewById<Button>(R.id.iat_stop).setOnClickListener(this)
findViewById<Button>(R.id.iat_cancel).setOnClickListener(this)
// 初始化EditText控件
mResultText = findViewById(R.id.iat_text)
// 初始化语音听写对象
mIat = SpeechRecognizer.createRecognizer(this, mInitListener)
// 初始化听写Dialog
mIatDialog = RecognizerDialog(this, mInitListener)
}
// 点击事件处理
override fun onClick(view: View) {
if (null == mIat) {
showToast("创建对象失败,请确认libmsc.so放置正确,且有调用createUtility进行初始化")
return
}
when (view.id) {
R.id.iat_recognize -> startListening() // 开始听写
R.id.iat_recognize_stream -> executeStream() // 执行音频流识别
R.id.iat_stop -> stopListening() // 停止听写
R.id.iat_cancel -> cancelListening() // 取消听写
}
}
// 开始听写
private fun startListening() {
// 清空缓冲区、结果文本和结果集
buffer.setLength(0)
mResultText.text.clear()
mIatResults.clear()
// 设置听写参数
setParam()
// 显示听写对话框或开始无UI听写
if (dialogType == 0) {
mIatDialog?.setListener(mRecognizerDialogListener)
mIatDialog?.show()
} else {
resultCode = mIat?.startListening(mRecognizerListener)
if (resultCode != ErrorCode.SUCCESS) {
showToast("听写失败,错误码:$resultCode")
}
}
showToast("开始听写")
}
// 停止听写
private fun stopListening() {
mIat?.stopListening()
showToast("停止听写")
}
// 取消听写
private fun cancelListening() {
mIat?.cancel()
showToast("取消听写")
}
// 初始化监听器
private val mInitListener = InitListener { code ->
Log.e(TAG, "SpeechRecognizer init() code = $code")
if (code != ErrorCode.SUCCESS) {
showToast("初始化失败,错误码:$code")
}
}
// 听写监听器
private val mRecognizerListener: RecognizerListener = object : RecognizerListener {
override fun onBeginOfSpeech() {
showToast("开始说话")
}
override fun onError(error: SpeechError) {
showToast(error.getPlainDescription(true))
}
override fun onEndOfSpeech() {
showToast("结束说话")
}
override fun onResult(results: RecognizerResult, isLast: Boolean) {
if (resultType == "json") {
printResult(results)
} else if (resultType == "plain") {
buffer.append(results.resultString)
mResultText.text = buffer.toString()
mResultText.setSelection(mResultText.length())
}
if (isLast && cyclic) {
handler.sendMessageDelayed(Message.obtain(), 100)
}
}
override fun onVolumeChanged(volume: Int, data: ByteArray) {
Log.e(TAG, "onVolumeChanged: ${data.size}")
}
override fun onEvent(eventType: Int, arg1: Int, arg2: Int, obj: Bundle) {
// 会话ID相关处理...
}
}
// 听写UI监听器
private val mRecognizerDialogListener: RecognizerDialogListener = object : RecognizerDialogListener {
override fun onResult(results: RecognizerResult, isLast: Boolean) {
printResult(results)
}
override fun onError(error: SpeechError) {
showToast(error.getPlainDescription(true))
}
}
// 打印听写结果
private fun printResult(results: RecognizerResult) {
val text = JsonParser.parseIatResult(results.resultString)
try {
val resultJson = JSONObject(results.resultString)
val sn = resultJson.optString("sn")
mIatResults[sn] = text
} catch (e: JSONException) {
e.printStackTrace()
}
val resultBuffer = StringBuffer()
for (key in mIatResults.keys) {
resultBuffer.append(mIatResults[key])
}
mResultText.text = resultBuffer.toString()
mResultText.setSelection(mResultText.length())
}
// 设置听写参数
private fun setParam() {
with(mIat!!) {
setParameter(SpeechConstant.PARAMS, null)
setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD)
setParameter(SpeechConstant.RESULT_TYPE, resultType)
setParameter(SpeechConstant.LANGUAGE, language)
setParameter(SpeechConstant.ACCENT, "mandarin")
setParameter(SpeechConstant.VAD_BOS, "5000")
setParameter(SpeechConstant.VAD_EOS, "1800")
setParameter(SpeechConstant.ASR_PTT, "1")
setParameter(SpeechConstant.AUDIO_FORMAT, "wav")
setParameter(SpeechConstant.ASR_AUDIO_PATH,
"${Environment.getExternalStorageDirectory().toString()}/msc/helloword.wav")
}
}
// 执行音频流识别操作
private fun executeStream() {
// 音频流识别相关代码...
}
// 展示吐司提示
private fun showToast(str: String) {
Toast.makeText(this, str, Toast.LENGTH_SHORT).show()
}
// 自定义弹框显示
private fun showAlertDialog() {
dialog = AlertDialog.Builder(this)
.setTitle("自定弹框")
.setMessage("正在识别,请稍后...")
.setIcon(R.mipmap.ic_launcher)
.create()
dialog?.show()
}
// Handler处理
@SuppressLint("HandlerLeak")
private val handler = object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == handlerCode) {
executeStream()
}
}
}
companion object {
private const val TAG = "MainActivity"
}
}
3.实验结果展示
3.学习中遇到的问题及解决
问题1: 在模拟器中运行时报错
解决方案:.so文件适用于arm架构的处理器,而模拟器是x86架构的因此文件加载失败。在AndroidStudio中创建的arm架构模拟器难以运行,因此最后选择在我的手机上运行,市面上的手机CPU架构几乎都是arm架构的
**问题2:在手机上运行出错 **
解决方案:权限问题,在手机中给应用授予录音权限即可成功运行
4.学习感悟、思考
通过本次实验,加深了我对语音识别系统开发的基本流程,通过解决实验中遇到的一些问题,我明白了要从多个方面去考虑问题出现的可能原因,每当运行出现bug,我习惯于寻找代码中的可能出现的问题,而忽视了作为系统的软件开发,其他要素可能出现的问题,比如上面的问题一,我一开始认为是so文件放置的位置不对,或者缺少引入依赖的代码,我调试了很久也没有也没能解决,后来我从其他同学那里得知so适用于arm架构,这才解决了问题。问题二我始终认为是代码逻辑出现了问题,反复调试无果这才发现是权限不够。以后还要加强对自己代码的信心,从其它方面去寻找问题原因。