小程式--語音合成tts 對接多平臺(訊飛,思必馳,百度)

jinggoing發表於2019-01-18

小程式功能特點

  1. 文字轉語音
  2. 多平臺多發音人可選
  3. 可調語速
  4. 可提供音訊下載
  5. 良心產品無廣告?

小程式碼

小程式--語音合成tts 對接多平臺(訊飛,思必馳,百度)

已對接線上語音識別服務

  1. 思必馳dui平臺 (超過40個免費可選發音人)
  2. 訊飛開放平臺 (5個免費可選發音人)
  3. 百度語音(4個免費發音人可選)

小程式截圖

小程式--語音合成tts 對接多平臺(訊飛,思必馳,百度)

服務端主要程式碼

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似乎無解)。

相關文章