Applets feature

  1. Text-to-speech
  2. Multi-platform multi-pronunciator optional
  3. Adjustable speed
  4. Audio downloads are available
  5. Conscience products without advertising 😆

Small program code

The online voice recognition service has been connected

  1. Spitzer DUI platform (over 40 free speakers)
  2. Iflytek Open Platform (5 free speakers)
  3. Baidu Voice (4 free speakers optional)

Applet screenshot

Server main code

class TTSController extends Controller {
  async tts () {
    let params = this.ctx.query
    letResult = null // Call different interfaces based on plAT parametersif (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)} / / set the type of the response, The client receives a file stream this.ctx.response.type ='audio/mpeg'
    this.ctx.body = result
  }
}
Copy the code

Applet client Template code (using MPvue)

<template>
  <div class="container">
    <div class="preview">
      <textarea :class="textAreaFocus? 'focus' : ''" 
      auto-height @focus="bindTextAreaFocus" 
      @blur="bindTextAreaBlur" placeholder="Please enter text" 
      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"> Select platform </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"> select the speaker </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 class="desc"< span style = "max-width: 100%; clear: both; min-height: 1emtype="default-light"
        session-from="weapp"</ contacts-button > </div> </div> </template>Copy the code

The script code

<script>
import voiceIdArray from './voiceIdArray'

export default {

  data () {
    return {
      array: voiceIdArray.aispeech,
      platArr: [{id: 'xf', name: IFlytek}, {id: 'aispeech', name: Spitz}, {id: 'baidu', name: "Baidu"}], platIndex: 1, index: 26, text: 'Reform breeze blows everywhere, breeze blows everywhere, spring breeze blows everywhere. \n The Chinese people are true to their spirit, true to their spirit, true to their spirit. \n It's such a crazy world that rats serve cats as bridesmaids. \n Qi Delong, Qi Dongqiang. \n Chidelong's Dong Delong Dong Qiang. `, 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: 'Text-to-speech service, optional for multiple pronunciators'
    }
  },
  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: 'Loading'
      })
      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'// Default value}, success (res) {wx.hideloading () wx.setclipboardData ({data: res.data.short_url, success (res) { wx.showToast({ title:'Link has been copied, please download it with your browser (not available on ios)',
                icon: 'none',
                duration: 3000
              })
            }
          })
        }
      })
    },
    audioPlay () {
      this.audioCtx.src = this.getAudioSrc()
      if(! this.audioCtx.src) { wx.showToast({ title:'Please enter text first',
          icon: 'none',
          duration: 2000
        })
        return false
      }
      wx.showLoading({
        title: 'Loading'
      })
      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>
Copy the code

In the process of interface docking, Baidu’s is the most convenient because it can be used directly with SDK, while Iflytek’s is the most troublesome and requires to do parameter encryption by itself. Although Spitz duI does not provide SDK, the document is written in detail and the docking process is convenient and fast.

One thing that can’t be fixed is the fact that you can’t download the app directly inside the app, only provide a link, and then the user can open a browser to download it (iPhone seems to have no solution).