DiscordのBotでボイスチャンネルに音楽を流す

検索してもなかなか出ないしめっちゃハマったので書きます。
今流行のDiscordのBotをめちゃくちゃ簡単に作れる「Discord.Net」という神ライブラリがあるのですが、
それをつかってボイスチャンネルにmp3を流す方法です。
Discord.Netの基本的なことは[ここ](http://rtd.discord.foxbot.me/en/legacy/getting_started.html)を読むとわかりやすいです。

#環境
- Windows10
- Visual Studio
- Discord.Net

# 手順
- Discord.NetとDiscord.Net.AudioをNugetで検索して入れる
- **必要な2つのDLL**を入れる(重要)
- NAudioもNugetから入れる
- サンプルコードの通りに書く

なお、再生するファイルはmp3です。

# 参考
[Voice — Discord.Net 0.9.4 documentation](http://rtd.discord.foxbot.me/en/legacy/features/voice.html)

ここに全部載ってた。英語が読めなかった(小並）

# DLLのダウンロード
Nugetを使うライブラリの方はいいとして、それとは別にexeファイルと同じ場所（デバッグ中ならbin/DEBUG）に入れるべきDLLが2つあります。
↓画像
![SS_NoName_20161218175237_No00.jpg](https://qiita-image-store.s3.amazonaws.com/0/47220/09fe52dc-cbca-45d3-0c46-dcd978fcf784.jpeg)
↑の矢印のところのリンクをクリックして、GitHubのDownloadボタンを押してダウンロードします。
DLした[「libsodium.dll」](https://github.com/RogueException/Discord.Net/blob/master/src/Discord.Net.Audio/libsodium.dll)と[「opus.dll」](https://github.com/RogueException/Discord.Net/blob/master/src/Discord.Net.Audio/opus.dll)をexeファイルと同じ場所にコピーしてください。

# コード
## ClientがAudioを使うよという宣言

```C#
var _client = new DiscordClient();

_client.UsingAudio(x => 
{
        x.Mode = AudioMode.Outgoing;
});
```
これを使う前にどこかで実行しておいてください。

## ボイスチャンネルに参加（Join）

```C#
var voiceClient = await _client.GetService<AudioService>().Join(voiceChannel);
```
このvoiceClientを使って音を送信します。`voiceChannel`は、例えばMessageEventArgsの`.User.VoiceChannel`にアクセスすると、そのユーザーの参加しているボイスチャンネルが取得できます。
たとえばこう

```C#
_client.MessageReceived += (s,e) => { var vc = e.User.VoiceChannel;};
```

## NAudioで音声のバイトデータを送る
さっきのページからの引用です。

```C#
using NAudio;
using NAudio.Wave;
using NAudio.CoreAudioApi;

public void SendAudio(string filePath)
{
        var channelCount = _client.GetService<AudioService>().Config.Channels; // Get the number of AudioChannels our AudioService has been configured to use.
        var OutFormat = new WaveFormat(48000, 16, channelCount); // Create a new Output Format, using the spec that Discord will accept, and with the number of channels that our client supports.
        using (var MP3Reader = new Mp3FileReader(filePath)) // Create a new Disposable MP3FileReader, to read audio from the filePath parameter
        using (var resampler = new MediaFoundationResampler(MP3Reader, OutFormat)) // Create a Disposable Resampler, which will convert the read MP3 data to PCM, using our Output Format
        {
                resampler.ResamplerQuality = 60; // Set the quality of the resampler to 60, the highest quality
                int blockSize = OutFormat.AverageBytesPerSecond / 50; // Establish the size of our AudioBuffer
                byte[] buffer = new byte[blockSize];
                int byteCount;

                while((byteCount = resampler.Read(buffer, 0, blockSize)) > 0) // Read audio into our buffer, and keep a loop open while data is present
                {
                        if (byteCount < blockSize)
                        {
                                // Incomplete Frame
                                for (int i = byteCount; i < blockSize; i++)
                                        buffer[i] = 0;
                        }
                        _vClient.Send(buffer, 0, blockSize); // Send the buffer to Discord
                }
        }

}
```
NAudioについては知らなかったのでそのままコピペして使いました。

## まとめたクラス

```C#
using Discord;
using Discord.Audio;
using System;
using System.Threading.Tasks;
using NAudio.Wave;

namespace SinobigamiBot
{
    class VoiceSample
    {
        private DiscordClient Client { get; set; }
        public IAudioClient VoiceClient { get; set; }

        public VoiceSample(DiscordClient client)
        {
            Client = client;
        }

        /// <summary>
        /// これを呼び出して使う
        /// </summary>
        /// <returns></returns>
        public async Task SendAudio(Channel vChannel, string filepath)
        {
            await JoinChannel(vChannel);
            SendAudio(filepath);
        }

        private async Task JoinChannel(Channel vChannel)
        {
            VoiceClient = await Client.GetService<AudioService>().Join(vChannel);

        }

        private void SendAudio(string filepath)
        {
            if (!System.IO.File.Exists(filepath))
                throw new Exception("not found!!!!" + filepath);

            var channelCount = Client.GetService<AudioService>().Config.Channels; // Get the number of AudioChannels our AudioService has been configured to use.
            var OutFormat = new WaveFormat(48000, 16, channelCount); // Create a new Output Format, using the spec that Discord will accept, and with the number of channels that our client supports.
            using (var MP3Reader = new Mp3FileReader(filepath)) // Create a new Disposable MP3FileReader, to read audio from the filePath parameter
            using (var resampler = new MediaFoundationResampler(MP3Reader, OutFormat)) // Create a Disposable Resampler, which will convert the read MP3 data to PCM, using our Output Format
            {
                resampler.ResamplerQuality = 60; // Set the quality of the resampler to 60, the highest quality
                int blockSize = OutFormat.AverageBytesPerSecond / 50; // Establish the size of our AudioBuffer
                byte[] buffer = new byte[blockSize];
                int byteCount;

                while ((byteCount = resampler.Read(buffer, 0, blockSize)) > 0) // Read audio into our buffer, and keep a loop open while data is present
                {
                    if (byteCount < blockSize)
                    {
                        // Incomplete Frame
                        for (int i = byteCount; i < blockSize; i++)
                            buffer[i] = 0;
                    }
                    VoiceClient.Send(buffer, 0, blockSize); // Send the buffer to Discord
                }
            }

        }
    }
}
```
インスタンスを生成して`SendAudio`に引数を渡せば再生できます！

# まとめ
***2つのDLL***のダウンロードを忘れないこと！
あとは公式のページを読む！