GoogleChrome でリアルタイムに音声認識してビデオに字幕を表示する


# はじめに

ビデオ会議アプリでオンライン帰省などが流行っていますが、
表現力として字幕表示はぜひとも習得しておきたいところですね。

と思ってるところに、落合陽一さんがツイートした内容に触発されて、
リアルタイム字幕表示をやってる人を結構見かけたので私も便乗します。

<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/xdiversity?src=hash&amp;ref_src=twsrc%5Etfw">#xdiversity</a> <a href="https://twitter.com/xdiversity_org?ref_src=twsrc%5Etfw">@xdiversity_org</a> <a href="https://twitter.com/hashtag/stayhometokyo?src=hash&amp;ref_src=twsrc%5Etfw">#stayhometokyo</a> <a href="https://twitter.com/hashtag/%E9%9F%B3%E5%A3%B0%E6%96%87%E5%AD%97%E5%A4%89%E6%8F%9B?src=hash&amp;ref_src=twsrc%5Etfw">#音声文字変換</a> <a href="https://t.co/VNKQcZONsR">pic.twitter.com/VNKQcZONsR</a></p>&mdash; 落合陽一 (@ochyai) <a href="https://twitter.com/ochyai/status/1260610691041357825?ref_src=twsrc%5Etfw">May 13, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 要約

GoogleChrome の SpeechRecognition APIを字幕表示に使いました。

下記を組み合わせただけです。

- [.NET Frameworkで音声認識をしてzoomのビデオ画面に字幕を出す](https://qiita.com/yoh1496/items/0ebf0bac208898b19172)
- [puppeteer で ChromeのSpeechRecognitionAPI を使用する
](https://qiita.com/yoh1496/items/cc2fd729050ad2419a34)


# 準備

環境としては

- Windows 10 Pro 64bit 
- OBS 24.0.3
- obs-websocker 4.7.0
- Google Chrome 81.0.4044.138

を使っています。

## OBSの設定

このように、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/612e184f-81f3-d24c-c9d8-694ac05d4cc0.png)

Webカメラの入力を`映像キャプチャデバイス`として挿入し、
「jimaku_fixed」「jimaku_not_fixed」という名前で`テキスト（GDI+）`を挿入します。

## 実装

「[puppeteer で ChromeのSpeechRecognitionAPI を使用する](https://qiita.com/yoh1496/items/cc2fd729050ad2419a34) 」で行ったように、
puppeteerを使って、Google ChromeのSpeechRecognition APIの結果を受け取り、
「[.NET Frameworkで音声認識をしてzoomのビデオ画面に字幕を出す](https://qiita.com/yoh1496/items/0ebf0bac208898b19172)」で行ったように、
OBS上の字幕を制御します。

### ソースコード（一部抜粋）

あまり変更がないので一部抜粋して載せます。

```javascript
    await page.exposeFunction("handleRecognized", text => {
      obs.sendCallback("SetTextGDIPlusProperties", 
      {
          source: "jimaku_not_fixed",
          text: text
      }, prop => {
          console.log(text);
      });
    });

    await page.exposeFunction("handleRecognizedFixed", text => {
      obs.sendCallback("SetTextGDIPlusProperties", 
      {
          source: "jimaku_fixed",
          text: text
      }, prop => {
          console.log(('fixed'), text);
      });
      obs.sendCallback("SetTextGDIPlusProperties", 
      {
          source: "jimaku_not_fixed",
          text: ''
      }, () => {});
    })
```

SpeechRecognition APIは、結果に `isFixed` というフラグを付けて、
未確定の文章と、確定済みの文章を送ってきます。

なので、このように `handleRecognized` `handleRecognizedFixed` の2関数を準備して、
未確定の文章の場合は `handleRecognized` を、確定済みの文章の場合は `handleRecognizedFixed` 関数を実行するようにしています。

```javascript
for (var i = event.resultIndex; i < event.results.length; ++i) {
  if (event.results[i].isFinal) {
    window.handleRecognizedFixed(event.results[i][0].transcript);
  } else {
    window.handleRecognized(event.results[i][0].transcript);
  }
}
```

# 実行結果

こんな感じ。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">音声認識を puppeteer + OBS で行ってみました。確定前の文字列を表示できるのはとても利便性が向上してよいですね。認識自体も早い。<br><br>ちなみに背景のミニオンはミラーレスをWebカメラ化して撮影しているリアルタイムのものです。<a href="https://twitter.com/hashtag/%E9%9F%B3%E5%A3%B0%E8%AA%8D%E8%AD%98?src=hash&amp;ref_src=twsrc%5Etfw">#音声認識</a> <a href="https://twitter.com/hashtag/OBS?src=hash&amp;ref_src=twsrc%5Etfw">#OBS</a> <a href="https://t.co/tP7BzVk8gV">pic.twitter.com/tP7BzVk8gV</a></p>&mdash; いしぐ (@yoh1496) <a href="https://twitter.com/yoh1496/status/1263055788852965376?ref_src=twsrc%5Etfw">May 20, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ちょうどいいので、最近出たキヤノンの一眼レフをWebカメラ化できる `EOS Webcam Utility` を使用してみました。

# おわりに

ちゃんとカメラで顔を写し、
仮想Webカメラで配信すればリアルタイム字幕配信ができちゃいますね。

仮想Webカメラについては↓
[Zoom背景使いたいけどグリーンスクリーンもスペックも足りない・・・せや！！！](https://qiita.com/yoh1496/items/c4ec35c97bd22862a081)

今回使用したスクリプトはこちら↓
https://github.com/yoh1496/puppeteer_speech_recognition/blob/master/speech_to_obs.js