4
respostas

[Bug] Sobre o reconhecimento de números negativos.

Eu notei que, quando falamos algum número negativo, é possível que o Number.isNaN() retorne true. Pelo o que testei, o motivo disso é que, quando falamos, por exemplo, "menos 10", o reconhecimento de fala entende como "- 10", e, por ter esse espaço entre o "-" e o "10", o método Number.isNaN() entende que não é um número.

Para comprovar isso, eu testei com números diferentes, o que retornou "-1", sem o espaço. Eu mudei para inglês também, de maneira que "mynus 10" foi entendido como "-10". O problema não está no código, mas diria que é uma limitação do reconhecimento de fala.

Se alguém tiver alguma solução ou outro ponto de vista, comente aqui, por favor.

4 respostas

Olá José, tudo bem?

Poderia postar seu código completo para que possamos analisar e verificar?

Fico no aguardo.

Att.

Claro!

Se quiser ver o projeto completo, segue: https://github.com/alberto-palmeira/reconhecimento-voz-alura

Se quiser vê-lo funcionando, segue: https://alberto-palmeira.github.io/reconhecimento-voz-alura/

Seguem os códigos:

sortear-numero.js

function geraNumeroAleatorio () {
    return parseInt(Math.random() * (menorValor - maiorValor - 1 ) + maiorValor + 1);
};

const menorValor = 10;
const maiorValor = 1000;

const numeroSecreto = geraNumeroAleatorio();
console.log(numeroSecreto);

const elementoMenorValor = document.querySelector('#menor-valor');
elementoMenorValor.innerHTML = menorValor;

const elementoMaiorValor = document.querySelector('#maior-valor');
elementoMaiorValor.innerHTML = maiorValor;

validacao.js

function verificaValorValido (chute) {
    const numero = +chute;

    console.log(numero);

    if (!Number(numero)) {
        elementoChute.innerHTML += `<div>Valor inválido</div>`;
        return false;
    };

    if (chuteForInvalido(numero)) {
        elementoChute.innerHTML += `<div>Valor inválido. O número secreto está entre ${menorValor} e ${maiorValor}`;
        return false;
    };

    if (numero === numeroSecreto) {
        document.body.innerHTML = `
        <h2>Você acertou!</h2>
        <h3>O número secreto era ${numeroSecreto}!</h3>

        <button id="jogar__novamente" class="botao__jogar">Jogar novamente</button>
        <button id="parar__reconhecimento" class="botao__jogar">Parar reconhecimento de voz</button>
        `;       
    } else if (numero < numeroSecreto) {
        elementoChute.innerHTML += `<div>O número secreto é maior <i class="fa-solid fa-arrow-up-long"></i></div>`;
        return false;
    } else {
        elementoChute.innerHTML += `<div>O número secreto é menor <i class="fa-solid fa-arrow-down-long"></i></div>`;
        return false;
    }
}

function chuteForInvalido (numero) {
    return numero > maiorValor || numero < menorValor;
};

reconhecimento-de-voz.js

// ---------- Funções ----------
function exibeChuteNaTela (chute) {
    elementoChute.innerHTML = `
        <div>Você disse:</div>
        <span class="box">${chute}<span>
    `
}

// ---------- Lógica ----------

const SpeechRecognition = window.SpeechRecognition || webkitSpeechRecognition;

let elementoChute = document.querySelector('#chute');
let elementoMensagem = document.querySelector('#mensagem__acerto');

const recognition = new SpeechRecognition();

recognition.lang = 'pt-Br';

recognition.onresult = (evento) => {
    let chute = evento.results[0][0].transcript;

    exibeChuteNaTela(chute);
    verificaValorValido(chute);
};

recognition.onend = () => {
    recognition.start();
}

recognition.start();

document.body.addEventListener('click', e => {
    if (e.target.id === 'jogar__novamente') {
        window.location.reload();
    };

    if (e.target.id == 'parar__reconhecimento') {
        recognition.onend = () => {
            recognition.stop();
        };
    }
});

Olá José, tudo bem?

Primeiramente me desculpe pela demora no retorno. :)

Bem, fiz o download do seu projeto e realizei alguns testes baseados no seu feedback. Uma coisa que achei interessante é que a velocidade em que se diz o número importa na maneira como o reconhecimento vai atuar: se vai colocar espaço, se vai escrever o número por extenso ou se vai retornar o número como um número de fato.

Outros pontos que observei foram os seguintes:

  • Se o número falado for negativo e dito de forma pausada, a tendência da API é escrever a palavra "menos" ao invés do sinal "-";
  • Se o número falado for negativo, estiver entre 1 e 9 e expressado de forma pausada, a tendência do resultado retornado pela transcrição é o texto "menos" concatenado com o nome do número por extenso. Ex: "menos cinco", "menos seis", etc.
  • Se o número falado for negativo mas for maior que 9 e também for dito de forma pausada, o resultado tende a ser algo como "menos 50", "menos 12" etc.
  • Caso o número seja positivo, estiver entre 1 e 9 e também for dito de forma pausada, o resultado também tende a ser o número por extenso: "cinco", "nove", "três" etc.
  • Números positivos ou negativos maiores que 9 sempre foram retornados como números (independente se a fala foi pausada ou rápida);
  • Por fim, para os cenários de fala mais rápida e clara, os números foram retornados corretamente (sejam positivos ou negativos).

Levando isso em consideração cheguei à seguinte solução:

recognition.onresult = (evento) => {
    const numerosPorExtenso = {
        "zero": 0,
        "um": 1,
        "dois": 2,
        "três": 3,
        "quatro": 4,
        "cinco": 5,
        "seis": 6,
        "sete": 7,
        "oito": 8,
        "nove": 9
    };

    let chute = evento.results[0][0].transcript.toLowerCase().replaceAll(" ", "");
    console.log("in: ", chute);
    for (let [nome, numero] of Object.entries(numerosPorExtenso)) {
        if (chute.includes(nome)) {
            chute = chute.replace(nome, numero);
            break;
        }
    }
    if (chute.startsWith("menos")) {
        chute = chute.replace(/menos/g, "-", chute);
    }
    console.log("out: ", chute);

    exibeChuteNaTela(chute);
    verificaValorValido(chute);
};

No evento onresult criei um objeto que faz o mapeamento dos números por extenso entre 0 e 9 para suas versões numéricas. Assim posso fazer a troca do texto "cinco" pelo valor "5" quando ele for retornado, por exemplo.

Em seguida, normalizei o retorno das informações da propriedade transcript para que o texto retornado seja sempre em letras minúsculas e com quaisquer espaços removidos. Depois exibo esse dado na tela para saber como o dado veio originalmente:

let chute = evento.results[0][0].transcript.toLowerCase().replaceAll(" ", "");
console.log("in: ", chute);

Depois, com um loop for...of eu percorro os valores e as propriedades do objeto numerosPorExtenso para verificar se na transcrição retornada há algum dos números que geralmente são retornados por extenso (nos meus testes, como disse, frequentemente são aqueles entre 0 e 9) e se tiver, eu realizo a substituição pelo valor correto. Ex:

Se o transcript retornar "menos cinco", a variável chute terá como valor a string menoscinco (sem espaços) e o trecho chute.includes(nome) vai verificar que o valor cinco existe no conteúdo variável chute o qual levará ao replace() que trocará cinco por 5. Aí, o conteúdo final da variável chute será menos5 até aqui.

Depois, precisamos apenas nos livrar da palavra menos, caso esta exista. É o que o trecho chute = chute.replace(/menos/g, "-", chute); faz resultando no valor -5 que será repassado para a função exibeChuteNaTela(chute);.

Nos testes que fiz exaustivamente, não houve nenhum resultado definido como "Valor inválido!" quando falei números (mesmo quando ele retornava esse número por extenso). Como em todas as tentativas que fiz ele nunca retornou, por extenso, valores maiores que 9, não me preocupei em validar se ao falar "50" em algum momento ele retornaria "cinquenta" (pois isso nunca aconteceu). Mas nada indica que nunca acontecerá, certo?!

Por hora, essa seria a melhor solução dados os testes e os recursos que temos disponíveis na linguagem e na API em si. O ideal seria que a própria API de SpeechRecognition fornecesse uma configuração em que pudéssemos dizer que, ao detectar números na fala, se ela deve retorná-los como um valor numérico ou por extenso. Contudo, isso não se encontra disponível atualmente (até onde sei).

Faça os testes e me fala depois se deu certo ou se no seu caso deu algum bug ainda.

Espero ter ajudado,

Att.

Olá pensei em uma solução um pouco diferente para o problema do reconhecimento dos números negativos. No meu caso as vezes ele reconhecia como -100 e outras Menos 200. Resolvi da seguinte forma:

function verificaSePossuiValorValido(chute){


    numeroNegativo(chute)

    console.log(novoChute)
    const numero = + novoChute



    if (VerificaSeChuteNumero(numero)){
            console.log('Valor inválido')
        }


    if(VerificaIntervalo(novoChute)){
        console.log(`O chute não está no intervalo do jogo. Entre ${menorValor} e ${maiorValor}`)

    }
}

function VerificaSeChuteNumero(numero) {
    return Number.isNaN(numero)
}

function VerificaIntervalo (chute){

    return chute > maiorValor || chute < menorValor
}

function numeroNegativo(chute) {

    if (chute[0].codePointAt() === 8722 ) {

        novoChute = parseInt(`-${chute.slice(1)}`)
        return chute
    }

    else if (chute.slice(0,5) === 'Menos'){
        novoChute = parseInt(`-${chute.slice(6,-1)}`)

    }
    else {
        novoChute = chute
    }

}

Não sei se é a mais adequada mas pelo que eu testei resolveu o meu problema. A unica coisa que eu tive q criar uma nova variavel chamada de novoChute, porque não consegui fazer ele atualiza o valor do chute inicial.