WEBで音声操作

昔、「しゃべる!DSお料理ナビ」というゲームがあって調理中でも音声でレシピの手順を進める事が出来た。
今やDSは使わないけどiPadとかで音声操作できるブラウザがあったら便利かもと検索したら「Speech Recognition API」というJavaScriptのAPIが見つかったので試して見ることにした。

「Speech Recognition API」は昔は限られた環境でしか利用できなかったようですが最近はiOSでも使えるみたいなので試して見ました。
https://uda2.com/speechme/
右上のマイクボタン(音声操作START)を押すと音声操作が開始します。(非対応なブラウザではボタンが現れません。)
「次へ」で次のステップに進んで「戻る」で前のステップに戻って「終了」で音声認識を終了します。
recognition.interimResultsをtrueにする事で聞きながら音声解読するので反応が早くなります。
resultイベント(音声が確認できたら)の最後に毎度停止再開をしてるのはコマンドが短いのに「まだ何か言うかも」と待機するのを抑止する為です。
上の方法で永続化はされてるのですがスタートしてから5分間と任意でストップするまで永続化したかったのでタイマーを仕掛けて状態を維持するようにしています。
他に誤認識を考慮してワードをセレクトしています。
「戻る」は何度か「本」「婆」「保存」と謎な誤認識をされたので入れてます。

var speechme = () => {
  const recbtnObj = document.getElementById('recbtn');
  const recognition = new webkitSpeechRecognition() || new SpeechRecognition();
  if (typeof recognition === 'undefined') return;
  recbtnObj.style.display = "block";
  var recognizing = false;
  var rectime = 0;
  recognition.interimResults = true;
  recognition.maxAlternatives = 10;
  recognition.addEventListener('result', function(event) {
    const results = event.results;
    let strs = [];
    let command = "";
    paragraph: for (let i = event.resultIndex; i < results.length; i++) {//文節(今回は0だけ)
      for (let j = 0; j < results[i].length; j++){//候補
        const result = results[i][j].transcript;
        if (results[i].isFinal) {
          recognition.stop();
          recognizing = false;
        }
        strs.push({c: results[i][j].confidence, s: result});
        if (result == "ストップ" || result == "止めて" || result == "停止" || result == "終了") command = "stop";
        if (result == "次へ" || result == "次" || result == "月" || result == "進む" || result == "進んで" || result == "そんで" || result == "そして" || result == "それから" || result == "オッケー") command = "next";
        if (result == "戻る" || result == "本" || result == "婆" || result == "保存" || result == "戻って" || result == "バック") command = "back";
        if (command != "") break paragraph;
      }
    }
    if (command == "stop") return recStop();
    if (command == "next") stepxstep(1);
    if (command == "back") stepxstep(-1);
    //console.log(JSON.stringify(strs));
    document.getElementById('debug').innerText = JSON.stringify(strs);
    recognition.stop();
    recognition.start();
  });
  recbtnObj.addEventListener('click', () => {
    if (recbtn.textContent == 'START') {
      rectime = Date.now(); 
      recStart();
    } else {
      recStop();
    }
  });
  recognition.addEventListener('start', () => {
    recbtnObj.className = "online";
  });
  recognition.addEventListener('end', () => {
    recbtnObj.className = "offline";
    recognizing = false;
  });
  function recStop() {
    recognition.stop();
    recognizing = false;
    recbtn.textContent = 'START';
  }
  function recStart() {
    recognition.start();
    recognizing = true;
    recbtn.textContent = 'STOP';
  }
  setInterval(() => {
    if (recbtn.textContent == 'STOP' && recognizing === false) {//録音状態なのにAPI停止
      const mysec = Math.floor((Date.now() - rectime) / 1000);
      if (mysec < 60*5) {
        recognition.start();
        recognizing = true;
      } else {
        recStop();
      }
    }
  }, 1000);
};
speechme();

Tags: ,

トラックバック

コメントを書く