20212419 2023-2024-2 《移动平台开发与实践》第6次作业

一、实验内容

掌握基于Android平台的程序设计技术,并能够编写完成语音识别系统。
设计并开发一个语音识别应用系统。
通过使用RecognizerIntent实现语音识别功能,开发一个Android语音识别系统。

二、实验过程

2.1创建应用并下载对应的SDK

根据科大讯飞语音识别官网教程进行sdk获取和appid获取,首先需要进行科大讯飞平台的注册与认证,这里相较于百度地图申请速度就很快了。

进入讯飞开放平台:个人注册完成后,进入控制台。
点击创建新应用。

2.2配置资源文件

1.复制文件中libs里面的三个文件,到项目的libs中
在这里插入图片描述
2.在项目“/module/src/main/”下新建JniLibs文件夹,然后在build.gradle(:app)的Android中添加如下代码。

sourceSets{
        main{
            jniLibs.srcDir 'libs'
        }
    }

3.将“Demo/scr/main/assets/”下的文件复制到项目对应文件夹下, 是一些图标。以及项目的两个库函数。

具体项目目录如下:
在这里插入图片描述
到此项目的基本配置完成,下面是代码的撰写

2.3编写代码

1.对应权限的申请,将下面代码添加至AndroidManifest.xml

<!--连接网络权限,用于执行云端语音能力 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <!--读取网络信息状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!--获取当前wifi状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

activity_main.xml布局文件代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="识别到的内容"
        android:textColor="@color/white"
        android:layout_marginStart="32dp"
        android:layout_marginTop="128dp"
        android:textSize="50sp"
        android:textStyle="bold"/>

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn_start"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        style="@android:style/Widget.Button"
        android:layout_marginTop="64dp"
        android:layout_marginStart="32dp"
        android:layout_marginEnd="32dp"
        android:text="开始识别"
        android:textSize="22sp"
        android:textStyle="bold"
        android:textColor="@color/white"/>
</LinearLayout>

2.新建JsonPares文件负责json数据解析

package com.example.kedaxunfei

import org.json.JSONObject
import org.json.JSONTokener

/**
 * Json结果解析类
 */
