【小程序】语音识别 (无需转码、无需后台)

微信小程序目前录音仅能生成 mp3和acc两种格式,而百度语音识别,讯飞语音识别都不支持mp3和acc的;

最后发现腾讯云阿里云支持mp3,反正用的是腾讯的小程序,将就着用腾讯的接口咯

不想解说了,直接说流程+代码+注释吧

准备:

1.小程序账号(appid)

2.腾讯云控制后台申请语音识别服务(获取SecretId 和SecretKey),对应代码中的马赛克,自行替换

 

不建议直接搬代码就用,所以多少看下接口文档吧

小程序录音接口腾讯云语音识别接口

主要流程:

一、小程序录音及临时路径获取

二、根据临时读取录音文件并转换成base64编码

三、调用语音识别接口(submit())

  1. 参数获取

  2. 加密并获取签名(需要用到hmac_sha1加密,找了挺久才找到的,在最下面)

  3. 发送请求

const app = getApp();
const recorder = wx.getRecorderManager();
const player = wx.createInnerAudioContext();
const file = wx.getFileSystemManager();
var that;
Page({
  /**
   * 页面的初始数据
   */
  data: {
    apikey: '马赛克',
    secret_id:'马赛克',
    token:"",
    recording: false,
    cancel_record:false,
    start_y: '',

    fileBase64: '', //base64的文件
    rate: 8000,
    filePath: '',//录音文件
    fileLen:0,//录音长度
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    that = this;
    //先定好停止录音后要干嘛
    recorder.onStop(function suc(e) {
      //保存录音文件的临时路径
      that.setData({
        filePath: e.tempFilePath,
      })
      wx.setStorageSync('filePath', e.tempFilePath);
      //友好的菊花加载
      wx.showLoading({
        title: '文件读取中...'
      })
      //读取录音文件,并转为base64编码
      file.readFile({
        filePath: e.tempFilePath,
        encoding: 'base64',
        success: function (e) {
          that.setData({
            fileBase64: e.data
          })
          console.log(e);
        },
        complete() {
          wx.hideLoading();
        },
      })
    })
  },
  //语音识别
  submit(){    
    let apikey = that.data.apikey;
    let param = {
      Action: 'SentenceRecognition',
      Version: '2018-05-22',
      ProjectId: '0',//腾讯云项目ID
      SubServiceType: 2, //一句话识别,没得选
      EngSerViceType: '8k',//引擎类型 8k(电话) 或 16k,没说明白
      SourceType: 1,//0 url  1数据
      VoiceFormat: 'mp3',//格式 MP3 或 wav
      UsrAudioKey: new Date().getTime(),//唯一识别?直接用时间戳吧珂珂
      Data: that.data.fileBase64,
      DataLen: app.base64_decode(that.data.fileBase64).length,//未进行base64编码时的数据长度
      // Url :'',你懂的
      Timestamp: parseInt(new Date().getTime()/1000),
      Nonce: parseInt(new Date().getTime() / 1000),
      SecretId: that.data.secret_id,      
    };
    
    //把参数按键值大小排序并拼接成字符串
    let data = ksort(param);
    let arr = [];
    for (var x in data) {
      data[x] = encodeURI(data[x]);
      arr.push(x + '=' + data[x]);
    }
    let str = arr.join('&');
    //签名生成
    let sign = 'POSTaai.tencentcloudapi.com/?' + str;
    sign = b64_hmac_sha1(apikey,sign);
    data['Signature'] = sign;
    //友好的菊花提示
    wx.showLoading({
      title: '发送中...',
    })
    wx.request({
      url: 'https://aai.tencentcloudapi.com/',
      data:data,
      header:{
        'content-type':'application/x-www-form-urlencoded'
      },
      method:'post',
      success:function(e){console.log(e.data.Response)},
      complete(){wx.hideLoading();}
    })
  },
  //手指按下
  clickDown(e){
    console.log('start');
    that.setData({
      recording: true,
      start_y: e.touches[0].clientY,
      cancel_record: false,
    })
    
    //开始录音
    recorder.start({
      duration: 60000,//最大时长
      sampleRate: that.data.rate,//采样率
      numberOfChannels: 1,//录音通道数
      encodeBitRate: 24000,//编码码率,有效值见下表格
      format: 'mp3',//音频格式
      // frameSize: 2000,//指定大小 kb
    })
  },
  //手指移动
  clickMove(e){
    if (e.touches[0].clientY - that.data.start_y <= -50) {
      that.setData({
        cancel_record: true,
      })
    } else {
      that.setData({
        cancel_record: false,
      })
    }
    return false;
  },
  //手指松开
  clickUp(e){
    console.log('end');
    if (that.data.cancel_record) {
      wx.showModal({
        title: '提示',
        content: '您选择了取消发送,确定吗?',
        confirmText: '继续发送',
        cancelText: '取消重录',
        success: res => {
          if (res.confirm) {
            wx.showToast({
              title: '发送成功',
            })
          } else {
            wx.showToast({
              title: '您选择了取消',
            })
          }
          that.setData({
            recording: false
          })
        }
      })
    } else {
      wx.showToast({
        title: '发送成功',
      })
      that.setData({
        recording: false
      })
    }
    recorder.stop();
    return false;
    
  },
  //播放
  play(){
    player.src = that.data.filePath;
    player.play();
  },

})

//对象按键值排序方法
function ksort(obj){
    let temp = 'Action';
    let k_arr = [];
    for(var x in obj){
      k_arr.push(x);
    }
    k_arr.sort();
    let res = {};
    for(let i = 0;i<k_arr.length;i++){
      let k = k_arr[i];
      res[k] = obj[k];
    }
    return res;
}

