Envoyer un enregistreur audio microphone du navigateur à Google speech en texte-Javascript


Envoyer l'enregistreur audio du microphone du navigateur à Google speech to text. Il n'est pas nécessaire de diffuser et de socket, ni de le faire avec une requête HTTP via Node.js vers le serveur Google ni via une requête HTTP du côté client (navigateur).

Le problème auquel je suis confronté:

L'implémentation côté client est effectuée, ainsi que l'implémentation côté serveur. Les deux implémentations fonctionnent indépendamment l'une de l'autre. Je reçois des données audio du microphone et je suis capable de le lire, ainsi que de tester l'implémentation côté serveur en utilisant l'audio.échantillon brut donné par Google.

Cependant, lorsque j'essaie d'envoyer les données du microphone du navigateur à mon serveur de nœud, puis au serveur Google, je reçois un problème d'encodage: "Obtenir une réponse vide du serveur Google".

Ma question est de savoir comment puis-je modifier l'encodage du fichier audio, puis l'envoyer à Google Speech au serveur de texte en utilisant Javascript.

Author: Mark Storer, 2019-08-15

2 answers

J'ai joué avec le et je peux faire fonctionner la parole en texte en utilisant l'API Google et l'enregistrement audio du navigateur. Je me demande si l'objet config pourrait avoir été la cause des problèmes que vous avez rencontrés.

Les composants que j'ai utilisés sont un nœud.js Serveur: serveur.js et un simple client (index.html et application client.js). Tous dans le même dossier.

J'utilise le Google Speech to Text Client Library pour cela, vous devrez donc ajouter un fichier de clé API Google (APIKey.json) fournir des informations d'identification.

Si vous exécutez le serveur de Nœud, puis pointez votre navigateur sur http://localhost:3000/, qui devrait vous permettre de tester le code.

J'ai dessiné beaucoup de code côté client à partir de ici, en utilisant l'enregistreur de Matt Diamond.code js aussi.

Serveur.js

const express = require('express');
const multer = require('multer');
const fs = require('fs');

const upload = multer();

const app = express();
const port = 3000;

app.use(express.static('./'));

async function testGoogleTextToSpeech(audioBuffer) {
    const speech = require('@google-cloud/speech');
    const client = new speech.SpeechClient( { keyFilename: "APIKey.json"});

    const audio = {
    content: audioBuffer.toString('base64'),
    };
    const config = {
    languageCode: 'en-US',
    };
    const request = {
    audio: audio,
    config: config,
    };

    const [response] = await client.recognize(request);
    const transcription = response.results
    .map(result => result.alternatives[0].transcript)
    .join('\n');
    return transcription;
}

app.post('/upload_sound', upload.any(), async (req, res) => {
    console.log("Getting text transcription..");
    let transcription = await testGoogleTextToSpeech(req.files[0].buffer);
    console.log("Text transcription: " + transcription);
    res.status(200).send(transcription);
});

app.listen(port, () => {
    console.log(`Express server listening on port: ${port}...`);
});

Index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Speech to text test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" type="text/css" href="https://bootswatch.com/4/cerulean/bootstrap.min.css">
</head>
<body style="padding:50px;">
    <h1>Speech to text test</h1>
    <div id="controls">
    <button id="recordButton">Record</button>
    <button id="transcribeButton" disabled>Get transcription</button>
    </div>
    <div id="output"></div>
    <script src="https://cdn.rawgit.com/mattdiamond/Recorderjs/08e7abd9/dist/recorder.js"></script>
    <script src="client-app.js"></script>
</body>
</html>

Application client.js

let rec = null;
let audioStream = null;

const recordButton = document.getElementById("recordButton");
const transcribeButton = document.getElementById("transcribeButton");

recordButton.addEventListener("click", startRecording);
transcribeButton.addEventListener("click", transcribeText);

function startRecording() {

    let constraints = { audio: true, video:false }

    recordButton.disabled = true;
    transcribeButton.disabled = false;

    navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
        const audioContext = new window.AudioContext();
        audioStream = stream;
        const input = audioContext.createMediaStreamSource(stream);
        rec = new Recorder(input, { numChannels:1 })
        rec.record()
    }).catch(function(err) {
        recordButton.disabled = false;
        transcribeButton.disabled = true;
    });
}

function transcribeText() {
    transcribeButton.disabled = true;
    recordButton.disabled = false;
    rec.stop();
    audioStream.getAudioTracks()[0].stop();
    rec.exportWAV(uploadSoundData);
}

