腾讯云语音识别云开发微信小程序

一、实现方式

通过录音管理器 RecorderManager调用手机的录音功能实现音频的在线采集,通过采集到的音频的base64字符串调用云开发侧实现的腾讯云一句话识别云函数,然后将识别结果回调到小程序页面中。

二、实现流程

第一步:开通云开发控制台并创建云端项目环境

添加描述

添加描述

添加描述

第二步:在小程序项目根目录下创建本地云函数根目录functions,在项目根目录找到 project.config.json 文件,新增 cloudfunctionRoot 字段,值为刚才创建的本地云函数根目录名称

第三步:创建一句话识别云函数并配置tencentcloud-sdk-nodejs依赖

第四步:安装依赖

在asr云函数目录上右键选择在"在终端中打开"


E:\tencentcloudcode\wechat\functions\asr>npm install
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142

> protobufjs@6.8.8 postinstall E:\tencentcloudcode\wechat\functions\asr\node_modules\protobufjs
> node scripts/postinstall

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN asr@1.0.0 No description
npm WARN asr@1.0.0 No repository field.

added 101 packages from 194 contributors and audited 186 packages in 8.85s
found 0 vulnerabilities



第五步:在一句话识别云函数目录下的入口文件index.js中实现一句话识别的API调用Demo,然后上传Demo至云端

// 云函数入口文件
const cloud = require('wx-server-sdk')  // 引入云开发服务的内核SDK

cloud.init(   //初始化一个'wx-server-sdk' SDK 实例
{
  env: 'ai-test-t7t64'   // 开通云开发服务后创建的云环境的环境ID(默认可以创建两个ID)
}
)

// 云函数入口函数
exports.main = async (event, context) => {
   
const tencentcloud = require("tencentcloud-sdk-nodejs");  //引入腾讯云SDK

// 下面的代码可以通过explorer在线生成(https://console.cloud.tencent.com/api/explorer?Product=aai&Version=2018-05-22&Action=SentenceRecognition&SignVersion=)
const AaiClient = tencentcloud.aai.v20180522.Client;
const models = tencentcloud.aai.v20180522.Models;

const Credential = tencentcloud.common.Credential;
const ClientProfile = tencentcloud.common.ClientProfile;
const HttpProfile = tencentcloud.common.HttpProfile;

let cred = new Credential("", "");
let httpProfile = new HttpProfile();
httpProfile.endpoint = "aai.tencentcloudapi.com";
let clientProfile = new ClientProfile();
clientProfile.httpProfile = httpProfile;
let client = new AaiClient(cred, "ap-guangzhou", clientProfile);

let req = new models.SentenceRecognitionRequest();
let base64Data=event.x   //接收客户端post的x参数,值类型为base64字符串
let DataLen = event.s    //接收音频文件的大小
var params =  {"ProjectId":0,"SubServiceType":2,"EngSerViceType":"16k_zh","SourceType":1,"VoiceFormat":"mp3","UsrAudioKey":"www","Data":base64Data,"DataLen":DataLen}  // 定义SDK的请求参数字典
params = JSON.stringify(params)   // 转换为json字符串
req.from_json_string(params);
return new Promise((resolve, reject) => {   // 通过Promise容器来接收异步API的回调,然后通过当前脚本返回给客户端
  client.SentenceRecognition(req, function(errMsg, response)  {  // 此接口是异步的,那么当前脚本无法对外直接访问接口返回值
    if (errMsg) {
      resolve({ "Result": errMsg })
    }
    // resp = response.to_json_string()
    resolve({ "Result": response})
});
})
}

注:云函数的入口文件index.js中调用的"一句话识别"API方法"SentenceRecognition”是异步的,如果直接拷贝Explorer中生成的Demo,将无法为小程序客户端返回"SentenceRecognition”的回调数据,脚本最终会返回null;所以这里我们需要使用Promise对象来获取"SentenceRecognition"的回调数据,然后返回给小程序客户端


第六步:小程序中实现音频在线采集页面

在小程序公共配置文件app.json中,添加页面生成参数

"pages/voicec/voicec",


点击"编译"生成页面目录及页面


voicec.wxml

