Amazon Chime SDKのアプリケーションにBGM/SE機能を追加する

5 min read読了の目安(約5000字

この記事は、こちらの記事を改変したものになります。
https://cloud.flect.co.jp/entry/2021/03/01/130602

はじめに

前回の記事では、Amazon Chime SDK for JS[1]のVoice Focusを用いてノイズ除去を行う方法をご紹介しました(参照)。今回は、さらに発展させてノイズ除去をした後、BGMやSEを追加する方法をご紹介します。これまでに何度かご紹介してきた仮想背景の音声版といったところでしょうか(参照)。

作るものは下の動画のようなものになります。大雨の中で講義をしている動画に対して、雨の音の代わりに宇宙っぽいBGMを追加しています(00:16-)。併せて背景を変えてあげれば、大雨の中にいた人があっという間に宇宙で話をしている状況に変わります。

https://youtu.be/awfXdqRoC6I

なお、今回はAmazon Chime SDKのノイズ除去機能であるVoice Focusの詳細については説明を行いません。前回の記事をご参照ください(参照)。

Amazon Chime SDK におけるAudio InputとMixing

私がAmazon Chime SDKを触り始めたのはちょうど1年くらい前からです。当時より私はAmazon Chime SDKがHTML5 MediaStreamを入力として扱える点で非常に拡張性が高く柔軟性に優れていると評価していました(参照)[2]。当然Audio InputもMedia Streamを入力として扱うことができます。

ということで、音声(マイク入力など)にBGMやSEをつけるには、Web Audio APIを使って元々の音声にBGMをmixして、そこからMediaStreamを取り出し、Amazon Chime SDKのAudio Inputとして設定してあげればよさそうです。Amazon Chime SDKでは、この処理を内部的に行ってくれるmixIntoAudioInputというAPIを提供してくれています。

image

Voice Focusとの連携における課題

さて、これを使えば一件落着!としたいところですが、実はこの方式は問題があります。mixIntoAudioInputを使う方法とVoice Focusを併用すると、Voice FocusがBGMやSEをノイズとしてきれいに除去してしまうのです。あー、さもありなん・・・。ドキュメントに詳細が記載されていないので、mixIntoAudioInputを提供するDefaultDeviceControllerのソースコードを確認してみましょう。(2021/2/26時点の最新ソース(6596622238e69c5202c3e697aeeedd54f2a0d35b))

  mixIntoAudioInput(stream: MediaStream): MediaStreamAudioSourceNode {
    <snip...>
      node = DefaultDeviceController.getAudioContext().createMediaStreamSource(stream); // <---(1)
      node.connect(this.getMediaStreamOutputNode());                                    // <---(2)
    <snip...>
  }

(1)でmixするMediaStreamの入力ノードを作成し、(2)で出力用のノードに接続しています。この出力用のノードはgetMediaStreamOutputNodeで取得しています。それではgetMediaStreamOutputNodeも見てみましょう。

  private getMediaStreamOutputNode(): AudioNode {
    return this.transform?.nodes?.start || this.getMediaStreamDestinationNode();
  }

ということで、VoiceFocusを使用している場合は、その入力ノード(transformデバイスの入力ノード)を出力用ノードとして返しています。その結果、下図のようにMixするBGMやSEなどの入力はVoice Focusの入力ノードに接続されることになります(赤線部分)。なお、VoiceFocusを使わない場合は直接DestinationNodeに接続されます。(青線部分)
image

つまり、mixIntoAudioInputで入力されたBGMやSEがVoiceFocusにノイズと認識されると除去されてしまうことがわかります。せっかく追加したBGMやSEがきれいになかったことにされてしまうということですね。これは大問題です。

この処理は、ドキュメントに記載されていない内部処理なので、今後処理が変わる可能性はあります。しかし、少なくとも現時点ではBGMの置き換えにmixIntoAudioInputを使えないですね。解決策の候補の一つとしては、DefaultDeviceControllerを継承してmixIntoAudioInputをオーバライドすることが考えられます。上記mixIntoAudioInputの(2)の部分で、this.getMediaStreamOutputNode()this.getMediaStreamDestinationNode()に置き換えて、直接出力ノードを取得してしまうという方法です。しかし、getMediaStreamDestinationNode()はprivateメソッドのため継承先では呼ぶことができません。手詰まりです。

あきらめるしかないでしょうか?おじさんを宇宙に連れて行ってあげられないのでしょうか?いや、もう少し考えてみましょう。

Amazon Chime SDKの拡張性

ここでもう一度振り返りたいのが、Audio InputとしてMedia Streamが使えるというポイントです。要はどのような加工をしてもMediaStreamとして出力できればAmazon Chime SDKに入力できます。そこで、Amazon Chime SDKに入力する前にノイズ除去とBGMのミキシングをしてしまえばいいのではないかと思います。つまり下図のような構成にします。
image

これを行うためにはノイズ除去処理機能が必要ですが、Amazon Chime SDKにはVoice Focusがあります。この機能を流用させていただくことにしましょう。前回はVoice Focusで作成したTransformDeviceを直接audioInputに指定していました。今回は、作成したTransformDeviceから出力を抽出し、Web Audio APIでBGMとMixします。そしてMixした結果(MediaStream)をaudioInputに指定に指定します。
image

デモ

下記の動画は、実際動かした結果です。ノイズ除去をした状態からBGMを追加することができていますね。

https://youtu.be/awfXdqRoC6I

リポジトリ

今回評価に用いたデモのソースコードは次のリポジトリにおいてあります。

https://github.com/w-okada/flect-chime-sdk-demo

Noise Suppression(Voice Focus)や仮想背景は画面上部のメニューバーのSettingから設定できます。また、BGM/SEはMenuを開いてBGM/SEから流すことができます。

image

また、本リポジトリのアプリケーションは、ここで紹介した機能以外にも、チャット機能やホワイトボード機能も搭載しています。 またCognito連携も実装しています。

まとめ

今回は、Amazon Chime SDK でBGM/SEを追加してみました。通常の使用方法でVoice Focusと同時使用すると、BGM/SEを除去してしまうのでVoice Focusの出力をフックしてそこにBGM/SEを追加してみました。結果は、デモでご覧いただいたとおり、うまくできたかなと思います。今後、mixIntoAudioInputが改良されてもう少し直接的にMixができるようになるかもしれませんが、今のところはこれで実現できることがわかりました。

Acknowledgments

デモはこちらの "クリエイティブ・コモンズ表示ライセンス(再利用を許可する)" の映像を使用させて頂きました。

https://www.youtube.com/watch?v=6gBtE-n8j2E

BGMはこちらのBGMを使用させていただきました。

https://otologic.jp

仮想背景にはこちらの画像を使用させていただきました。

https://www.irasutoya.com/
脚注
  1. Amazon Chime SDK for JSはAmazonが提供するビデオ会議システムをWebアプリに組み込むためのSDK。 ↩︎

  2. 他のSDKはデバイスIDを指定することが多かったです。現在は未調査ですが。 ↩︎