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

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

1.实验内容

设计并开发一个语音识别应用系统。
通过使用RecognizerIntent实现语音识别功能,开发一个Android语音识别系统。

2.实验过程

2.1下载语音识别的SDK

这里我们选择的是科大讯飞的语音识别(流失版)
(1)先到讯飞开发平台进行账号注册和实名认证。下载网站
(2)点击我的应用,然后创建应用,其中的分类什么的都可以随便选,记住其中的APPID就行,(13398b0d)
在这里插入图片描述
(3)点击应用名称,进入页面,往下滑,会看到Android MSC 的下载
在这里插入图片描述
(4)点击下载后,再次选择语音识别(流式版)即可

2.2 配置实验环境

(1)打开下载后的压缩包,进入到libs目录,将以下三个文件复制到实验项目下的app/libs目录下即可,然后右键将Msc.jar文件导入库(Add As Library)
在这里插入图片描述
在这里插入图片描述
但后续实验过程中会报错,据了解,是在libs目录下只要有Msc.jar文件即可!!!
(2)在src/main目录下新建JniLibs文件夹,再将两个so文件复制其目录下
在这里插入图片描述
(3)在buile.gradle.kts添加下面代码

sourceSets {
        getByName("main") {
            jniLibs.srcDirs("libs")
        }
    }

在这里插入图片描述
(4)在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"/>

在这里插入图片描述

2.3编写核心代码

(1)activity_main.xml布局文件代码:
添加最简单的按钮(开始识别)和显示语音识别结果的内容的TextView即可

<?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 字符串解析成可读的文本,其中使用了 JSONObject 和 JSONTokener 这两个类来处理 JSON 数据。方便在Mainactivity文件中调用该类,最终显示出语音识别

package com.example.wjb

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,也就是(13398b0d)也是最关键的一步!!!

package com.example.wjb
import android.app.Application
import com.iflytek.cloud.SpeechUtility

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

(4)Mainactivity.kt文件

package com.example.wjb


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.wjb.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 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"
    }
}

2.4实验结果

手机打开开发者模式,连上数据线,下载app。然后就可以进行语音识别。以下视频也可以看出语音识别还是有一些弊端的,就是不能准确的获取我的名字(希望不是我普通话的问题)

3.学习中遇到的问题及解决

  • 问题1:在AndroidManifest.xml添加权限是爆红,也没说明原因

  • 在这里插入图片描述

  • 问题1解决方案:重新找一个同学的权限代码,再复制进去就可以了

  • 问题2:运行项目时,显示so文件的一些问题,报错特别长,很难看懂,于是将报错内容复制发给gpt,通过gpt得知是so文件重复了

  • 在这里插入图片描述

  • 在这里插入图片描述

  • 问题2解决方案:根据gpt的回答,可以知道在 jniLibs 和 libs 目录中都有相同的库文件。所以我们把libs目录下的so文件删除,只留Msc.jar文件即可。再次运行发现不报错

  • 问题3:运行项目后点击开始识别后,出现“创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化”的错误
    在这里插入图片描述

  • 问题3解决方案:根据同学们的提示,说实验真机就可以不报错了。于是我将手机设置成开发者模式,连上数据线,将软件下载到手机上就可以进行语音识别了。

4.学习感悟、思考等)

本次实验是移动平台的最后一次实验,也相对简单,到开放平台上下载sdk(与第五次实验类似),然后导入包进行环境配置,编写核心代码即可完成实验。经过这么多次实验,对于Android studio的熟练度也越来越高。面对报错时也相对从容,之前看到那么多红,都不知道该怎么做。现在我至少能大致判断出是so文件出错了。然后摘取一些内容复制给gpt,让gpt告诉我们出了什么错,根据gpt的回答再进行调试。
这学期的课也到此结束了,本学期的课程,让我学习到了特别多,之前总觉得开发一款app是非常难的。但是现在看来,我也可以做出一个百度定位、语音识别。让自己特别有成就感。对于app开发有了深入的了解,至少知道了四大组件,如何开放等等。
最后感谢王志强老师的辛勤付出!

参考资料

Android Studio语音识别实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/656178.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年4月—马克思主义基本原理概论真题及答案解析(上海自考)

目录 1.选择题 2.简答题 3.论述题 1.选择题 2.简答题

aws glue配置读取本地kafka数据源

创建连接时填写本地私有ip地址&#xff0c;选择网络配置 配置任务选择kafka作为数据源 但是执行任务时日志显示连接失败 文档提到只能用加密通信 如果您希望与 Kafka 数据源建立安全连接&#xff0c;请选择 Require SSL connection (需要 SSL 连接)&#xff0c;并在 Kafka priv…

text-embedding 嵌入模型

为什么使用embedding 计算机只能处理数字&#xff0c;但我们希望它能够理解文字、图片或其他形式的数据。这就是embedding的作用。它将这些复杂的数据转换成数字表示&#xff0c;就像给它们贴上了标签一样。这些数字表示不仅保留了原始数据的重要信息&#xff0c;还能在计算机…

【OpenGL手册14】实例化

目录 一、说明 二、实例化 三、实例化数组 四、小行星带 五、完整代码 六、结论 一、说明 实例化渲染&#xff0c;是用少数数据做模板&#xff0c;实现海量物体渲染的手段方法。用实例化渲染&#xff0c;需要对每个实例产生一定描述数据。如何实现&#xff1f;请看本文下…

Vue3实战笔记(36)—粒子特效完成炫酷的404

文章目录 前言404特效总结 前言 昨天介绍了一个粒子特效小例子&#xff0c;不够直观&#xff0c;下面直接实战在自己的项目中实现一个好玩滴。 404特效 更改之前创建好的404.vue: <template><div class"container"><vue-particles id"tspartic…

