我们用到的是微信小程序的同声传译小插件,首先去开通这个插件
这个插件可以实现
-
语音转文字
-
语音合成
-
文本翻译
接下来开发的第一步,
将富文本转为纯文字,下面是封装好的方法可以直接使用
// 富文本转纯文字
export function RichToText(richText) {
return richText.replace(/<[^>]+>/g, "").replace(/ /g, " ")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, '"')
.replace(/'/g, "'")
.replace(/'/g, "'")
.replace(/“/g, '"')
.replace(/”/g, '"')
.replace(/‘/g, "'")
.replace(/’/g, "'")
.replace(/—/g, "—")
.replace(/–/g, "–");
}
第二步,进行语音合成,将文本合成语音,这里官方提供的插件有一个限制,就是单次合成的内容大小是1000字节,
如果当我们字数较多时候,我们可以分段去合成,并存储他们的顺序(因为合成的时候,有的文字多,有的文字少,所以会导致合成回来的语音顺序会乱),语音合成方法如下,先把你所有的内容按照一定的长度进行分段,然后分别进行语音合成,记得带顺序
// 文本长度和你每段能翻译的长度进行对比,100字节==500汉字,安全起见,我们可以把长度给小一点
if (this.text.length > 300) {
this.count = Math.ceil(this.text.length / this.size)
for (let i = 0; i <= this.text.length; i += this.size) {
const label = this.text.slice(i, i + this.size)
//分段进行语音合成,并把顺序传入
this.openVoice(label, i).then(res => {
this.voiceUrl.push(res) //将合成的语音及顺序存入数组
})
}
} else {
this.count = 1
console.log('text', this.text)
openVoice(this.text, 0).then(res => {
this.voiceUrl = [res]
})
}
const plugin = requirePlugin('WechatSI');
// 语音合成
export function openVoice(label, sort) {
return new Promise((resolve, reject) => {
plugin.textToSpeech({
lang: "zh_CN",
tts: true,
content: label,
success: (res) => {
resolve({
url: res.filename,
sort
})
},
fail: (res) => {
reject(res)
}
})
})
}
第三步,就可以进行语音的播放和暂停和停止啦
const innerAudioContext = wx.createInnerAudioContext()
// 语音播放
yuyinPlay() {
if (this.voiceUrl.length == 0) return;
//将前面存的带顺序的音频数组进行排序
const urlArr = this.voiceUrl.sort((a, b) => a.sort - b.sort).map(i => i.url)
function play(index) {
innerAudioContext.src = urlArr[index]; //设置音频地址
innerAudioContext.play(); //播放音频
}
play(this.curIndex) //存储一个全局的curIndex,以便暂停的时候继续播放
//音频自然播放结束事件
innerAudioContext.onEnded(() => {
if (this.curIndex + 1 >= this.count) {
innerAudioContext.destroy() //多次会调用播放新的文件时,提前销毁实例,可避免-99错误
innerAudioContext = null
return
}
this.curIndex++
play(this.curIndex)
})
},
// 语音暂停
yuyinPause() {
innerAudioContext.pause()
},
// 退出播放--结束播放
yuyinEnd() {
innerAudioContext.stop()
innerAudioContext.destroy() //多次会调用播放新的文件时,提前销毁实例,可避免-99错误
innerAudioContext = null
},
以上就是完整的播放啦
完整代码附上
<template>
<view class="">
<view class="liste-all" @click="yuyinPlay">
<u-icon name="volume-up" color="#2878FF" size="40"></u-icon>
听全文
</view>
<!-- 播放悬浮窗 -->
<view class="float-audio" :style="{right: floatShow?'25rpx':'-100%'}">
<image :src="coverImg" mode="" class="img"></image>
<view class="icon-list">
<u-icon name="pause" @click="yuyinPause" size='36' v-if="pauseShow==true"></u-icon>
<u-icon name="play-right-fill" @click="yuyinPlay" size='36' v-else></u-icon>
<u-icon name="close" @click="yuyinEnd" size='36'></u-icon>
</view>
</view>
</view>
</view>
</template>
<script>
const innerAudioContext = wx.createInnerAudioContext()
import {
RichToText,
openVoice
} from '@/utils/utils.js'
export default {
data() {
return {
text: '放入你想朗读的内容,如果是纯文本,则不需要使用RichToText方法',
voiceUrl: [],
size: 300, //一次读取最大值
count: 1,
curIndex: 0,
pauseShow: false,
floatShow: false,
}
},
onLoad(option) {
this.toText(this.text)
},
onBackPress(options) {
innerAudioContext.stop()
},
methods: {
// 富文本转纯文字
toText(val) {
if (val != null && val != '') {
this.text = RichToText(val)
if (this.text.length > this.size) {
this.count = Math.ceil(this.text.length / this.size)
for (let i = 0; i <= this.text.length; i += this.size) {
const label = this.text.slice(i, i + this.size)
this.openVoice(label, i).then(res => {
this.voiceUrl.push(res)
})
}
} else {
this.count = 1
console.log('text', this.text)
openVoice(this.text, 0).then(res => {
this.voiceUrl = [res]
})
}
} else return ''
},
// 语音播放
yuyinPlay() {
if (this.voiceUrl.length == 0) return;
this.floatShow = true
this.pauseShow = true
const urlArr = this.voiceUrl.sort((a, b) => a.sort - b.sort).map(i => i.url)
function play(index) {
innerAudioContext.src = urlArr[index]; //设置音频地址
innerAudioContext.play(); //播放音频
}
play(this.curIndex)
innerAudioContext.onEnded(() => {
if (this.curIndex + 1 >= this.count) {
this.floatShow = false
innerAudioContext.destroy() //销毁当前实例
innerAudioContext = null
return
}
this.curIndex++
play(this.curIndex)
})
},
// 语音暂停
yuyinPause() {
this.pauseShow = false
innerAudioContext.pause()
},
// 退出播放--结束播放
yuyinEnd() {
this.floatShow = false
innerAudioContext.stop()
innerAudioContext.destroy() //销毁当前实例
innerAudioContext = null
},
},
}
</script>
<style lang="scss" scoped>
.float-audio {
width: 220rpx;
height: 227rpx;
position: fixed;
top: 530rpx;
// right: 25rpx;
background: #ffffff;
border-radius: 16rpx;
padding: 5rpx;
box-sizing: border-box;
transition: all 0.5s ease-in-out;
.icon-list {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 25rpx;
}
.img {
flex-shrink: 0;
width: 210rpx;
height: 160rpx;
border-radius: 16rpx;
}
}
</style>