2017年1月16日月曜日

録音した声をほぼリアルタイムで Google Cloud Speech API で解析してみた

概要

Mac 内臓のマイクを使って録音した声をほぼリアルタイムで解析できるスクリプトを作成してみました
言語は Nodejs を使っています
自作 Siri っぽい感じになっています
事前に SoX をインストールしてください

環境

  • Mac OS X 10.11.6
  • Nodejs 5.9.1
  • npm 3.7.3
  • SoX 14.4.2
  • Google Cloud Speech API v1beta1 (2016/8/16 時点)

pakcage.json

  • vim package.json
{
  "name": "google-cloud-speech-api",
  "version": "1.0.0",
  "description": "export GCLOUD_PROJECT=speech-api-test-pj && export GOOGLE_APPLICATION_CREDENTIALS=./your-project-id-xxxxxxxxxxxx.json && node recognize.js",
  "main": "rec.js",
  "dependencies": {                 
    "async": "^1.5.2",
    "google-auto-auth": "^0.2.4",
    "google-proto-files": "^0.3.0",
    "googleapis": "^12.0.0",
    "grpc": "^0.15.0",
    "chokidar": "latest",
    "node-record-lpcm16": "latest"
  },
  "devDependencies": {},
  "scripts": {
  },
  "author": "hawksnowlog",
  "license": "ISC"
}
  • npm install

音声を録音するスクリプト

  • vim rec.js
var record = require('node-record-lpcm16'),
    fs     = require('fs');

process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(chunk) {
  if (chunk.trim() == "s") {
    console.log("start");
    var file = fs.createWriteStream('voice.wav', { encoding: 'binary' });
    record.start({
      sampleRate : 44100,
      verbose : true
    })
    .pipe(file);
  } else if (chunk.trim() == "e") {
    console.log("end");
    process.exit(1);
  }
});
process.stdin.on('end', function() {
  //do something
});

Google Cloud Speech API をコールするスクリプト

Google Cloud Platform に申し込みを行い APIs Manager でプロジェクトを作成し認証用の Json ファイル ( この記事内では your-project-id-xxxxxxxxxxxx.json ) を取得してください

  • vim recognize.js
'use strict';

var google = require('googleapis');
var async = require('async');
var fs = require('fs');
var chokidar = require('chokidar')
var watcher = chokidar.watch('voice.wav', {
  ignored: /[\/\\]\./,
  persistent: true
});

watcher.on('ready', function() { 
  console.log("Start watcing the voice file");
});
watcher.on('change', function(path) {
  var stats = fs.statSync(path);
  var fileSizeInBytes = stats["size"];
  if (fileSizeInBytes > 0) {
    console.log(`Changed the voice file -> ${path}`);
    main(path, console.log);
  }
});

var speech = google.speech('v1beta1').speech;

function getAuthClient (callback) {
  google.auth.getApplicationDefault(function (err, authClient) {
    if (err) {
      return callback(err);
    }
    if (authClient.createScopedRequired && authClient.createScopedRequired()) {
      authClient = authClient.createScoped([
        'https://www.googleapis.com/auth/cloud-platform'
      ]);
    }
    return callback(null, authClient);
  });
}

function prepareRequest (inputFile, callback) {
  fs.readFile(inputFile, function (err, audioFile) {
    if (err) {
      return callback(err);
    }
    console.log('Got audio file!');
    var encoded = new Buffer(audioFile).toString('base64');
    var payload = {
      config: {
        encoding: 'LINEAR16',
        sampleRate: 16000,
        languageCode: 'ja-JP'
      },
      audio: {
        content: encoded
      }
    };
    return callback(null, payload);
  });
}

function main (inputFile, callback) {
  var requestPayload;

  async.waterfall([
    function (cb) {
      prepareRequest(inputFile, cb);
    },
    function (payload, cb) {
      requestPayload = payload;
      getAuthClient(cb);
    },
    function sendRequest (authClient, cb) {
      console.log('Analyzing speech...');
      speech.syncrecognize({
        auth: authClient,
        resource: requestPayload
      }, function (err, result) {
        if (err) {
          return cb(err);
        }
        console.log('result:', JSON.stringify(result, null, 2));
        cb(null, result);
      });
    }
  ], callback);
}

実行する

ターミナルを 2 つ起動して録音用のスクリプト解析用のスクリプトを実行します

  • node rec.js
  • export GCLOUD_PROJECT=your-project-id && export GOOGLE_APPLICATION_CREDENTIALS=./your-project-id-xxxxxxxxxxxx.json && node recognize.js

録音開始は「s」を入力します
この状態でマイクに対して声を掛けると録音が始まります (rec.js)
そして、録音が終了すると録音した音声ファイル (voice.wav) が更新され Google Cloud Speech による解析が始まります (recognize.js)

  • 録音時のログ
s
start
Recording with sample rate 44100...
Recording 8192 bytes
Recording 8192 bytes
Recording 8192 bytes
Recording 8192 bytes
Recording 8192 bytes
Recording 4902 bytes
End Recording: 3945.646ms
  • 解析時のログ
Start watcing the voice file
Changed the voice file -> voice.wav
Got audio file!
Analyzing speech...
result: {
  "results": [
    {
      "alternatives": [
        {
          "transcript": "甲子園始まってます",
          "confidence": 1
        }
      ]
    }
  ]
}

最後に

録音した声を Google Cloud Speech API で解析する方法を紹介しました
「ほぼ」リアルタイムなのは録音が完了してから解析が始まるので「ほぼ」という表現を使っています

本当は会話中の状態から解析したかったのですが、難しそうだったのと解析の精度も落ちそうだったのでやめました
今回の挙動は Siri っぽいので UX 的にもこれでいいかなといったところです

本当にリアルタイムで解析したい場合は Chrome などにある Web Speech API とかを使ってローカルで解析できるような仕組みを作る必要があると思います

0 件のコメント:

コメントを投稿