object JsonParser {
    fun parseIatResult(json: String?): String {
        val ret = StringBuffer()
        try {
            val tokener = JSONTokener(json)
            val joResult = JSONObject(tokener)
            val words = joResult.getJSONArray("ws")
            for (i in 0 until words.length()) {

                val items = words.getJSONObject(i).getJSONArray("cw")
                val obj = items.getJSONObject(0)
                ret.append(obj.getString("w"))

            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return ret.toString()
    }
}

3.新建SpeechApplication文件,负责将初始化,需要填写应用的appid

package com.example.kedaxunfei


import android.app.Application
import com.iflytek.cloud.SpeechUtility

class SpeechApplication: Application() {
    override fun onCreate() {
        SpeechUtility.createUtility(this@SpeechApplication, "appid=6526455f")
        super.onCreate()
    }
}

MainActivity中代码

package com.example.kedaxun


import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.kedaxun.JsonParser.parseIatResult
import com.iflytek.cloud.ErrorCode
import com.iflytek.cloud.InitListener
import com.iflytek.cloud.RecognizerResult
import com.iflytek.cloud.SpeechConstant
import com.iflytek.cloud.SpeechError
import com.iflytek.cloud.SpeechRecognizer
import com.iflytek.cloud.ui.RecognizerDialog
import com.iflytek.cloud.ui.RecognizerDialogListener
import org.json.JSONException
import org.json.JSONObject
import android.Manifest


class MainActivity : AppCompatActivity(), View.OnClickListener {
    private var mIat: SpeechRecognizer? = null // 语音听写对象
    private var mIatDialog: RecognizerDialog? = null // 语音听写UI

    // 用HashMap存储听写结果
    private val mIatResults: HashMap<String?, String> = LinkedHashMap()
    private var mSharedPreferences: SharedPreferences? = null //缓存
    private val mEngineType = SpeechConstant.TYPE_CLOUD // 引擎类型
    private val language = "zh_cn" //识别语言
    private var tvResult: TextView? = null //识别结果
    private var btnStart: Button? = null //开始识别
    private val resultType = "json" //结果内容数据格式
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tvResult = findViewById<TextView>(R.id.tv_result)
        btnStart = findViewById<Button>(R.id.btn_start)
        btnStart?.setOnClickListener(this)
        initPermission() //权限请求
//        checkAndRequestPermissions()

        // 使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mIat = SpeechRecognizer.createRecognizer(this@MainActivity, mInitListener)
        // 使用UI听写功能,请根据sdk文件目录下的notice.txt,放置布局文件和图片资源
        mIatDialog = RecognizerDialog(this@MainActivity, mInitListener)
        mSharedPreferences = getSharedPreferences(
            "ASR",
            MODE_PRIVATE
        )
    }

    override fun onClick(v: View) {
        if (null == mIat) {
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
            showMsg("创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化")
            return
        }
        mIatResults.clear() //清除数据
        setParam() // 设置参数
        mIatDialog!!.setListener(mRecognizerDialogListener) //设置监听
        mIatDialog!!.show() // 显示对话框
    }

    /**
     * 初始化监听器。
     */
    private val mInitListener = InitListener { code ->
        Log.d(TAG, "SpeechRecognizer init() code = $code")
        if (code != ErrorCode.SUCCESS) {
            showMsg("初始化失败,错误码:$code,请点击网址https://www.xfyun.cn/document/error-code查询解决方案")
        }
    }

    /**
     * 听写UI监听器
     */
    private val mRecognizerDialogListener: RecognizerDialogListener =
        object : RecognizerDialogListener {
            override fun onResult(results: RecognizerResult, isLast: Boolean) {
                printResult(results) //结果数据解析
            }

            /**
             * 识别回调错误.
             */
            override fun onError(error: SpeechError) {
                showMsg(error.getPlainDescription(true))
            }
        }

    /**
     * 数据解析
     *
     * @param results
     */
    private fun printResult(results: RecognizerResult) {
        val text = parseIatResult(results.resultString)
        var sn: String? = null
        // 读取json结果中的sn字段
        try {
            val resultJson = JSONObject(results.resultString)
            sn = resultJson.optString("sn")
        } catch (e: JSONException) {
            e.printStackTrace()
        }
        mIatResults[sn] = text
        val resultBuffer = StringBuffer()
        for (key in mIatResults.keys) {
            resultBuffer.append(mIatResults[key])
        }
        tvResult!!.text = resultBuffer.toString() //听写结果显示
    }

    /**
     * 参数设置
     *
     * @return
     */
    fun setParam() {
        // 清空参数
        mIat!!.setParameter(SpeechConstant.PARAMS, null)
        // 设置听写引擎
        mIat!!.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType)
        // 设置返回结果格式
        mIat!!.setParameter(SpeechConstant.RESULT_TYPE, resultType)
        if (language == "zh_cn") {
            val lag = mSharedPreferences!!.getString(
                "iat_language_preference",
                "mandarin"
            )
            Log.e(TAG, "language:$language") // 设置语言
            mIat!!.setParameter(SpeechConstant.LANGUAGE, "zh_cn")
            // 设置语言区域
            mIat!!.setParameter(SpeechConstant.ACCENT, lag)
        } else {
            mIat!!.setParameter(SpeechConstant.LANGUAGE, language)
        }
        Log.e(TAG, "last language:" + mIat!!.getParameter(SpeechConstant.LANGUAGE))

        //此处用于设置dialog中不显示错误码信息
        //mIat.setParameter("view_tips_plain","false");

        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mIat!!.setParameter(
            SpeechConstant.VAD_BOS,
            mSharedPreferences!!.getString("iat_vadbos_preference", "4000")
        )

        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mIat!!.setParameter(
            SpeechConstant.VAD_EOS,
            mSharedPreferences!!.getString("iat_vadeos_preference", "1000")
        )

        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mIat!!.setParameter(
            SpeechConstant.ASR_PTT,
            mSharedPreferences!!.getString("iat_punc_preference", "1")
        )

        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mIat!!.setParameter(SpeechConstant.AUDIO_FORMAT, "wav")
        mIat!!.setParameter(
            SpeechConstant.ASR_AUDIO_PATH,
            Environment.getExternalStorageDirectory().toString() + "/msc/iat.wav"
        )
    }

    /**
     * 提示消息
     * @param msg
     */
    private fun showMsg(msg: String) {
        Toast.makeText(this@MainActivity, msg, Toast.LENGTH_SHORT).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        if (null != mIat) {
            // 退出时释放连接
            mIat!!.cancel()
            mIat!!.destroy()
        }
    }

//    // 声明请求码
//    private val REQUEST_RECORD_AUDIO = 1
//    private val REQUEST_WRITE_EXTERNAL_STORAGE = 2
//
//    // 检查并请求权限
//    private fun checkAndRequestPermissions() {
//        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
//            != PackageManager.PERMISSION_GRANTED
//        ) {
//            // REQUEST_RECORD_AUDIO 是我们定义的请求码
//            ActivityCompat.requestPermissions(
//                this, arrayOf<String>(Manifest.permission.RECORD_AUDIO),
//                REQUEST_RECORD_AUDIO
//            )
//        }
//        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
//            != PackageManager.PERMISSION_GRANTED
//        ) {
//            // REQUEST_WRITE_EXTERNAL_STORAGE 是我们定义的请求码
//            // 注意:从Android 10开始,Google建议使用MediaStore API或Scoped Storage
//            ActivityCompat.requestPermissions(
//                this, arrayOf<String>(Manifest.permission.WRITE_EXTERNAL_STORAGE),
//                REQUEST_WRITE_EXTERNAL_STORAGE
//            )
//        }
//    }
//
//    // 处理权限请求结果
//    override fun onRequestPermissionsResult(
//        requestCode: Int,
//        permissions: Array<String?>,
//        grantResults: IntArray
//    ) {
//        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
//        when (requestCode) {
//            REQUEST_RECORD_AUDIO -> {
//                if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//                    // RECORD_AUDIO 权限被授予
//                } else {
//                    // RECORD_AUDIO 权限被拒绝
//                }
//                return
//            }
//
//            REQUEST_WRITE_EXTERNAL_STORAGE -> {
//                if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//                    // WRITE_EXTERNAL_STORAGE 权限被授予
//                } else {
//                    // WRITE_EXTERNAL_STORAGE 权限被拒绝
//                }
//                return
//            }
//        }
//    }
    /**
     * android 6.0 以上需要动态申请权限
     */
    private fun initPermission() {
        val permissions = arrayOf<String>(
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.ACCESS_NETWORK_STATE,
            Manifest.permission.INTERNET,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
        )
        val toApplyList = ArrayList<String>()
        for (perm in permissions) {
            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(
                    this,
                    perm
                )
            ) {
                toApplyList.add(perm)
            }
        }
        val tmpList = arrayOfNulls<String>(toApplyList.size)
        if (!toApplyList.isEmpty()) {
            ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123)
        }
    }

    /**
     * 权限申请回调,可以作进一步处理
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // 此处为android 6.0以上动态授权的回调,用户自行实现。
    }

    companion object {
        private const val TAG = "MainActivity"
    }
}

3.实验结果演示视频

Android实验六

三、学习中遇到的问题及解决

  • 问题1:录制语音后没有显示识别的语音文字
  • 问题1分析:一开始我以为是代码问题,但是经过我仔细反复多方面查找,始终没有找到有错误的地方,Androidstudio的日志显示运行一切正常,最后我发现是因为转换的文字是白色的,然后手机界面也是白色的,所以看不出来
  • 问题1解决方案:将手机调至夜间模式即可。

四、学习感悟、思考等

通过这次实验,我掌握了基于Android平台的程序设计技术,并成功设计和开发了一个语音识别应用系统。整个过程让我深入理解了语音识别的基本原理和实现方法,尤其是如何使用Android中的RecognizerIntent来实现这一功能。
通过亲自设计和实现一个语音识别应用系统,我对Android平台有了更加全面和深入的理解,也为今后的开发工作积累了宝贵的经验。这次实验无疑是我技术成长道路上的一个重要里程碑。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值