<!--pages/voicec/voicec.wxml-->
<view class="REC">
  <view class="time">{{status==0?'录音时长':(status==3?'录音结束':'录音中')}}:{{time}} 秒 ({{duration/1000}}秒)</view>
  <view class="rin">
  <view class="{{status==3 && actionStatus==0?'show':'hide'}}" bindtap="play" hover-class="skip">{{actionStatus==1?'播放中':'播放录音'}}</view>
    <view class="{{status==3?'show':'hide'}}" bindtap="again" hover-class="skip">再次录制</view> 
  </view>
  <view class="anniu">
    <view class="{{status==0?'highlight':'gray'}}" bindtap="start" hover-class="skip">开始</view>
    <view class="{{status==1?'highlight':'gray'}}" bindtap="stop" hover-class="skip">暂停</view>
    <view class="{{status==2?'highlight':'gray'}}" bindtap="continue" hover-class="skip">继续</view>
    <view class="{{(status==1 || status==2)?'highlight':'gray'}}" bindtap="shutoff" hover-class="skip">停止</view>
    <view class="{{status==3?'highlight':'gray'}}" bindtap="recognition" hover-class="skip">识别</view>
  </view>
   <view class="progress">
    <progress percent="{{time*(100/(duration/1000))}}"  stroke-width="10" backgroundColor="#fff" border-radius="15" stroke-width="4" color="#7FFF00" active />
  </view>
</view>
<view class=".REC">
  <textarea placeholder="录音完成后点击识别可将音频转文字" auto-focus value="{{ Words }}" />
</view>

使用的组件:

进度条progress

多行输入框textarea

使用的视图容器:

view

使用的XML语法:

双大括号数据绑定之三元运算

使用的视图层:

bindtap事件绑定

voicec.js

// pages/voicec/voicec.js
const recorderManager = wx.getRecorderManager()  // 获取全局唯一的录音管理器 RecorderManager
const innerAudioContext = wx.createInnerAudioContext()  // 创建内部 audio 上下文 InnerAudioContext 对象。
var init  // 声明一个全局变量,let为局部变量
Page({  // 使用Page函数作为Page构造器来注册一个页面

  /**
   * 页面的初始数据
   */
  data: {
    voiceSize:0, // 音频的大小
    time: 0, // 初始时间
    duration: 60000, // 录音长时间为1分钟
    localFilePath: "",  //录音文件在本地的路径
    status: 0,  // 录音器的状态:开始1,暂停2,继续1,停止3
    actionStatus: 0, //录音播放状态,1为播放状态,0为未播放状态
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function(options) {

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function() {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function() {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function() {

  },


  /**开始录音 */
  start: function() {
    clearInterval(init) // 取消之前的计时
    recorderManager.onStart((res) => {  // 监听录音开始事件
      console.log('开始录音')
      this.setData({
        status: 1 // 录音开始状态为1
      })
    })

    recorderManager.onStop((res) => {  // 监听录音停止事件
      console.log('停止录音', res)
      this.setData({
        tempFilePath: res.tempFilePath, // 如果录音停止了,修改本地文件地址
        status: 3
      })
      this.timeCounter(this.data.time) // 取消计时
    })

    const options = { //定义录音参数
      duration: this.data.duration,  // 录音时长 
      format: 'mp3', // 音频格式
    }
    this.timeCounter()  // 开始计时
    recorderManager.start(options) // 开始录音
  },

  /**
   * 录音暂停
   */
  stop: function() {
    recorderManager.onPause(() => {
      console.log('recorder pause')
      this.setData({
        status: 2
      })
    })
    this.timeCounter(this.data.time) // 取消计时,暂时和停止都是取消计时
    recorderManager.pause() // 暂停录音
  },

  /**
   * 录音继续
   */
  continue: function() {
    this.setData({
      status: 1  // 标记为正在录音状态
    })
    this.timeCounter() // 在之前的计时基础上继续+1计时
    recorderManager.resume()  // 继续录音
  },

  /**
   * 录音停止
   */
  shutoff: function() {
    recorderManager.onStop((res) => {
      console.log('recorder stop', res)
      this.setData({
        tempFilePath: res.tempFilePath,  // 录音生成文件的本地路径
        status: 3  // 标记录音状态为停止
      })
    })
    this.timeCounter(this.data.time)   // 取消计时
    recorderManager.stop() // 停止录音

  },
  /**
   * 录音识别
   */
  recognition: function() {
    var that=this;
    wx.getFileInfo({
      filePath:this.data.tempFilePath,
      success (res) {
        console.log("录音文件的大小为"+res.size)
        that.data.voiceSize=res.size
      }
    })
    wx.cloud.init() // 初始化云函数环境
    wx.cloud.callFunction({  // 调用云函数
      // 云函数名称
      name: 'asr',  // 调用的云函数的名称
      // 传给云函数的参数
      data: {
        s:that.data.voiceSize,   // 音频文件的大小
        // x: wx.getFileSystemManager().readFileSync(this.data.tempFilePath, 'base64') 
        x: wx.getFileSystemManager().readFileSync("files/test.mp3", 'base64')  // 读取本地文件的base64字符串
      },
      success: function(res) {
        console.log(res)
        that.setData({  // 发送数据到视图层
          Words: res.result.Result.Result
        })
      },
      fail: console.error
    })
  },

  /**
   * 录音播放
   */
  play: function() {
    innerAudioContext.src = this.data.tempFilePath // 音频资源的地址,用于直接播放
    innerAudioContext.obeyMuteSwitch = false // 是否遵循系统静音开关,默认为 true,当此参数为 false 时,即使用户打开了静音开关,也能继续发出声音

    
    if (this.data.actionStatus == 0) {
      this.setData({
        actionStatus: 1
      })
      innerAudioContext.play()  // 播放音频
    }
  
    innerAudioContext.onEnded(() => {  //监听音频自然播放至结束的事件
      innerAudioContext.stop()  // 停止播放
      this.setData({
        actionStatus: 0
      })
    })
  },

  
  timeCounter: function(time) {  // 定义一个计时器函数
    var that = this
    if (time == undefined) {
   
      init = setInterval(function() { // 设定一个计时器ID。按照指定的周期(以毫秒计)来执行注册的回调函数
        var time = that.data.time + 1; // 每秒钟计时+1
        that.setData({
          time: time
        })
      }, 1000);
    } else {
      clearInterval(init) // 取消计时
      console.log("暂停计时")
    }
  },

  /**
   * 重新录制
   */
  again: function() {
    var that = this
    wx.showModal({  // 显示模态对话框
      title: "重新录音", //提示的标题 
      content: "是否重新录制?", //提示的内容
      success(res) {
        if (res.confirm) { // 点击了确定
          that.setData({  // 重置初始化数据
            time: 0, 
            tempFilePath: "", 
            status: 0,
            actionStatus: 0
          })
          innerAudioContext.stop() // 停止音频
        }
      }
    })
  }
})

使用到的知识点:  Page 构造器

录音管理器

HTTPS 网络请求

文件管理器FileSystemManager读取指定编码的文件内容

数据传递setData

注意:如果自定义函数中嵌套了wx等对象函数,数据传递应该先声明"var that=this",然后再嵌套函数,如wx.request中使用"that.setData"来传递数据

voicec.json

{
  "navigationBarTitleText": "云开发一句话识别在线测试",
  "backgroundColor": "#eeeeee"
}

全局配置

voicec.wxss

/* pages/voicec/voicec.wxss */
.REC {
  border-radius: 25rpx;
  background-color: rgb( 199,237,204 );
  padding: 6rpx 0rpx;
  margin: 15rpx 35rpx;
}

.rin {
  justify-content: space-between;
  align-items: center;
  margin: 0rpx 120rpx;
  display: flex;
}

.rin .show {
  background-color: rgb(178, 228, 228);
  padding: 15rpx;
  width: 210rpx;
  border: 5rpx solid rgb(127, 204, 214);
  border-radius: 20rpx;
  font-size: 28rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}

.rin .hide {
  padding: 15rpx;
  align-items: center;
  border-radius: 20rpx;
  display: flex;
  width: 215rpx;
  font-size: 28rpx;
  justify-content: center;
  border: 5rpx solid #eee;
  pointer-events: none;
  background-color: rgba(137, 190, 178, 0.445);
}

.time {
  text-align: center;
  line-height: 75rpx;
  font-size: 28rpx; 
}

.progress {
  margin: 25rpx;
}

.play {
  margin: 0rpx 25rpx;
}

.content {
  line-height: 60rpx;
  font-size: 28rpx;
  display: flex;
  justify-content: center;
}

.anniu {
  display: flex;
  margin: 10rpx 50rpx;
  justify-content: space-between;
}

.highlight {
  display: flex;
  font-size: 28rpx;
  width: 80rpx;
  height: 80rpx;
  justify-content: center;
  border-radius: 50%;
  align-items: center;
  background-color: rgb(107, 194, 53);
  border: 5rpx solid rgb(127, 204, 214);
}

.skip {
  transform: scale(0.9);
}



.anniu .gray {
  pointer-events: none;
  background-color: rgba(137, 190, 178, 0.445);
  display: flex;
  width: 80rpx;
  height: 80rpx;
  font-size: 28rpx;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
  border: 5rpx solid rgb(241, 244, 245); 
}

WXSS样式学习

测试效果


admin
admin管理员

上一篇:【玩转腾讯云】【腾讯云语音合成TTS】短视频批量生成器
下一篇:Apache Tomcat AJP协议漏洞分析(CVE-2020-1938)

留言评论

暂无留言