【微信小程序】微信小程序--倒放音频的实现
2020-01-13 01:54
113 查看
微信小程序–倒放音频的实现
注:灵感来源与玩法参考:https://www.bilibili.com/video/av76976000
设计思路:
- 1.微信小程序端:使用微信开发者工具实现微信小程序端的展示及交互的设计:
- 1.1包括小程序的展示页面:
- 1.2与服务器交互的逻辑部分:index.js页面。
- 2.服务器端:服务器端采用java(jdk1.8) + tomcat搭建。
- 2.1为完成音频的正常反转,我们还需要python语言完成转换音频的脚本,所以必须还要python语言环境,需要导入pydub包和ffmepg包(这两个包在python2.7中存在,可以不用额外导入)。
正式开始我们项目的编写:
1.微信小程序端:
- 1.1微信小程序的架构:
- 1.1.1 pages就是用来展示各个页面的。单独的一个page(比如图中的index就是一个页面)就是一个单独的页面。因为我们这个小程序只要需要一个页面就可以了,所以我们就是用系统配置的index page。logs是创建项目就给定的page,可以不用管。
- 1.1.2 utils可以参见这个博客https://www.cnblogs.com/bellagao/p/6305485.html,我们当前的项目是不需要用到的。
- 1.1.3 接下来的五个文件是关于全局配置的。暂时我们也不需要特别的关注,感兴趣的可以去官方文档就行。
- 1.2小程序页面的展示
- 1.2.1 进入index.wxml,语言类似于html语言,只有一部分不同,希望下代码如下:
<view class="container"> <button bindtap="startRecordMp3" class='btn'>开始录音</button> <view class="=combine"><text>\n</text></view> <button bindtap="stopRecord" class='btn'>停止录音</button> <view class="=combine"><text>\n</text></view> <button bindtap="playRecord" class='btn'>播放录音</button> </view>
以上代码非常简单实现了三个按钮,分别使用bindtap绑定了index.js中的方法,即一点击改button就会调用index.js中的方法。
写完这些代码之后,页面就会是这个样子:
此时点击是不会有任何作用的,接下来我们进入index.js中编写我们的逻辑部分。
- 1.3小程序逻辑的编写
const recorderManager = wx.getRecorderManager() const innerAudioContext = wx.createInnerAudioContext() const appURL = "http://xx.xxx.xx.xxx:8080/reverseAudio/reverseAudio/" Page({ /** * 页面的初始数据 */ data: { src:"",//本机存储录音的临时路径 audioTime: "",//录音的时间 reverseAudioURl: "",//服务器存储录音的临时路径 downloadAudio:""//本机存放反转后的临时路径 }, /** * 录制mp3音频 */ startRecordMp3: function () { recorderManager.start({ format: 'mp3' }); }, /** * 停止录音 */ stopRecord: function () { recorderManager.stop() }, /** * 播放录音 */ playRecord: function () { var that = this; // if (that.data.src == '') { // console.log("未进行录音"); // return; // } console.log("开始播放"); innerAudioContext.src = that.data.downloadAudio; innerAudioContext.play() }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { var that = this; recorderManager.onStart(function (res) { console.log("---开始录音---"); }); //停止音频监听事件 recorderManager.onStop(function (res) { console.log(res) that.setData({ src: res.tempFilePath, audioTime: res.duration }) console.log("音频长度" + that.data.audioTime); //上传音频 wx.uploadFile({ url: appURL + "upload.do",//这是你自己后台的连接 filePath: that.data.src, name: "file",//后台要绑定的名称 header: { "Content-Type": "multipart/form-data" }, //参数绑定 formData: { audioTime: that.data.audioTime, }, success: function (ress) { var data = JSON.parse(ress.data); console.log(data); console.log("后台回复:" + data.code + " == " + data.obj); that.setData({ reverseAudioURl: data.obj }); console.log("开始下载==="); //下载反转音频 wx.downloadFile({ url: appURL + "download.do?fileName=" + that.data.reverseAudioURl, success: function (res) { that.data.downloadAudio = res.tempFilePath; } }); wx.showToast({ title: '倒放完成', duration: 500 }) }, fail: function (ress) { wx.showToast({ title: '录音有误,时间不允许超过7秒哦~', duration: 1000 }) } }); }); //播放监听的时间 innerAudioContext.onError(function (res){ console.log("错误播放--" + res.errMsg+" = "+res.errCode); wx.showToast({ title: '录音好像出了些小问题,重新录一下试试看~', duration: 1000 }) }); innerAudioContext.onPlay(function (res){ console.log("监听播放--"+res); }); innerAudioContext.onEnded(function (res) { console.log("自然播放--" +res); }); innerAudioContext.onCanplay(function (res) { console.log("进入播放状态--" +res); }); }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } })
- 1.3.1 编写说明:
为了能够录音完成后发送给服务端,服务端再将反转后的音频发回。我们需要做一些设计: - 1.3.2 开始录音:这个方法没有任何复杂的逻辑,调用相应的方法,执行即可。为了保证能拿到具体的信息相关,也可以加上录音方法的监听事件。
- 1.3.3 停止录音:这个方法我们要做一些设计:当录音完成后我们应该通过录音完成的回调方法。拿到录音完成的音频的临时存储路径,并将其发送给服务端。此时我们需要给page的data设置一个值,用来存储服务端成功接收并反转后的音频地址。也就是我在页面上写出的reverseAudioURl。最后,因为innerAudioContext.src的一些局限性(无法以get请求携带参数来返回一个音频流,这会在服务端会报错的),所以我们需要在data中设置一个downloadAudio用来存放 小程序端从服务端下载来的反转音频(临时文件按路径)。
**理清停止录音的逻辑:
第一步:停止录音;
第二步:上传录音;
第三步:将收到的服务器发来的生成的路径赋值给page的data的reverseAudio中,并在将反转后的音频下载到本地,路径存储在data的reverseAudio中。
**为什么要这样做第三步?
由于服务器接收到录音后,肯定将录音反转后存储在服务器的某个位置上。但是又因为微信小程序是不允许使用session的,所以没有办法鉴别当前点击播放按钮的用户是要播放哪个音频。所以我采用了这种方法:将生成好的音频地址当做返回结果发送给小程序,小程序在处理完停止录音的success回调函数的时候,又会将这个地址发回给服务器,服务器再根据这个地址,将音频返回。从而下载到音频。
- 1.3.4 最后我们根据上述原因,把reverseAudio当做参数发回服务器,服务器将文件发回,小程序将地址存储在data的reverseAudio中。
- 1.3.5 播放录音:点击播放录音,使用data.reverseAudio赋值给InnerAudioContext.src,即可正常进入播放,如果为了其严谨性,将其中的一些错误播放之类的监听函数也可以加载进去。
2.服务端(Java编写):
- 2.1 服务端的架构:
- 2.1.1 我们采用mavn架构,Spring+SpringMVC(其实也可以不用采用Spring,毕竟项目极其的娇小)。controller层只需要做一些简单的数据接收(显然我没有按照这个风格写,因为项目小,又是一个小demo,所以没那么严谨)。
package com.reverse.audio.controller; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import com.reverse.audio.service.Reverse; @Controller @RequestMapping("/reverseAudio") public class Main { @Autowired Reverse reverse; public String path = "/tmp/WechatMini/ReverseAudio/"; @RequestMapping("/upload.do") @ResponseBody public Result recvAudio(MultipartFile file,String audioTime) { if(audioTime == null || audioTime.isEmpty() |Integer.parseInt(audioTime) > 7000) { return new Result(Result.fail, new String("音频时长超时")); } File f = new File(path+"In/"+UUID.randomUUID().toString().replace("-", "").toString()+"Recive.mp3");// try { file.transferTo(f); } catch (IllegalStateException | IOException e) { e.printStackTrace(); return new Result(Result.fail, new String("文件转换出错")); } String result = null; try { result = reverse.reverse(f.getAbsolutePath()); } catch (IOException e) { e.printStackTrace(); return new Result(Result.fail, new String("音频反转出错1")); } catch (InterruptedException e) { e.printStackTrace(); return new Result(Result.fail, new String("音频反转出错2")); } return new Result(Result.success, result); } @RequestMapping("download.do") public ResponseEntity<byte[]> download(HttpServletRequest request, String fileName) throws IOException { System.out.println("播放参数:"+fileName); File file = new File(fileName); if(!file.exists()) { System.out.println("文件不存在"); return null; } byte[] body = null; InputStream is = new FileInputStream(file); body = new byte[is.available()]; is.read(body); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attchement;filename=" + file.getName()); HttpStatus statusCode = HttpStatus.OK; ResponseEntity<byte[]> entity = new ResponseEntity<>(body, headers, statusCode); return entity; } }
接下来是service层:
package com.reverse.audio.service; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import org.springframework.stereotype.Service; @Service public class Reverse { public static String executer= "python";//调用者是python public static String file_path = "/tmp/WechatMini/ReverseAudio/Code/ReverseAudio.py";// python绝对路径 public static String format = "mp3";//转换前的音频格式(微信录制我们定义采用MP3格式) public String reverse(String orginFilePath) throws IOException, InterruptedException { String[] command_line = new String[] {executer,file_path,orginFilePath,format}; Process process = Runtime.getRuntime().exec(command_line); BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; StringBuilder sb = new StringBuilder(); while ((line = in.readLine()) != null) { sb.append(line); System.out.println("读出的内容"+line); } in.close(); process.waitFor(); return sb.toString(); } }
接下来是python脚本(python脚本一定要放在service中指定的位置下)
from pydub import AudioSegment from sys import argv import uuid def reverse(pathOrgin,format): # ted = AudioSegment.from_file(str(pathOrgin),str(format)) # backwards = ted.reverse() # resultPath = "/tmp/WechatMini/ReverseAudio/Out/"+str(uuid.uuid1()).replace("-","")+"1.wav" #导出格式为wav backwards.export(resultPath,format="wav") #打印出来,java在调用的时候才能拿到返回值 print(resultPath) pathOrgin = str(argv[1]) format = str(argv[2]) reverse(pathOrgin,format)
这已经是所有的服务端代码。
这是我在整个过程中遇到的所有的坑:
- 1.服务端一切正常却无法链接
- 在微信开发者工具中,打开【设置】= =》【项目设置】= =》【不检验合法域名】,设置完成后如图所示:
- - 2.无法解析文件上传之后的success回调函数
- 这个在文档中明确写道
因为返回值是一个String,而我在服务端返回的是一个JSON对象,所以应该这样解决:
success: function (res) { var data = JSON.parse(res.data); that.setData({ reverseAudioURl: data.obj }); }
- 3.整个开发过程中最大的坑,为什么我录的音在第三方处理的时候回格式错误?
- 开发工具录的音,不支持在python脚本中的处理,而【真机】是可以的
- 这是一个惨无人道的坑,因为录音的格式错误,一度都让我放弃了,直到我在某个小小的角落里找到了这个答案,就是因为微信开发者工具知识开发者工具,不能代替真机,所以当有一些问题的时候,多试试,多搜搜就能找到答案。
最后:
要想微信小程序正式上线,仍然需要购买域名并配置,具体情况参见微信开发文档,微信开发者社区也有许多精彩的问题和解答。
- 点赞 1
- 收藏
- 分享
- 文章举报
相关文章推荐
- 优化微信小程序音频播放,实现多音频同时播放,互不干扰。
- java实现微信小程序支付
- 微信小程序6位或多位验证码密码输入框功能的实现代码
- 微信小程序实现签到功能
- 微信小程序利用canvas实现六边形蜘蛛图
- Android实现微信自动抢红包的程序
- 微信小程序实现卡片左右滑动效果的示例代码
- 微信小程序swiper实现轮播图,可触发点击事件
- 微信小程序例子——点击文字实现页面跳转
- 微信小程序实现弹出菜单
- 微信小程序实现点赞、取消点赞功能
- 微信小程序实现传递多个参数与事件处理
- ifanr:微信小程序中实现手势缩放图片
- 微信小程序实现订单倒计时
- 微信小程序开发者工具初体验及实现技术初探
- 微信小程序实现圆形进度条动画
- 微信小程序ajax实现请求服务器数据及模版遍历数据功能示例
- 微信小程序实现下拉列表且单行内容过多显示省略号
- 微信小程序定义和调用全局变量globalData的实现
- 微信小程序之ES6与事项助手的功能实现