diff --git a/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj b/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
index 120085313..280dc619e 100644
--- a/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
+++ b/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
@@ -56,14 +56,17 @@
IAudioClient.cs
-
- Net\WebSockets\VoiceWebSocket.cs
+
+ InternalFrameEventArgs.cs
-
- Net\WebSockets\VoiceWebSocket.Events.cs
+
+ InternalIsSpeakingEventArgs.cs
-
- Opus\Enums.cs
+
+ Net\VoiceWebSocket.cs
+
+
+ Opus\OpusConverter.cs
Opus\OpusDecoder.cs
@@ -74,9 +77,6 @@
SimpleAudioClient.cs
-
- Sodium.cs
-
Sodium\SecretBox.cs
@@ -89,16 +89,9 @@
VoiceDisconnectedEventArgs.cs
-
- VoicePacketEventArgs.cs
-
-
- {1b5603b4-6f8f-4289-b945-7baae523d740}
- Discord.Net.Commands
-
{8d71a857-879a-4a10-859e-5ff824ed6688}
Discord.Net
diff --git a/src/Discord.Net.Audio/AudioExtensions.cs b/src/Discord.Net.Audio/AudioExtensions.cs
index 68ef9b203..678b48615 100644
--- a/src/Discord.Net.Audio/AudioExtensions.cs
+++ b/src/Discord.Net.Audio/AudioExtensions.cs
@@ -2,6 +2,11 @@
{
public static class AudioExtensions
{
+ public static DiscordClient UsingAudio(this DiscordClient client, AudioServiceConfig config = null)
+ {
+ client.Services.Add(new AudioService(config));
+ return client;
+ }
public static AudioService Audio(this DiscordClient client, bool required = true)
=> client.Services.Get(required);
}
diff --git a/src/Discord.Net.Audio/AudioService.cs b/src/Discord.Net.Audio/AudioService.cs
index 684dcfa17..daa5dd5b0 100644
--- a/src/Discord.Net.Audio/AudioService.cs
+++ b/src/Discord.Net.Audio/AudioService.cs
@@ -17,15 +17,12 @@ namespace Discord.Audio
public event EventHandler Connected = delegate { };
public event EventHandler Disconnected = delegate { };
- public event EventHandler PacketReceived = delegate { };
public event EventHandler UserIsSpeakingUpdated = delegate { };
private void OnConnected()
=> Connected(this, EventArgs.Empty);
private void OnDisconnected(ulong serverId, bool wasUnexpected, Exception ex)
=> Disconnected(this, new VoiceDisconnectedEventArgs(serverId, wasUnexpected, ex));
- internal void OnPacketReceived(VoicePacketEventArgs e)
- => PacketReceived(this, e);
private void OnUserIsSpeakingUpdated(User user, bool isSpeaking)
=> UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking));
diff --git a/src/Discord.Net.Audio/Discord.Net.Audio.xproj b/src/Discord.Net.Audio/Discord.Net.Audio.xproj
index 053555480..4eb480f88 100644
--- a/src/Discord.Net.Audio/Discord.Net.Audio.xproj
+++ b/src/Discord.Net.Audio/Discord.Net.Audio.xproj
@@ -7,7 +7,7 @@
dff7afe3-ca77-4109-bade-b4b49a4f6648
- Discord
+ Discord.Audio
..\..\artifacts\obj\$(MSBuildProjectName)
..\..\artifacts\bin\$(MSBuildProjectName)\
diff --git a/src/Discord.Net.Audio/VoicePacketEventArgs.cs b/src/Discord.Net.Audio/InternalFrameEventArgs.cs
similarity index 71%
rename from src/Discord.Net.Audio/VoicePacketEventArgs.cs
rename to src/Discord.Net.Audio/InternalFrameEventArgs.cs
index 1f509182b..b74dc8295 100644
--- a/src/Discord.Net.Audio/VoicePacketEventArgs.cs
+++ b/src/Discord.Net.Audio/InternalFrameEventArgs.cs
@@ -2,7 +2,7 @@
namespace Discord
{
- public class VoicePacketEventArgs : EventArgs
+ internal class InternalFrameEventArgs : EventArgs
{
public ulong UserId { get; }
public ulong ChannelId { get; }
@@ -10,7 +10,7 @@ namespace Discord
public int Offset { get; }
public int Count { get; }
- public VoicePacketEventArgs(ulong userId, ulong channelId, byte[] buffer, int offset, int count)
+ public InternalFrameEventArgs(ulong userId, ulong channelId, byte[] buffer, int offset, int count)
{
UserId = userId;
ChannelId = channelId;
diff --git a/src/Discord.Net.Audio/InternalIsSpeakingEventArgs.cs b/src/Discord.Net.Audio/InternalIsSpeakingEventArgs.cs
new file mode 100644
index 000000000..641e863f4
--- /dev/null
+++ b/src/Discord.Net.Audio/InternalIsSpeakingEventArgs.cs
@@ -0,0 +1,14 @@
+namespace Discord.Audio
+{
+ internal class InternalIsSpeakingEventArgs
+ {
+ public ulong UserId { get; }
+ public bool IsSpeaking { get; }
+
+ public InternalIsSpeakingEventArgs(ulong userId, bool isSpeaking)
+ {
+ UserId = userId;
+ IsSpeaking = isSpeaking;
+ }
+ }
+}
diff --git a/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs b/src/Discord.Net.Audio/Net/VoiceWebSocket.cs
similarity index 95%
rename from src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs
rename to src/Discord.Net.Audio/Net/VoiceWebSocket.cs
index b0534f820..c8b8ec5bf 100644
--- a/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs
+++ b/src/Discord.Net.Audio/Net/VoiceWebSocket.cs
@@ -49,6 +49,14 @@ namespace Discord.Net.WebSockets
public int Ping => _ping;
internal VoiceBuffer OutputBuffer => _sendBuffer;
+ internal event EventHandler UserIsSpeaking = delegate { };
+ internal event EventHandler FrameReceived = delegate { };
+
+ private void OnUserIsSpeaking(ulong userId, bool isSpeaking)
+ => UserIsSpeaking(this, new InternalIsSpeakingEventArgs(userId, isSpeaking));
+ internal void OnFrameReceived(ulong userId, ulong channelId, byte[] buffer, int offset, int count)
+ => FrameReceived(this, new InternalFrameEventArgs(userId, channelId, buffer, offset, count));
+
internal VoiceWebSocket(DiscordClient client, AudioClient audioClient, JsonSerializer serializer, Logger logger)
: base(client, serializer, logger)
{
@@ -58,7 +66,7 @@ namespace Discord.Net.WebSockets
_targetAudioBufferLength = _config.BufferLength / 20; //20 ms frames
_encodingBuffer = new byte[MaxOpusSize];
_ssrcMapping = new ConcurrentDictionary();
- _encoder = new OpusEncoder(48000, _config.Channels, 20, _config.Bitrate, OpusApplication.Audio);
+ _encoder = new OpusEncoder(48000, _config.Channels, 20, _config.Bitrate, OpusApplication.MusicOrMixed);
_sendBuffer = new VoiceBuffer((int)Math.Ceiling(_config.BufferLength / (double)_encoder.FrameLength), _encoder.FrameSize);
}
@@ -223,7 +231,7 @@ namespace Discord.Net.WebSockets
ulong userId;
if (_ssrcMapping.TryGetValue(ssrc, out userId))
- RaiseOnPacket(userId, Channel.Id, result, resultOffset, resultLength);
+ OnFrameReceived(userId, Channel.Id, result, resultOffset, resultLength);
}
}
}
@@ -440,7 +448,7 @@ namespace Discord.Net.WebSockets
case OpCodes.Speaking:
{
var payload = (msg.Payload as JToken).ToObject(_serializer);
- RaiseIsSpeaking(payload.UserId, payload.IsSpeaking);
+ OnUserIsSpeaking(payload.UserId, payload.IsSpeaking);
}
break;
default:
@@ -449,9 +457,9 @@ namespace Discord.Net.WebSockets
}
}
- public void SendPCMFrames(byte[] data, int bytes)
+ public void SendPCMFrames(byte[] data, int offset, int count)
{
- _sendBuffer.Push(data, bytes, CancelToken);
+ _sendBuffer.Push(data, offset, count, CancelToken);
}
public void ClearPCMFrames()
{
diff --git a/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.Events.cs b/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.Events.cs
deleted file mode 100644
index c218c7b29..000000000
--- a/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.Events.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Discord.Audio;
-using System;
-
-namespace Discord.Net.WebSockets
-{
- internal sealed class IsTalkingEventArgs : EventArgs
- {
- public readonly ulong UserId;
- public readonly bool IsSpeaking;
- internal IsTalkingEventArgs(ulong userId, bool isTalking)
- {
- UserId = userId;
- IsSpeaking = isTalking;
- }
- }
-
- public partial class VoiceWebSocket
- {
- internal event EventHandler IsSpeaking;
- private void RaiseIsSpeaking(ulong userId, bool isSpeaking)
- {
- if (IsSpeaking != null)
- IsSpeaking(this, new IsTalkingEventArgs(userId, isSpeaking));
- }
-
- internal event EventHandler OnPacket;
- internal void RaiseOnPacket(ulong userId, ulong channelId, byte[] buffer, int offset, int count)
- {
- if (OnPacket != null)
- OnPacket(this, new VoicePacketEventArgs(userId, channelId, buffer, offset, count));
- }
- }
-}
diff --git a/src/Discord.Net.Audio/Opus/Enums.cs b/src/Discord.Net.Audio/Opus/Enums.cs
deleted file mode 100644
index 2ead1de24..000000000
--- a/src/Discord.Net.Audio/Opus/Enums.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-namespace Discord.Audio.Opus
-{
- internal enum OpusCtl : int
- {
- SetBitrateRequest = 4002,
- GetBitrateRequest = 4003,
- SetInbandFECRequest = 4012,
- GetInbandFECRequest = 4013
- }
-
- /// Supported coding modes.
- internal enum OpusApplication : int
- {
- ///
- /// Gives best quality at a given bitrate for voice signals. It enhances the input signal by high-pass filtering and emphasizing formants and harmonics.
- /// Optionally it includes in-band forward error correction to protect against packet loss. Use this mode for typical VoIP applications.
- /// Because of the enhancement, even at high bitrates the output may sound different from the input.
- ///
- Voip = 2048,
- ///
- /// Gives best quality at a given bitrate for most non-voice signals like music.
- /// Use this mode for music and mixed (music/voice) content, broadcast, and applications requiring less than 15 ms of coding delay.
- ///
- Audio = 2049,
- /// Low-delay mode that disables the speech-optimized mode in exchange for slightly reduced delay.
- Restricted_LowLatency = 2051
- }
-
- internal enum OpusError : int
- {
- /// No error.
- OK = 0,
- /// One or more invalid/out of range arguments.
- BadArg = -1,
- /// The mode struct passed is invalid.
- BufferToSmall = -2,
- /// An internal error was detected.
- InternalError = -3,
- /// The compressed data passed is corrupted.
- InvalidPacket = -4,
- /// Invalid/unsupported request number.
- Unimplemented = -5,
- /// An encoder or decoder structure is invalid or already freed.
- InvalidState = -6,
- /// Memory allocation has failed.
- AllocFail = -7
- }
-
-}
diff --git a/src/Discord.Net.Audio/Opus/OpusConverter.cs b/src/Discord.Net.Audio/Opus/OpusConverter.cs
new file mode 100644
index 000000000..2a5a4f567
--- /dev/null
+++ b/src/Discord.Net.Audio/Opus/OpusConverter.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace Discord.Audio.Opus
+{
+ public enum OpusApplication : int
+ {
+ Voice = 2048,
+ MusicOrMixed = 2049,
+ LowLatency = 2051
+ }
+ public enum OpusError : int
+ {
+ OK = 0,
+ BadArg = -1,
+ BufferToSmall = -2,
+ InternalError = -3,
+ InvalidPacket = -4,
+ Unimplemented = -5,
+ InvalidState = -6,
+ AllocFail = -7
+ }
+
+ public abstract class OpusConverter : IDisposable
+ {
+ protected enum Ctl : int
+ {
+ SetBitrateRequest = 4002,
+ GetBitrateRequest = 4003,
+ SetInbandFECRequest = 4012,
+ GetInbandFECRequest = 4013
+ }
+
+#if NET45
+ [SuppressUnmanagedCodeSecurity]
+#endif
+ protected unsafe static class UnsafeNativeMethods
+ {
+ [DllImport("opus", EntryPoint = "opus_encoder_create", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr CreateEncoder(int Fs, int channels, int application, out OpusError error);
+ [DllImport("opus", EntryPoint = "opus_encoder_destroy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void DestroyEncoder(IntPtr encoder);
+ [DllImport("opus", EntryPoint = "opus_encode", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Encode(IntPtr st, byte* pcm, int frame_size, byte[] data, int max_data_bytes);
+ [DllImport("opus", EntryPoint = "opus_encoder_ctl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int EncoderCtl(IntPtr st, Ctl request, int value);
+
+ [DllImport("opus", EntryPoint = "opus_decoder_create", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr CreateDecoder(int Fs, int channels, out OpusError error);
+ [DllImport("opus", EntryPoint = "opus_decoder_destroy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void DestroyDecoder(IntPtr decoder);
+ [DllImport("opus", EntryPoint = "opus_decode", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Decode(IntPtr st, byte* data, int len, byte[] pcm, int frame_size, int decode_fec);
+ }
+
+ protected IntPtr _ptr;
+
+ /// Gets the bit rate of this converter.
+ public const int BitsPerSample = 16;
+ /// Gets the input sampling rate of this converter.
+ public int InputSamplingRate { get; }
+ /// Gets the number of channels of this converter.
+ public int InputChannels { get; }
+ /// Gets the milliseconds per frame.
+ public int FrameLength { get; }
+ /// Gets the number of samples per frame.
+ public int SamplesPerFrame { get; }
+ /// Gets the bytes per frame.
+ public int FrameSize { get; }
+ /// Gets the bytes per sample.
+ public int SampleSize { get; }
+
+ protected OpusConverter(int samplingRate, int channels, int frameLength)
+ {
+ if (samplingRate != 8000 && samplingRate != 12000 &&
+ samplingRate != 16000 && samplingRate != 24000 &&
+ samplingRate != 48000)
+ throw new ArgumentOutOfRangeException(nameof(samplingRate));
+ if (channels != 1 && channels != 2)
+ throw new ArgumentOutOfRangeException(nameof(channels));
+
+ InputSamplingRate = samplingRate;
+ InputChannels = channels;
+ FrameLength = frameLength;
+ SampleSize = (BitsPerSample / 8) * channels;
+ SamplesPerFrame = samplingRate / 1000 * FrameLength;
+ FrameSize = SamplesPerFrame * SampleSize;
+ }
+
+ #region IDisposable Support
+ private bool disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ disposedValue = true;
+ }
+ ~OpusConverter() {
+ Dispose(false);
+ }
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+ }
+}
diff --git a/src/Discord.Net.Audio/Opus/OpusDecoder.cs b/src/Discord.Net.Audio/Opus/OpusDecoder.cs
index fb5483327..9077ea9cf 100644
--- a/src/Discord.Net.Audio/Opus/OpusDecoder.cs
+++ b/src/Discord.Net.Audio/Opus/OpusDecoder.cs
@@ -1,64 +1,16 @@
using System;
-using System.Runtime.InteropServices;
-using System.Security;
namespace Discord.Audio.Opus
{
/// Opus codec wrapper.
- internal class OpusDecoder : IDisposable
+ internal class OpusDecoder : OpusConverter
{
-#if NET45
- [SuppressUnmanagedCodeSecurity]
-#endif
- private unsafe static class UnsafeNativeMethods
- {
- [DllImport("opus", EntryPoint = "opus_decoder_create", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr CreateDecoder(int Fs, int channels, out OpusError error);
- [DllImport("opus", EntryPoint = "opus_decoder_destroy", CallingConvention = CallingConvention.Cdecl)]
- public static extern void DestroyDecoder(IntPtr decoder);
- [DllImport("opus", EntryPoint = "opus_decode", CallingConvention = CallingConvention.Cdecl)]
- public static extern int Decode(IntPtr st, byte* data, int len, byte[] pcm, int frame_size, int decode_fec);
- }
-
- private readonly IntPtr _ptr;
-
- /// Gets the bit rate of the encoder.
- public const int BitRate = 16;
- /// Gets the input sampling rate of the encoder.
- public int InputSamplingRate { get; private set; }
- /// Gets the number of channels of the encoder.
- public int InputChannels { get; private set; }
- /// Gets the milliseconds per frame.
- public int FrameLength { get; private set; }
- /// Gets the number of samples per frame.
- public int SamplesPerFrame { get; private set; }
- /// Gets the bytes per sample.
- public int SampleSize { get; private set; }
- /// Gets the bytes per frame.
- public int FrameSize { get; private set; }
-
- /// Creates a new Opus decoder.
- /// Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000.
- /// Number of channels (1 or 2) in input signal.
- /// Length, in milliseconds, that each frame takes. Supported Values: 2.5, 5, 10, 20, 40, 60
- /// Coding mode.
- /// A new OpusEncoder
- public OpusDecoder(int samplingRate, int channels, int frameLength)
+ /// Creates a new Opus decoder.
+ /// Sampling rate of the input PCM (in Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000
+ /// Length, in milliseconds, of each frame. Supported Values: 2.5, 5, 10, 20, 40, or 60
+ public OpusDecoder(int samplingRate, int channels, int frameLength)
+ : base(samplingRate, channels, frameLength)
{
- if (samplingRate != 8000 && samplingRate != 12000 &&
- samplingRate != 16000 && samplingRate != 24000 &&
- samplingRate != 48000)
- throw new ArgumentOutOfRangeException(nameof(samplingRate));
- if (channels != 1 && channels != 2)
- throw new ArgumentOutOfRangeException(nameof(channels));
-
- InputSamplingRate = samplingRate;
- InputChannels = channels;
- FrameLength = frameLength;
- SampleSize = (BitRate / 8) * channels;
- SamplesPerFrame = samplingRate / 1000 * FrameLength;
- FrameSize = SamplesPerFrame * SampleSize;
-
OpusError error;
_ptr = UnsafeNativeMethods.CreateDecoder(samplingRate, channels, out error);
if (error != OpusError.OK)
@@ -69,39 +21,24 @@ namespace Discord.Audio.Opus
/// PCM samples to decode.
/// Offset of the frame in input.
/// Buffer to store the decoded frame.
- /// Length of the frame contained in output.
- public unsafe int DecodeFrame(byte[] input, int inputOffset, byte[] output)
- {
- if (disposed)
- throw new ObjectDisposedException(nameof(OpusDecoder));
-
+ public unsafe int DecodeFrame(byte[] input, int inputOffset, int inputCount, byte[] output)
+ {
int result = 0;
fixed (byte* inPtr = input)
- result = UnsafeNativeMethods.Decode(_ptr, inPtr + inputOffset, SamplesPerFrame, output, output.Length, 0);
+ result = UnsafeNativeMethods.Decode(_ptr, inPtr + inputOffset, inputCount, output, SamplesPerFrame, 0);
if (result < 0)
- throw new Exception("Decoding failed: " + ((OpusError)result).ToString());
+ throw new Exception(((OpusError)result).ToString());
return result;
}
-#region IDisposable
- private bool disposed;
- public void Dispose()
- {
- if (disposed)
- return;
-
- GC.SuppressFinalize(this);
-
- if (_ptr != IntPtr.Zero)
- UnsafeNativeMethods.DestroyDecoder(_ptr);
-
- disposed = true;
- }
- ~OpusDecoder()
- {
- Dispose();
- }
-#endregion
+ protected override void Dispose(bool disposing)
+ {
+ if (_ptr != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.DestroyDecoder(_ptr);
+ _ptr = IntPtr.Zero;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Discord.Net.Audio/Opus/OpusEncoder.cs b/src/Discord.Net.Audio/Opus/OpusEncoder.cs
index d08bb1c32..258faf481 100644
--- a/src/Discord.Net.Audio/Opus/OpusEncoder.cs
+++ b/src/Discord.Net.Audio/Opus/OpusEncoder.cs
@@ -1,76 +1,31 @@
using System;
-using System.Runtime.InteropServices;
-using System.Security;
namespace Discord.Audio.Opus
{
/// Opus codec wrapper.
- internal class OpusEncoder : IDisposable
+ internal class OpusEncoder : OpusConverter
{
-#if NET45
- [SuppressUnmanagedCodeSecurity]
-#endif
- private unsafe static class UnsafeNativeMethods
- {
- [DllImport("opus", EntryPoint = "opus_encoder_create", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr CreateEncoder(int Fs, int channels, int application, out OpusError error);
- [DllImport("opus", EntryPoint = "opus_encoder_destroy", CallingConvention = CallingConvention.Cdecl)]
- public static extern void DestroyEncoder(IntPtr encoder);
- [DllImport("opus", EntryPoint = "opus_encode", CallingConvention = CallingConvention.Cdecl)]
- public static extern int Encode(IntPtr st, byte* pcm, int frame_size, byte[] data, int max_data_bytes);
- [DllImport("opus", EntryPoint = "opus_encoder_ctl", CallingConvention = CallingConvention.Cdecl)]
- public static extern int EncoderCtl(IntPtr st, OpusCtl request, int value);
- }
-
- private readonly IntPtr _ptr;
-
- /// Gets the bit rate of the encoder.
- public const int BitsPerSample = 16;
- /// Gets the input sampling rate of the encoder.
- public int InputSamplingRate { get; }
- /// Gets the number of channels of the encoder.
- public int InputChannels { get; }
- /// Gets the milliseconds per frame.
- public int FrameLength { get; }
- /// Gets the number of samples per frame.
- public int SamplesPerFrame { get; }
- /// Gets the bytes per sample.
- public int SampleSize { get; }
- /// Gets the bytes per frame.
- public int FrameSize { get; }
- /// Gets the bit rate in kbit/s.
- public int? BitRate { get; }
+ /// Gets the bit rate in kbit/s.
+ public int? BitRate { get; }
/// Gets the coding mode of the encoder.
public OpusApplication Application { get; }
- /// Creates a new Opus encoder.
- /// Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000.
- /// Number of channels (1 or 2) in input signal.
- /// Length, in milliseconds, that each frame takes. Supported Values: 2.5, 5, 10, 20, 40, 60
- /// Bitrate (kbit/s) used for this encoder. Supported Values: 1-512. Null will use the recommended bitrate.
- /// Coding mode.
- /// A new OpusEncoder
- public OpusEncoder(int samplingRate, int channels, int frameLength, int? bitrate, OpusApplication application)
+ /// Creates a new Opus encoder.
+ /// Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000
+ /// Number of channels in input signal. Supported Values: 1 or 2
+ /// Length, in milliseconds, that each frame takes. Supported Values: 2.5, 5, 10, 20, 40, 60
+ /// Bitrate (kbit/s) used for this encoder. Supported Values: 1-512. Null will use the recommended bitrate.
+ /// Coding mode.
+ public OpusEncoder(int samplingRate, int channels, int frameLength, int? bitrate, OpusApplication application)
+ : base(samplingRate, channels, frameLength)
{
- if (samplingRate != 8000 && samplingRate != 12000 &&
- samplingRate != 16000 && samplingRate != 24000 &&
- samplingRate != 48000)
- throw new ArgumentOutOfRangeException(nameof(samplingRate));
- if (channels != 1 && channels != 2)
- throw new ArgumentOutOfRangeException(nameof(channels));
if (bitrate != null && (bitrate < 1 || bitrate > 512))
throw new ArgumentOutOfRangeException(nameof(bitrate));
- InputSamplingRate = samplingRate;
- InputChannels = channels;
- Application = application;
- FrameLength = frameLength;
- SampleSize = (BitsPerSample / 8) * channels;
- SamplesPerFrame = samplingRate / 1000 * FrameLength;
- FrameSize = SamplesPerFrame * SampleSize;
BitRate = bitrate;
+ Application = application;
- OpusError error;
+ OpusError error;
_ptr = UnsafeNativeMethods.CreateEncoder(samplingRate, channels, (int)application, out error);
if (error != OpusError.OK)
throw new InvalidOperationException($"Error occured while creating encoder: {error}");
@@ -86,59 +41,39 @@ namespace Discord.Audio.Opus
/// Buffer to store the encoded frame.
/// Length of the frame contained in outputBuffer.
public unsafe int EncodeFrame(byte[] input, int inputOffset, byte[] output)
- {
- if (disposed)
- throw new ObjectDisposedException(nameof(OpusEncoder));
-
+ {
int result = 0;
fixed (byte* inPtr = input)
result = UnsafeNativeMethods.Encode(_ptr, inPtr + inputOffset, SamplesPerFrame, output, output.Length);
if (result < 0)
- throw new Exception("Encoding failed: " + ((OpusError)result).ToString());
+ throw new Exception(((OpusError)result).ToString());
return result;
}
/// Gets or sets whether Forward Error Correction is enabled.
public void SetForwardErrorCorrection(bool value)
{
- if (disposed)
- throw new ObjectDisposedException(nameof(OpusEncoder));
-
- var result = UnsafeNativeMethods.EncoderCtl(_ptr, OpusCtl.SetInbandFECRequest, value ? 1 : 0);
+ var result = UnsafeNativeMethods.EncoderCtl(_ptr, Ctl.SetInbandFECRequest, value ? 1 : 0);
if (result < 0)
- throw new Exception("Encoder error: " + ((OpusError)result).ToString());
+ throw new Exception(((OpusError)result).ToString());
}
/// Gets or sets whether Forward Error Correction is enabled.
public void SetBitrate(int value)
{
- if (disposed)
- throw new ObjectDisposedException(nameof(OpusEncoder));
-
- var result = UnsafeNativeMethods.EncoderCtl(_ptr, OpusCtl.SetBitrateRequest, value * 1000);
+ var result = UnsafeNativeMethods.EncoderCtl(_ptr, Ctl.SetBitrateRequest, value * 1000);
if (result < 0)
- throw new Exception("Encoder error: " + ((OpusError)result).ToString());
+ throw new Exception(((OpusError)result).ToString());
}
- #region IDisposable
- private bool disposed;
- public void Dispose()
- {
- if (disposed)
- return;
-
- GC.SuppressFinalize(this);
-
- if (_ptr != IntPtr.Zero)
- UnsafeNativeMethods.DestroyEncoder(_ptr);
-
- disposed = true;
- }
- ~OpusEncoder()
- {
- Dispose();
- }
- #endregion
+ protected override void Dispose(bool disposing)
+ {
+ if (_ptr != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.DestroyEncoder(_ptr);
+ _ptr = IntPtr.Zero;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Discord.Net.Audio/Sodium.cs b/src/Discord.Net.Audio/Sodium.cs
deleted file mode 100644
index 7c98dffba..000000000
--- a/src/Discord.Net.Audio/Sodium.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Discord.Audio
-{
-}
diff --git a/src/Discord.Net.Commands/CommandExtensions.cs b/src/Discord.Net.Commands/CommandExtensions.cs
index 497ce6721..8a530583b 100644
--- a/src/Discord.Net.Commands/CommandExtensions.cs
+++ b/src/Discord.Net.Commands/CommandExtensions.cs
@@ -2,7 +2,12 @@
{
public static class CommandExtensions
{
- public static CommandService Commands(this DiscordClient client, bool required = true)
+ public static DiscordClient UsingCommands(this DiscordClient client, CommandServiceConfig config = null)
+ {
+ client.Services.Add(new CommandService(config));
+ return client;
+ }
+ public static CommandService Commands(this DiscordClient client, bool required = true)
=> client.Services.Get(required);
}
}
diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
index f54f8e3a1..d6798248a 100644
--- a/src/Discord.Net.Commands/CommandService.cs
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -21,13 +21,13 @@ namespace Discord.Commands
//Groups store all commands by their module, used for more informative help
internal IEnumerable Categories => _categories.Values;
- public event EventHandler Command = delegate { };
- public event EventHandler CommandError = delegate { };
+ public event EventHandler CommandExecuted = delegate { };
+ public event EventHandler CommandErrored = delegate { };
private void OnCommand(CommandEventArgs args)
- => Command(this, args);
+ => CommandExecuted(this, args);
private void OnCommandError(CommandErrorType errorType, CommandEventArgs args, Exception ex = null)
- => CommandError(this, new CommandErrorEventArgs(errorType, args, ex));
+ => CommandErrored(this, new CommandErrorEventArgs(errorType, args, ex));
public CommandService(CommandServiceConfig config)
{
diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs
index e2169ec7b..79cae8857 100644
--- a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs
+++ b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs
@@ -1,8 +1,16 @@
-namespace Discord.Commands.Permissions.Levels
+using System;
+
+namespace Discord.Commands.Permissions.Levels
{
public static class PermissionLevelExtensions
{
- public static CommandBuilder MinPermissions(this CommandBuilder builder, int minPermissions)
+ public static DiscordClient UsingPermissionLevels(this DiscordClient client, Func permissionResolver)
+ {
+ client.Services.Add(new PermissionLevelService(permissionResolver));
+ return client;
+ }
+
+ public static CommandBuilder MinPermissions(this CommandBuilder builder, int minPermissions)
{
builder.AddCheck(new PermissionLevelChecker(builder.Service.Client, minPermissions));
return builder;
diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs
index 6d6ee01c3..41527a50d 100644
--- a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs
+++ b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs
@@ -1,8 +1,14 @@
namespace Discord.Commands.Permissions.Userlist
{
public static class BlacklistExtensions
- {
- public static CommandBuilder UseGlobalBlacklist(this CommandBuilder builder)
+ {
+ public static DiscordClient UsingGlobalBlacklist(this DiscordClient client, params ulong[] initialUserIds)
+ {
+ client.Services.Add(new BlacklistService(initialUserIds));
+ return client;
+ }
+
+ public static CommandBuilder UseGlobalBlacklist(this CommandBuilder builder)
{
builder.AddCheck(new BlacklistChecker(builder.Service.Client));
return builder;
diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs
index 1bf376fc2..ced4c3fdc 100644
--- a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs
+++ b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs
@@ -1,17 +1,13 @@
-using System.Collections.Generic;
-
-namespace Discord.Commands.Permissions.Userlist
+namespace Discord.Commands.Permissions.Userlist
{
public class BlacklistService : UserlistService
{
- public BlacklistService(IEnumerable initialList = null)
+ public BlacklistService(params ulong[] initialList)
: base(initialList)
{
}
public bool CanRun(User user)
- {
- return !_userList.ContainsKey(user.Id);
- }
+ => !_userList.ContainsKey(user.Id);
}
}
diff --git a/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs
index dfdd82db4..da8264312 100644
--- a/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs
+++ b/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs
@@ -13,12 +13,11 @@ namespace Discord.Commands.Permissions.Userlist
public DiscordClient Client => _client;
public IEnumerable UserIds => _userList.Select(x => x.Key);
- public UserlistService(IEnumerable initialList = null)
+ public UserlistService(params ulong[] initialUserIds)
{
- if (initialList != null)
- _userList = new ConcurrentDictionary(initialList.Select(x => new KeyValuePair(x, true)));
- else
- _userList = new ConcurrentDictionary();
+ _userList = new ConcurrentDictionary();
+ for (int i = 0; i < initialUserIds.Length; i++)
+ _userList.TryAdd(initialUserIds[i], true);
}
public void Add(User user)
@@ -31,6 +30,7 @@ namespace Discord.Commands.Permissions.Userlist
{
_userList[userId] = true;
}
+
public bool Remove(User user)
{
if (user == null) throw new ArgumentNullException(nameof(user));
@@ -44,7 +44,7 @@ namespace Discord.Commands.Permissions.Userlist
return _userList.TryRemove(userId, out ignored);
}
- public void Install(DiscordClient client)
+ void IService.Install(DiscordClient client)
{
_client = client;
}
diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs
index 9076179c2..2e29ee47a 100644
--- a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs
+++ b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs
@@ -1,8 +1,14 @@
namespace Discord.Commands.Permissions.Userlist
{
public static class WhitelistExtensions
- {
- public static CommandBuilder UseGlobalWhitelist(this CommandBuilder builder)
+ {
+ public static DiscordClient UsingGlobalWhitelist(this DiscordClient client, params ulong[] initialUserIds)
+ {
+ client.Services.Add(new WhitelistService(initialUserIds));
+ return client;
+ }
+
+ public static CommandBuilder UseGlobalWhitelist(this CommandBuilder builder)
{
builder.AddCheck(new WhitelistChecker(builder.Service.Client));
return builder;
diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs
index bdf3bcad6..ae25d3fd1 100644
--- a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs
+++ b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs
@@ -1,17 +1,13 @@
-using System.Collections.Generic;
-
-namespace Discord.Commands.Permissions.Userlist
+namespace Discord.Commands.Permissions.Userlist
{
public class WhitelistService : UserlistService
{
- public WhitelistService(IEnumerable initialList = null)
+ public WhitelistService(params ulong[] initialList)
: base(initialList)
{
}
public bool CanRun(User user)
- {
- return _userList.ContainsKey(user.Id);
- }
+ => _userList.ContainsKey(user.Id);
}
}
diff --git a/src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs b/src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs
index 35e25b601..cb3579983 100644
--- a/src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs
+++ b/src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs
@@ -1,8 +1,8 @@
namespace Discord.Commands.Permissions.Visibility
{
public static class PrivateExtensions
- {
- public static CommandBuilder PrivateOnly(this CommandBuilder builder)
+ {
+ public static CommandBuilder PrivateOnly(this CommandBuilder builder)
{
builder.AddCheck(new PrivateChecker());
return builder;
diff --git a/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj b/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj
index 3ebd121f7..cab137c25 100644
--- a/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj
+++ b/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj
@@ -50,15 +50,15 @@
ModuleExtensions.cs
+
+ ModuleFilter.cs
+
ModuleManager.cs
ModuleService.cs
-
- ModuleType.cs
-
diff --git a/src/Discord.Net.Modules/ModuleChecker.cs b/src/Discord.Net.Modules/ModuleChecker.cs
index dfc7970d1..7b54c8a2d 100644
--- a/src/Discord.Net.Modules/ModuleChecker.cs
+++ b/src/Discord.Net.Modules/ModuleChecker.cs
@@ -6,7 +6,7 @@ namespace Discord.Modules
public class ModuleChecker : IPermissionChecker
{
private readonly ModuleManager _manager;
- private readonly FilterType _filterType;
+ private readonly ModuleFilter _filterType;
internal ModuleChecker(ModuleManager manager)
{
@@ -16,7 +16,7 @@ namespace Discord.Modules
public bool CanRun(Command command, User user, Channel channel, out string error)
{
- if (_filterType == FilterType.Unrestricted || _filterType == FilterType.AllowPrivate || _manager.HasChannel(channel))
+ if (_filterType == ModuleFilter.None || _filterType == ModuleFilter.AlwaysAllowPrivate || _manager.HasChannel(channel))
{
error = null;
return true;
diff --git a/src/Discord.Net.Modules/ModuleExtensions.cs b/src/Discord.Net.Modules/ModuleExtensions.cs
index d430a9325..5a0cb9abb 100644
--- a/src/Discord.Net.Modules/ModuleExtensions.cs
+++ b/src/Discord.Net.Modules/ModuleExtensions.cs
@@ -2,7 +2,25 @@
{
public static class ModuleExtensions
{
- public static ModuleService Modules(this DiscordClient client, bool required = true)
+ public static DiscordClient UsingModules(this DiscordClient client)
+ {
+ client.Services.Add(new ModuleService());
+ return client;
+ }
+ public static DiscordClient AddModule(this DiscordClient client, T instance, string name = null, ModuleFilter filter = ModuleFilter.None)
+ where T : class, IModule
+ {
+ client.Modules().Add(instance, name ?? nameof(T), filter);
+ return client;
+ }
+ public static DiscordClient AddModule(this DiscordClient client, string name = null, ModuleFilter filter = ModuleFilter.None)
+ where T : class, IModule, new()
+ {
+ client.Modules().Add(new T(), name ?? nameof(T), filter);
+ return client;
+ }
+
+ public static ModuleService Modules(this DiscordClient client, bool required = true)
=> client.Services.Get(required);
}
}
diff --git a/src/Discord.Net.Modules/ModuleType.cs b/src/Discord.Net.Modules/ModuleFilter.cs
similarity index 73%
rename from src/Discord.Net.Modules/ModuleType.cs
rename to src/Discord.Net.Modules/ModuleFilter.cs
index 215debab6..08fa09a5d 100644
--- a/src/Discord.Net.Modules/ModuleType.cs
+++ b/src/Discord.Net.Modules/ModuleFilter.cs
@@ -3,15 +3,15 @@
namespace Discord.Modules
{
[Flags]
- public enum FilterType
+ public enum ModuleFilter
{
- /// Disables the event and command filtesr.
- Unrestricted = 0x0,
+ /// Disables the event and command filters.
+ None = 0x0,
/// Uses the server whitelist to filter events and commands.
ServerWhitelist = 0x1,
/// Uses the channel whitelist to filter events and commands.
ChannelWhitelist = 0x2,
/// Enables this module in all private messages.
- AllowPrivate = 0x4
+ AlwaysAllowPrivate = 0x4
}
}
diff --git a/src/Discord.Net.Modules/ModuleManager.cs b/src/Discord.Net.Modules/ModuleManager.cs
index 7acd5669f..091529229 100644
--- a/src/Discord.Net.Modules/ModuleManager.cs
+++ b/src/Discord.Net.Modules/ModuleManager.cs
@@ -41,10 +41,7 @@ namespace Discord.Modules
public event EventHandler MessageDeleted = delegate { };
public event EventHandler MessageUpdated = delegate { };
public event EventHandler MessageReadRemotely = delegate { };
-
- private readonly DiscordClient _client;
- private readonly string _name, _id;
- private readonly FilterType _filterType;
+
private readonly bool _useServerWhitelist, _useChannelWhitelist, _allowAll, _allowPrivate;
private readonly ConcurrentDictionary _enabledServers;
private readonly ConcurrentDictionary _enabledChannels;
@@ -55,12 +52,12 @@ namespace Discord.Modules
public IModule Instance { get; }
public string Name { get; }
public string Id { get; }
- public FilterType FilterType { get; }
+ public ModuleFilter FilterType { get; }
public IEnumerable EnabledServers => _enabledServers.Select(x => x.Value);
public IEnumerable EnabledChannels => _enabledChannels.Select(x => x.Value);
- internal ModuleManager(DiscordClient client, IModule instance, string name, FilterType filterType)
+ internal ModuleManager(DiscordClient client, IModule instance, string name, ModuleFilter filterType)
{
Client = client;
Instance = instance;
@@ -70,10 +67,10 @@ namespace Discord.Modules
Id = name.ToLowerInvariant();
_lock = new AsyncLock();
- _allowAll = filterType == FilterType.Unrestricted;
- _useServerWhitelist = filterType.HasFlag(FilterType.ServerWhitelist);
- _useChannelWhitelist = filterType.HasFlag(FilterType.ChannelWhitelist);
- _allowPrivate = filterType.HasFlag(FilterType.AllowPrivate);
+ _allowAll = filterType == ModuleFilter.None;
+ _useServerWhitelist = filterType.HasFlag(ModuleFilter.ServerWhitelist);
+ _useChannelWhitelist = filterType.HasFlag(ModuleFilter.ChannelWhitelist);
+ _allowPrivate = filterType.HasFlag(ModuleFilter.AlwaysAllowPrivate);
_enabledServers = new ConcurrentDictionary();
_enabledChannels = new ConcurrentDictionary();
@@ -115,10 +112,10 @@ namespace Discord.Modules
public void CreateCommands(string prefix, Action config)
{
- var commandService = _client.Commands(true);
+ var commandService = Client.Commands(true);
commandService.CreateGroup(prefix, x =>
{
- x.Category(_name);
+ x.Category(Name);
x.AddCheck(new ModuleChecker(this));
config(x);
});
diff --git a/src/Discord.Net.Modules/ModuleService.cs b/src/Discord.Net.Modules/ModuleService.cs
index 5fc7a5dd6..29297f8d4 100644
--- a/src/Discord.Net.Modules/ModuleService.cs
+++ b/src/Discord.Net.Modules/ModuleService.cs
@@ -20,7 +20,7 @@ namespace Discord.Modules
Client = client;
}
- public void Install(T module, string name, FilterType type)
+ public T Add(T module, string name, ModuleFilter type)
where T : class, IModule
{
if (module == null) throw new ArgumentNullException(nameof(module));
@@ -33,6 +33,7 @@ namespace Discord.Modules
var manager = new ModuleManager(Client, module, name, type);
_modules.Add(module, manager);
module.Install(manager);
+ return module;
}
public ModuleManager GetManager(IModule module)
diff --git a/src/Discord.Net/Extensions.cs b/src/Discord.Net/Extensions.cs
index 0c75f361d..f92e81881 100644
--- a/src/Discord.Net/Extensions.cs
+++ b/src/Discord.Net/Extensions.cs
@@ -8,6 +8,22 @@ using System.Runtime.CompilerServices;
namespace Discord
{
+ public static class DiscordClientExtensions
+ {
+ public static DiscordClient AddService(this DiscordClient client, T instance)
+ where T : class, IService
+ {
+ client.Services.Add(instance);
+ return client;
+ }
+ public static DiscordClient AddService(this DiscordClient client)
+ where T : class, IService, new()
+ {
+ client.Services.Add(new T());
+ return client;
+ }
+ }
+
internal static class InternalExtensions
{
internal static readonly IFormatProvider _format = CultureInfo.InvariantCulture;