SpeechSynthesisUtterance 讓瀏覽器說話

SpeechSynthesisUtterance 讓瀏覽器說話

瀏覽器本身就可以發音

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

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


頁面製作

接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的好處就是即便就是複製文件中的html code,頁面第一眼看上去也會有設計感。


SpeechSynthesisUtterance

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

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

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

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


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

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

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,而且每種語系的最大值是不一樣的。

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

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


第三坑 Chrome的Google語音

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

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

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

原本想說Google好佛心,可以這樣子給使用者玩,後來Augustus在測試時,隨便丟了一段新聞的內文用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語音、原始碼、筆記後心得。原本以為照著用,然後用vue.js把參數接一接,馬上就可以完成的,但一接參數就踩坑了。
Augustus
Let's Write
Let's Write
Publisher Logo

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *