Watsonで入門するリアルタイム音声認識

When

2017/03/28

At

[WebAudio.tokyo #4](https://webaudiotokyo.connpass.com/event/53163/)

---

## 自己紹介

ちきさん
(Tomohiro Noguchi)

Twitter/GitHub/Qiita: [@ovrmrw](https://twitter.com/ovrmrw)

ただのSIer。

Angular Japan User Group (ng-japan)スタッフ。

![3a2512bb-aa72-4515-af42-1f1721252f39.jpg](https://qiita-image-store.s3.amazonaws.com/0/74793/c753b39c-1581-ea1d-87d1-181f37bda60d.jpeg)

---

今回のGitHubリポジトリ

フロントエンド: [ovrmrw/webaudio-meetup-react-example](https://github.com/ovrmrw/webaudio-meetup-react-example)

バックエンド: [ovrmrw/cognitive-server-starter](https://github.com/ovrmrw/cognitive-server-starter)

---

### あるとき僕は、リアルタイム音声認識をやりたいと思い立った。

---

### 何から始めればいいんだろう？

---

## リアルタイムに音声入力したいと思ったら把握しなければいけないこと。

- [Navigator.getUserMedia](https://developer.mozilla.org/ja/docs/Web/API/Navigator/getUserMedia)
- [AudioContext](https://developer.mozilla.org/ja/docs/Web/API/AudioContext)
- [AudioContext.createMediaStreamSource()](https://developer.mozilla.org/ja/docs/Web/API/AudioContext/createMediaStreamSource)
- [AudioContext.createScriptProcessor()](https://developer.mozilla.org/ja/docs/Web/API/AudioContext/createScriptProcessor)
- [ScriptProcessorNode.onaudioprocess](https://developer.mozilla.org/ja/docs/Web/API/ScriptProcessorNode/onaudioprocess)
- [WebSocket](https://developer.mozilla.org/ja/docs/Web/API/WebSockets_API)
- 。。。。。その他諸々
 
---

### つらい :innocent:

---

### 探したらお手軽っぽいの見つかった。

---

# Watson Speech to Text

https://speech-to-text-demo.mybluemix.net/

Speech-to-Text APIのデモサイト。

マイクがあれば音声認識を実際に試すことができる。

---

## watson-developer-cloud/speech-to-text-nodejs

https://github.com/watson-developer-cloud/speech-to-text-nodejs

前ページのデモサイトをgit cloneして動かせるGitHubリポジトリ。

サンプルコードが以前はjQueryベースだったが、今はReactベースになって多少読みやすくなった。

---

## Watsonデモサイトで使われているお手軽ライブラリ

- [saebekassebil/microphone-stream](https://github.com/saebekassebil/microphone-stream)
    - WebAudio APIをうまいことラップしてStreamとして扱えるようにするライブラリ。

- [watson-developer-cloud/speech-javascript-sdk](https://github.com/watson-developer-cloud/speech-javascript-sdk)
    - 上記のライブラリをさらにラップしてWatson Speech-to-Text APIを簡単に扱えるようにするライブラリ。

---

### WebAudio APIを直接叩かずにリアルタイム音声入力できる。しかもWatsonで音声認識できる。 :raised_hands:

---

(connpassより)

> イベント概要

> WebAudio.tokyoはWeb Audio APIのための勉強会です。


＿人人人人人人人人人人人人人人人人人人人＿
＞　Web Audio APIのための勉強会です。　＜
￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣

---

### Reactでミニマムなサンプルコードを書いてみよう

---

（デモ）

---

## 必要なライブラリをimport

```ts:src/Mic.tsx
import * as recognizeMicrophone from 'watson-speech/speech-to-text/recognize-microphone'
```

(TypeScriptの場合)

---

## Watsonを使うためのTokenを取得するメソッド

```ts:src/Mic.tsx
  getTokenAsync() {
    return fetch('http://localhost:4000/api/watson/speech-to-text/token')
      .then(res => res.json())
      .then(data => data.token)
  }
```

(Tokenを取得するためのバックエンドサーバーが localhost:4000 で稼働している)

---

## AudioContextをオン/オフするためのメソッド

RECボタン/STOPボタンがクリックされたときに呼ばれる。

```ts:src/Mic.tsx
  toggleMicrophoneState() {
    if (this.recognizeStream) {
      this.stopRecognizeStream()

    } else if (!this.isRecording) {
      this.isRecording = true

      this.getTokenAsync()
        .then(token => {
          this.setState({ transcripts: [] })
          this.startRecognizeStream(token)
        })
    }
  }
```

---

## RecognizeStreamを開始するメソッド

Watsonサーバーから返ってくる認識後のデータは `stream.on('data', .....)` で受け取る。

```ts:src/Mic.tsx
  startRecognizeStream(token) {
    const stream = recognizeMicrophone({
      token,
      model: 'ja-JP_BroadbandModel',
      objectMode: true,
      extractResults: true,
    })

    stream.on('data', data => {
      if (data.final) {
        const transcript = data.alternatives[0].transcript
        this.setState({ transcripts: [...this.state.transcripts, transcript] })
      }
    })

    this.recognizeStream = stream
  }
```

---

## RecognizeStreamを終了するメソッド

`stream` の `stop()`, `removeAllListeners()` を呼ぶのはお作法みたいなもの。

```ts:src/Mic.tsx
  stopRecognizeStream() {
    if (this.recognizeStream) {
      this.recognizeStream.stop()
      this.recognizeStream.removeAllListeners()
    }

    this.isRecording = false
    this.recognizeStream = null
  }
```

---

## src/Mic.tsx の全コード

```ts:src/Mic.tsx
import * as React from 'react'
import * as recognizeMicrophone from 'watson-speech/speech-to-text/recognize-microphone'

export class Mic extends React.PureComponent {
  private isRecording = false
  private recognizeStream = null

  constructor(props) {
    super(props)
    this.state = { transcripts: [] }
  }

  getTokenAsync() {
    return fetch('http://localhost:4000/api/watson/speech-to-text/token')
      .then(res => res.json() as any)
      .then(data => data.token)
  }

  async toggleMicrophoneState() {
    if (this.recognizeStream) {
      this.stopRecognizeStream()
    } else if (!this.isRecording) {
      this.isRecording = true
      await this.getTokenAsync()
        .then(token => {
          this.setState({ transcripts: [] })
          this.startRecognizeStream(token)
        })
    }
  }

  startRecognizeStream(token) {
    const stream = recognizeMicrophone({
      token,
      model: 'ja-JP_BroadbandModel',
      objectMode: true,
      extractResults: true,
    })
    stream.on('data', data => {
      if (data.final) {
        const transcript = data.alternatives[0].transcript
        this.setState({ transcripts: [...this.state.transcripts, transcript] })
      }
    })
    this.recognizeStream = stream
  }

  stopRecognizeStream() {
    if (this.recognizeStream) {
      this.recognizeStream.stop()
      this.recognizeStream.removeAllListeners()
    }
    this.isRecording = false
    this.recognizeStream = null
  }

  render() {
    const buttonName = !this.recognizeStream ? 'REC' : 'STOP'
    return (
      <div>
        <div>Mic Component</div>
        <button onClick={() => this.toggleMicrophoneState().then(() => this.forceUpdate())}>
          {buttonName}
        </button>
        <div>{this.state.transcripts.join(', ')}</div>
      </div>
    )
  }
}
```

---


### たったこれだけでWatsonのリアルタイム音声認識を使いこなせる！ :cool:

---

### 最初はお手軽に動かして、少しずつソースコードを読んで理解を深めよう :runner_tone1:

---

### Thanks :raised_hands: