在2021年实现一个集成语音识别功能的微信小程序的极简教程

1,929 阅读7分钟

前言

因为距离我上一次写微信小程序大概已经有3,4年了,最近又重新开始写起来这玩意,很正常自然地踩了一些坑,在这里记录一下,希望给研发相关APP的朋友们节约一点点时间。

(`・ω・´)本文将主要围绕原生开发以及语音识别应用开发的过程中所遇到的问题展开。

一. 怎么构建一个语音识别应用

首先,我们在一开始就放弃了自己去造一个语音识别轮子的想法,转而选择使用第三方API来实现相关功能 (特指Speech=>Text部分)。经过考察初步选定了科大讯飞的语音识别API和调用微信小程序第三方插件的两种解决方案。

说句公道说话,科大讯飞的api是挺强大的,但是在前期调研之中,发现不是太好做...看完文档发现,讯飞的语音听写api是基于websocket,而且支持的音频格式与wx.startRecord接口生成的silk后缀文件有点出入...亦即要利用相关api实现我们的功能的话,录音完成后要先转码,再上传,实在有一、、复杂。

这时候,微信同声传译插件闪亮登场了!

不用转码,不用websocket,直接调用即可实现语音转文字!

是不是很吸引人呢...但事实证明,这条路还是有一些坑的(虽然理论上来说还是比用讯飞api要方便快捷就是了)

简易步骤:

1. 在小程序管理后台中加入第三方插件微信同声传译

2. 修改app.json

//app.json
{
  ...
    "plugins": {
        ...
        "WechatSI": {
            "version": "0.3.1",
            "provider": "wx069ba97219f66d99"
        }
    }
}

3. 在需要使用的页面中加入相应js

Example:

//index.js
var plugin = requirePlugin("WechatSI")
let manager = plugin.getRecordRecognitionManager()
Page({
    ...
    onLoad: function() {
        manager.onRecognize = function(res) {
            console.log("current result", res.result)
        }
        manager.onStop = function(res) {
            console.log("record file path", res.tempFilePath)
            console.log("result", res.result)
        }
        manager.onStart = function(res) {
            console.log("成功开始录音识别", res)
        }
        manager.onError = function(res) {
            console.error("error msg", res.msg)
        }
    },
    startRecord:function(){
        manager.start();
    },
    stopRecord:function(){
        manager.stop();
    }
})

注意:recordRecoManager是全局唯一的;对manager的事件绑定建议在onLoad或者其他页面hook上实现,放到startRecord方法里实测是有负面影响的。

好了,这时候如无意外应该在真机上已经可以实现语音识别的功能了——是的,由于不明原因,该功能在小程序开发工具上面表现成谜,从来没有成功识别过,所以只能够用真机调试的方法来开发。

如果只是普通的语音识别功能,到这里已经结束了,但是...我们的需求还有着让用户能够回放自己的录音的部分...

诶?上面manager.onStop方法不是已经有回调res.tempFilePath吗?

请看这条Warning:

image.png

通过研究onStop回传的录音文件,发现的确毛都听不到——而且这条Warning不是每次调用都会出现的!!!可以想像刚开始在开发者工具上面完全听不到回放又因为上面不明原因的bug导致识别不出结果的我有多懵逼...无论怎么想都好像是自己代码写错了吧,于是开始了漫长地debug,直到有一次机缘巧合,触发了Warning...

虽然这句话讲得好像在真机上就能够听到录音回放一样,可由于测试环境的制约,还是额外调用了wx.startRecord来满足能让用户听到回放的需求,亦即在插件onStart开始识别的时候,我们同时调用录音接口,来另外生成一个录音文件。

经过实践证明,it works. 两者可以同时存在。所以我们已经可以通过wx.playVoice听到刚刚的录音啦~

PS:官方文档说playVoice方法已经不再维护了,但是实测可以继续使用,如果需要更多功能,可以使用wx.createInnerAudioContext()代替。

二. 原生开发中遇到的一些问题

原本听了别人的安利,是想试下用uni-app去做这次小程序的开发的,但后来根据我们的需求评估了一下要实现的功能,根据我毫无数据支持的瞎猜多年程序研发经验,第三方框架反而才是我们这个完全符合“小而美”字面意义的真·小程序的最大性能瓶颈......因此还是从实际出发,选择了以WXML + WXSS + WXS这一套原生全家桶去做开发。

所以本节主要记录了一些与小程序原生相关的问题 (:3[▓▓]

1. WXSS对伪类选择器的不完善,如:hover or :active

在官方文档中WXSS是不支持伪类选择器的,理论上来说像:hover,:focus:active这些都不顶用。

image.png

然而在实践中发现,对于伪类选择器,微信小程序不是完全不支持,应该说是支持不完全。

.floating-panel:hover{
  background-color: #4abcdd;
}
.floating-panel:focus{
  background-color: #7d7d7d;
}
.floating-panel:active{
  background-color: #343434;
}

以这段代码为例,模拟器上单纯悬停不会触发:hover,在点击触发:active后不松手移动光标反而有了:hover的效果...其他如:first-child等没有一一实验,但是论坛里面的大哥大姐们说可以。

虽然讲句公道说话,在微信小程序的使用场景中,:hover的确很鸡肋,但在某些场景,比如点击按钮时,伪类选择器好像还挺必要的吧?

经过笔者细心耐心仔细地百度研究,解决方法主要两种:

  1. 官方tips: 建议使用小程序内置组件的 hover-class 属性来实现,放弃:active
  2. 民间偏方: 使用数据驱动的方式,用js(i.e. 给节点的class增加一个双向绑定)的方式来实现:
<!--Example-->
<view class="choose {{num==1?'active':''}}" data-num="1" bindtap="itemClick"></view>

2. 不支持对DOM节点进行直接操作

微信小程序不支持对DOM节点的直接操作,想要增减页面元素的话,还是那句话,要通过数据驱动的方式迂回进行,i.e. 通过wx:if 或者 wx:for来实现

3. 标签<text>内只支持<text>标签!

有一天,我顺手在<text>里面放了个<b>,然后被<b></b>包裹的内容神奇地消失了...第一反应我以为是之前写了什么神奇的CSS造成了这个bug,原地review + 测试半小时——直到我放弃挣扎改回<text>,后来在官方文档的某个角落里面看到这句话: "text 组件内只支持 text 嵌套"

是的,不止是<b>,<i>,<span>,还有其他除<text>外所有标签,都不支持...

4. 资源路径中不能够有中文,包括文件名

由于开发模拟器和我的ios测试机上面界面一切正常,开头没有发现这个问题,直到留意到在安卓手机上存在图片资源不能够成功加载的情况,我才把部分用了中文名的图片改英文,修复了这个bug...所以路径要全英真的是前人的血泪教训呀...╥﹏╥

5. 云开发

emmmm...云开发应该也算是原生开发的一部分吧..? 微信小程序的云开发是基于nodejs的,基本上开发工具一开始就初始化了了几个简单的例子。下面给出一点点Tips吧:

  • 刚开始要应用时会提示要安装wx-server-sdk,但其实这玩意不是安装在cloudfunctions文件夹里面的,而是要在各个云函数的目录里面分别npm install,同理,每个云函数都会有各自的package.jsonnode_modules
  • 之前没用过云开发的数据库想试试的话,别忘了在数据库里面先新建Collection。每个Collection可以视作关系数据库的一个表,也可以视作JSON里面的一个数组

结语

在2021年开发微信小程序还是挺方便快捷的,不禁让我想起来曾经被微信X5引擎的bug们狠狠鞭挞的峥嵘岁月。

Anyway, 希望对大家有所帮助吧~

声明:由于本人近两年没有做过小程序开发,也没认真看文档,如果本文存在缺漏谬误,实属正常,请朋友们轻喷为盼。