微信小程序语音同步智能识别的实现案例代码解析

一、背景

在小程序的一些应用场景中,会有语音转文字的需求。原有的做法一般是先通过小程序的录音功能录下语音文件,然后再通过调用语音智能识别WebApi(比如百度云AI平台,科大讯飞平台)将语音文件转成文字信息,以上的做法比较繁琐且用户的体验性较差。

为解决此问题,微信直接开放了同声传译的插件,小程序作者可以直接使用该插件进行语音同声传译的开发。此文章将通过前后端整合应用的完整案例完成语音的实时转换,并将语音上传到服务端后台备份。

二、同声传译插件介绍

微信同声传译由微信智聆语音团队、微信翻译团队与公众平台联合推出的同传开放接口,首期开放语音转文字、文本翻译、语音合成接口,为开发者赋能。

1、 微信小程序后台添加插件

进入微信小程序后台-->进入设置-->第三方设置-->添加插件->搜索同声传译-->完成添加。


2、 微信小程序启用插件

在小程序app.json文件中增加插件版本等信息:

"plugins": {
 "WechatSI": {
 "version": "0.3.3",
 "provider": "wx069ba97219f66d99"
 }
 },

在页面程序文件中引入插件:

/* index.js */

const plugin = requirePlugin("WechatSI")

// 获取**全局唯一**的语音识别管理器**recordRecoManager**
const manager = plugin.getRecordRecognitionManager()

recordRecoManager 对象的方法列表:

方法 参数 说明
start options 开始识别
stop 结束识别
onStart callback 正常开始录音识别时会调用此事件
onRecognize callback 有新的识别内容返回,则会调用此事件
onStop callback 识别结束事件
onError callback 识别错误事件

官方开发文档:插件的语音识别管理器

三、语音同步转换的前端实现

1、界面UI与操作

UI参考微信官方的DEMO:长按按钮进行录音,松开按钮实时将录音转换为文字。

用户可对同步转换的文字进行编辑,同时可将原始语音文件与文字上传后台服务端。

2、代码实现

语音同步转换的主要代码:

//导入插件
const plugin = requirePlugin("WechatSI");
// 获取**全局唯一**的语音识别管理器**recordRecoManager**
const manager = plugin.getRecordRecognitionManager();

/**
 * 加载进行初始化
 */
 onLoad: function () {
 	//获取录音权限
	app.getRecordAuth();
	//初始化语音识别回调
 this.initRecord();
 },

 ...

/**
 * 初始化语音识别回调
 * 绑定语音播放开始事件
 */
 initRecord: function () {
 //有新的识别内容返回,则会调用此事件
 manager.onRecognize = (res) => {
 let currentData = Object.assign({}, this.data.currentTranslate, {
 text: res.result,
 });
 this.setData({
 currentTranslate: currentData,
 });
 this.scrollToNew();
 };

 // 识别结束事件
 manager.onStop = (res) => {
 let text = res.result;

 console.log(res.tempFilePath);

 if (text == "") {
 this.showRecordEmptyTip();
 return;
 }

 let lastId = this.data.lastId + 1;

 let currentData = Object.assign({}, this.data.currentTranslate, {
 text: res.result,
 translateText: "正在识别中",
 id: lastId,
 voicePath: res.tempFilePath,
 duration: res.duration
 });

 this.setData({
 currentTranslate: currentData,
 recordStatus: 1,
 lastId: lastId,
 });
 //将当前识别内容与语音文件加入列表
 this.addRecordFile(currentData, this.data.dialogList.length);
 //刷新列表
	 this.scrollToNew();
 };

 // 识别错误事件
 manager.onError = (res) => {
 this.setData({
 recording: false,
 bottomButtonDisabled: false,
 });
 };

 },

 /**
 * 按住按钮开始语音识别
 */
 streamRecord: function (e) {
 let detail = e.detail || {};
 let buttonItem = detail.buttonItem || {};
 //开始中文录音
 manager.start({
 lang: buttonItem.lang,
 });

 this.setData({
 recordStatus: 0,
 recording: true,
 currentTranslate: {
 // 当前语音输入内容
 create: util.recordTime(new Date()),
 text: "正在聆听中",
 lfrom: buttonItem.lang,
 lto: buttonItem.lto,
 },
 });
 //刷新列表
 this.scrollToNew();
 },

 /**
 * 松开按钮结束语音识别
 */
 streamRecordEnd: function (e) {
 let detail = e.detail || {}; // 自定义组件触发事件时提供的detail对象
 let buttonItem = detail.buttonItem || {};

 // 防止重复触发stop函数
 if (!this.data.recording || this.data.recordStatus != 0) {
 console.warn("has finished!");
 return;
 }

 manager.stop();

 this.setData({
 bottomButtonDisabled: true,
 });
 },

