小程序开发-音频

点击蓝色字关注我们!

一个努力中的公众号

长的好看的人都关注了

9410e512aab1e29988e06396cf7dfac9.png

本文主要讲解 小程序开发-音频录音播放以及语音识别,文章很长 但都是干货、市面上语音识别 大部分只支持pcm、wma,只有腾讯支持mp3  但是XX文档能力十分拉基,需要花费时间才能读懂 所以本文使用百度AI语音识别

第一步 创建录音页面文件

    在app.js文件中添加录音页面,然后保存会自动生成record文件

781036fb9edf5b1243a8779e93f33072.png

    record页面文件图如下

f8dc873f69f09c4165db33fe69a37f21.png

第二步 record.wxml 页面代码

    <!--pages/record/record.wxml-->

    <!-- 录音 -->

    <view class='record-login' catch:longpress="sayVideo" 

       catch:touchmove="handleTouchMove" 

       catch:touchend="sayVideoEnd">

        <image src='../../images/operation/record.png' mode="widthFix">

        </image>

    </view>

    record.png 图片

cf905f7f4f6a36259cd2f01a83a8f659.png

第三步 record.wxss css代码

    /* pages/record/record.wxss */

    .record-login {

        display: block;

        margin-left: auto;

        margin-right: auto;

        padding-left: 1rem;

        padding-right: 1rem;

        box-sizing: border-box;

        font-size: 18px;

        text-align: center;

        text-decoration: none;

        border-radius: 1rem;

        -webkit-tap-highlight-color: transparent;

        overflow: hidden;

        color: #000;

        background-color: #f8f8f8;

        position: fixed;

        bottom: 0;

        left: 0;

        width: 100%;

    }

    .record-login image {

        width: 2rem;

        height: 3rem;

    }

第四步 record.js 页面的方法(重点)

    // pages/record/record.js

    //录音管理

    import api from '../../utils/config/urlConfig.js';

    const recorderManager = wx.getRecorderManager()

    //音频组件控制

    const innerAudioContext = wx.createInnerAudioContext()

    //创建audio控件

    const myaudio = wx.createInnerAudioContext();

    const app = getApp()

    //语音识别

    Page({

    /**

       * 页面的初始数据

       */

    data: {

        //授权状态

        status: 1,

        //录音变量

        tempFilePath: "",

        //是否发生

        send: true,
        //触摸点的坐标信息

        startPoint: [],

    },

    // ----------------------------------- 录音

    //按住说话

    sayVideo: function(e) {

        var that = this;

        this.setData({

        send: true,//长按时应设置为true,为可发送状态

        startPoint: e.touches[0],//记录触摸点的坐标信息

    })

    const options = {

        duration: 60000,//指定录音的时长,单位 ms

        sampleRate: 16000,//采样率

        numberOfChannels: 1,//录音通道数

        encodeBitRate: 96000,//编码码率

        format: 'm4a',//音频格式,有效值 aac/mp3

        frameSize: 50,//指定帧大小,单位 KB

    };

    //如果状态不为2 那么表示没有授权

    if (this.data.status != 2) {

        //没有授权

        wx.authorize({

            scope: 'scope.record',

            success() {

            console.log("录音授权成功");

            //第一次成功授权后 状态切换为2

            that.setData({

                status: 2,

            })

        },

    fail() {

        wx.openSetting({

            success: (res) => {

                console.log(res.authSetting);

                if (!res.authSetting['scope.record']) {

                    //未设置录音授权

                    console.log("未设置录音授权");

                    wx.showModal({

                    title: '提示',

                    content: '您未授权录音,功能将无法使用',

                    showCancel: false,

                    success: function (res) { },

                    });

                } else {

                    //第二次才成功授权

                    console.log("设置录音授权成功");

                    that.setData({

                    status: 2,

                    })

                    recorderManager.start(options);

                }

            },

            fail: function () {

                wx.showModal({

                title: '提示',

                content: '您未授权录音,功能将无法使用',

                showCancel: false,

                success: function (res) { },

                });

            }

           })

         }

       });

     } else {

            recorderManager.start(options);

     }

    },

    //松开

    sayVideoEnd: function (e) {

    //先停止录音

    recorderManager.stop();

    var that = this;

    //判断是否需要上传

    if (this.data.send){

    //监听录音停止的事件

    recorderManager.onStop((res) => {

        if (res.duration < 1000) {

            wx.showModal({

                title: '提示',

                content: '录音时间太短',

                showCancel: false,

                success: function (res) { },

            });

            return;

        } else {

        //查询录音的数据

        console.log(res.tempFilePath);

        innerAudioContext.autoplay = true

        innerAudioContext.src = res.tempFilePath,

        innerAudioContext.onPlay(() => {

            console.log('开始播放')

        })

        innerAudioContext.onError((res) => {

        })

        // 文件临时路径

        var tempFilePath = res.tempFilePath; 

         //转换格式 默认silk后缀

        var temp = tempFilePath.replace('.m4a', '');

        app.fileReq('你后端接口路径', tempFilePath, '参数',

            function (res) {

                console.log(res);

            }

        ) }

    });

        this.setData({

            send: true//设置为发送语音

        })

    }

    },

    //滑动取消

    handleTouchMove: function (e) {

    //计算距离,当滑动的垂直距离大于25时,则取消发送语音

    if (Math.abs(e.touches[e.touches.length - 1].clientY -

        this.data.startPoint.clientY) > 5) {

            this.setData({

                send: false//设置为不发送语音

            })

        }

    },

    /**

       * 生命周期函数--监听页面加载

       */

    onLoad: function (options) {

    },

    /**

       * 生命周期函数--监听页面初次渲染完成

       */

    onReady: function () {

    //设置页面title

    wx.setNavigationBarTitle({

        title: '测验'

    })

    },

    /**

       * 生命周期函数--监听页面显示

       */

    onShow: function () {

    },

    /**

       * 生命周期函数--监听页面隐藏

       */

    onHide: function () {

    },

    /**

       * 生命周期函数--监听页面卸载

       */

    onUnload: function () {

    },

    /**

       * 页面相关事件处理函数--监听用户下拉动作

       */

    onPullDownRefresh: function () {

    }, 

    /**

       * 页面上拉触底事件的处理函数

       */

    onReachBottom: function () {

    },

    /**

       * 用户点击右上角分享

       */

    onShareAppMessage: function (res) {

        if (res.from === 'button') {

            console.log("来自页面内转发按钮");

            console.log(res.target);

        } else {

            console.log("来自右上角转发菜单")

        }

        return {

            title: 'EgoEnglish',

            path: '/pages/login/loigin',

            imageUrl: "/images/login/wechat.png",

            success: (res) => {

            console.log("转发成功", res);

            },

            fail: (res) => {

                console.log("转发失败", res);

            }

          }

    }, 

  })

第四步 Java后端文件上传

    文件上传代码如下  

    注:需引入百度的Api Jar  maven方式如下

    <dependency>

        <groupId>com.baidu.aip</groupId>

        <artifactId>java-sdk</artifactId>

        <version>4.4.0</version>

    </dependency>

阿里的JSON Jar

    <dependency>

        <groupId>com.google.code.gson</groupId>

        <artifactId>gson</artifactId>

        <version>2.8.6</version>

    </dependency>

packagecom.demo.api.service.impl;
importjava.io.File;
importjava.text.SimpleDateFormat;
importjava.util.Arrays;
importjava.util.Date;
importjava.util.List;
importjavax.annotation.Resource;
importjavax.servlet.http.HttpServletRequest;
importorg.apache.commons.io.FileUtils;
importorg.springframework.stereotype.Service;
importorg.springframework.web.multipart.MultipartFile;
importcom.haithink.api.service.ApiFileService;
importcom.haithink.base.common.utils.ReturnResult;
importcom.haithink.base.common.utils.Status;
importcom.haithink.base.util.LogUtils;
importcom.haithink.thirdParty.BaiDuVoiceDiscernService;
/**
* @author 作者: tutu
* @version 创建时间:2019年10月25日 下午7:57:16
* @explain 类说明 微信文件上传类接口实现类
*/
@Service
publicclassApiFileServiceImpl implementsApiFileService {
   @Resource
   privateBaiDuVoiceDiscernService service;
   /**
    * 文件上传路径
    */
   privatefinalString uploadPath = "F:\\demo\\小程序\\" + newSimpleDateFormat("yyyy-MM-dd").format(newDate()).toString()  + "\\m4a";
   
   
   /**
    * 文件上传-上传录音
    * @param request
    * @param uploadFile ---录音文件
    * @return
    */
   @Override
   publicReturnResult record(HttpServletRequest request, MultipartFile uploadFile) {
      ReturnResult result = newReturnResult();
      try{
         // 如果目录不存在就创建
         File uploadDir = newFile(uploadPath);
         if(!uploadDir.exists()) {
            uploadDir.mkdir();
         }
         // 获取文件的 名称.扩展名
         String oldName = uploadFile.getOriginalFilename();
         String extensionName = "";
         // 获取原来的扩展名
         if((oldName != null) && (oldName.length() > 0)) {
            intdot = oldName.lastIndexOf('.');
            if((dot > -1) && (dot < (oldName.length() - 1))) {
               extensionName = oldName.substring(dot);
            }
         }
         // 构建文件名称
         String fileName = System.currentTimeMillis() + "_" + System.nanoTime()
               ;
         //定义文件类型变量
         String[] fileType = { ".CD", ".WAVE", ".AIFF", ".AU", ".MPEG", ".MP3",
               ".MPEG-4", ".MIDI", ".WMA", ".RealAudio", ".VQF", ".OggVorbis",
               ".AMR",".pcm", ".m4a" };
         //存储文件类型
         List<String> fileTyepLists = Arrays.asList(fileType);
         //循环类型次数
         intfileTypeOnCount = 0;
         for(String fileTyepListss : fileTyepLists) {
            if(fileTyepListss.equalsIgnoreCase(extensionName)) {
               // -----如果是音频文件的话
               // 构建文件路径
               String filePath = uploadPath + File.separator + fileName + extensionName;
               // 保存文件
               try{
                  FileUtils.writeByteArrayToFile(newFile(filePath), uploadFile.getBytes());
               } catch(Exception e) {
                  e.printStackTrace();
               }
            } else{
               fileTypeOnCount++;
            }
         }
         //如果循环类型次数等于全部文件类型,那么该文件不是音频文件
         if(fileTypeOnCount == fileTyepLists.size()) {
            result.setStatus(Status.UNKNOWN);
            result.setMsg("不是音频文件");
            returnresult;
         }
         //上传成功
         result.setStatus(Status.SUCCESS);
         result.setMsg("成功");
         returnresult;
      } catch(Exception e) {
         LogUtils.COMMON.error("文件上传-上传录音 error:", e);
         result.setStatus(Status.FAIL);
         result.setMsg("上传失败");
         returnresult;
      }
   }
}

    
百度语音接口

    首先 搜索百度语音选择第一个

    0d7c07de4c059ee96f017237e1878b79.png

    选择语音识别,点进来以后 点击立即使用,创建一个项目,记好AppID 、API Key、 Secret Key,剩下的就不在这里多做介绍,大家可自行学习。

261357c99e1dbdbb284567fe0d6648fa.png

接下来就是接口对接

