SpeechSynthesisUtterance 讓瀏覽器說話

SpeechSynthesisUtterance 讓瀏覽器說話
SpeechSynthesisUtterance 讓瀏覽器說話

瀏覽器本身就可以發音

在寫這篇以前,以為想要讓網頁發出聲音,就要接 Google 的什麼 API 之類的,查了一下後,才知道瀏覽器本身就有這個功能,而且聲音也會不一樣,像 Google有 名的機械女聲就是Chrome上才有。

本篇筆記是紀錄 August 的開發過程,以及中間踩到的幾個坑。


頁面製作

接 SpeechSynthesisUtterance 的功能,很久以前就寫了一個 Demo 了,當時是放在 CodePen 上,那時在找幾個頁面樣版,就用了 Material Kit 這套。

這次把頁面優化,並且加上可以手動調整參數的功能,就一樣是用 Material Kit。

最後完成的頁面在這,可以先玩玩看:

https://letswritetw.github.io/letswrite-speech_synthesis_utterance-api/

頁面預計是做成三張卡片,一張是點了按鈕就會發聲,一張是輸入文字後會發聲,最後一張就是發聲的參數調整。

HTML 的部份,因為 Material Kit 的基底是 Bootstrap,所以都直接用它所有的 Gird、Card、Button、Slider 等,UI framework 的好處就是即便就是複製文件中的範例程式碼,頁面第一眼看上去也會有設計感。


SpeechSynthesisUtterance

SpeechSynthesisUtterance 在 MDN 上的說明,最主要的就是一頁:

https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance

原本以為照著用,然後用 Vue.js 把參數接一接,馬上就可以完成的,但,不設參數是還好,複製貼上 code 一下就可以讓瀏覽器講話了。一接參數就踩坑了,光是調整講話速度的倍速(rate),就研究了 2 個多小時。

詳細的程式碼附在文未的程式碼那段,這邊就來寫遇到的坑。


第一坑 拿瀏覽器支援的所有語音

參考文件上寫的,拿所有瀏覽器的語音方式如下:

var synth = window.speechSynthesis;
var voices = synth.getVoices();

voices 就會是一個陣列,裡面就是所有支援的語音列表。

這邊是第一個坑,JavaScript 拿語音要時間。

當寫了 var voices = synth.getVoices(); 後,就開始對陣列寫個 forEach 去塞進 option 裡,然後就會看見 option 什麼都沒有,因為 voices 還沒抓到陣列,所以 console.log(voices) 會得到的是空陣列。

原本的解決方法是,寫一個 setTimeout,在 500 毫秒後才寫 var voices = synth.getVoices();,但遇到的問題就是每個瀏覽器抓到陣列的時間不一樣,500 毫秒也許 Chrome 讀到了,但 FireFox 卻還沒,後來就放棄這個解法,改用之前學到的 Promise。

Promise 的部份可參考這篇:Promise, Async, Await 基本使用筆記

原本是用 Async 寫:

var voices = await synth.getVoices();

但,出來的結果還是空陣列。

後來不死心 Google 了一下,發現網路上有高手寫了 範例,稍微修改後寫成這樣:

var synth = window.speechSynthesis;
function setVoices() {
  return new Promise((resolve, reject) => {
  let timer;
  timer = setInterval(() => {
    if(synth.getVoices().length !== 0) {
      resolve(synth.getVoices());
      clearInterval(timer);
    }
  }, 10);
 })
}
setVoices().then(voices => console.log(voices));

首先把取得語音列表的部份寫成 Promise,在 Promise 裡再用 setInterval ,每 10 毫秒去檢查一次 synth.getVoices() 是不是空值,不是的時候才回傳 synth.getVoices() 出來。

實作後,確認這樣做不會得到空值,語音選單可以用 forEach 塞 input 了。

在此我們感謝高手們孜孜不倦的努力及分享。


第二坑 調整速度倍率

文件中裡面寫調整速度倍率的參數是:SpeechSynthesisUtterance.rate

rate 的值是 0.1 – 10,指的是幾倍,比方如果設 1.5,那講話速度就會是平常速度的 1.5 倍。預設值是 1。

文件裡面有一句話一開始有看到,但沒在意,就踩坑了:

Some speech synthesis engines or voices may constrain the minimum and maximum rates further.

「一些語音合成引擎或語音可能會進一步限制最小和最大速率。」

原本想說限制就限制,沒什麼,結果 2 個小時的青春就死在這。

它沒有寫,當超過限制的倍速時,會給的是預設值 1,而且每種語系的最大值是不一樣的。

然後 August 就很苦腦的找為什麼怎麼調 rate,講出來的速度都沒變?

在 2 個小時的逝去的青春及實驗下,目前得知,在用 Chrome 的時候,最好的設定是 0.1 – 2,不要超過 2,一超過 2,有些語音就會回到預設值的 1,甚至是整個功能就掛掉了,不論怎麼調都不再發聲,得關掉頁籤後再重新打開才行。


第三坑 Chrome 的 Google 語音

如果是用 Chrome 抓語音列表,會看見陣列多出幾個 Google 才有的語音:

Chrome才有的Google語音列表
Chrome 才有的 Google 語音列表

Google 國語 講出來的就是卡X諾X新聞的那種女機械音,這些是 Chrome 才會有,FireFox 跟 Safari 是沒有的。

原本想說 Google 好佛心,可以這樣子給使用者玩,後來 August 在測試時,隨便丟了一段新聞的內文用 Google 國語唸,突然,唸到一半就中斷了,而且會發生在點其它語音也不會有反應的狀況。

實測後,發現唸超過 80 幾個字,Google 語音就會斷,然後讓整個語音的功能掛掉,如果是先唸到 79 個字,再讓它唸第二次 79 個字,加起來超過 80 幾也一樣斷。

發現這個狀況後,目前沒找到解法,即便是看 MDN 給的範例頁面也有一樣情況,最後只能忍痛對陣列用 filter 把 Google 語音的給濾掉。


原始碼

Demo 頁的原始碼放到 GitHub 上了:

https://github.com/letswritetw/letswrite-speech_synthesis_utterance-api

Demo 再放一次,歡迎來玩玩:

https://letswritetw.github.io/letswrite-speech_synthesis_utterance-api/


筆記後心得

目前 SpeechSynthesisUtterance,在IE上完全不支援,很好,不用理會那隻上古神獸。

但在看 MDN 的說明文件,有看見後面附了一個 Web Speech API,沒仔細看,還不知道這兩者的差異在哪,就等以後真的遇到專案有這需求時再來研究吧~

Summary
SpeechSynthesisUtterance 讓瀏覽器說話
Article Name
SpeechSynthesisUtterance 讓瀏覽器說話
Description
本篇大綱:瀏覽器本身就可以發音、頁面製作、SpeechSynthesisUtterance、第一坑 拿瀏覽器支援的所有語音、第二坑 調整速度倍率、第三坑 Chrome 的 Google 語音、原始碼、筆記後心得。
Augustus
Let's Write
Let's Write
https://letswrite.tw/wp-content/uploads/2020/08/logo_512.jpg
訂閱
通知
guest

0 Comments
Inline Feedbacks
看所有留言