Question

I am experimenting with the HTML5 speech recognition API to log what i say on screen. I have a keyword that must be said first in order for other words to be picked up, so it works kind of like Apple's Siri. When you say "Okay, Google" it then listens out for two more words, and when it hears them, it prints it on screen. The way the code works is like this:

  • Listen for "Okay, Google"
  • Log that you have heard it, and open new function looking for new words
  • If you hear "15" or "30", print them to screen
  • When you hear the word "Finished", exit the function, and both if statements.

At the minute i can only exit the first function, i cannot exit both if statements. When you say the word "Finished" you can still say "15" or "30" and have them printed to the screen, but i do not want you to be able to say print these two numbers to the screen after saying "Finished". I want you to have to say "Okay, Google" again before you can say the two numbers.

Here is the code, i hope somebody can help. Thanks in advance.

recognition.onresult = function(event){
  var resultsLength = event.results.length -1 ;
  var ArrayLength = event.results[resultsLength].length -1;
  var Word = event.results[resultsLength][ArrayLength].transcript;
  console.log(Word);
  if (Word.indexOf('okay', 'google') != -1) {
    test.style.borderColor = "white";
    recognition.onresult = function(event){
      var resultsLength = event.results.length -1 ;
      var ArrayLength = event.results[resultsLength].length -1;
      var Word = event.results[resultsLength][ArrayLength].transcript;
      console.log(Word);
      document.getElementById('test').value = Word;
      if (Word.indexOf('15') != -1) {
        test.innerHTML = '15';
      }
      if (Word.indexOf('30') != -1) {
        test.innerHTML = '30';
      }
      if (Word.indexOf('finished') != -1) {
        test.style.borderColor = "black";
        return;
      }
    }
  }
}
Was it helpful?

Solution

On line 8 of the code you've posted, you assign a handler function to recognition.onresult:

recognition.onresult = function(event){
   // content removed
}

The problem is that, when you hear the word "finished", you don't remove or replace this handler at all. Next time the event is fired, exactly the same handler kicks in again and will print the words.

What you need to do is, when you hear the word "finished", set the onresult handler to something else:

if (Word.indexOf('finished') != -1) {
    recognition.onresult = function(event){
        // do something different now that we've heard "Finished"
    }
    test.style.borderColor = "black";
    return;
}

Note that from what you describe, what you want is to reassign the initial function (the entire code you assign in line 1) back to the handler again. This is starting to get very convoluted and difficult, since you're now dealing with two anonymous functions, one nested inside the other, and you want to assign the outer one from within the inner one.

In this case, your code will be a lot easier to read if you break these anonymous functions out and give them names so that you can assign/reassign them to recognition.onresult repeatedly.

This is by no means a full implementation and I haven't tested the following, but see if the following makes sense:

recognition.onresult = listenForOkayGoogle;

function listenForOkayGoogle(event) {
   var Word = ...;
   if (Word.indexOf('okay', 'google') != -1) {
      recognition.onresult = listenForNumbersUntilFinished;
   }
}

function listenForNumbersUntilFinished(event) {
   var Word = ...;
   if (Word.indexOf('15') != -1) {
      // print it
   }
   if (Word.indexOf('30') != -1) {
      // print it
   }
   if (Word.indexOf('finished') != -1) {
      recognition.onresult = listenForOkayGoogle;
   }
}

You start by assigning listenForOkayGoogle as your handler. Whenever the event is fired, this handler kicks in and checks whether one of the magic words has been spoken, and if so it replaces the handler with listenForNumbersUntilFinished. Now, each time the event is fired, that handler kicks in instead. If the word matches one of the relevant numbers, you print it; if instead it matches the word 'finished', you assign the listenForOkayGoogle handler again. You are in effect using the Strategy Pattern here.

OTHER TIPS

The 'top-level if' have already finished before nested 'if' executes. To solve your problem your should not set a new event handler when receive 'okay google'. Instead you should implement a some kind of state machine. For example:

function setup(recognition) {
    var isListeningOkayGoogle = true;
    recognition.onresult = function(event) {
        var resultsLength = event.results.length -1 ;
        var ArrayLength = event.results[resultsLength].length -1;
        var Word = event.results[resultsLength][ArrayLength].transcript;
        console.log(Word);

        if (isListeningOkayGoogle) {
            if (Word.indexOf('okay', 'google') != -1) {
                test.style.borderColor = "white";
                isListeningOkayGoogle = false;
            }
        } else {
            document.getElementById('test').value = Word;
            if (Word.indexOf('15') != -1) {
                test.innerHTML = '15';
            }
            if (Word.indexOf('30') != -1) {
                test.innerHTML = '30';
            }
            if (Word.indexOf('finished') != -1) {
                test.style.borderColor = "black";
                isListeningOkayGoogle = true; // start listening 'Okay Google' again
            }
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top