小程序功能特点
文本转语音
多平台多发音人可选
可调语速
可提供音频下载
良心产品无广告????
小程序码
已对接在线语音识别服务
思必驰 dui 平台 (超过 40 个免费可选发音人)
讯飞开放平台 (5 个免费可选发音人)
百度语音 (4 个免费发音人可选)
小程序截图
服务端主要代码
class TTSController extends Controller {
async tts () {
let params = this.ctx.query
let result = null
// 根据 plat 参数来调用不同的接口
if (params.plat === ‘xf’) {
result = await this.ctx.service.xftts.getTts(params)
} else if (params.plat === ‘baidu’) {
result = await this.ctx.service.baidutts.getTts(params)
} else {
result = await this.ctx.service.aispeechtts.getTts(params)
}
// 设置 response 的类型,这样客户端接收到的就是一个文件流
this.ctx.response.type = ‘audio/mpeg’
this.ctx.body = result
}
}
小程序客户端 template 代码(使用的 mpvue)
<template>
<div class=”container”>
<div class=”preview”>
<textarea :class=”textAreaFocus? ‘focus’ : ””
auto-height @focus=”bindTextAreaFocus”
@blur=”bindTextAreaBlur” placeholder=” 请输入文本 ”
v-model=”text” maxlength=”256″/>
</div>
<div class=”setting”>
<picker @change=”bindPlatChange” v-model=”platIndex” range-key=”name” :range=”platArr”>
<div class=”item”>
<div class=”label”> 选择平台 </div>
<div class=”value voice”>
{{platArr[platIndex].name}}
</div>
</div>
</picker>
<picker @change=”bindPickerChange” v-model=”index” range-key=”name” :range=”array”>
<div class=”item”>
<div class=”label”> 选择发音人 </div>
<div class=”value voice”>
{{array[index].name}}
</div>
</div>
</picker>
<div class=”item speed”>
<div class=”label”> 调节语速 </div>
<div class=”value”>
<slider @change=”onSpeedChange” :value=”speedObj.default” :step=’speedObj.step’ activeColor=”#6F8FFF” :min=”speedObj.min” :max=”speedObj.max” show-value />
</div>
</div>
</div>
<div style=”height: 140rpx;”>
<div class=”btn-group”>
<div class=”item”><button @click=”audioPlay” type=”main”> 播放合成语音 </button> </div>
<div class=”item”> <button @click=”audioDownload” type=”submain”> 复制链接下载 </button> </div>
</div>
</div>
<div class=”desc”>
说明:tts 是英文 text to speech 的缩写,即文本转语音技术
<contact-button
type=”default-light”
session-from=”weapp”> 联系客服
</contact-button>
</div>
</div>
</template>
script 代码
<script>
import voiceIdArray from ‘./voiceIdArray’
export default {
data () {
return {
array: voiceIdArray.aispeech,
platArr: [{id: ‘xf’, name: ‘ 科大讯飞 ’}, {id: ‘aispeech’, name: ‘ 思必驰 ’}, {id: ‘baidu’, name: ‘ 百度 ’}],
platIndex: 1,
index: 26,
text: ` 改革春风吹满地,吹满地,春风吹满地。\n 中国人民真争气,真争气,人民真争气。\n 这个世界太疯狂,耗子都给猫当伴娘。\n 齐德隆,齐东强。\n 齐德隆的咚得隆咚锵。`,
voiceId: ‘lili1f_diantai’,
speed: 1,
textAreaFocus: false,
audioCtx: null,
ttsServer: ‘https://tts.server.com’,
audioSrc: ”,
downloadUrl: ”,
xfSpeedObj: {
min: 0,
max: 100,
default: 50,
step: 1
},
aispeechSpeedObj: {
min: 0.7,
max: 2,
default: 1,
step: 0.1
},
baiduSpeedObj: {
min: 0,
max: 9,
default: 5,
step: 1
},
speedObj: {}
}
},
watch: {
platIndex (newVal, oldVal) {
if (newVal === 2) {
this.array = voiceIdArray.baidu
this.index = 0
this.speedObj = this.baiduSpeedObj
}
if (newVal === 1) {
this.array = voiceIdArray.aispeech
this.index = 26
this.speedObj = this.aispeechSpeedObj
}
if (newVal === 0) {
this.array = voiceIdArray.xf
this.index = 0
this.speedObj = this.xfSpeedObj
}
}
},
onShareAppMessage () {
return {
title: ‘ 文本转语音服务,多发音人可选 ’
}
},
methods: {
onSpeedChange (e) {
this.speedObj.default = e.target.value
},
bindPlatChange (e) {
this.platIndex = e.target.value * 1
},
bindPickerChange (e) {
this.index = e.target.value
},
getAudioSrc () {
if (this.text === ”) {
return false
}
const speed = this.speedObj.default
const voiceId = this.array[this.index].id
const plat = this.platArr[this.platIndex].id
return encodeURI(`${this.ttsServer}/tts?plat=${plat}&voiceId=${voiceId}&speed=${speed}&text=${this.text}`)
},
getDownloadUrl () {
const plat = this.platArr[this.platIndex].id
const voiceId = this.array[this.index].id
wx.showLoading({
title: ‘ 加载中 ’
})
wx.request({
url: ‘https://tts.server.com/getdownloadurl’,
data: {
plat: plat,
voiceId: voiceId,
speed: this.speedObj.default,
text: this.text
},
header: {
‘content-type’: ‘application/json’ // 默认值
},
success (res) {
wx.hideLoading()
wx.setClipboardData({
data: res.data.short_url,
success (res) {
wx.showToast({
title: ‘ 链接已复制请用浏览器下载 (ios 端无法下载)’,
icon: ‘none’,
duration: 3000
})
}
})
}
})
},
audioPlay () {
this.audioCtx.src = this.getAudioSrc()
if (!this.audioCtx.src) {
wx.showToast({
title: ‘ 请先输入文本 ’,
icon: ‘none’,
duration: 2000
})
return false
}
wx.showLoading({
title: ‘ 加载中 ’
})
this.audioCtx.play()
},
audioDownload () {
this.getDownloadUrl()
},
bindTextAreaBlur (e) {
this.textAreaFocus = false
this.text = e.target.value
},
bindTextAreaFocus () {
this.textAreaFocus = true
}
},
created () {
this.speedObj = this.aispeechSpeedObj
},
mounted () {
this.audioCtx = wx.createInnerAudioContext()
this.audioCtx.onEnded((res) => {
wx.hideLoading()
})
this.audioCtx.onPlay((res) => {
wx.hideLoading()
})
wx.showShareMenu({
withShareTicket: true
})
}
}
</script>
接口对接过程中,百度的是最方便的因为有 sdk 可以直接使用,讯飞的最麻烦需要自己做参数加密,思必驰 dui 的虽然没提供 SDK 但是文档写的比较详细对接过程也很方便快速。
目前无法解决的就是,小程序内无法直接下载的问题,只能提供链接,然后用户自己打开浏览器进行下载(iPhone 似乎无解)。