Amazon Chime SDKのアプリケーションにBGM/SE機能を追加する
はじめに
前回の記事では、Amazon Chime SDK for JS[1]のVoice Focusを用いてノイズ除去を行う方法をご紹介しました(参照)。今回は、さらに発展させてノイズ除去をした後、BGMやSEを追加する方法をご紹介します。これまでに何度かご紹介してきた仮想背景の音声版といったところでしょうか(参照)。
作るものは下の動画のようなものになります。大雨の中で講義をしている動画に対して、雨の音の代わりに宇宙っぽいBGMを追加しています(00:16-)。併せて背景を変えてあげれば、大雨の中にいた人があっという間に宇宙で話をしている状況に変わります。
なお、今回は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を提供してくれています。
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に接続されます。(青線部分)
つまり、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のミキシングをしてしまえばいいのではないかと思います。つまり下図のような構成にします。
これを行うためにはノイズ除去処理機能が必要ですが、Amazon Chime SDKにはVoice Focusがあります。この機能を流用させていただくことにしましょう。前回はVoice Focusで作成したTransformDeviceを直接audioInputに指定していました。今回は、作成したTransformDeviceから出力を抽出し、Web Audio APIでBGMとMixします。そしてMixした結果(MediaStream)をaudioInputに指定に指定します。
デモ
下記の動画は、実際動かした結果です。ノイズ除去をした状態からBGMを追加することができていますね。
リポジトリ
今回評価に用いたデモのソースコードは次のリポジトリにおいてあります。
Noise Suppression(Voice Focus)や仮想背景は画面上部のメニューバーのSettingから設定できます。また、BGM/SEはMenuを開いてBGM/SEから流すことができます。
また、本リポジトリのアプリケーションは、ここで紹介した機能以外にも、チャット機能やホワイトボード機能も搭載しています。 またCognito連携も実装しています。
まとめ
今回は、Amazon Chime SDK でBGM/SEを追加してみました。通常の使用方法でVoice Focusと同時使用すると、BGM/SEを除去してしまうのでVoice Focusの出力をフックしてそこにBGM/SEを追加してみました。結果は、デモでご覧いただいたとおり、うまくできたかなと思います。今後、mixIntoAudioInputが改良されてもう少し直接的にMixができるようになるかもしれませんが、今のところはこれで実現できることがわかりました。
Acknowledgments
デモはこちらの "クリエイティブ・コモンズ表示ライセンス(再利用を許可する)" の映像を使用させて頂きました。
BGMはこちらのBGMを使用させていただきました。
仮想背景にはこちらの画像を使用させていただきました。
Discussion