浏览器上的连续语音识别,例如" ok google"或者"嘿siri"

时间:2017-11-14 03:34:21

标签: javascript html5 google-chrome speech-recognition webspeech-api

我正在进行POC,我的要求是我想在浏览器上实现OK googleHey Siri这样的功能。

我正在使用Chrome浏览器Web speech api。我注意到的事情是,我不能继续识别,因为它会在一段时间后自动终止,并且由于安全问题我知道它的相关性。我只是做了另一个黑客,就像SpeechReognition终止end事件时我start SpeechRecogntion path,但这不是实施此类解决方案的最佳方式,因为假设如果我在不同的浏览器选项卡上使用相同应用程序的2个实例,那么它不起作用或可能是我在我的浏览器中使用另一个使用语音识别的应用程序然后应用程序都没有表现与预期相同。我正在寻找解决这个问题的最佳方法。

提前致谢。

1 个答案:

答案 0 :(得分:3)

由于您的问题是您无法长时间连续运行SpeechRecognition,因此只有当您在麦克风中获得一些输入时,才能启动SpeechRecognition。

这种方式只有当有一些输入时,你才会启动SR,寻找你的magic_word 如果找到magic_word,那么您将能够正常使用SR进行其他任务。

这可以通过WebAudioAPI检测到,这与SR遭受的时间限制无关。您可以通过MediaDevices.getUserMedia的LocalMediaStream来提供它。

有关详情,请参阅以下脚本,您可以看到this answer

以下是如何将其附加到SpeechRecognition:

const magic_word = ##YOUR_MAGIC_WORD##;

// initialize our SpeechRecognition object
let recognition = new webkitSpeechRecognition();
recognition.lang = 'en-US';
recognition.interimResults = false;
recognition.maxAlternatives = 1;
recognition.continuous = true;

// detect the magic word
recognition.onresult = e => {
    // extract all the transcripts
    var transcripts  = [].concat.apply([], [...e.results]
      .map(res => [...res]
        .map(alt => alt.transcript)
      )
    );
  if(transcripts.some(t => t.indexOf(magic_word) > -1)){
    //do something awesome, like starting your own command listeners
  }
  else{
    // didn't understood...
  }
}
// called when we detect silence
function stopSpeech(){
    recognition.stop();
}
// called when we detect sound
function startSpeech(){
    try{ // calling it twice will throw...
      recognition.start();
  }
  catch(e){}
}
// request a LocalMediaStream
navigator.mediaDevices.getUserMedia({audio:true})
// add our listeners
.then(stream => detectSilence(stream, stopSpeech, startSpeech))
.catch(e => log(e.message));


function detectSilence(
  stream,
  onSoundEnd = _=>{},
  onSoundStart = _=>{},
  silence_delay = 500,
  min_decibels = -80
  ) {
  const ctx = new AudioContext();
  const analyser = ctx.createAnalyser();
  const streamNode = ctx.createMediaStreamSource(stream);
  streamNode.connect(analyser);
  analyser.minDecibels = min_decibels;

  const data = new Uint8Array(analyser.frequencyBinCount); // will hold our data
  let silence_start = performance.now();
  let triggered = false; // trigger only once per silence event

  function loop(time) {
    requestAnimationFrame(loop); // we'll loop every 60th of a second to check
    analyser.getByteFrequencyData(data); // get current data
    if (data.some(v => v)) { // if there is data above the given db limit
      if(triggered){
        triggered = false;
        onSoundStart();
        }
      silence_start = time; // set it to now
    }
    if (!triggered && time - silence_start > silence_delay) {
      onSoundEnd();
      triggered = true;
    }
  }
  loop();
}

作为 a plunker ,因为StackSnippets和jsfiddle的iframe都不会允许两个版本的gUM ......