EXCEL如何自动根据上行内容填充到空格

接上篇文章&#xff0c;经过宏命令后会有空格出现&#xff0c;那么如何自动根据上行内容填充到空格呢&#xff1f; 请看步骤~ ctrl G 选择空值–》定位 -》 按 -》然后等于上一行 -》ctrl enter 一。全选表格&#xff0c; ctrl G 调出界面&#xff0c;选择空值按下定位 …

二分答案思想下的二进制问题

序列合并 题目描述 给定一个长度为 n n n 的非负整数序列 { a n } \{a_n\} {an​}&#xff0c;你可以进行 k k k 次操作&#xff0c;每次操作你选择两个相邻的数&#xff0c;把它们合并成它们的按位或。 形式化地&#xff0c;一次操作中&#xff0c;你选择一个下标 i i …

Transformer模型架构笔记

0. 简介 Transformer是一种用于自然语言处理&#xff08;NLP&#xff09;和其他序列到序列&#xff08;sequence-to-sequence&#xff09;任务的深度学习模型架构&#xff0c;它在2017年由Vaswani等人首次提出。Transformer架构引入了自注意力机制&#xff08;self-attention …

windows中每日定时执行python脚本,解决问题

由于需要一个每天定时执行的任务&#xff0c;所以需要定时启动&#xff0c;网上看了很多方法&#xff0c;感觉不能在python脚本种写个while true 定时执行&#xff0c;占资源不说还不可靠。 最后考虑通过系统工具定时启动&#xff0c;发现linux中有crontab&#xff0c;windows…

JMH304-剑侠情缘2网络版+2017纹饰端+翅膀+单机+外网整理+各种副本

资源介绍&#xff1a; 藏剑-太虚-梁山-杀手堂种树地宫师门纹饰装备长流云阳套等等———– 做登录器联系站长 资源截图&#xff1a; 下载地址

2023、2024国赛web复现wp

2023 Unzip 类型&#xff1a;任意文件上传漏洞 主要知识点&#xff1a;软链接 随便上传一个一句话木马文件&#xff0c;得到一串php代码 根据代码上传zip文件发现进入后还是此页面 代码审计&#xff1a; <?php error_reporting(0); highlight_file(__FILE__);$finfo fin…

Mac免费软件推荐

1. iTerm2 - 功能强大的终端 iTerm2 是一个功能强大且灵活的终端仿真器&#xff08;可替代系统默认终端&#xff09;&#xff0c;适合需要在 macOS 上进行大量终端操作的用户。其丰富的功能和高可定制性使得 iTerm2 成为许多开发者和系统管理员的首选工具。无论是处理多个会话…

基于MyBatisPlus表结构维护工具

SuperTable表结构维护工具 一、简述 用于同步表实体与数据库表结构&#xff0c;同步建表、删改字段、索引&#xff0c;种子数据的工具… 一、开发环境 JDK&#xff1a;JDK8SpringBoot&#xff1a;2.7.2MyBatisPlus: 3.5.6MySQL: 5.7其他依赖&#xff1a;略 二、特性 表结…

5G工业数采网关的功能及工业应用-天拓四方

随着5G技术的不断发展&#xff0c;其在工业领域的应用日益广泛。5G工业数采网关作为连接工业设备与网络的重要枢纽&#xff0c;具备多种功能&#xff0c;为工业自动化、智能制造和智慧工厂提供了强大的支持。本文将详细解析5G工业数采网关的功能&#xff0c;并探讨其在工业领域…

【调试笔记-20240528-Linux-用 OpenWrt-23.05 SDK 编译 frp 软件包】

调试笔记-系列文章目录 调试笔记-20240528-Linux-用 OpenWrt-23.05 SDK 编译 frp 软件包 文章目录 调试笔记-系列文章目录调试笔记-20240528-Linux-用 OpenWrt-23.05 SDK 编译 frp 软件包 前言一、调试环境操作系统&#xff1a;Ubuntu 22.04.4 LTS编译环境调试目标 二、调试步…

剖析【C++】——类与对象(中)——小白篇—超详解

目录 1.类的6个默认成员函数&#xff1a; 1. 默认构造函数&#xff08;Default Constructor&#xff09; 2. 析构函数&#xff08;Destructor&#xff09; 3. 拷贝构造函数&#xff08;Copy Constructor&#xff09; 4. 拷贝赋值运算符&#xff08;Copy Assignment Operato…

【RK3288 Android10 T8pro usb hid-multitouch idc配置】

【RK3288 Android10 T8pro usb hid-multitouch idc配置】 文章目录 【RK3288 Android10 T8pro usb hid-multitouch idc配置】背景代码分析1. 读取配置文件2. 标志内外置屏幕3. 设置输入设备4. findviewport()5. 根据对应的viewport来计算相应的mapping的参数 结论 背景 T8pro …

C++网络编程——socket

在服务器中&#xff0c;需要建立一个socket套接字才能对外提供一个网络通信接口&#xff0c;在Linux系统中套接字仅是一个文件描述符&#xff0c;也就是一个int类型的值 socket概念 socket 的原意是“插座”&#xff0c;在计算机通信领域&#xff0c;socket 被翻译为“套接字…

骆驼大赛

目录 一&#xff0c;主版图 二&#xff0c;骰子 三&#xff0c;初始设置 四&#xff0c;核心规则 五&#xff0c;结算 这是适合5-8人玩的一个概率推理类的回合制桌游。 一&#xff0c;主版图 赛道由16个格子组成&#xff0c;编号为1-16。 一共7个骆驼&#xff0c;其中正…

python如何巧妙地利用内置函数与列表切片组织舞会派对

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、问题分析 三、解决方案 1. 利用内置函数创建参会人员名单 2. 利用列表切片…