Android 标准语音识别框架:SpeechRecognizer 的封装和调用

本文聚焦Android语音识别,介绍如何实现识别服务,包括定义接口、解析请求参数等;阐述请求识别的步骤,如声明权限、创建入口实例等;还探究系统调度原理,涵盖检测、初始化、开始及停止识别等环节,最后结合图示展示机制链路。

speech-base_recognition.png

前言

此前,笔者梳理了语音相关的两篇文章:

  1. 如何打造车载语音交互:Google Voice Interaction 给你答案:介绍的是 3rd Party App 如何通过 Voice Interaction API 快速调用系统的语音交互服务快速完成确认、选择的基础语音对话
  2. 直面原理:5 张图彻底了解 Android TextToSpeech 机制:侧重于阐述 TTS Engine App 如何提供 Text-to-Speech 文字转语音服务,以及 3rd Party App 又如何便捷地调用这些服务。

还缺最后一块即如何向系统提供语音识别SpeechRecognizer 服务、3rd Party App 如何使用他们,以及系统和联系这两者?

本篇文章将为你补齐这块知识点。

如何实现识别服务?

首先我们得提供识别服务的实现,简单来说继承 RecognitionService 实现最重要的几个抽象方法即可:

  1. 首先可以定义抽象的识别 Engine 的接口 IRecognitionEngine
  2. 在 RecognitionService 启动的时候获取识别 engine 提供商的实现实例
  3. onStartListening() 里解析识别请求 Intent 中的参数,比如语言、最大结果数等信息封装成 json 字符串传递给 engine 的开始识别。那么 Engine 也需要依据参数进行识别实现方面的调整,并将识别过程中相应的状态、结果返回,比如开始说话 beginningOfSpeech() 、结束说话 endOfSpeech() 、中间结果 partialResults()
  4. onStopListening() 里调用 engine 的停止识别,一样的需要 engine 回传结果,比如最终识别结果 results()
  5. onCancel() 里执行 engine 提供的 release() 进行识别 engine 的解绑、资源释放
interface IRecognitionEngine {
   
   
    fun init()

    fun startASR(parameter: String, callback: Callback?)

    fun stopASR(callback: Callback?)

    fun release(callback: Callback?)
}

class CommonRecognitionService : RecognitionService() {
   
   
    private val recognitionEngine: IRecognitionEngine by lazy {
   
   
        RecognitionProvider.provideRecognition()
    }

    override fun onCreate() {
   
   
        super.onCreate()
        recognitionEngine.init()
    }

    override fun onStartListening(intent: Intent?, callback: Callback?) {
   
   
        val params: String = "" // Todo parse parameter from intent

        recognitionEngine.startASR(params, callback)
    }

    override fun onStopListening(callback: Callback?) {
   
   
        recognitionEngine.stopASR(callback)
    }

    override fun onCancel(callback: Callback?) {
   
   
        recognitionEngine.release(callback)
    }
}

当然不要忘记在 Manifest 中声明:

<service
    android:name=".recognition.service.CommonRecognitionService"
    android:exported="true">
    <intent-filter>
        <action android:name="android.speech.RecognitionService"/>
    </intent-filter>
</service>

如何请求识别?

首先得声明 capture audio 的 Runtime 权限,还需补充运行时权限的代码逻辑。

<manifest ... >
    <uses-configuration android:name="android.permission.RECORD_AUDIO"/>
</manifest>

另外,Android 11 以上的话,需要额外添加对识别服务的包名 query 声明。

<manifest ... >
    ...
    <queries>
        <intent>
            <action
                android:name="android.speech.RecognitionService" />
        </intent>
    </queries>
</manifest>

权限满足之后,最好先检查整个系统里是否有 Recognition 服务可用,NO 的话,直接结束即可。

class RecognitionHelper(val context: Context) {
   
   
    fun prepareRecognition(): Boolean {
   
   
        if (!SpeechRecognizer.isRecognitionAvailable(context)) {
   
   
            Log.e("RecognitionHelper", "System has no recognition service yet.")
            return false
        }
        ...
    }
}

有可用服务的话,通过 SpeechRecognizer 提供的静态方法创建调用识别的入口实例,该方法必须在主线程调用

class RecognitionHelper(val context: Context) : RecognitionListener{
   
   
    private lateinit var recognizer: SpeechRecognizer

    fun prepareRecognition(): Boolean {
   
   
        ...
        recognizer = SpeechRecognizer.createSpeechRecognizer(context)
        ...
    }
}

当然如果系统搭载的服务不止一个,并且已知了其包名,可指定识别的实现方:

public static SpeechRecognizer createSpeechRecognizer (Context context, 
                ComponentName serviceComponent)

接下来就是设置 Recognition 的监听器,对应着识别过程中各种状态,比如:

  • onPartialResults() 返回的中间识别结果,通过 SpeechRecognizer#RESULTS_RECOGNITION key 去 Bundle 中获取识别字符串 getStringArrayList(String)
  • onResults() 将返回最终识别的结果,解析办法同上
  • onBeginningOfSpeech():检测到说话开始
  • onEndOfSpeech():检测到说话结束
  • onError() 将返回各种错误,和 SpeechRecognizer#ERROR_XXX 中各数值相对应,例如没有麦克风权限的话,会返回 ERROR_INSUFFICIENT_PERMISSIONS
  • 等等
class RecognitionHelper(val context: Context) : RecognitionListener{
   
   
    ...
    fun prepareRecognition(): Boolean {
   
   
        ...
        recognizer.setRecognitionListener(this)
        return true
    }

    override fun onReadyForSpeech(p0: Bundle?) {
   
   
        TODO("Not yet implemented")
    }

    override fun onBeginningOfSpeech() {
   
   
        TODO("Not yet implemented")
    }

    override fun onRmsChanged(p0: Float) {
   
   
        TODO("Not yet implemented")
    }

    override fun onBufferReceived(p0: ByteArray?) {
   
   
        TODO("Not yet implemented")
    }

    override fun onEndOfSpeech() {
   
   
        TODO("Not yet implemented")
    }

    override fun onError(p0: Int) {
   
   
        TODO("Not yet implemented")
    }

    override fun onResults(p0: Bundle?) {
   
   
        TODO("Not yet implemented")
    }

    override fun onPartialResults(p0: Bundle?) {
   
   
        TODO("Not yet implemented")
    }

    override fun onEvent(p0: Int, p1: Bundle?) {
   
   
        TODO("Not yet implemented")
    }
}

之后创建识别的必要 Intent 信息并启动,信息包括:

  • EXTRA_LANGUAGE_MODEL:必选,期望识别的偏好模型,比如代码里设置的自由形式的 LANGUAGE_MODEL_FREE_FORM 模型,还有依赖网络搜索的 LANGUAGE_MODEL_WEB_SEARCH 模型等
  • EXTRA_PARTIAL_RESULTS:可选,是否要求识别服务回传识别途中的结果,默认 false
  • EXTRA_MAX_RESULTS:可选,设置允许服务返回的最多结果数值,int 类型
  • EXTRA_LANGUAGE:可选,设置识别语言,默认情况下是 Locale.getDefault() 的地区语言(笔者使用的是 Google Assistant 提供的识别服务,暂不支持中文,所以此处配置的 Locale 为 ENGLISH)

另外,需要留意两点:1. 此方法必须在上述监听器设置之后进行,2. 该方法得在主线程发起

class RecognitionHelper(val context: Context) : RecognitionListener{
   
   
    ...
    fun startRecognition() {
   
   
 
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechMerger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值