クライアントアプリから Google Cloud Speech-to-Text を使ってみた(マイク編)

[前回の記事](https://qiita.com/masatomix/items/9b67e40ba3ba9f4eb660)のつづきです。前提の環境やソースコードなどはすべて前回の記事とおなじなので、そちらを先にご参照ください。

## 今回のコンテンツ

[入門ガイド](https://cloud.google.com/speech-to-text/docs/how-to?hl=ja)にある**[「ストリーミング入力の音声文字変換」](https://cloud.google.com/speech-to-text/docs/streaming-recognize?hl=ja)** をやってみましょう。

## TL;DR

- MacでもWindowsでもマイクによる音声認識が可能でした
- ただ [SoX - Sound eXchange](https://sourceforge.net/projects/sox/) というライブラリをOSごとにインストールする方式なので、WEB開発むけという意味だとこの方式は採用しないかな、、。。

## ライブラリのインストールなど環境設定

Node.jsのクライアントアプリケーションからOSのマイクデバイスにアクセスするには、[SoX - Sound eXchange](https://sourceforge.net/projects/sox/) のインストールなどが必要です。基本はすべてチュートリアルに説明があるのでインストールできればなんでもOKなのですが、一応作業メモを。

### Mac

Macの環境構築はシンプルです

```console
brew install sox
```

するだけ。

### Windows

バイナリをインストールして、パスを通すなどが必要です。**ハマりました**。

バイナリへの直リンクは[ココ](https://sourceforge.net/projects/sox/files/latest/download) でです。
ダウンロードしたモノをインストールしたのち、下記の通りインストールディレクトリ``C:\Program Files (x86)\sox-14-4-2`` へパスを通します。

![path](https://imgur.com/VsweX2D.png)

さらにさらに環境によるかもしれませんが、こちらでやった限り [Error: spawn rec ENOENT](https://github.com/gillesdemey/node-record-lpcm16/issues/8) の現象が発生し、
``sample_ts\node_modules\node-record-lpcm16\recorders\sox.js`` に手修正が必要でした、、、。

```javascript
  let args = [
    '--type', 'waveaudio', // audio type    <-この1行を追加
    '--default-device',
    '--no-show-progress', // show no progress
    '--rate', options.sampleRate, // sample rate
    '--channels', options.channels, // channels
    '--encoding', 'signed-integer', // sample encoding
    '--bits', '16', // precision (bits)
    '--type', options.audioType, // audio type
    '-' // pipe
  ]
```

うーん、とても「簡単」とはいかないですね、、メンドクサイ。



## やってみる

コードは前回の記事で取得済なので、実行するだけです。

### Mac

```console
% pwd
/xxx/speech_node_samples/sample_ts
% export GOOGLE_APPLICATION_CREDENTIALS="`pwd`/firebase-adminsdk.json"
%
% npx ts-node src/index3.ts
Listening, press Ctrl+C to stop.
Transcription: テストをしています
Transcription: もう一度テストです

(終了するにはCtrl-C)
^C
% 
```

ちゃんと音声認識できていますね！
数秒無音が続くと音声認識が実行されて画面に結果が表示され、また待ち状態になります。

### Windows

(cloneしたコードは``t:\speech_node_samples\sample_ts\`` に展開されているとします)

```console
T:\speech_node_samples\sample_ts> set GOOGLE_APPLICATION_CREDENTIALS=t:\speech_node_samples\sample_ts\firebase-adminsdk.json

T:\speech_node_samples\sample_ts>　npx ts-node src\index3.ts
Listening, press Ctrl+C to stop.
Transcription: テストをしています
Transcription: もう一度テストです

(終了するにはCtrl-C)
^Cバッチ ジョブを終了しますか (Y/N)? y
T:\speech_node_samples\sample_ts>
```

Windowsでも、(環境設定はメンドクサかったですが) ちゃんと音声認識できていますね！



### コードの中身

コードを見ておきましょう。これも[ストリーミング入力の音声文字変換](https://cloud.google.com/speech-to-text/docs/streaming-recognize?hl=ja)のほぼまんまですが。

```typescript:src/index3.ts
import speech from '@google-cloud/speech'
const recorder = require('node-record-lpcm16');

async function main() {
  // Creates a client
  const client = new speech.SpeechClient()
  const sampleRateHertz = 16000

  const config = {
    encoding: 'LINEAR16',
    languageCode: 'ja-JP',
    model: 'default',
    sampleRateHertz: sampleRateHertz,
    interimResults: false, // If you want interim results, set this to true
  }

  const request: any = {
    config: config,
  }

  // Create a recognize stream
  const recognizeStream = client
    .streamingRecognize(request)
    .on('error', console.error)
    .on('data', (data) =>
      process.stdout.write(
        data.results[0] && data.results[0].alternatives[0]
          ? `Transcription: ${data.results[0].alternatives[0].transcript}\n`
          : '\n\nReached transcription time limit, press Ctrl+C\n',
      ),
    )

  // Start recording and send the microphone input to the Speech API.
  // Ensure SoX is installed, see https://www.npmjs.com/package/node-record-lpcm16#dependencies
  recorder
    .record({
      sampleRateHertz: sampleRateHertz,
      threshold: 0,
      // Other options, see https://www.npmjs.com/package/node-record-lpcm16#options
      verbose: false,
      recordProgram: 'rec', // Try also "arecord" or "sox"
      silence: '10.0',
    })
    .stream()
    .on('error', console.error)
    .pipe(recognizeStream)

  console.log('Listening, press Ctrl+C to stop.')
}

if (!module.parent) {
  main().catch(console.error)
}

```

流れとしては

- ``@google-cloud/speech``ライブラリの ``streamingRecognize ``メソッドで、音声認識をおこなうStreamを作成し、認識結果を受信したときの挙動を設定しておきます(結果を画面に表示するだけ)
- ``node-record-lpcm16``ライブラリをつかってマイクからの音声を受信し、先ほどのStreamをセットしておく

となります。
以上で「**マイクからの音声をもとに、クライアントアプリケーションから、ライブラリを用いて音声認識する**」ことができました。



## まとめ

前回までのコンテンツも含めて、以下のことができるようになりました。

- **音声ファイルをもとに、クライアントアプリケーションから、ライブラリを用いて音声認識する** ことができました。
- **音声ファイルをもとに、クライアントアプリケーションから、RESTを用いて音声認識する** ことができました。
- **マイクからの音声をもとに、クライアントアプリケーションから、ライブラリを用いて音声認識する** ことができました。
- 今回のマイクからの音声受信は、各OSにSoxというプログラムのインストールが必要だったり、WEBアプリなどからは利用できなそう、ということがわかりました。

おつかれさまでしたー。

## 関連リンク

- [Speech-to-Text ドキュメント](https://cloud.google.com/speech-to-text/docs?hl=ja) 公式
- [ストリーミング入力の音声文字変換](https://cloud.google.com/speech-to-text/docs/streaming-recognize?hl=ja)