//这个是找了很久的加密函数
function b64_hmac_sha1(k, d, _p, _z) {
  // heavily optimized and compressed version of http://pajhome.org.uk/crypt/md5/sha1.js
  // _p = b64pad, _z = character size; not used here but I left them available just in case
  if (!_p) { _p = '='; } if (!_z) { _z = 8; } function _f(t, b, c, d) { if (t < 20) { return (b & c) | ((~b) & d); } if (t < 40) { return b ^ c ^ d; } if (t < 60) { return (b & c) | (b & d) | (c & d); } return b ^ c ^ d; } function _k(t) { return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514; } function _s(x, y) { var l = (x & 0xFFFF) + (y & 0xFFFF), m = (x >> 16) + (y >> 16) + (l >> 16); return (m << 16) | (l & 0xFFFF); } function _r(n, c) { return (n << c) | (n >>> (32 - c)); } function _c(x, l) { x[l >> 5] |= 0x80 << (24 - l % 32); x[((l + 64 >> 9) << 4) + 15] = l; var w = [80], a = 1732584193, b = -271733879, c = -1732584194, d = 271733878, e = -1009589776; for (var i = 0; i < x.length; i += 16) { var o = a, p = b, q = c, r = d, s = e; for (var j = 0; j < 80; j++) { if (j < 16) { w[j] = x[i + j]; } else { w[j] = _r(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); } var t = _s(_s(_r(a, 5), _f(j, b, c, d)), _s(_s(e, w[j]), _k(j))); e = d; d = c; c = _r(b, 30); b = a; a = t; } a = _s(a, o); b = _s(b, p); c = _s(c, q); d = _s(d, r); e = _s(e, s); } return [a, b, c, d, e]; } function _b(s) { var b = [], m = (1 << _z) - 1; for (var i = 0; i < s.length * _z; i += _z) { b[i >> 5] |= (s.charCodeAt(i / 8) & m) << (32 - _z - i % 32); } return b; } function _h(k, d) { var b = _b(k); if (b.length > 16) { b = _c(b, k.length * _z); } var p = [16], o = [16]; for (var i = 0; i < 16; i++) { p[i] = b[i] ^ 0x36363636; o[i] = b[i] ^ 0x5C5C5C5C; } var h = _c(p.concat(_b(d)), 512 + d.length * _z); return _c(o.concat(h), 512 + 160); } function _n(b) { var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", s = ''; for (var i = 0; i < b.length * 4; i += 3) { var r = (((b[i >> 2] >> 8 * (3 - i % 4)) & 0xFF) << 16) | (((b[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4)) & 0xFF) << 8) | ((b[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4)) & 0xFF); for (var j = 0; j < 4; j++) { if (i * 8 + j * 6 > b.length * 32) { s += _p; } else { s += t.charAt((r >> 6 * (3 - j)) & 0x3F); } } } return s; } function _x(k, d) { return _n(_h(k, d)); } return _x(k, d);
}

页面

<view class="main">
  <button bindtap='play'>播放录音</button>
  <button bindtap='submit'>上传</button>
  
  <button bindtouchstart="clickDown" bind:touchend="clickUp"  touchcancel="clickUp" bindtouchmove="clickMove" class="record_btn">
  <span wx:if="{{recording}}">{{cancel_record?'松开取消':'松开发送'}}</span>
  <span wx:else>按下录音</span>
  </button>
</view>

样式

.record_btn{
  position:absolute;
  bottom: 0;
  width: 100%;
  height:110rpx;
}

简洁的页面

录音=>上传=>然后去看控制台日志哈

  • 9
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
以下是一个简单的Android视频转码程序,使用FFmpeg进行转码,将MP4格式的视频文件换为AVI格式: ```java import android.os.Environment; import android.util.Log; import java.io.File; import java.io.IOException; public class VideoTranscoder { private static final String TAG = "VideoTranscoder"; private static final String VIDEO_PATH = Environment.getExternalStorageDirectory().getPath() + "/test.mp4"; private static final String OUTPUT_PATH = Environment.getExternalStorageDirectory().getPath() + "/test.avi"; public static void transcode() { File inputFile = new File(VIDEO_PATH); if (!inputFile.exists()) { Log.e(TAG, "Input file not exists"); return; } File outputFile = new File(OUTPUT_PATH); if (outputFile.exists()) { outputFile.delete(); } try { // 初始化FFmpeg FFmpeg.getInstance(context).loadBinary(new LoadBinaryResponseHandler() {}); // 转码命令 String[] cmd = {"-i", inputFile.getPath(), "-c:v", "copy", "-c:a", "copy", outputFile.getPath()}; // 执行转码命令 FFmpeg.getInstance(context).execute(cmd, new ExecuteBinaryResponseHandler() { @Override public void onSuccess(String message) { Log.d(TAG, "Transcode succeed: " + message); } @Override public void onFailure(String message) { Log.e(TAG, "Transcode failed: " + message); } @Override public void onProgress(String message) { Log.d(TAG, "Transcode progress: " + message); } @Override public void onStart() { Log.d(TAG, "Transcode started"); } @Override public void onFinish() { Log.d(TAG, "Transcode finished"); } }); } catch (FFmpegNotSupportedException e) { Log.e(TAG, "FFmpeg not supported", e); } } } ``` 在这个示例中,我们使用了FFmpeg库进行视频转码,首先需要初始化FFmpeg,并加载FFmpeg二进制文件。然后,我们定义了转码命令,使用“-i”参数指定输入文件路径,使用“-c:v”和“-c:a”参数指定输出文件的视频和音频编码格式,最后指定输出文件路径。最后,我们执行FFmpeg命令,并设置相应的回调函数来处理转码过程中的状态和结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值