Skip to content

Instantly share code, notes, and snippets.

@guanguans
Forked from Eotones/README.md
Created October 9, 2020 07:21
Show Gist options
  • Save guanguans/89908dda7ee891462ca1137cdd302d32 to your computer and use it in GitHub Desktop.
Save guanguans/89908dda7ee891462ca1137cdd302d32 to your computer and use it in GitHub Desktop.
speechSynthesis強制使用Chrome中的Google小姐中文語音

speechSynthesis強制使用Chrome中的Google小姐中文語音

網路上的window.speechSynthesis教學主要都只有說切換指定語言

像是這樣就能切換成中文語音:

const synth = window.speechSynthesis;

const speak = (msg) => {
  let u = new SpeechSynthesisUtterance();
  u.lang = 'zh-TW';
  u.text = msg;
  synth.speak(u);
};

speak("你要讀出的中文內容1");
speak("你要讀出的中文內容2");

但是這種寫法在系統有支援多種語音的情況下只會選擇系統預設語音

  • win7繁中 - 無支援中文語音
    • 2020/8月補充: 現在win7繁中版可以去載Chromium版的Edge來使用微軟中文語音
  • win7簡中 - 有支援微軟中文語音(系統預設)
  • win10 - 簡繁中都有支援微軟中文語音(系統預設)

如果想要切換成其他語音(例如Chrome才有的Google中文語音)

就要另外再寫

const synth = window.speechSynthesis;

const speak = (msg) => {
  let u = new SpeechSynthesisUtterance();
  //u.lang = 'zh-TW'; //這句絕對不要用
  u.text = msg;

  // 用 synth.getVoices() 取得支援的語音列表
  // 要注意網頁開啟後首次執行 window.speechSynthesis 後才會把語音功能載入
  // 大概要花幾秒鐘的時間才能完全載入,再來才能正確取得語音列表
  // 所以要想辦法延遲 synth.getVoices(); 的執行時間
  // 
  // 或是使用下面的事件偵測
  // (這個事件在載入過程會被觸發多次,很難用,他是偵測changed不是偵測完全載入完成,
  // 但這是window.speechSynthesis裡唯一的一個事件偵測,沒其他選擇)
  // var voices;
  // window.speechSynthesis.onvoiceschanged = function() {
  //   voices = synth.getVoices();
  //   // 注意 speak() 不要直接寫在這裡,不然會被重覆觸發多次
  // };
  let voices = synth.getVoices();

  for(let index = 0; index < voices.length; index++) {
    /*
    "Google US English"
    "Google 日本語"
    "Google 普通话(中国大陆)"
    "Google 粤語(香港)"
    "Google 國語(臺灣)"
    */

    //console.log(this.voices[index].name);
    if(voices[index].name == "Google 國語(臺灣)"){       
      //u.lang = 'zh-TW'; //這句絕對不要用
      //要使用Google中文語音的話請不要再用u.lang指定語言
      //不然可能又會被切回系統預設的中文語音
      u.voice = voices[index];
      break;
    }else{
      //如果沒有則使用預設中文語音
      u.lang = 'zh-TW';
    }
  }

  synth.speak(u);
};

speak("你要讀出的中文內容1");
speak("你要讀出的中文內容2");

參考資料

window.speechSynthesis是瀏覽器內原生的語音功能,詳細用法可以看以下MDN的教學文件

