3分钟

任务 6 将语音识别功能与语音合成功能结合

任务目的

现在已经完成对输入的语音进行识别的功能,也已经完成了将指定字符串合成语音的功能,最后需要将语音识别功能与语音合成功能结合,完成整个趣味复读机的实验。

任务步骤

1.修改 index.js 文件

将 index.js 修改为以下代码。

const appid = "yourAppId"     # 设置用户属性, 包括 secret_id, secret_key, region等。Appid 已在CosConfig中移除,请在参数 Bucket 中带上 Appid。Bucket 由 BucketName-Appid 组成
const secretid = "yourSecretId"    # 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
const secretkey = "yourSecretKey"  # 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
let plugin = requirePlugin("QCloudAIVoice")
plugin.setQCloudSecret(appid, secretid, secretkey)
let manager = plugin.getRecordRecognitionManager()
const recordOptions = {
  duration: 60000,
  engine_model_type: "16k_0"
};
let innerAudioContext = wx.createInnerAudioContext()

Page({
  data: {
    message_list: [],
    scroll_height: wx.getSystemInfoSync().windowHeight - 54,
    page_index: 0,
    cancel: false,
    status: 0,
    tips: ['按住 说话', '松开 结束', '取消 发送'],
    voiceTypes: [{
      name: '亲和女声',
      num: 0,
      checked: true
    }, {
      name: '亲和男声',
      num: 1,
      checked: false
    }, {
      name: '成熟男声',
      num: 2,
      checked: false
    }, {
      name: '温暖女声',
      num: 4,
      checked: false
    }],
    voiceType: 0,
    state: {
      'normal': 0,
      'pressed': 1,
      'cancel': 2
    },
    toView: '',
    currentText: ''
  },

  onLoad: function () {
    let that = this
    manager.onStart((res) => {
      console.log('recorder start', res.msg)
    })
    manager.onStop((res) => {
      that.setData({
        currentText: res.result
      })
      that.sendVoice(res.tempFilePath, 1)
      that.voiceCompose(res.result)
    })
    manager.onError((res) => {
      console.log('recorder error', res.errMsg)
    })
    manager.onRecognize((res) => {
      that.setData({
        currentText: res.result
      })
    })
    wx.authorize({
      scope: 'scope.record',
      success() {
        wx.showToast({
          title: '录音授权成功',
        })
      }
    })
  },
  record: function () {
    manager.start(recordOptions);
  },
  stop: function () {
    // wx.showModal({
    //   title: '识别成功',
    //   content: this.data.currentText,
    // })
    manager.stop();
    // this.sendVoice("tempfile", 1)
    // this.sendVoice("tempfile", 0)
  },
  touchStart: function (e) {
    // 触摸开始
    var startY = e.touches[0].clientY;
    // 记录初始Y值
    this.setData({
      startY: startY,
      status: this.data.state.pressed
    });
  },
  touchMove: function (e) {
    // 触摸移动
    var movedY = e.touches[0].clientY;
    var distance = this.data.startY - movedY;
    this.setData({
      status: distance > 50 ? this.data.state.cancel : this.data.state.pressed
    });
  },
  touchEnd: function (e) {
    // 触摸结束
    var endY = e.changedTouches[0].clientY;
    var distance = this.data.startY - endY;
    this.setData({
      cancel: distance > 50 ? true : false,
      status: this.data.state.normal
    });
    // 不论如何,都结束录音
    this.stop();
  },
  scrollToBottom: function () {
    this.setData({
      toView: 'row_' + (this.data.message_list.length - 1)
    });
  },
  sendVoice: function (tempUrl, num) {
    var message_list = this.data.message_list;
    var message = {
      myself: num,
      head_img_url: '/images/userimg.jpg',
      content: tempUrl,
    };
    message_list.push(message);
    this.setData({
      message_list: message_list
    })
    this.scrollToBottom()
    setTimeout(() => {
      wx.hideLoading();
    }, 500)
  },
  changeVoiceType: function (e) {
    let voiceType = e.currentTarget.dataset.voicetype;
    let arrys = this.data.voiceTypes;
    for (let i = 0; i < arrys.length; i++) {
      if (arrys[i].num == voiceType) {
        arrys[i].checked = true
      } else {
        arrys[i].checked = false
      }
    }
    this.setData({
      voiceTypes: arrys,
      voiceType: voiceType
    })
  },
  playVoice: function (e) {
    innerAudioContext.src = e.currentTarget.dataset.src
    innerAudioContext.onPlay(() => {
      console.log('开始播放')
    })
    innerAudioContext.onError((res) => {
      console.log(res.errMsg)
      console.log(res.errCode)
    })
    innerAudioContext.play()
  },
  voiceCompose: function (content) {
    let that = this
    let voiceType = that.data.voiceType
    let res = wx.cloud.callFunction({
      name: 'invokeTTS',
      data: {
        "transObj": {
          "content": content,
          "voiceType": voiceType
        }
      },
      success: (res) => {
        that.sendVoice(res.result, 0)
      },
      fail: (err) => {
        console.log(err)
      }
    })
  }
})

新的代码添加了调用云函数 invokeTTS 的过程,并对已有函数做了一些修改。

新代码添加了 voiceCompose 函数,将语音识别的文本调用 invokeTTS 云函数对语音进行合成。

voiceCompose: function (content) {
  let that = this
  let voiceType = that.data.voiceType
  let res = wx.cloud.callFunction({
    name: 'invokeTTS',
    data: {
      "transObj": {
        "content": content,
        "voiceType": voiceType
      }
    },
    success: (res) => {
      that.sendVoice(res.result, 0)
    },
    fail: (err) => {
      console.log(err)
    }
  })
}

另外修改了 stop 函数和 manager 的 onStop 函数,完成对函数的调用。

stop: function () {
  // wx.showModal({
  //   title: '识别成功',
  //   content: this.data.currentText,
  // })
  manager.stop();
  // this.sendVoice("tempfile", 1)
  // this.sendVoice("tempfile", 0)
}

manager.onStop((res) => {
  that.setData({
    currentText: res.result
  })
  that.sendVoice(res.tempFilePath, 1)
  that.voiceCompose(res.result)
})

2.使用手机验证结果

重新编译小程序并使用手机扫码在手机上运行小程序。

验证趣味复读机效果

此时说完一句话后,可以看到自己发出的语音和系统回复的语音,点击对话框可以听到自己语音和TTS接口合成的语音,说明实验成功。

注意:现在使用手机测试的小程序是开发版本,只有小程序的管理员和管理员设置有权限的人员可以使用。如果想让让所有人都可以使用小程序,需要正式发布小程序。正式发布小程序需要进行审批等操作。