iT邦幫忙

2023 iThome 鐵人賽

DAY 11
0
Vue.js

Vue3歡樂套件箱耶系列 第 11

開箱11:文字轉語音不專業版~Speech Synthesis範例應用

  • 分享至 

  • xImage
  •  

本篇開箱Web Speech API提供了瀏覽器內建的文字轉語音功能。

大家應該用過Google的語音功能吧?其實我們可以透過Web Speech API就能實現簡單的文字轉語音功能喔!

介紹

Web Speech API 中的 SpeechSynthesis是語音服務的控制介面;它可以用來取得裝置上可用的合成聲音的訊息,開始、暫停語音,或除此之外的其他指令。

前往>>MDN 範例Demo

前往>>MDN 範例程式碼

典型範例

你可以貼到瀏覽器檢查工具console貼上以下程式碼就能播出了喔!

 const synthesis = window.speechSynthesis;
 const utterance = new SpeechSynthesisUtterance('這裡是文字');
synthesis.speak(utterance);

屬性方法介紹

SpeechSynthesis

SpeechSynthesis.cancel():移除所有語音談話隊列中的談話。
SpeechSynthesis.getVoices(): 傳回目前裝置所有可用聲音的清單。
SpeechSynthesis.pause():把SpeechSynthesis物件為暫停狀態。
SpeechSynthesis.resume():把SpeechSynthesis物件為非暫停狀態:如果已經暫停了則
繼續。
SpeechSynthesis.speak():增加一個utterance到語音談話隊列;它會在其他語音播放
完之後播放。

SpeechSynthesisUtterance

SpeechSynthesisUtterance.lang: 設定語音的語言
SpeechSynthesisUtterance.pitch:設定語音的音調(音高)範圍從0(最小)到2(最大)。預設值為1
SpeechSynthesisUtterance.rate:設定說話的速度。預設值是1,範圍是0.1到10,表示語速
的倍數,例如2表示正常語速的兩倍
SpeechSynthesisUtterance.text:設定在語音時將合成的文字内容。
SpeechSynthesisUtterance.voice: 設定用於說話的聲音。
SpeechSynthesisUtterance.volume:設定將在其中發言的音量。區間範圍是0到1,預設是1

今日姊姊Demo時間

那我們今天就把MDN的範例改寫成vue3吧!

Demo網址:https://hahasister-ironman-project.netlify.app/#/textToSpeech

https://ithelp.ithome.com.tw/upload/images/20231207/201420169FltI43oJA.png

備註:
-本人實測一下成果,音調有時候沒效果是語系的關係,如果你輸入是中文,要測試音調的不同,請選「google國語台灣」

-預設值的聲音為"Mei-Jia",查了一下應該是https://harposoftware.com/en/mandarin-taiwan/378-mei-jia-nuance-voice.html

<template>
  <div class="container">
    <h1>文字轉語音</h1>
    <p>說明:請在下面的輸入框中輸入文字,再按下"播放”按钮播放。</p>
    <p>備註:可調整語音速度/音調/發音語系</p>
    <hr />
    <form @submit.prevent="speak" class="pt-8">
      <label for="txt">輸入文字</label>
      <textarea
        id="txt"
        v-model="inputTxt"
        rows="4"
        class="w-[80%] block p-2.5 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500"
        placeholder="請輸入文字"
      ></textarea>
      <div>
        <label for="rate">速度</label>
        <input
          v-model="rateValue"
          type="range"
          min="0.5"
          max="2"
          step="0.1"
          id="rate"
        />
        <div class="rate-value">{{ rateValue }}</div>
        <div class="clearfix"></div>
      </div>
      <div>
        <label for="pitch">音調</label>
        <input
          v-model="pitchValue"
          type="range"
          min="0"
          max="2"
          step="0.1"
          id="pitch"
        />
        <div class="pitch-value">{{ pitchValue }}</div>
        <div class="clearfix"></div>
      </div>
      <select
        v-model="selectedVoice"
        class="block p-2.5 w-full bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500"
      >
        <option v-for="voice in voices" :key="voice.name" :value="voice.name">
          {{ `${voice.name} (${voice.lang})`
          }}{{ voice.default ? '(預設)' : '' }}
        </option>
      </select>
      <div class="controls">
        <button id="play" type="submit" class="border border-gray-300">
          播放
        </button>
      </div>
    </form>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const inputTxt = ref('');
const selectedVoice = ref('');
const rateValue = ref(1);
const pitchValue = ref(1);
const voices = ref([]);
const synth = window.speechSynthesis;

const populateVoiceList = () => {
  voices.value = synth
    .getVoices()
    .sort((a, b) => a.name.toUpperCase().localeCompare(b.name.toUpperCase()));

  console.log('voices清單', voices.value);
  if (!selectedVoice.value && voices.value.length > 0) {
    selectedVoice.value = voices.value.filter(i => i.default == true)[0].name;
  }
};

const speak = () => {
  if (synth.speaking) {
    synth.cancel();
    console.log('speechSynthesis.speaking');
    return;
  }

  if (inputTxt.value !== '') {
    const utterThis = new SpeechSynthesisUtterance(inputTxt.value);

    utterThis.onend = () => {
      console.log('SpeechSynthesisUtterance.onend');
    };

    utterThis.onerror = () => {
      console.error('SpeechSynthesisUtterance.onerror');
    };

    for (const voice of voices.value) {
      if (voice.name === selectedVoice.value) {
        utterThis.voice = voice;
        break;
      }
    }
    utterThis.pitch = pitchValue.value;
    utterThis.rate = rateValue.value;
    synth.speak(utterThis);
  }
};
onMounted(() => {
  populateVoiceList();

  if (synth.onvoiceschanged !== undefined) {
    synth.onvoiceschanged = populateVoiceList;
  }
});
</script>

那我們明天再見了~

參考資料
https://blog.csdn.net/qq_32963841/article/details/118304543
https://ithelp.ithome.com.tw/articles/10196799


上一篇
開箱10:自適應高度TextArea~resize-textarea-vue3範例應用
下一篇
開箱12:語音轉文字不專業版~SpeechRecognition範例應用
系列文
Vue3歡樂套件箱耶30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言