【人狼やAmong Usで使える】Discordでボイスチャットを別のボイスチャットに転送する

友人とオンラインでゲームをするとき、Discordでボイスチャット（ボイチャ）をしながらゲームをすることが多いのですが、ボイチャについて、友人から以下のようなことが出来ないか相談を受けました。

・人狼やAmong Usでゲームから追放されても会議のボイチャを聞きながら脱落者同士で話をしたい
・FPSのカスタムマッチをするときに、参加者のボイチャを聞きながら観戦者同士で話をしたい

DiscordのBOTからBOTへ音声を転送できるようにすれば実現できるのではないかと考え作成することにしました。作成するにあたっては以下2つのBOTを参考にさせて頂きました。

【参考にしたBOT】
 - suzukeyさんの[discord_transfer](https://github.com/suzukey/discord_transfer)
 - Tumoi Yorozuさんの[DiscordWerewolf.ts](https://github.com/TumoiYorozu/DiscordWerewolf.ts)

# 完成イメージ
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3334373939382f61343935303364332d613863612d636132302d363964392d6238653932323666306661662e67 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945857/16166c29-8e41-f804-5ec0-d6e53ef1d857.gif)
画像は[suzukeyさんの記事](https://qiita.com/suzukey/items/87e075f43da2a714fe71)から引用しています。

# suzukeyさんのdiscord_transfer
Googleで検索をかけたところ、[suzukeyさんの記事](https://qiita.com/suzukey/items/87e075f43da2a714fe71)で[ボイス転送ができるBOT(discord_transfer)](https://github.com/suzukey/discord_transfer)が既に作られていることを知りました。
しかしながらこのBOTでは、AudioStreamが合成できないために、複数人が発話した際は1枠を奪い合い、音が途切れ途切れになってしまうことが問題点として挙げられていました。

# Tumoi YorozuさんのDiscordWerewolf.ts
更に調べていたところ、Tumoi Yorozuさんが制作されている、[Discord 上で人狼ゲームを行うためのBOT(DiscordWerewolf.ts)](https://github.com/TumoiYorozu/DiscordWerewolf.ts)に、生存者のボイチャを霊界ボイチャへ転送する機能があることを知りました。
この機能の処理を参考に、discord_transferのAudioStreamの合成処理を実装できそうだということが分かりました。

# 実装（DiscordWerewolf.tsベース）
discord_transferにAudioStreamの合成処理を実装するために、DiscordWerewolf.tsのソースコードとにらめっこしましたが、DiscordWerewolf.tsは人狼のためのBOTであるため、オーディオ関係以外にもたくさんの機能があり最初は私には理解が難しい状況でした。

そこでまずはdiscord_transferに対して不足している処理を実装するのではなく、ソースリーディングを兼ねてDiscordWerewolf.tsをベースにボイス転送の処理のみ残し、それ以外の今回は不要のため削除する方向で実現することにしました。

これでできたBOTが[DiscordVoiceTransfer.ts](https://github.com/jirno7pr/DiscordVoiceTransfer.ts)です。

# 実装（discord_transferベース）
DiscordWerewolf.tsベースのBOTを制作した過程で、discord_transferのAudioStreamが合成できない問題点の解決方法が分かりました。
以下のように実装しました。
```php:discord_transfer/modules/core.js

    //前略

    // Streamにデータを流して初期化しないと正常に動作しない
    this.connection.from.play(new Silence(), { type: "opus" })
    this.connection.to.play(new Silence(), { type: "opus" })

　　// AudioMixerの準備をする
    const mixer = new AudioMixer.Mixer({
      channels: 2,
      bitDepth: 16,
      sampleRate: 48000
    });

    this.connection.to.play(mixer, { type: 'converted' });
    this.audioMixer = mixer;

    this.connection.from.on("speaking", (user, speaking) => {
      // 転送元の誰かが発話した際のみ処理を行う
      if (speaking) {
        if (this.audioMixer == null) {
          throw "audioMixer is null";
        }else{
          // 他のStreamと合成をする
          const stream = this.connection.from.receiver.createStream(user, { mode: "pcm" });
          const standaloneInput = new AudioMixer.Input({
              channels: 2,
              bitDepth: 16,
              sampleRate: 48000,
              volume: 80
          });
          this.audioMixer.addInput(standaloneInput);
          const p = stream.pipe(standaloneInput);
          stream.on('end', () => {
            if (this.audioMixer != null) {
              //Stream終了時、各オブジェクトを破棄する
              this.audioMixer.removeInput(standaloneInput);
              standaloneInput.destroy();
              stream.destroy();
              p.destroy();
            }
          });
        }
      }
    })
```
プルリクを投げマージして頂いていますので、上記対応でdiscord_transferでも問題なくボイチャ転送ができるようになっています。
# まとめ
ボイスチャットの転送をできるDiscordBOTを作成することができました。
以下どちらもGitHubリポジトリのREADMEに使い方が載っているので、興味があれば使ってみてください。
 - [discord_transfer](https://github.com/suzukey/discord_transfer)
 - [DiscordVoiceTransfer.ts](https://github.com/jirno7pr/DiscordVoiceTransfer.ts)

参考にさせて頂いたsuzukeyさんとTumoi Yorozuさんに感謝致します。ありがとうございました。
それでは。