编辑识别文字并完上传的主要代码:

/**
 * 页面的初始数据
 */
 data: {
 edit_text_max: 200,
 remain_length: 200,
 edit_text: "",
 is_focus: false,
 tips: "",
 index: -1,
 voicePath: "",

 },

/**
 * 加载初始化
 */
 onLoad: function (options) {
 //根据传入的文字内容填充编辑框
 this.setEditText(options.content)

 this.setData({
 index: index,
 oldText:options.content,
 voicePath: options.voicePath
 })

 },

 /**
 * 编辑文字
 */
 editInput: function (event) {
 console.log(event)
 if (event.detail.value.length > this.getEditTextMax()) {

 } else {
 this.data.edit_text = event.detail.value
 this.updateRemainLength(this.data.edit_text)
 }
 },

 /**
 * 上传文字与语音文件
 */
 editConfirm: function (event) {
 let json=this.data.edit_text
 //调用微信上传文件api将信息上传至服务端webApi
 wx.uploadFile({
 url: api.wxFileUploadUrl,
 filePath: this.data.voicePath,
 name: "file",
 header: {
 Authorization: wx.getStorageSync("loginFlag"),
 "Content-Type": "multipart/form-data",
 },
 formData: {
 openId: app.globalData.userInfo.openId,
 realName: "语音文件",
 json: JSON.stringify(json),
 },
 success: (result) => {
 console.log("success:", result);
 if (result.statusCode == "200") {
  let data = JSON.parse(result.data);
  console.log("data", data);
  if (data.success == true) {
  let module = data.module;
  console.log("module", module);
  app.showInfo("上传成功");
  setTimeout( ()=>{
  wx.navigateBack();
  }, 2000)

  } else {
  app.showInfo("异常错误" + data.errMsg + ",请重新进入");
  wx.navigateTo({
  url: "/pages/index/index",
  });
  }
 } else {
  app.showInfo("访问后台异常,重新进入系统");
  wx.navigateTo({
  url: "/pages/index/index",
  });
 }
 },
 fail: (result) => {
 console.log("fail", result);
 wx.navigateTo({
  url: "/pages/index/index",
 });
 },
 complete: () => {},
 });

 },

四、后端SpringBoot实现语音文件上传webApi

1、SpringBoot项目API相关结构树

2、文件上传工具类的实现

tools工具类包中主要存文件通用的文件上传工具类,该工具类会将文件上传至配置指定的文件夹下,并将文件信息写入upload_file表中。

  • 文件信息实体类:与数据库中表upload_file对应;
  • 文件存储仓库类:通过Spring Data JPA接口实现数据的CRUD;
  • 文件上传工具接口:对外统一封装文件上传方法;
  • 文件上传工具实现类:实现文件上传方法接口。

文件信息实体类:UploadFile.java

/**
 * 文件信息表
 *
 * @author zhuhuix
 * @date 2020-04-20
 */
@Entity
@Getter
@Setter
@Table(name = "upload_file")
public class UploadFile {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 @NotNull(groups = Update.class)
 private Long id;

 /**
 * 文件实际名称
 */
 @Column(name = "real_name")
 private String realName;

 /**
 * 文件名
 */
 @NotNull
 @Column(name = "file_name")
 private String fileName;

 /**
 * 文件主名称
 */
 @NotNull
 @Column(name = "primary_name")
 private String primaryName;

 /**
 * 文件扩展名
 */
 @NotNull
 private String extension;

 /**
 * 存放路径
 */
 @NotNull
 private String path;

 /**
 * 文件类型
 */
 private String type;

 /**
 * 文件大小
 */
 private Long size;

 /**
 * 上传人
 */
 private String uploader;

 @JsonIgnore
 @Column(name = "create_time")
 @CreationTimestamp
 private Timestamp createTime;

 public UploadFile(String realName, @NotNull String fileName, @NotNull String primaryName, @NotNull String extension, @NotNull String path, String type, Long size, String uploader) {
 this.realName = realName;
 this.fileName = fileName;
 this.primaryName = primaryName;
 this.extension = extension;
 this.path = path;
 this.type = type;
 this.size = size;
 this.uploader = uploader;
 }

 @Override
 public String toString() {
 return "UploadFile{" +
  "fileName='" + fileName + '\'' +
  ", uploader='" + uploader + '\'' +
  ", createTime=" + createTime +
  '}';
 }
}

文件存储仓库类:UploadFileRepository.java

/**
 * 上传文件DAO接口层
 *
 * @author zhuhuix
 * @date 2020-04-03
 */
public interface UploadFileRepository extends JpaRepository<UploadFile, Long>, JpaSpecificationExecutor<UploadFile> {
//该接口继承JpaRepository及CrudRepository接口,已实现了如findById,save,delete等CRUD方法
}

UploadFileRepository 接口继承JpaRepository及CrudRepository接口,已实现了如findById,save,delete等CRUD方法

文件上传工具接口:UploadFileTool.java

/**
 * 文件上传接口定义
 *
 * @author zhuhuix
 * @date 2020-04-20
 */
public interface UploadFileTool {

 /**
 * 文件上传
 * @param multipartFile 文件
 * @return 上传信息
 */
 UploadFile upload(String uploader,String realName,MultipartFile multipartFile);
}

文件上传工具实现类:UploadFileToolImpl.java

/**
 * 文件上传实现类
 *
 * @author zhuhuix
 * @date 2020-04-20
 */
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class UploadFileToolImpl implements UploadFileTool {

 private final UploadFileRepository uploadFileRepository;

 @Value("${uploadFile.path}")
 private String path;

 @Value("${uploadFile.maxSize}")
 private long maxSize;

 public UploadFileToolImpl(UploadFileRepository uploadFileRepository) {
 this.uploadFileRepository = uploadFileRepository;
 }

 @Override
 @Transactional(rollbackFor = Exception.class)
 public UploadFile upload(String uploader, String realName, MultipartFile multipartFile) {
 //检查文件大小
 if (multipartFile.getSize() > maxSize * Constant.MB) {
  throw new RuntimeException("超出文件上传大小限制" + maxSize + "MB");
 }
 //获取上传文件的主文件名与扩展名
 String primaryName = FileUtil.mainName(multipartFile.getOriginalFilename());
 String extension = FileUtil.extName(multipartFile.getOriginalFilename());
 //根据文件扩展名得到文件类型
 String type = getFileType(extension);
 //给上传的文件加上时间戳
 LocalDateTime date = LocalDateTime.now();
 DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMddhhmmssS");
 String nowStr = "-" + date.format(format);
 String fileName = primaryName + nowStr + "." + extension;

 try {
  String filePath = path + type + File.separator + fileName;
  File dest = new File(filePath).getCanonicalFile();
  if (!dest.getParentFile().exists()) {
  dest.getParentFile().mkdirs();
  }
  multipartFile.transferTo(dest);
  if (ObjectUtil.isNull(dest)) {
  throw new RuntimeException("上传文件失败");
  }

  UploadFile uploadFile = new UploadFile(realName, fileName, primaryName, extension, dest.getPath(), type, multipartFile.getSize(), uploader);
  return uploadFileRepository.save(uploadFile);

 } catch (Exception e) {
  e.printStackTrace();
  throw new RuntimeException(e.getMessage());
 }

 }

 /**
 * 根据文件扩展名给文件类型
 *
 * @param extension 文件扩展名
 * @return 文件类型
 */
 private static String getFileType(String extension) {
 String document = "txt doc pdf ppt pps xlsx xls docx csv";
 String music = "mp3 wav wma mpa ram ra aac aif m4a";
 String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
 String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
 if (image.contains(extension)) {
  return "image";
 } else if (document.contains(extension)) {
  return "document";
 } else if (music.contains(extension)) {
  return "music";
 } else if (video.contains(extension)) {
  return "video";
 } else {
  return "other";
 }
 }
}

注意,该程序代码中用到了@Value注解获取配置文件中的uploadFile.path及uploadFile.maxsize参数,一般在项目静态配置文件中按如下书写(yml配置文件)。

# 测试环境文件存储路径
uploadFile:
 path: C:\startup\file\
 # 文件大小 /M
 maxSize: 50

3、小程序上传文件接口的实现

wx-miniprogram包定义了小程序CRM webApi的接口,小程序调用webApi实现文件的上传及其他功能。

  • 微信小程序 webApi:对外提供小程序上传文件webApi;
  • 微信小程序服务接口:封装小程序上传文件服务接口;
  • 微信小程序服务实现:小程序上传文件服务的实现,该服务实现中会调用tools包中的UploadFile接口进行文件的上传。

微信小程序CRM webApi:WxMiniCrmController.java

