微信小程序----实现语音的录制,并将语音转为文字

最近在开发微信小程序,遇到一个需求,实现类似于微信发送语音的效果,但是我这个是发送语音后直接转为文字展示。
在这里插入图片描述

  1. 先说微信小程序的textarea

    1. 0/200. 是限制用户输入的最长长度
      1. 0这个位置 首先是根据用户输入的长度变化而变化的
      2. 200 是用户输入最长度
      3. 通过bindinput去监听用户的输入的长度,如果用户输入小于最大值,将长度赋值给0的位置
    2. 在用真机调试的时候发现,textarea的输入值还有placeholder回悬浮在手机上,通过多次查询发现给textarea添加fixed属性会解决
    3. 在真机调试的时候发现一点开textarea这个页面,输入框就会自动谈到顶部,通过 cursor-spacing=“90” 会解决这个问题
<textarea fixed="{{true}}" class='yuyinCon' maxlength="{{orderNoteMax}}" placeholder='请输入内容'
                value='{{noteNum}}' cursor-spacing="90" bindinput="bindInputNote" placeholder-class="init_textarea">
                <text class="currentWordNumber">{{currentWordNumber|0}}/{{orderNoteMax}}</text>
 </textarea>
bindInputNote(e){
	this.setData({
		noteNum:e.detail.value.trim()
	})
	var len = ((e.detail.value).toString()).length
	if(len > this.data.orderNoteMax) return;
	this.setData({
		currentWordNumber:len
	})
}

再来说实现录制语音—通过微信小程序的同声传译的插件

  1. 需要在微信公众平台去添加插件
  2. 在app.json添加
"plugins": {
    "WechatSI": {
      "version": "插件的最新版本号",
      "provider": "插件的AppID"
    }
  },
  1. 在需要的页面中先
//引入插件:微信同声传译
const plugin = requirePlugin('WechatSI');
//获取全局唯一的语音识别管理器recordRecoManager
const manager = plugin.getRecordRecognitionManager();
  1. 对同声传译进行初始化
//识别语音 -- 初始化
initRecord(){
	const that = this;
	// 当有新的识别内容返回,则会调用此事件
	manager.onRecognize = function(res){
		console.log(res)
	}

	// 正常开始录音识别时会调用此事件
	manager.onStart = function(res){
		console.log("成功开始录音识别", res)
	}

	// 识别错误事件
    manager.onError = function (res) {
      console.error("error msg", res)
    }

	//识别结束事件
	manager.onStop = function(res){
		console.log('..............结束录音')
      console.log('录音临时文件地址 -->' + res.tempFilePath);
      console.log('录音总时长 -->' + res.duration + 'ms');
      console.log('文件大小 --> ' + res.fileSize + 'B');
      console.log('语音内容 --> ' + res.result);

      if (res.result == '') {
        wx.showModal({
          title: '提示',
          content: '听不清楚,请重新说一遍!',
          showCancel: false,
          success: function (res) { }
        })
        return;
      }
	}
},

