2017-04-05 15:11:39 qq_38125123 阅读数 18212
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5906 人正在学习 去看看 杨波

1、概述

   通过微信小程序wx.startRecord()和wx.stopRecord()接口录音并上传silk录音文件至服务器,通过ffmpeg将silk录音文件转成wav录音文件,再通过百度语音识别 REST API 获取语音识别后的结果。

2、代码实现

   录音和语音文件上传


   node.js服务端接收语音文件代码

   silk文件转wav文件

   我使用的是silk-v3-decoder将silk文件转wav文件


silk-v3-decoder 使用方法


   百度语音识别 REST API识别wav文件

1、通过API Key和Secret Key获取的access_token


通过API Key和Secret Key获取的access_token文档


2、通过token 调用百度语音识别 REST API识别接口


3、语音识别优化

   通过上述操作后,发现识别的内容和实际内容差别很大


百度语音识别 REST API文档

   查看文档可知:采样率:8000/16000 仅支持单通道

   在ffmpeg里对应的设置方式分别是:

   -ar rate 设置采样率

   -ac channels 设置声道数

   修改converter.sh文件,修改为下图所示


修改后的converter.sh文件

2019-02-15 12:14:12 chen_pan_pan 阅读数 4718
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5906 人正在学习 去看看 杨波

小程序实现语音识别功能,通过语音的方式代替手动输入查询。经过查询微信小程序api,发现微信并没有对外提供语音识别的api,所以要另外想办法。经过多发查找资料发现了思路。

解决思路:

微信小程序提供了录音的功能,通过录音的方式,然后把录音文件传到服务器,后台服务器将语音转码,然后再调用第三方语音识别api,我这里使用的是百度的api,最后在将识别的文字返回给微信小程序。

直接上代码:

小程序端代码:

startRecord: function() {

if (this.recorderManager == null) {

this.recorderManager = wx.getRecorderManager();

this.options = {

duration: 10000,

sampleRate: 16000,

numberOfChannels: 1,

encodeBitRate: 64000,

format: 'mp3',

frameSize: 50

}

}

this.recorderManager.start(this.options);

this.recorderManager.onStop((res) => {

console.log(res)

wx.uploadFile({

url: 'https://xxxx',//将录音文件传到后台服务器

filePath: res.tempFilePath,

method:'POST',

name: 'file',

header: {

'content-type': 'multipart/form-data'

},

success: function(res) {

console.log(res);

},

fail: function() {

console.log("语音识别失败");

}

})

});

},

stopRecord: function() {

this.recorderManager.stop()

}

 

服务端代码:

注意:需要使用mp3plugin.jar包,网上可以下载到。

    // 百度语音识别
    public static final String APP_ID = "xxx";
    public static final String API_KEY = "xxx";
    public static final String SECRET_KEY = "xxx";

    /**
     * @Description TODO
     * @return
     */
    @RequestMapping(value = "speechRecognition", method = RequestMethod.POST)
    @ResponseBody
    public Object speechReco(HttpServletRequest request) {
        MultipartFile file = ((MultipartHttpServletRequest) request).getFile("file");
        try {
            byte[] pcmBytes = mp3Convertpcm(file.getInputStream());
            org.json.JSONObject resultJson = speechBdApi(pcmBytes);
            System.out.println(resultJson.toString());
            if (null != resultJson && resultJson.getInt("err_no") == 0) {
                return resultJson.getJSONArray("result").get(0).toString().split(",")[0];
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }

    /**
     * @Description MP3转换pcm
     * @param mp3Stream
     *            原始文件流
     * @return 转换后的二进制
     * @throws Exception
     */
    public byte[] mp3Convertpcm(InputStream mp3Stream) throws Exception {
        // 原MP3文件转AudioInputStream
        BufferedInputStream zipTest=new BufferedInputStream(mp3Stream);
        //重新包装一层,不然会报错。
        AudioInputStream mp3audioStream = AudioSystem.getAudioInputStream(zipTest);
        // 将AudioInputStream MP3文件 转换为PCM AudioInputStream
        AudioInputStream pcmaudioStream = AudioSystem.getAudioInputStream(AudioFormat.Encoding.PCM_SIGNED,
                mp3audioStream);
        byte[] pcmBytes = IOUtils.toByteArray(pcmaudioStream);
        pcmaudioStream.close();
        mp3audioStream.close();
        return pcmBytes;
    }

    /**
     * @Description 调用百度语音识别API
     * @param pcmBytes
     * @return
     */
    public static org.json.JSONObject speechBdApi(byte[] pcmBytes) {
        // 初始化一个AipSpeech
        AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
        // 可选:设置网络连接参数
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);
        // 调用接口
        org.json.JSONObject res = client.asr(pcmBytes, "pcm", 16000, null);
        return res;
    }

 

如果我的文章帮助到了大家,减少大家的弯路,愿意打赏的请扫下面的二维码。也可留言。

 

2018-12-27 16:37:47 zhouyong6284 阅读数 276
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5906 人正在学习 去看看 杨波

最近需要在小程序上实现语音识别,将需要用到的功能都总结下,供大家参考。语音识别用的是科大讯飞,百度的接口比较费时,原理都是一样,大家可自行选择。

看到有朋友说需要源码,传至GitHub上了:https://github.com/zgliuwen/dabailuyin

一,小程序端实现录音并上传至服务器

1.1,录音:wx.getRecorderManager()

由于目前第三方接口都限制了时间,在1分钟以内,所以录音参数里也需要设置。参考下图。

 

1.2,上传服务器:wx.uploadFile

二,服务器访问科大讯飞识别接口

2.1,服务器用的是springboot+mysql搭建。

小程序端传上来的是mp3,需要转成pcm数据,参考下图

 

2.2,调用讯飞识别接口,具体参考下图

 

大家也可以体验下我做的小程序“大白录音”的识别效果。

2018-11-01 20:45:26 TXX_c 阅读数 6687
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5906 人正在学习 去看看 杨波

微信小程序目前录音仅能生成 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;
}