/**
 * 微信小程序Crm webApi
 *
 * @author zhuhuix
 * @date 2020-03-30
 */
@Slf4j
@RestController
@RequestMapping("/api/wx-mini")
@Api(tags = "微信小程序Crm接口")
public class WxMiniCrmController {

 private final WxMiniCrm wxMiniCrm;

 public WxMiniCrmController(WxMiniCrm wxMiniCrm) {
 this.wxMiniCrm = wxMiniCrm;
 }

 @ApiOperation(value = "微信小程序端上传文件")
 @PostMapping(value = "/fileUpload")
 public ResponseEntity fileUpload(HttpServletRequest request) {
 MultipartHttpServletRequest req = (MultipartHttpServletRequest) request;

 MultipartFile multipartFile = req.getFile("file");
 String openId = req.getParameter("openId");
 String realName = req.getParameter("realName");
 String json = req.getParameter("json");

 return ResponseEntity.ok(wxMiniCrm.uploadFile(json, openId,realName, multipartFile));

 }
}

微信小程序CRM服务接口:WxMiniCrm.java

/**
 * 微信小程序CRM服务接口定义
 *
 * @author zhuhuix
 * @date 2020-04-20
 */
public interface WxMiniCrm {

 /**
 * 将微信小程序传入的json对象写入数据库,并同时将文件上传至服务端
 *
 * @param json  微信端传入json对象
 * @param openId 上传人
 * @param realName 文件实际名称
 * @param multipartFile 上传文件
 * @return 返回上传信息
 */
 Result<UploadFile> uploadFile(String json, String openId, String realName,MultipartFile multipartFile);
}

微信小程序CRM服务实现:WxMiniCrmImpl.java

/**
 * 微信小程序CRM实现类
 *
 * @author zhuhuix
 * @date 2020-04-20
 */
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class WxMiniCrmImpl implements WxMiniCrm {

 private final UploadFileTool uploadFileTool;

 public WxMiniCrmImpl(UploadFileTool uploadFileTool) {
 this.uploadFileTool = uploadFileTool;
 }

 @Override
 @Transactional(rollbackFor = Exception.class)
 public Result<UploadFile> uploadFile(String json, String openId,String realName, MultipartFile multipartFile) {
 return new Result<UploadFile>().ok(uploadFileTool.upload(openId,realName, multipartFile));
 }
}

4、小程序上传文件接口的查看

访问Swagger2可查看该接口,Swagger2与SpringBoot的集成可参考SpringBoot JWT认证机制项目集成Swagger2

五、实际测试

语音测试正常

上传文件至后台:

上传的日志信息查看:

总结

