小程序语音流下载及播放
最近学着写了写小程序,这两天比较头疼的一个事情是如何播放从服务器获取的音频,使用post请求的话得到的是二进制流文件,费了好大功夫也没法播放出来,效果大概是下图这个样子:
该数据流我使用python脚本获取后重定向到MP3文件中是可以播放的,但是在小程序中播放死活没搞定。
遍查百度google,发现不少人都有这个问题。既然直接播放无法解决,那就曲线救国,先把音频下载下来,然后再播放,这样小程序播放二进制流的问题就变成了播放本地音频的问题(文后有具体代码)。在官方文档上下载音频并播放的实例是酱紫的:
(贴示例代码点这里: https://developers.weixin.qq.com/miniprogram/dev/api/network/download/wx.downloadFile.html)
这个下载API是可以满足我的下载要求的。首先,该接口可以指定header,笔者使用的语音服务器必须要在header中验证token(笔者用的猎户语音https://shimo.im/docs/VubvfaOnAuMFjuj1);其次,post请求中的各个参数可以写到url后面,例如上面栗子中加参数: url: ‘https://example.com/audio/123?lan=zh-CN&ctp=10&aue=3&per=0’。
笔者将这些玩意儿整合好写到小程序中后,却发现了另一个坑,音频可以下载下来,可以得到temp路径,可以执行playVoice,还走了success,但是就是不出声音!心中一万只草NM呼啸而过~
为了播放这个音频,把小程序提供的各种音频API基本都试了,又是一坨一坨的坑。经过测试发现,InnerAudioContext这个API可以搞定,真机和开发工具中都表现正常;而BackgroundAudioManager在开发工具上根本不出声,但是在真机上work。笔者最先尝试出的是backgroundAudioManager,后来改用了InnerAudioContext。
先介绍InnerAudioContext的使用
官网代码如下:
直接把这些东西丢到函数里,然后调用会有问题,每调用一次就创造了一个InnerAudioContext,没有必要,而且不可控。正确的姿势是酱紫的:
第一步,在Page外面放全局变量:
const innerAudioContext = wx.createInnerAudioContext()
第二步,在onReady函数里放一些设置和回调:
innerAudioContext.autoplay = false // 这里设置为不自动播放,笔者希望想让播的时候再播
innerAudioContext.onPlay(() => {
console.log('开始播放')
})
innerAudioContext.onError((res) => {
console.log(res.errMsg)
console.log(res.errCode)
})
第三步,写个play函数,里面放上播放地址,加上播放的调用就可以的,在使用的时候直接调用该play函数就行了,这样不会重复创建InnerAudioContext,管理起来也方便。
play: function{
// 地址,这里换成download下来的那个tempFilePath地址就可以(可以在page外再设置一个全局变量,download里面把tempFilePath存进去,如:filePath = res.tempFilePath;这里就能直接用filePath了)
innerAudioContext.src = 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46';
innerAudioContext.play(); // 播放
}
}
该方法介绍完了
下面介绍BackgroundAudioManager使用。
首先,像官方文档写的那样,在onLoad里面做声明:
this.backgroundAudioManager = wx.getBackgroundAudioManager()
this.backgroundAudioManager.title = '此时此刻'
this.backgroundAudioManager.epname = '此时此刻'
this.backgroundAudioManager.singer = '许巍'
this.backgroundAudioManager.coverImgUrl = 'http://y.gtimg.cn/music/photo_new/T002R300x300M000003rsKF44GyaSk.jpg?max_age=2592000'
这里把backgroundAudioManager前加了this,方便其他函数中调用,需要注意的是,title、singer等等看似无关紧要的东西不能删掉,笔者删掉后在真机上出现了奇奇怪怪的bug。
最后,将下载文件API示例中的playVoice那部分替换成this.backgroundAudioManager.src = res.tempFilePath就可以播放出来了(如官网所言,只要指定了src音频就会自动播放)
于是乎, 从下载二进制流音频到播放的主体代码如下,再加上onLoad上的那段声明就可以运行了:
TTSRequest: function () {
var that = this;
var payload = "lan=zh-CN&ctp=10&pdt=" + "1000" + "&aue=3&per=0&tex=" + this.data.test_text + "&spd=5&rate=3&vol=5&sn=DD2F135A-3157-40BC-88C7-003A54296B3F"
wx.downloadFile({
url: this.data.tts_url + '?' + payload,
header: {
'content-type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + this.data.token,
},
success(res){
console.log('download res:', res);
if (res.statusCode === 200) {
var voice = res.tempFilePath;
console.log('voice:', voice);
that.backgroundAudioManager.src = voice; //替换掉playVoice那段
} else {
wx.showToast({
title: 'something wrong!',
image: "/images/icon/connect.png"
})
}
}
})
},
终于在周五的傍晚解决了问题,祝笔者及各位码友周末快乐~