.NET Frameworkで音声認識をしてzoomのビデオ画面に字幕を出す

# はじめに

みなさん、Zoom楽しんでますでしょうか？
Webカメラが無いことを良いこと（？）に、自分のビデオ表示で好き勝手やってる人も
少なからずいるんじゃないかと思います。（いない）

前回記事参照： 「[Zoom背景使いたいけどグリーンスクリーンもスペックも足りない・・・せや！！！](https://qiita.com/yoh1496/items/c4ec35c97bd22862a081)」

電気屋さんでWebカメラ売ってないのは本当に困りますねーもう。

# やったこと

音声認識結果を字幕としてバーチャルWebカメラにオーバーレイして表示します。

こんな感じ（出オチ感）↓
![サンプル](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/f690609f-71cc-6e92-d4d7-67dce7555287.png)

## 概要

こんな感じです。

![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/dc0549e8-8de7-a2e2-e17d-d0e0cf899ccc.png)

前回同様OBSを使用します。OBSで動画と字幕を仮想Webカメラにレンダリングして、それをZoomで配信する感じです。

なお、音声認識には .NET Framework を使用して、OBSの字幕制御にはプラグイン「obs-websocket」と node.js を使用しました。

# やり方

環境としてはWindows 10 64bitのノートパソコンを使用して、開発／配信を行いました。
↓これぐらいの結構古いマシンでも全然イケます。

```
CPU: Core i5-2520M
RAM：16GB
```

## 準備（開発環境）

まずは開発環境を整えます。Visual Studioを入れます。
今回は同人（個人利用？）アプリの開発なのでCommunity Editionでいきます。
昔入れた Visual Studio 2015 Community が入っていたのでそれ使いました。

あとはnode.jsにPATHを通しておけば終わり！

```cmd
> node --version
v8.9.1
```

バージョン v8.9.1 ←おい

## 準備（配信環境）

OBS v24系（←これ大事。[らしいです](http://hatsune.hatenablog.jp/entry/2020/04/10/081927)）に obs-websocketを導入します。

https://github.com/Palakis/obs-websocket

インストーラーは、[Releases](https://github.com/Palakis/obs-websocket/releases) のページからダウンロードしましょう。
「obs-websocket-4.7.0-Windows-Installer.exe」をダウンロード／実行しました。

### シーンのセッティング

まずは背景を設定します。これは静止画でもシャイア・ラブーフさんの動画でもなんでも大丈夫です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/0099302b-b18d-1247-7cf9-cfc83a4ae49d.png)

追加された画像を適当に配置した後は、右クリックして「追加」→「テキスト（GDI+）」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/4ffe73a1-fab2-449d-97fc-f238383eeecb.png)

名前は `jimaku` としておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/9c3ac5be-1f5f-37e5-0f34-648a965fe938.png)

適当に自分好みのスタイルに設定して、配置を決定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/4cd6356d-1357-a1c0-2f3a-3cc1b72c486b.png)

## 開発

「音声認識」と「字幕制御」を分けて開発します。例外処理？知らん。

### 音声認識

.NET Frameworkの場合、 `System.Speech.Recognition` 名前空間の
`SpeechRecognitionEngine` クラスを使用することで追加インストール不要で音声認識できます。

https://docs.microsoft.com/ja-jp/dotnet/api/system.speech.recognition.speechrecognitionengine?view=netframework-4.8

ほぼドキュメントのコピペですが、標準出力に音声認識結果を出力するようにしましょう。

```csharp
using System;
using System.Speech.Recognition;

namespace SpeechToConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var recognizer = new SpeechRecognitionEngine(new System.Globalization.CultureInfo("ja-JP")))
            {
                recognizer.LoadGrammar(new DictationGrammar());
                recognizer.SpeechRecognized += Recognizer_SpeechRecognized;
                recognizer.SetInputToDefaultAudioDevice();
                recognizer.RecognizeAsync(RecognizeMode.Multiple);
                Console.ReadLine();
            }
        }

        private static void Recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            Console.WriteLine(e.Result.Text);
        }
    }
}
```

`Release`でビルドして、EXEファイルとして実行できるようにしておきましょう。
実行してマイクに向かってしゃべった言葉が認識されてコンソールに出力されればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/d1e10e3c-dfc7-39ff-3f23-78eec6a06659.png)


### 字幕制御

字幕制御は `node.js` でちゃちゃっとやります。下記コマンドで必要パッケージをインストールして…

```sh
mkdir text_to_obs
npm init
npm install obs-websocket-js --save
```

下記ファイルを `index.js` として作成します。

```javascript
const OBSWebSocket = require('obs-websocket-js');

const obs = new OBSWebSocket();

obs.connect({ address: 'localhost:4444' })
  .then(() => {
    console.log("opened");
  });

process.stdin.resume();
process.stdin.setEncoding("utf-8");
process.stdin.on("data", function(chunk) {
    obs.sendCallback("SetTextGDIPlusProperties", 
    {
        source: "jimaku",
        text: chunk
    }, prop => {
        console.log(chunk);
    });
});
process.stdin.on('end', function() {
    obs.disconnect();
});
```

それが完成したら、先ほどビルドした音声認識EXEファイルを同じフォルダに入れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/345b441f-3bad-8fe3-b5e0-cc40cc0bb3e0.png)


## 実行

### WebSocketの有効化

**ここからの設定は非常に危険です。設定内容を理解したうえで自己責任でお願いいたします。**

まずはメニューを開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/c5e7ac60-0b28-a950-d9a2-88539a2e000d.png)

「WebSocketsサーバーを有効にする」にチェックを入れ、
ポート「4444」を選択し、OKを押します。

### バーチャルWebカメラの起動

前回記事同様、OBSの内容をWebカメラからの入力として配信するために
VirtualCamを「Start」します

### 音声認識・字幕制御の起動

先ほどEXEファイルを格納したフォルダでcmdを起動し、下記コマンドを実行します。

```cmd
chcp 65001 & SpeechToConsole.exe | node index.js
```

上記コマンドは コードページを `UTF-8` に変換し、
`SpeechToConsole.exe` を実行し、出てくる出力内容を `node index.js` で起動されるプロセスにパイプしています。

このコマンドを実行して、うまく字幕制御プログラムがOBSと接続できると下記のような通知が出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/b695394c-0a9e-94c4-2b80-0a1467c46aa5.png)

# 結果

## 「Zoomに字幕を表示しています」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/6eae6c30-801e-e80f-0c48-a786e2287cdf.png)

## 「字幕のテストだよ」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/dd533a8c-ac53-ba00-f79f-74f6162214a4.png)

## 「字幕なんですけど、ちゃんと音声認識されてるんですかね」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/cce7bc08-4348-2c19-1078-b8bab36d1914.png)

# 終わりに

Zoomって字幕表示機能あるんですね。

https://zoom.us/jp-jp/accessibility.html

ただ、これは自分の顔に表示されるのが利点で、マイクがミュートになっていても
意思表示できるのが便利です！（）

ちなみに先日開催されたZoom飲み会で披露したところ、
音声認識がダメダメすぎてしゃべっている内容の字幕だと認識されませんでした（完）


# 参考URL

[OBS Studio + OBS-WebSocketで字幕表示するときの2020/04/09現在の注意点](http://hatsune.hatenablog.jp/entry/2020/04/10/081927)

[タピオカミルクティーを飲む人のイラスト（男子学生）](https://www.irasutoya.com/2019/05/blog-post_84.html)