【無料】中国語テキスト読み上げ機能(Web Speech API)を試す

ブラウザ上で無料で使える「Web Speech API」を利用して中国語のテキスト読み上げ(Text to Speech)機能を試してみました。 人間が読み上げるのと変わらず流暢で、再生速度の微調整にも対応しているので中国語の学習にも利用できます。

Web Speech API(TTS)

まず、「Web Speech API」のText to Speech(TTS)とはどのような機能なのか試してみます。

↑の再生ボタンを押すと、テキストエリアの文章を読み上げます。初期値として適当な文章を設定していますが、内容は変更可能です。

※注意「InternetExplorer」等の一部ブラウザは対応していません。対応ブラウザやWeb Speech API(TTS)の仕様については下記参照。

developer.mozilla.org

音声合成言語の比較

コンボボックスには読み上げを行う音声合成言語一覧が設定されます。

Chrome

例えば、このブログを「Windows10+Chromeブラウザ」で開いた場合は、コンボボックス内には

  • Google 普通话(中国大陆)
  • Microsoft Huihui Desktop - Chinese (Simplified)

の2つが表示されます。「Google 普通话(中国大陆)」を選択して「再生」するとGoogleの音声合成言語で読み上げを行います。

Edge

また「Windows10+Edgeブラウザ」で開いた場合、コンボボックス内には

  • Microsoft Huihui - Chinese (Simplified, PRC)
  • Microsoft Kangkang - Chinese (Simplified, PRC)
  • Microsoft Xiaoxiao Online (Natural) - Chinese (Mainland)
  • Microsoft Yaoyao - Chinese (Simplified, PRC)
  • Microsoft Yunyang Online (Natural) - Chinese (Mainland)

の5つが選択可能です。

Kangkangは男性の声、Huihui・Yaoyaoは女性の声で共にWindows10に最初からインストールされているオフラインTTS音声です。そのため、ネット回線が無くても読み上げ可能です。

▼参考:Windows10の合成言語

support.microsoft.com

Xiaoxiao・Yunyangはオンライン音声で、かなり自然な読み上げです。

Web Speech API(音声認識)

Web Speech APIの音声認識機能も実装してみました。

↑マイク入力開始ボタンを押して、中国語でマイクに話しかけると、テキストエリアに文字が表示されます。

対応ブラウザやWeb Speech API(音声認識)の仕様については下記参照 

developer.mozilla.org

まとめ

「Web Speech API」を試してみました。

オフラインTTSはいかにも機械が読み上げていると感じる音声ですが、オンライン音声の方はかなり自然で違和感がありません

共に「多音字」にも対応していて「行不行,银行」「什么,喀什」等も正しく発音できます。また、 再生速度の微調整(0.1~10倍速)も出来るのでシャドーイングなどの中国語学習にも利用できるかと思います。

ちなみに、中国の人工知能TOP企業「科大讯飞」が提供している「讯飞配音」では更に細かい設定や「子供から大人」、各地方方言にまで対応した音声読み上げ機能を提供しています。

参考:javascriptソース

Web Speech APIの機能を実現するためにこのブログ記事で追加したjavascriptを記載しておきます。「zh-CN」を「ja-JP」、「en-US」に変更するとそれぞれ日本語、英語のTTS、音声認識となります。

TTS

<script>
var synth = window.speechSynthesis;

var inputForm = document.querySelector('#tts-form');
var inputTxt = document.querySelector('#tts-txt');
var voiceSelect = document.querySelector('#tts-select');
var rate = document.querySelector('#tts-rate');

var voices = [];

function populateVoiceList() {
  voices = synth.getVoices().sort(function (a, b) {
      const aname = a.name.toUpperCase(), bname = b.name.toUpperCase();
      if ( aname < bname ) return -1;
      else if ( aname == bname ) return 0;
      else return +1;
  });
  var selectedIndex = voiceSelect.selectedIndex < 0 ? 0 : voiceSelect.selectedIndex;
  voiceSelect.innerHTML = '';
  for(i = 0; i < voices.length ; i++) {
    var option = document.createElement('option');
    option.textContent = voices[i].name;
    
    if(voices[i].lang != 'zh-CN') {
      continue;
    }

    option.setAttribute('data-lang', voices[i].lang);
    option.setAttribute('data-name', voices[i].name);
    voiceSelect.appendChild(option);
  }
  voiceSelect.selectedIndex = selectedIndex;
}

populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
  speechSynthesis.onvoiceschanged = populateVoiceList;
}

function speak(){
    if (synth.speaking) {
        console.error('speechSynthesis.speaking');
        return;
    }
    if (inputTxt.value !== '') {
    var utterThis = new SpeechSynthesisUtterance(inputTxt.value);
    utterThis.onend = function (event) {
        console.log('SpeechSynthesisUtterance.onend');
    }
    utterThis.onerror = function (event) {
        console.error('SpeechSynthesisUtterance.onerror');
    }
    var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
    for(i = 0; i < voices.length ; i++) {
      if(voices[i].name === selectedOption) {
        utterThis.voice = voices[i];
        break;
      }
    }
    utterThis.rate = rate.value;
    synth.speak(utterThis);
  }
}

inputForm.onsubmit = function(event) {
  event.preventDefault();

  speak();

  inputTxt.blur();
}
</script>
    

音声認識

<script>
var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
var recognition = new SpeechRecognition();
recognition.lang = 'zh-CN';

var outputTxt = document.querySelector('#sr-txt');
var speechButton = document.querySelector('#sr-btn');

speechButton.addEventListener('click', function () {
  recognition.start();
});
recognition.onresult = function (e) {
  let result = e.results[0][0].transcript;
  outputTxt.value = result;
};
</script>