Отправить диктофон микрофона из браузера в Google Speech в текст - Javascript

Отправить диктофон микрофона из браузера в Google Speech в текст. Нет необходимости в потоковой передаче и сокете, и нет необходимости делать это с помощью HTTP-запроса через Node.js на сервер Google или через HTTP-запрос со стороны клиента (браузера).

Проблема, с которой я столкнулся:

Выполнена реализация на стороне клиента, а также реализация на стороне сервера. Обе реализации работают независимо друг от друга. Я получаю аудиоданные с микрофона и могу их воспроизвести, а также могу протестировать реализацию на стороне сервера, используя образец audio.raw, предоставленный Google.

Однако, когда я пытаюсь отправить данные микрофона из браузера на мой сервер узла, а затем на сервер Google, я получаю проблему с кодировкой: «Получение пустого ответа от сервера Google».

Мой вопрос в том, как я могу изменить кодировку аудиофайла, а затем отправить его на сервер Google Speech to Text с помощью Javascript.


person VnoitKumar    schedule 15.08.2019    source источник
comment
во-первых, если вы просто получаете пустой ответ от Google, как вы определяете, что существует проблема с кодировкой? Какие у вас есть доказательства того, что это именно та проблема? Во-вторых, если вы ожидаете, что мы поможем вам исправить ваш код, вам нужно будет показать нам его (соответствующие части).   -  person ADyson    schedule 15.08.2019


Ответы (2)


Я поигрался с, и я могу преобразовать речь в текст, работая с помощью Google API и аудиозаписи браузера. Мне интересно, мог ли объект конфигурации быть причиной проблем, с которыми вы столкнулись.

Я использовал компоненты Node.js Server: server.js и простой клиент (index.html и client-app.js). Все в одной папке.

Я использую для этого клиентскую библиотеку Google Speech to Text , поэтому вам нужно добавить файл ключей Google API (APIKey.json) для предоставления учетных данных.

Если вы запускаете сервер Node, укажите в браузере http://localhost:3000/, что позволит вам протестировать код.

Я нарисовал много клиентского кода из здесь, используя рекордер Мэтта Даймонда. .js код тоже.

server.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>

client-app.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);
}
person Terry Lennox    schedule 15.08.2019
comment
Большой! Спасибо! Я боролся с этим в своем приложении для реагирования с другими пакетами, но это решение отлично работает. Чтобы этот пример работал с реактивной сетью, вы должны 1) включить рекордер .js в ваш index.js как веб-скрипт (импорт не будет работать), 2) отредактировать рекордер.js, чтобы он установил window.Recorder = Recorder 3) использовать его в вашем приложении реакции как rec = new window.Recorder(input, { numChannels: 1 }) - person Olexander Korenyuk; 30.01.2020

@ terry-lennox, спасибо тебе большое. За четкий ответ.

Но я использую React в качестве внешнего интерфейса, поэтому получил пакет npm под названием Recorder-js

И код для справки, кто увидит этот пост в будущем.

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;

И во внутреннем коде для справки было небольшое изменение, с которым я столкнулся, и ошибка: Необходимо использовать одноканальный (моно) звук, чтобы исправить это, о котором я упоминал Ссылка, Ссылка. Нужно добавить audioChannelCount: 2 в config.

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;
person VnoitKumar    schedule 15.08.2019
comment
Прохладный! Рад, что смог помочь. Спасибо за отличный вопрос, удачного кодирования! - person Terry Lennox; 15.08.2019