function uploadSoundData(blob) {
    let filename = new Date().toISOString();
    let xhr = new XMLHttpRequest();
    xhr.onload = function(e) {
        if(this.readyState === 4) {
            document.getElementById("output").innerHTML = `<br><br><strong>Result: </strong>${e.target.responseText}`
        }
    };
    let formData = new FormData();
    formData.append("audio_data", blob, filename);
    xhr.open("POST", "/upload_sound", true);
    xhr.send(formData);
}
 7
Author: Terry Lennox, 2019-08-15 16:07:38

@ terry-lennox merci beaucoup. Pour la clarté de la Réponse.

Mais j'utilise React comme Frontal, donc j'ai un paquet npm appelé recorder-js

Et le code est pour référence qui voient ce post à l'avenir.

import Recorder from 'recorder-js';

import micGrey from './mic-grey.svg';
import micWhite from './mic-white.svg';

import './App.css';

var recorder = null;
var audioStream = null;

class App extends Component {
  constructor(props) {
    super(props);
    this.mic = React.createRef();

    this.accessMic = this.accessMic.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.handleSuccess = this.handleSuccess.bind(this);

    this.stopAccessingMic = this.stopAccessingMic.bind(this);
    this.getTextFromGoogle = this.getTextFromGoogle.bind(this);

    this.state = {
      isMicActive: false
    };
  }

  accessMic() {
    const audioContext = new (window.AudioContext ||
      window.webkitAudioContext)();

    recorder = new Recorder(audioContext);

    navigator.mediaDevices
      .getUserMedia({ audio: true })
      .then(this.handleSuccess)
      .catch(err => console.log('Uh oh... unable to get stream...', err));
  }

  handleSuccess(stream) {
    audioStream = stream;

    recorder.init(stream);
    recorder.start();
  }

  getTextFromGoogle(blob) {
    let filename = new Date().toISOString();
    let xhr = new XMLHttpRequest();
    xhr.onload = function(e) {
      if (this.readyState === 4) {
        console.log(e.target.responseText);
      }
    };
    let formData = new FormData();
    formData.append('audio_data', blob, filename);
    xhr.open('POST', 'http://localhost:3000/', true);
    xhr.send(formData);
  }

  handleClick() {
    const isMicActive = this.state.isMicActive;

    this.setState({
      isMicActive: !isMicActive
    });

    if (!isMicActive) {
      this.checkPermissions();
      this.accessMic();
    } else {
      this.stopAccessingMic();
    }
  }

  stopAccessingMic() {
    audioStream && audioStream.getTracks()[0].stop();
    recorder.stop().then(({ blob, buffer }) => {
      this.getTextFromGoogle(blob);
    });
  }

  checkPermissions() {
    navigator.permissions
      .query({ name: 'microphone' })
      .then(permissionObj => {
        console.log('Permission status - ', permissionObj.state);
      })
      .catch(error => {
        console.log('Permission status - Got error :', error);
      });
  }

  render() {
    return (
      <div className='App'>
        <div
          id='mic'
          ref={this.mic}
          onClick={this.handleClick}
          className={
            this.state.isMicActive ? 'mic-btn mic-btn-active' : 'mic-btn'
          }
        >
          <img src={this.state.isMicActive ? micWhite : micGrey} alt='mic' />
        </div>
      </div>
    );
  }
}
export default App;

Et le code back-end pour la référence, il y avait un petit changement auquel je faisais face et l'erreur est Doit utiliser un seul canal (mono) audio pour résoudre ce problème, j'ai fait référence à Lien, Lien . Besoin d'ajouter audioChannelCount: 2 dans la configuration.

var router = express.Router();
const multer = require('multer');
const fs = require('fs');

const upload = multer();

process.env.GOOGLE_APPLICATION_CREDENTIALS =
  'C:/Users/user/Desktop/Speech-to-Text-e851cb3889e5.json';

/* GET home page. */
router.post('/', upload.any(), async (req, res, next) => {
  console.log('Getting text transcription..');
  try {
    let transcription = await testGoogleTextToSpeech(req.files[0].buffer);
    console.log('Text transcription: ' + transcription);
    res.status(200).send(transcription);
  } catch (error) {
    console.log(error);
    res.status(400).send(error);
  }
});

async function testGoogleTextToSpeech(audioBuffer) {
  const speech = require('@google-cloud/speech');
  const client = new speech.SpeechClient();

  const audio = {
    content: audioBuffer.toString('base64')
  };
  const config = {
    languageCode: 'en-US',
    audioChannelCount: 2
  };
  const request = {
    audio: audio,
    config: config
  };

  try {
    const [response] = await client.recognize(request);
    const transcription = response.results
      .map(result => result.alternatives[0].transcript)
      .join('\n');
    return transcription;
  } catch (error) {
    return error;
  }
}
module.exports = router;
 3
Author: VnoitKumar, 2019-08-15 18:18:02