リモートワーク支援の為、2週間で無料サービスを出した話

久々の投稿になります。
[Empath](https://webempath.com/jpn/)というAI会社でアプリケーションサイドの開発をしています。
先日[リモトーキー (R:EmoTalkie)](https://remotalkie.webempath.ai/)というサービスをリリースしました。無料で使えます。
今回は[R:EmoTalkie](https://remotalkie.webempath.ai/)の開発背景 + 技術記事という構成で書かせていただきます。

#開発背景
最近の世間の事情により、慣れている人、慣れていない人もリモートワークへ移行せざるを得ない状況になっています。
そんな中、やはり**不安**や**ストレス**、**寂しさ**、そして**元気**がなくなってしまう人が多くなってきているというのをよく聞くようになり、[Empath](https://webempath.com/jpn/)で「何かしら力になりたい」ということで[R:EmoTalkie](https://remotalkie.webempath.ai/)を2020年4月24日にリリースしました。
<img width="1440" alt="スクリーンショット 2020-05-26 14.05.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396104/2f0f1817-522a-608b-80df-f2271b55cd86.png">
（実際の[R:EmoTalkie](https://remotalkie.webempath.ai/)の画面）

##どんなもの？
[R:EmoTalkie](https://remotalkie.webempath.ai/)は通話アプリケーションの裏で動かして、使用者の音声を解析し、ミーティングへの参加率や活性度を可視化することで、積極的にミーティングへの参加を促すサポートツールとなっています。
※ブラウザアプリケーションとして実装していますが、音声は解析にしか使用しておらず、保存もしていません。安心して使ってください。
<img width="1440" alt="スクリーンショット 2020-05-26 14.05.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396104/c473f4a4-3f38-8eab-eda2-c5c47fd73800.png">
（解析結果は数値では表さず、褒めたり励ます仕様に）

##無料？
**「この状況を乗り越えるための助けとなりたい」**という思いはあったのですが、リソース不足、昨今の事情への知識不足から、完璧なサービスをリリースするには限界があると思っていました。
そこで、1つの提案という形で無料でリリースすることにより、我々のことを広く知ってもらい、**共感を得た方々と一緒に作り上げたい**という思いから無料にしています。

##なぜ2週間？
今回はお試しのようなサービスでのリリースとはいえ、前述の通り「この状況を乗り越えるために何か助けとなりたい」という思いがあったため、早期に出すことが重要だと考えていました。
とはいえ、スタートアップでリソースが限られている為、作業者自体は1人で行う必要もあり、なんとか形として出せる期間が**2週間**でした。

#開発
今回のアプリケーションでは[WebRTC](https://webrtc.org/)のコードを一部書いていますが、通話機能をもたせておらず、あくまで[WebRTC](https://webrtc.org/)の音声取得部分だけを利用しています。
WebRTCをちゃんと実装したい場合は、他の人の記事ですが[WebRTCハンズオン 本編](https://qiita.com/yusuke84/items/43a20e3b6c78ae9a8f6c)を参考にしていただくと分かりやすいと思います。

##前提
音響周りの開発、webRTC関連の開発は初めてです。弊社には音響エンジニアがいますが、リソース的に入ることが難しいため、私1人で分からないながらも格闘したものとなります。
間違っているところがありましたらコメント等でご指摘いただければ幸いです。

##開発環境
エディター : [Visual Studio Code](https://azure.microsoft.com/ja-jp/products/visual-studio-code/)
開発言語 : [ReactJS](https://reactjs.org/)
解析API : [EmpathAPI](https://webempath.net/lp-jpn/)
通信ライブラリ : [axios](https://github.com/axios/axios)

##コード
読みやすいように、実際のコードから少々変更してポイントのみ記載しています。
また、WebRTCの解説等は別の有志の方々の記事の方が参考になるかと思いますので、詳しくはこちらでは書きません。

###マイクの使用開始
今回はカメラは使用せず、マイクのみとなるので、下記のように設定します。

```react:analyze.js
   navigator.mediaDevices
      .getUserMedia({
         audio: {
            echoCancellation: true,
            echoCancellationType: 'browser',
            noiseSuppression: true,
            sampleRate: { ideal: 11025 }
         },
         video: false
      })
      .then((stream) => {
         this.handleStartMeeting(stream);
      })
      .catch(this.handleFailed);
```
`echoCancellation`や`noiseSuppression`を有効にしていますが、あまり実感できず、、こちら詳しい方がいましたらコメントしていただけると助かります...

###Streamの取得

```react:analyze.js
// ①
context = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: SAMPLERATE }); // サンプリングレートを指定
...
// ②
   processor.onaudioprocess = (audio) => {
      this.micAnim(); //音量によって変わるマイクアニメーション
      var input = audio.inputBuffer.getChannelData(0);
      var bufferData = new Float32Array(BUFFER);

      for (var i = 0; i < BUFFER; i++) {
         bufferData[i] = input[i];
      }
      audioData.push(bufferData);
   }
   this.recordingTimer(); //5秒区切りの音声作成用タイマー
```
①の部分は重要です。流れてくる音声のサンプリングレートを11kで指定しています。
最後の行で5秒区切りの音声作成用Functionを呼び出します。


`onaudioprocess`については、[Web Sounder](https://weblike-curtaincall.ssl-lolipop.jp/portfolio-web-sounder/webaudioapi-basic/custom)で以下のように説明されています。
> onaudioprocessイベントの発生は以下のように実装されているようです.
    1. AudioDestinationNodeへの接続とイベントハンドラの設定が完了したときに発生
    2. 1. のあとは, バッファサイズのデータを処理するごとに発生

###5秒区切りの音声作成
EmpathAPIを使用する上で、重要になってくるのは、`5秒区切りの音声である`という部分になるかと思います。
そこを下記のタイマー処理で、5秒区切りの音声ArrayをAPIにて送るということをしています。

```react:analyze.js
   let Interval = setInterval(() => {
      var count = this.state.count - 1;
      if (count > 1) {
         this.setState({
            count: count,
         });
      } else {
         clearInterval(Interval);
         this.resetCount();
         if (this.state.speachCount > MIN_SPEECH_REC_TIME) {
            this.stopAndSend();
         }
         ....
      }
   }, 1000);
```
初期値５のcountを1秒起きのタイマーでカウントダウンをし、5秒経ったら`stopAndSend()`で一旦マイクを停止し、EmpathAPIへ音声データを投げる処理に入ります。
マイクの再スタートは作成するサービスによって変わってくるので、適宜再スタートさせてあげてください。
常時録音でもよいですが、EmpathAPIのコール数には上限があるため、1時間のミーティングで使用するとえらい数になり、すぐ上限を超えてしまうかと思います...

`if (this.state.speachCount > MIN_SPEECH_REC_TIME)`こちらの行では、最低発話時間を見ています。`ある一定の音量以上で発話した最低時間`を設定しないと、全く発話していないのにAPIを叩いてしまうことになります。また、物を落としたときなどの雑音も解析してしまうことにもなります。
今回は約1秒以上、音声が入ってきたものを発話としています。

###WAV変換
EmpathAPIに音声データを送るにはこれでは不十分なので、EmpathAPIで受け取れるWAVへ変換していきます。

```react:analyze.js
   let encodeWAV = function (samples, sampleRate) {
      ...
      let view = new DataView(buffer);
      ...
      view.setUint32(24, sampleRate, true); // サンプリングレート
      view.setUint16(34, 16, true); // サンプルあたりのビット
      ...
      return view;
   }
   ...
   let dataview = encodeWAV(mergeBuffers(audioData), 11025);
   let audioBlob = new Blob([dataview], { type: 'audio/wav' });
```
こちらの書き方はいくつも扱っている記事があるので、詳しくは記載していきません。
重要なのはWAVとして扱うことと、サンプリングレートを11025とすることです。


###EmpathAPI送信
こちらは[前の記事](https://qiita.com/k_5/items/5ba84b3adaf1dca7f81e)でも取り扱ったのと同じく、axiosを使用していきます。

```react:cllient.js
   const post = (wavData): Promise<*> => {
      let formData = new FormData();
      formData.append('apikey', key);
      formData.append('wav', wavData);

      return axios.post(url, formData, {
         headers: {
            'content-type': 'multipart/form-data',
         },
      }).then((res) => {
         return Promise.resolve(res);
      }).catch((response) => {
         return Promise.reject(response);
      })
   }
```
ポイントはapikeyとwavDataとなります。
apiKeyは[EmpathAPIのAPI Key設定](https://webempath.net/user/api_keys)にあります。(※ログインが必要です。)
wavDataは前項のaudioDataを使用します。
また、忘れてはいけないのはEmpathAPIは`multipart/form-data`で受けつけているので、こちらも設定をします。
戻り値は`{ "error": 0, "calm": 16, "anger": 9, "joy": 6, "sorrow": 17, "energy": 0 }`のような値が返ってきます。

##まとめ
ざっくりとしたポイント説明だったので、これだけでは作成は難しいかもしれません。が、大体こんな雰囲気で作られているということが伝わればと思います。
こうしたほうがいいよ、ここ間違っているよ、などがあればご連絡していただければと思います。

#おわりに
今回リリースさせていただいたアプリはあくまで「Empath技術を使うとこんなことができるよ」という提示をしたに過ぎません。ここからはもっとリモートワーカー達がより過ごしやすくなるサービスとするには、たくさんのアイディアや技術者が必要となってきます。
弊社と共になにか創生したい！という方々はぜひ[こちら](https://webempath.com/jpn/contact/)からご連絡をしていただければと思います。

#参考
[MDN web docs - getUserMedia](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia)
[Web Sounder](https://weblike-curtaincall.ssl-lolipop.jp/portfolio-web-sounder/webaudioapi-basic/custom)
[WebRTCハンズオン 本編](https://qiita.com/yusuke84/items/43a20e3b6c78ae9a8f6c)