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> <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> 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可是文檔寫的比較詳細對接過程也很方便快速。html
目前沒法解決的就是,小程序內沒法直接下載的問題,只能提供連接,而後用戶本身打開瀏覽器進行下載(iPhone彷佛無解)。vue