Adding Speech Output to Discord using C# - #1
Since i spent several days trying to figure out on how to get Speech- and Musicoutput to work on a discord bot using c# i decided to sum up my findings in a short Blog-Post. The Post is rather long therefore i split it in 2 parts.
The discord library can very easily be imported from nuget.
>dotnet add package Discord.Net.Commands
>dotnet add package Discord.Net.WebSocket
In my case this was Version 2.2.0 of the library.
Commands work very nicely but i decided to go for voice. Connecting to a voice channel is also very easy
var server =
_discordSocketClient.Guilds.FirstOrDefault(x => x.Name?.Contains("MyServerName") ?? false);
if (server != null)
{
var speechChannel =
server.Channels.FirstOrDefault(x => (x is IVoiceChannel && x.Name == "Allgemein"));
if (speechChannel is IVoiceChannel voiceChannel)
{
_logger.LogInformation($"Sending msg to {server.Name} - {speechChannel.Name}");
Works also nicely. But from ther on it gets awkward:
using (IAudioClient audioClient = await voiceChannel.ConnectAsync())
{
await audioClient.SetSpeakingAsync(true);
Boom - what then was:
Unable to load DLL 'libsodium.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
This was fixabel by using Google and i used the solution from StackOverflow, downloaded opus.dll and libsodium.dll from the links in the article (Remember: You have to rename libopus.dll to opus.dll). I put them into nativelibs/win32 and nativelibs/win64.
Adding that code int the csproj moves it to the correct location at buildtime:
<ItemGroup>
<None Update="nativelibs\win*\*.dll" CopyToPublishDirectory="Always">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
and implementing a short helper made it possible to select the correct bitness:
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace DiscordBotExample
{
public class LibOpusLoader
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
private static IntPtr _opusHandle = IntPtr.Zero;
private static IntPtr _libSodiumHandle = IntPtr.Zero;
public static void Init()
{
// the dll-loading is now available only for Windows-Plattform.
// For Linux/MAC you have to make sure that libsodium and opus codecs are installed on the system.
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
return;
}
string bitness = "win32";
if (Environment.Is64BitProcess)
{
bitness = "win64";
}
string opusDirPAth = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "nativelibs", bitness);
if (_opusHandle == IntPtr.Zero)
{
_opusHandle = LoadLibrary(Path.Combine(opusDirPAth, "opus.dll"));
}
if (_libSodiumHandle == IntPtr.Zero)
{
_libSodiumHandle = LoadLibrary(Path.Combine(opusDirPAth, "libsodium.dll"));
}
}
public static void Dispose()
{
if (_opusHandle != IntPtr.Zero)
{
FreeLibrary(_opusHandle);
}
if (_libSodiumHandle != IntPtr.Zero)
{
FreeLibrary(_libSodiumHandle);
}
}
}
}
Yeah - we can open an AudioStream now ….
using (var audioStream = audioClient.CreatePCMStream(AudioApplication.Music, voiceChannel.Bitrate)) {
// try to write something to the stream
}