class tts2 {
constructor(){
console.log("tts constructor");
window.speechSynthesis.cancel(); //強制中斷之前的語音
this.synth = window.speechSynthesis;
//
if (localStorage.getItem("ls_rate") === null) {
this.u_rate = 1.2; // 語速 0.1~10
}else{
this.u_rate = Number(localStorage.getItem("ls_rate"));
}
if (localStorage.getItem("ls_volume") === null) {
this.u_volume = 0.5; //音量 0~1
}else{
this.u_volume = Number(localStorage.getItem("ls_volume"));
}
if (localStorage.getItem("ls_pitch") === null) {
this.u_pitch = 1; //語調 0.1~2
}else{
this.u_pitch = Number(localStorage.getItem("ls_pitch"));
}
}
speak2(textToSpeak){
let u = new SpeechSynthesisUtterance();
u.rate = this.u_rate;
u.volume = this.u_volume;
u.pitch = this.u_pitch;
u.onend = (event) => {
//console.log(event);
console.log("tts.onend");
};
u.onerror = (event) => {
//console.log(event);
console.log("tts.onerror", event);
this.cancel2();
};
//
let voices = this.synth.getVoices();
for(let index = 0; index < voices.length; index++) {
/*
"Google US English"
"Google 日本語"
"Google 普通话(中国大陆)"
"Google 粤語(香港)"
"Google 國語(臺灣)"
*/
//console.log(voices[index].name);
if(voices[index].name == "Google 國語(臺灣)"){
//console.log("Y");
//u.lang = 'zh-TW';
u.voice = voices[index];
break;
}else{
//console.log("N");
u.lang = 'zh-TW';
}
}
//console.log("test");
let filter_text = this._textFilter(textToSpeak);
u.text = filter_text;
u.onstart = (event) => {
//console.log(event);
console.log("tts.onstart", filter_text);
};
if( (u.text.length > 0) || (u.text != null) ){
this.synth.speak(u);
}
return this;
}
cancel2(){
console.log("tts cancel");
window.speechSynthesis.cancel();
}
volume(volume_val){
let volume = Number(volume_val);
if(volume >= 0 && volume <= 1){
this.u_volume = volume;
localStorage.setItem("ls_volume", volume);
console.log(`音量調整為: ${this.u_volume}`);
}else{
console.log(`超出範圍`);
}
}
rate(rate_val){
let rate = Number(rate_val);
if(rate >= 0.5 && rate <= 2){
this.u_rate = rate;
localStorage.setItem("ls_rate", rate);
console.log(`語速調整為: ${this.u_rate}`);
}else{
console.log(`超出範圍`);
}
}
pitch(pitch_val){
let pitch = Number(pitch_val);
if(pitch >= 0.1 && pitch <= 2){
this.u_pitch = pitch;
localStorage.setItem("ls_pitch", pitch);
console.log(`語調調整為: ${this.u_pitch}`);
}else{
console.log(`超出範圍`);
}
}
reset(){
//localStorage.clear();
localStorage.removeItem("ls_volume");
localStorage.removeItem("ls_rate");
localStorage.removeItem("ls_pitch");
this.u_rate = 1.2; // 語速 0.1~10
this.u_volume = 0.5; //音量 0~1
this.u_pitch = 1; //語調 0.1~2
}
_textFilter(msg){
//網址不唸
msg = msg.replace(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g, "網址");
msg = msg.replace(/^(1){4,}$/g, "一一一");
msg = msg.replace(/^(2){4,}$/g, "二二二");
msg = msg.replace(/^(3){4,}$/g, "三三三");
msg = msg.replace(/^(4){4,}$/g, "四四四");
msg = msg.replace(/^(5){4,}$/g, "五五五");
msg = msg.replace(/^(6){4,}$/g, "六六六");
msg = msg.replace(/^(7){4,}$/g, "七七七");
msg = msg.replace(/^(8){4,}$/g, "八八八");
msg = msg.replace(/^(9){4,}$/g, "九九九");
msg = msg.replace(/^(w){4,}$/gi, "哇拉");
msg = msg.replace(/^(~){3,}$/g, "~~~");
msg = msg.replace(/^(\.){3,}$/g, "...");
msg = msg.replace(/^484$/gi, "四八四");
msg = msg.replace(/^87$/g, "八七");
msg = msg.replace(/^94$/g, "九四");
msg = msg.replace(/^9487$/g, "九四八七");
//過濾emoji(最多3個,超過就刪除)
msg = msg.replace(/(\ud83d[\ude00-\ude4f]){4,}/g, "");
return msg;
}
}
//
const tts = new tts2();
/*
tts
.speak2("大家看到我,就知道我是誰了,我就是歐付寶終結者RRRRRRRRRRRRRRRRRRRRR")
.speak2("測試1")
.speak2("測試2")
.speak2("測試3")
.speak2("測試4")
.speak2("測試5");
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment