検索してもなかなか出ないしめっちゃハマったので書きます。
今流行のDiscordのBotをめちゃくちゃ簡単に作れる「Discord.Net」という神ライブラリがあるのですが、
それをつかってボイスチャンネルにmp3を流す方法です。
Discord.Netの基本的なことはここを読むとわかりやすいです。
#環境
- Windows10
- Visual Studio
- Discord.Net
手順
- Discord.NetとDiscord.Net.AudioをNugetで検索して入れる
- 必要な2つのDLLを入れる(重要)
- NAudioもNugetから入れる
- サンプルコードの通りに書く
なお、再生するファイルはmp3です。
参考
Voice — Discord.Net 0.9.4 documentation
ここに全部載ってた。英語が読めなかった(小並)
DLLのダウンロード
Nugetを使うライブラリの方はいいとして、それとは別にexeファイルと同じ場所(デバッグ中ならbin/DEBUG)に入れるべきDLLが2つあります。
↓画像
↑の矢印のところのリンクをクリックして、GitHubのDownloadボタンを押してダウンロードします。
DLした「libsodium.dll」と「opus.dll」をexeファイルと同じ場所にコピーしてください。
コード
ClientがAudioを使うよという宣言
var _client = new DiscordClient();
_client.UsingAudio(x =>
{
x.Mode = AudioMode.Outgoing;
});
これを使う前にどこかで実行しておいてください。
ボイスチャンネルに参加(Join)
var voiceClient = await _client.GetService<AudioService>().Join(voiceChannel);
このvoiceClientを使って音を送信します。voiceChannel
は、例えばMessageEventArgsの.User.VoiceChannel
にアクセスすると、そのユーザーの参加しているボイスチャンネルが取得できます。
たとえばこう
_client.MessageReceived += (s,e) => { var vc = e.User.VoiceChannel;};
NAudioで音声のバイトデータを送る
さっきのページからの引用です。
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については知らなかったのでそのままコピペして使いました。
まとめたクラス
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のダウンロードを忘れないこと!
あとは公式のページを読む!