到此这篇关于微信小程序语音同步智能识别的实现案例代码解析的文章就介绍到这了,更多相关微信小程序语音同步智能识别内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 微信小程序实现语音识别转文字功能及遇到的坑

    最近为小程序增加语音识别转文字的功能,坑路不断,特此记录. 微信开发者工具 开发者工具上的录音文件与移动端格式不同,暂时只可在工具上进行播放调试,无法直接播放或者在客户端上播放 debug的时候发现,工具上录音的路径是http://tmp/xxx.mp3,客户端上录音是wxfile://xxx.mp3. 忽悠呢,不是格式不同,是映射路径不同. 其实做个兼容也不难,每次提示一行文字,很丑. 采样率与编码码率限制 每种采样率有对应的编码码率范围有效值,设置不合法的采样率或编码码率会导致录音失败.详细

  • 小程序实现按下录音松开识别语音

    本文实例为大家分享了小程序按下录音松开识别语音的具体代码,供大家参考,具体内容如下 wxml <view class='circle position-absol'> <text wx:if="{{!anmationShow}}" class='fz-12 fot-col block'>按住话筒说话,松开后自动识别文字</text> <text wx:if="{{anmationShow}}" class='fz-12 fo

  • 30分钟快速实现小程序语音识别功能

    前言 为了参加某个作秀活动,研究了一波如何结合小程序.科大讯飞实现语音录入.识别的实现.科大讯飞开发文档中只给出 Python 的 demo,并没有给出 node.js 的 sdk,但问题不大.本文将从小程序相关代码到最后对接科大讯飞 api 过程,一步步介绍,半个小时,搭建完成小程序语音识别功能!不能再多了! 当然,前提是最好掌握有一点点小程序.node.js 甚至是音频相关的知识.下面话不多说了,来一起看看详细的介绍吧 架构先行 架构比较简单,大伙儿可以先看下图.除了小程序,需要提供 3 个

  • 微信小程序和百度的语音识别接口详解

    介绍 因为项目需要,使用到了微信小程序和百度的语音接口 现在将项目中的一个小模块拿出来单独分享. 技术关键字 微微信小程序 百度语音接口 nodejs,express fluent-ffmegp 环境 windows 10 vs code 1.20.1 微信小程序开发工具 1.02.1802270 花生壳-提供域名和内容穿透-用于方便本地远程调试微信小程序 考虑到业务并不复杂,所以就将所有的代码都放在一个页面就可以了(wxml,wxss,js统称为一个页面) 文件目录 页面 index.wxml

  • 微信小程序语音同步智能识别的实现案例代码解析

    一.背景 在小程序的一些应用场景中,会有语音转文字的需求.原有的做法一般是先通过小程序的录音功能录下语音文件,然后再通过调用语音智能识别WebApi(比如百度云AI平台,科大讯飞平台)将语音文件转成文字信息,以上的做法比较繁琐且用户的体验性较差. 为解决此问题,微信直接开放了同声传译的插件,小程序作者可以直接使用该插件进行语音同声传译的开发.此文章将通过前后端整合应用的完整案例完成语音的实时转换,并将语音上传到服务端后台备份. 二.同声传译插件介绍 微信同声传译由微信智聆语音团队.微信翻译团队与

  • 微信小程序里长按识别二维码的实现过程

    前言 我们都知道公众号里的二维码可以长按识别,但是小程序限制比较严格,没有办法实现二维码的长按识别,一直以来我都是这样认为的,微信的官方规则里也是这么写的,直到今天上午,我无意间发现一个小程序里的二维码居然可以长按识别,于是就好奇的去研究了一番,结果还真的可以实现小程序里长按识别二维码.不知道是官方的漏洞还是程序的bug,但是既然这个功能可以实现,那当然要愉快的用上一用啦 老规矩,先看效果图 可以看到,我们成功的在小程序里实现了长按识别二维码的功能.下面就教大家如何一步步实现吧.因为官方的规格还

  • C#微信小程序服务端获取用户解密信息实例代码

     C#微信小程序服务端获取用户解密信息实例代码 实现代码: using AIOWeb.Models; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Web; namespace AIOWe

  • 微信小程序 ES6Promise.all批量上传文件实现代码

    微信小程序 ES6Promise.all批量上传文件实现代码 客户端 Page({ onLoad: function() { wx.chooseImage({ count: 9, success: function({ tempFilePaths }) { var promise = Promise.all(tempFilePaths.map((tempFilePath, index) => { return new Promise(function(resolve, reject) { wx.

  • 微信小程序商品详情页规格属性选择示例代码

    detail.wxml展示页面 <!--轮播图--> <swiper class="swiper" indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}" circular="{{circular}}"> &

  • 微信小程序页面缩放式侧滑效果的实现代码

    先给大家展示下效果图,大家感觉不错,请参考实现代码: 实现原理:点击按钮,往需要动画的div中添加或移除拥有动画效果的class. 由于微信小程序中不能操作page这个根节点,所以,只有用一个div(view)来模仿page根节点. 1.结构 <view class='page {{isFix?"pageShow":"pageHide"}}' > <view class='header'> <view class='h-toggle i

  • 微信小程序动态生成二维码的实现代码

    效果图如下: 实现 wxml <!-- 存放二维码的图片--> <view class='container'> <image bindtap="previewImg" mode="scaleToFill" src="{{imagePath}}"></image> </view> <!-- 画布,用来画二维码,只用来站位,不用来显示--> <view class=&qu

  • 微信小程序导航栏跟随滑动效果的实现代码

    效果图 .wxml <view class='tabBar'> <view class='tabItem wx:if="{{tabClick===0?"click":""}}"' bindtap='clickTab' data-index='0'>tab1</view> <view class='tabItem wx:if="{{tabClick===1?"click":&q

  • 微信小程序中button去除默认的边框实例代码

    微信小程序中button去除默认的边框的实现方法如下所示: button { position:relative; display:block; margin-left:auto; margin-right:auto; padding-left:14px; padding-right:14px; box-sizing:border-box; font-size:18px; text-align:center; text-decoration:none; line-height:2.5555555

  • 微信小程序 拍照或从相册选取图片上传代码实例

    这篇文章主要介绍了微信小程序 拍照或从相册选取图片上传代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 upload.wxml <!--pages/upload/upload.wxml--> <button bindtap='uploadPhoto'>拍照选取照片上传</button> upload.js // pages/upload/upload.js Page({ data: { imgData: [] }

随机推荐