package com.demo.thirdParty;
import java.io.File;
import java.io.FileInputStream;
import java.util.Base64;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import com.haithink.base.util.LogUtils;
import com.haithink.common.util.StringUtil;
import com.haithink.utils.GsonUtils;
import com.haithink.utils.OkHttpUtils;
/**
 * @author 作者: tutu
 * @version 创建时间:2019年10月25日 下午7:57:16
* @explain 类说明
 */
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class BaiDuVoiceDiscernService {
   /**
    * url , Token的url,http可以改为https
    */
   private static final String oauthUrl = "http://openapi.baidu.com/oauth/2.0/token";
   
   private static final String url = "http://vop.baidu.com/server_api";
   // 填写网页上申请的appkey 如 $apiKey="g8eBUMSokVB1BHGmgxxxxxx"
   private final String APP_KEY = "";
   // 填写网页上申请的APP SECRET 如 $SECRET_KEY="94dc99566550d87f8fa8ece112xxxxx"
   private final String SECRET_KEY = "";
   // 文件格式, 支持pcm/wav/amr 格式,极速版额外支持m4a 格式
   private final String format = "m4a";
   private String CUID = "1234567JAVA";
   // 采样率固定值
   private final int RATE = 16000;
   /**
    * dev_pid 语音识别模板
    */
   private int DEV_PID = 1737;
   /**
    * 保存访问接口获取的token
    */
   private String token;
   /**
    * 获取百度语音识别tocken
    */
   public String getTocken() {
      // 封装获取tocken的url
      String getTokenURL = oauthUrl + "?grant_type=client_credentials" + "&client_id=" + GsonUtils.urlEncode(APP_KEY)
            + "&client_secret=" + GsonUtils.urlEncode(SECRET_KEY);
      try {
         JsonObject retJson = OkHttpUtils.doGetRetJson(getTokenURL);
         // 如果返回结果有tocken
         if (StringUtil.isNotEmpty(retJson.get("access_token"))) {
            // 返回tocken
            return retJson.get("access_token").getAsString();
         } else {
            throw new Exception("获取tocken error");
         }
      } catch (Exception e) {
         LogUtils.COMMON.error("获取tocken error");
         return "";
      }
   }

   /**
    * 语音识别
    *
    * @param tocken --- 百度tocken
    * @param file   --- 文件file
    */
   public JsonObject voiceDiscern(String tocken, File file) {
      try {

         // 先进行文件转byte
         byte[] content = getFileContent(file);
         // 将byte转为base格式字符串
         Base64.Encoder encoder = Base64.getEncoder();
         // 定义请求参数
         JSONObject params = new JSONObject();
         String speech = encoder.encodeToString(content);
         params.put("dev_pid", DEV_PID);
         // params.put("lm_id",LM_ID);//测试自训练平台需要打开注释
         params.put("format", format);
         params.put("rate", RATE);
         params.put("token", token);
         params.put("cuid", CUID);
         params.put("channel", "1");
         params.put("len", content.length);
         params.put("speech", speech);
         
         //返回识别结果
         return OkHttpUtils.doPostRetJson(url, params.toJSONString());
      } catch (Exception e) {
         e.printStackTrace();
         return null;
      }

   }

   /**
    * 将文件转为byte
    *
    * @param file 文件file
    * @return
    */
   private byte[] getFileContent(File file) {
      FileInputStream is = null;
      byte[] content = null;
      try {
         // 先判断文件是否存在
         if (!file.canRead()) {
            System.err.println("文件不存在或者不可读: " + file.getAbsolutePath());
            throw new Exception("file cannot read: " + file.getAbsolutePath());
         }
         // 文件转byte
         is = new FileInputStream(file);
         content = GsonUtils.getInputStreamContent(is);
      } catch (Exception e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } finally {
         if (is != null) {
            try {
               is.close();
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }

      return content;
   }
   

   public static void main(String[] args) {
      BaiDuVoiceDiscernService service = new BaiDuVoiceDiscernService();
      String url = "F:\\demo\\小程序\\2019-10-25\\m4a\\201801021803043151.m4a";
      service.token = service.getTocken();
      if (StringUtil.isNotEmpty(service.token)) {
         service.voiceDiscern(service.token, new File(url));
      }

   }

}

本文接口交换采用OkHttps 大家可以去之前讲解的springboot项目中copy 或者可以到码云上copy

https://gitee.com/learning_stack_database/springboot

后续持续为大家更新小程序代码 本文如果对您有帮助 请点个关注

                                (QQ招聘群  710566091
                                 微信招聘群 请加图图微信)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值