可以看到录音的整个流程–实现过程

  1. 给按住说话的button绑定catchtouchstart事件,首先根据wx.getSetting判断用户是否打开了录音权限,如果没有打开,则通过wx.authorize,向用户打开授权请求,如果用户拒绝了,就给用户打开授权设置页面。
  2. 如果用户一开始就授权了录音设置,则调用语音开始录制的事件
  3. 给button绑定catchtouchend事件,触发语音录制结束事件
  4. 在语音录制结束后,还可以拿到录音的总时长,具体怎么展示可以自定义。我们可以将录音文件传给后端,后端返回可以播放的录音,在前端就可以播放这段录音。
    1. 通过创建 wx.createInnerAudioContext();`

tips:
在代码过程中,我通过catchtouchstart和catchtouchend分出触发点击录制和松开发送两个过程,我本想通过两个事件分别弹出“按住说话”和“松开发送”,经查验发现catchtouchend一直没有触发,查了好久才发现

1. “按住说话”和“松开发送" 我是通过wx:if去判断的,分别在两个事件里面去设置true or false
2. 按照流程,首先触发catchtouchstart,然后通过setData()去修改参数
3. 发现setData()会刷新页面,所以catchtouchend一直触发不了,
4. 我现在是吧两个弹框柔道一起,其他有待研究。。。

具体代码

 <view class="yuyinWrap">
              <textarea class='yuyinCon' fixed maxlength="{{orderNoteMax}}" placeholder='请输入内容'
                value='{{noteNum}}' cursor-spacing="90" bindinput="bindInputNote" placeholder-class="init_textarea">
                <text class="currentWordNumber">{{currentWordNumber|0}}/{{orderNoteMax}}</text>
              </textarea>
              <!--  -->
              <view class=''>
                <button class="yuyinBtn {{recordState == true ? 'yuyinBtnBg':''}}" catchtouchstart="touchStart"
                  catchtouchend="touchEnd">
                  按住 说话
                </button>
              </view>
              <view class="output-audio" wx:if="{{yuyinTime > 0}}">
                <!-- 默认状态isplay=F -->
                <view wx:if="{{isPlay === false}}" bindtap="bindPlay" style='width:160rpx' class="audio">
                  <view class="yuyin_icon">
                    <image style="width:100%;height:100%" src="../../images/vedio@2x.png"></image>
                  </view>
                  <view class="yuyin_time">{{yuyinTime}}s</view>
                </view>
                <!-- 正在播放状态isplay=T -->
                <view wx:if="{{isPlay === true}}" bindtap="bindStop" style='width:160rpx' class="audio">
                  <view class="yuyin_icon_act"> 
                    <image wx:if="{{isImg === 1}}" style="width:100%;height:100%" src="../../images/vedio3@2x.png">
                    </image>
                    <image wx:elif="{{isImg === 2}}" style="width:100%;height:100%" src="../../images/vedio2@2x.png">
                    </image>
                    <image wx:elif="{{isImg === 3}}" style="width:100%;height:100%" src="../../images/vedio1@2x.png">
                    </image>
                  </view>
                  <view class="yuyin_time">{{yuyinTime}}s</view>
                </view>
              </view>
              <mp-loading wx:if="{{isLoading}}" type="circle"></mp-loading>
              <!-- 开始语音 弹出语音图标表示正在录音 -->
              <cover-view class="startYuyinImage" wx:if="{{recordState == true}}">
                <cover-image src="../../images/yuyin.png"></cover-image>
                <cover-view>开始语音,松开发送</cover-view>
              </cover-view>
            </view>
//识别语音 -- 初始化
  initRecord: function () {
    const that = this;
    // 有新的识别内容返回,则会调用此事件
    manager.onRecognize = function (res) {
      console.log("读取到新内容:", res)
    }
    // 正常开始录音识别时会调用此事件
    manager.onStart = function (res) {
      console.log("成功开始录音识别", res)
      that.setData({
        isLoading: false,
        recordState: true, //录音状态
      })
    }
    // 识别错误事件
    manager.onError = function (res) {
      console.error("error msg", res)
    }
    //识别结束事件
    manager.onStop = function (res) {
      console.log('..............结束录音')
      console.log('录音临时文件地址 -->' + res.tempFilePath);
      console.log('录音总时长 -->' + res.duration + 'ms');
      console.log('文件大小 --> ' + res.fileSize + 'B');
      console.log('语音内容 --> ' + res.result);

      if (res.result == '') {
        wx.showModal({
          title: '提示',
          content: '听不清楚,请重新说一遍!',
          showCancel: false,
          success: function (res) { }
        })
        return;
      }
      var tempImagePath = res.tempFilePath
      wx.uploadFile({
        url: `${app.globalData.baseUrl}/upload/audio`, //此处换上你的接口地址
        filePath: tempImagePath,
        name: 'file',
        formData: {
          duration: parseInt(parseInt(res.duration) / 1000)
        },
        header: {
          // "Content-Type": "multipart/form-data",
          // 'accept': 'application/json',
          'Authorization': app.getToken() //若有token,此处换上你的token,没有的话省略
        },
        success: function (resaudio) {
          if (resaudio.statusCode == 200) {
            if (JSON.parse(resaudio.data).code == 200) {
              var text = res.result;
              // 获取输入框内容的长度
              var len = parseInt(text.length);
              //最多字数限制
              if (len > that.data.orderNoteMax) return;
              // 当输入框内容的长度大于最大长度限制(max)时,终止setData()的执行
              that.setData({
                currentWordNumber: len //当前字数
              });
              that.setData({
                noteNum: text,
                recordState: false,
                video: JSON.parse(resaudio.data).data.path,
                yuyinTime: parseInt(parseInt(res.duration) / 1000),
                audioId: JSON.parse(resaudio.data).data.id
              }, () => {
                that.checkLogin()
              })
            } else if (JSON.parse(resaudio.data).code == 401) {
              wx.showModal({
                title: '登录',
                showCancel:false,
                content: '确定重新登录?',
                confirmColor: "#000000",
                success: function (res) {
                  if (res.cancel) {
                    //点击取消,默认隐藏弹框
                  } else {
                    //点击确定
                    wx.reLaunch({
                      url: '../login/index',
                    })
                    return false;
                  }
                },
                fail: function (res) { },//接口调用失败的回调函数
                complete: function (res) { },//接口调用结束的回调函数(调用成功、失败都会执行)
              })
            } else {
              this.setData({
                error: JSON.parse(resaudio.data).message || 'Error',
              })
            }
          }
        },
        fail: function (res) {
          console.log(res);

        },
      })



    }
  },
  getSeeting() {
    const _this = this
    wx.getSetting({//获取用户当前设置
      success: res => {
        if (res.authSetting['scope.record']) {//查看是否授权了录音设置
          // 用户已经授权
          console.log("开始授权")
          _this.setData({
            init: false,
            isLoading: true
          })
          _this.timer = setTimeout(() => {
            manager.start({
              duration: 30000,
              lang: 'zh_CN',// 识别的语言,目前支持zh_CN en_US zh_HK sichuanhua
            })
          }, 10)

        } else {
          // 用户还没有授权,向 用户发起授权请求
          wx.authorize({//提前向用户发起授权请求,调用后会立刻弹窗询问用户是否同意授权小程序使用某项功能或获取用户的某些数据,但不会实际调用对应接口
            scope: 'scope.record',
            success() {//用户同意授权摄像头
              console.log("同意授权")
            },
            fail() {//用户不同意授权摄像头
              _this.openSetting()
            }
          })
        }

      },
      fail() {
        console.log('获取用户授权信息失败')
      }
    })
  },
  //语音  --按住说话
  touchStart: function (e) {
    this.getSeeting()

  },
  //语音  --松开结束
  touchEnd: function (e) {
    console.log("结束录音")
    this.setData({
      recordState: false,
      init: true,
      isLoading: false
    })

    manager.stop();
    clearTimeout(this.timer)
    console.log(this.data.recordState)

  },

  bindPlay() {
    console.log('播放监听')
    this.setData({
      isPlay: true,
    })
    myaudio.src = this.data.video
    myaudio.autoplay = true
    setTimeout(() => {
      myaudio.play()
    }, 500)

    myaudio.onPlay(() => {
      var num = 1
      this.imgTimer = setInterval(() => {
        if (num > 3) {
          num = 1
        } else {
          num++
        }
        this.setData({
          isImg: num
        })
        this.setData({

        })
      }, 500)
    })

    myaudio.onEnded(() => {
      this.setData({
        isPlay: false,
        isImg: 1,
        imgTimer: null,
      })
      console.log(this.data.yuyinTime)
      clearInterval(this.data.imgTimer)

    })

  },
  bindStop() {
    this.setData({
      isPlay: false,
      isImg: 1,
      imgTimer: null,
    })
    myaudio.stop()
    clearInterval(this.data.imgTimer)
  },
  • 11
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
其实作为H5想要做语音识别,自认为还说有各种弊端得,同时还是微信公众号里面,如果小程序得话,或许会简单一点,但是这里是在公众号里面开发,在这个过程中查阅个各种资料,其实里面得东西都大同小异,但是大多数并不全, 首先微信公众号里面得H5开发语音录入,试过各种方法,由于是java渣渣,页面技术只是了解简单得,因此在使用标签得时候,本身并不能适用于当前得需求,因此最后只能选择了微信自带得录音功能,使用微信提供的接口,(具体得接口使用还是去看微信接口得使用) 由于使用微信得接口得话,首先要上传到它得服务器上面去(虽然也有本地得文件储存id,但是好像并没有用),这里就涉及到了一个serverid,这个值是存在于微信临时素材库得id,后期要用它来获取录音得文件。 通过微信提供得获取临时素材文件得接口,得到了文件(具体方法网上一搜一大堆,后期我也会将完整得代码放进资源里面),但是这里有一个坑得问题,他的格式为.amr得格式,但是最后的目的是转换为文字,因此这里涉及到了格式转换 主要用了现成得技术,也是通过下载资源获取得jar以及实现得方法,将.amr转换为了MP3格式 然后就就是最后一部了,将mp3得音频文件转换为文字,我这里用的是讯飞得技术,当然百度得也可以, 其实整个需求得这个流程已经完成了,但是不得不说里面遇到得坑,由于是渣渣,除了人们总说的,转换为mp3得时候会报N/A得错误,我还遇到了unkowFormat这个错误。 这里只是记录一下自己在查询资料时没有一个符合自己需求得资料,同时也希望以后有这样需求得人,可以将思路捋的更加清楚,也可以进行交流.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值