最近折腾了一下之前那个小说app的听书功能,最开始想法很丰富,比如要搞成音乐播放一样,还带类似歌词滚动啥的东西,然后还去uniapp的插件商城转了一圈,随后就发现那里的插件一个比一个坑爹,要么自带bug要么不兼容,所以扯了好几天进展都反反复复,最后就干脆自己写了,采用uniapp自带的createInnerAudioContext方法。当然,这个方法也是坑一大堆,网上的教程带偏性很严重,要么就是细节都不齐全,不过也还是解决了问题,完成了简略版的听书功能。
所以,就整理个教程水一水吧。
这个教程也不局限于小说内容的听书,任何文字内容都可以处理下,实现语言播放功能。
那么,开始教程吧
前置说明:
百度的语音接口存在字数的限制,同时一次性太多字数也会需要更多的加载时间,所以合理的办法是通过将小说的内容根据段落拆分成数组,变为一个播放列表来进行依次播放,这样就可以明显减小加载延迟同时也减小播放失败的可能性。而且主要是,我发现很多支持听书的app,或者那些网站,都是采用这种模式,毕竟百度的接口是免费的。
百度语音接口参数说明:
这是一条标准的请求地址,可直接用于播放(目前是免费的)
https://ai.baidu.com/aidemo?type=tns&spd=5&pit=5&vol=15&dt=1&per=0&tex=牛逼啊
几个主要的参数如下:
type=tns:数据类型
spd=5:语速,可以是1-9的数字,数字越大,语速越快。
vol=15:音量
per=0:发声,0女生,1男生,3逍遥,4软萌(可能还有其它的,自行测试吧)
tex=**:需要播放的文字
具体步骤
1.在uniapp页面的script标签内全局加上如下代码,定义音频播放的全局对象。
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = false;
注意这里不能放在onload中,因为这个对象只允许创建一次,而不是每次页面加载都创建,那样会造成多次执行的问题。
2.定义相关字段,包括当前的段落id,播放状态,和内容list
playid:0,
playstatus:false,
contentList:[],
这里的内容contentList字段,我是通过将实际文字内容转换得到的,具体的内容大概如下,这是我的app接口传的数据,具体使用的时候,根据实际为准
这是小说的内容<br/><br/>  
这是第二行的数据<br/><br/>  
这是一条没啥用的数据<br/><br/>  
这是新的一条<br/><br/>  
结尾了<br/><br/>  
然后我通过以下方法,给contentList字段赋值,后面的方法里有,以下只是原理
var content = that.content;
content = content.replace("<h2>","").replace("</h2>","").replace(/[\r\n]/g,"").replace(/\ +/g,""); ;
content = content.split("<br/><br/>  ");
var list = [];
for(var i in content){
var arr = {
url:"https://ai.baidu.com/aidemo?type=tns&spd=5&pit=5&vol=15&dt=1&per=0&tex="+content[i].replace("<br/>  ",""),
text:content[i].replace("<br/>  ",""),
}
list.push(arr);
}
that.contentList = list;
3.定义播放相关方法。
//听书功能开始
play(){
var that = this;
uni.showToast({
title: '语音播放开始',
});
//获取当前文章内容(可以根据实际情况修改)
var data = that.currentChapter;
var content = data.content;
//获取文章内容结束
content = content.replace("<h2>","").replace("</h2>","").replace(/[\r\n]/g,"").replace(/\ +/g,""); ;
content = content.split("<br/><br/>  ");
var list = [];
for(var i in content){
var arr = {
url:"https://ai.baidu.com/aidemo?type=tns&spd=5&pit=5&vol=15&dt=1&per=0&tex="+content[i].replace("<br/>  ",""),
text:content[i].replace("<br/>  ",""),
}
list.push(arr);
}
that.contentList = list;
//上面一段的具体内容是,获取当前的文章内容,处理成json数组,来为播放做准备,同样的以实际情况为准
that.playStart(list);
},
//开始播放,根据playid选择播放的段落,默认第一条
playStart(list){
var that = this;
var playid = that.playid;
innerAudioContext.src = list[playid].url;
if(!that.playstatus){
innerAudioContext.play();
}
},
//依次播放的方法,播放一段自动到下一段,如果大于当前段落总数,则执行本文章内容全部播放完成后方法
playAdd(list){
var that = this;
that.playid++;
var total = list.length;
if(that.playid > total - 1){
that.playid = 0;
//执行本文章内容播放完成后的方法
}else{
that.playstatus = false;
innerAudioContext.stop();
that.playStart(list);
}
//return false
},
//暂停方法
playPause(){
var that = this;
that.playstatus = false;
innerAudioContext.pause();
},
4.在onload中监听
var that = this;
//监听当前段落是否播放完成,完成就播放下一段
innerAudioContext.onEnded(() => {
var list = that.contentList;
innerAudioContext.stop();
that.playAdd(list);
});
innerAudioContext.onPlay(() => {
console.log("播放开始了");
that.playstatus = true;
});
innerAudioContext.onError((res) => {
uni.showToast({
title: '语音播放失败,请检查当前网络',
icon:'none',
});
that.playstatus = false;
});
监听不能放在单独的方法内,因为监听会多次执行导致段落id被多次+1造成顺序问题。
5.onUnload中停止播放
innerAudioContext.stop();
离开页面后,执行停止方法。
4.定义一个点击事件,控制听书的开启和暂停,我这里用了colorUI的图标库
<text class="cuIcon-video" v-show="!playstatus" @tap="play()"></text>
<text class="cuIcon-videofill" v-show="playstatus" @tap="playPause()"></text>
5.效果如下:
教程就到这里,感兴趣的可以加群讨论,目前听书已经整合到app的源码,需要的可以去本站的交易专区购买。
本文来自投稿,不代表本站立场,如若转载,请注明出处:https://www.ruletree.club/archives/2220/