简洁的页面

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

2017-12-18 11:12:20 u011072139 阅读数 2741
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5906 人正在学习 去看看 杨波

现在人工智能非常火爆,很多朋友都想学,但是一般的教程都是为博硕生准备的,太难看懂了。最近发现了一个非常适合小白入门的教程,不仅通俗易懂而且还很风趣幽默。所以忍不住分享一下给大家。点这里可以跳转到教程。

由于最近项目中需要实现小程序端的语音识别,所以写了一个例子,供需要的同学参考。下面贴下代码

小程序端代码:

 

<!--index.wxml-->
<view class="container">
  <view class="userinfo">
    <button wx:if="{{!hasUserInfo && canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称 </button>
    <block wx:else>
      <image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image>
      <text class="userinfo-nickname">{{userInfo.nickName}}</text>
    </block>
  </view>
  <!-- <view class="usermotto"> -->
      <button type="default" bindtap='startLy' style='margin-top:10rpx;color:green;' disabled='{{disabled1}}' loading='{{loading1}}'>录音</button>
      
      <button type="default" bindtap='endLy' style='margin-top:10rpx;color:red;'>结束</button>

      <button type="default" bindtap='upload' style='margin-top:10rpx;color:blue;' disabled='{{disabled}}' loading='{{loading}}'>识别</button>
      <view>识别结果:{{res}}</view>
  <!-- </view> -->
</view>
//index.js
//获取应用实例
const app = getApp()

const recorderManager = wx.getRecorderManager()

recorderManager.onStart(() => {
  console.log('recorder start')
})
recorderManager.onResume(() => {
  console.log('recorder resume')
})
recorderManager.onPause(() => {
  console.log('recorder pause')
})
recorderManager.onStop((res) => {
  console.log('recorder stop', res)
  const { tempFilePath } = res
})
recorderManager.onFrameRecorded((res) => {
  const { frameBuffer } = res
  console.log('frameBuffer.byteLength', frameBuffer.byteLength)
})

const options = {
  //duration: 10000,
  sampleRate: 16000,//采样率
  numberOfChannels: 1,
  encodeBitRate: 96000,//编码码率
  format: 'mp3',
  frameSize: 50
}

const innerAudioContext = wx.createInnerAudioContext()
var tempPath = "";

Page({
  data: {
    motto: 'Hello World',
    userInfo: {},
    hasUserInfo: false,
    canIUse: wx.canIUse('button.open-type.getUserInfo'),
    res:'',
    loading: false,
    disabled: false,
    loading1: false,
    disabled1: false
  },
  //事件处理函数
  bindViewTap: function() {
    wx.navigateTo({
      url: '../logs/logs'
    })
  },
  onLoad: function () {
    if (app.globalData.userInfo) {
      this.setData({
        userInfo: app.globalData.userInfo,
        hasUserInfo: true
      })
    } else if (this.data.canIUse){
      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
      // 所以此处加入 callback 以防止这种情况
      app.userInfoReadyCallback = res => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    } else {
      // 在没有 open-type=getUserInfo 版本的兼容处理
      wx.getUserInfo({
        success: res => {
          app.globalData.userInfo = res.userInfo
          this.setData({
            userInfo: res.userInfo,
            hasUserInfo: true
          })
        }
      })
    }
  },
  getUserInfo: function(e) {
    console.log(e)
    app.globalData.userInfo = e.detail.userInfo
    this.setData({
      userInfo: e.detail.userInfo,
      hasUserInfo: true
    })
  },
  startLy: function(e){
    var that = this
    that.setData({ loading1: true });
    that.setData({ disabled1: true });
    recorderManager.start(options)
  },
  endLy: function(e){
    var that = this
    that.setData({ loading1: false });
    that.setData({ disabled1: false });
    recorderManager.stop();
    recorderManager.onStop((res) => {
      console.log('recorder stop', res)
      tempPath = res.tempFilePath;
      console.log('录音地址' + tempPath)
    })

  },
  upload: function(){
    var that = this
    that.setData({ loading: true});
    that.setData({ disabled: true});
    wx.uploadFile({
      //url: 'http://192.168.30.3:8080/gt_store_checking_in/voice/upload.action', //仅为示例,非真实的接口地址
      url: 'http://192.168.30.25:8080/gt_store_checking_in/voice/upload.action', //仅为示例,非真实的接口地址
      filePath: tempPath,
      name: 'file',
      formData: {
        'user': 'test'
      },
      success: function (res) {
        var data = res.data
        that.setData({res: data})
        //do something
        console.log('succ',data)
        that.setData({ loading: false });
        that.setData({ disabled: false });
      },
      fail: function(res){
        that.setData({ loading: false });
        that.setData({ disabled: false });
        console.log('fail',res.errMsg)
      }
    })
  }

  

})



后端使用java+百度语音识别+ffmpeg格式转换,后端带面,见小程序 语音识别(二)

 

 

 

 

 

没有更多推荐了,返回首页