From 55c5f0dadea6f40c034dbb4d1c081bf026c7d64e Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 5 Jan 2016 00:11:35 -0400 Subject: [PATCH] Cleaned up extension projects, added fluent extension methods --- .../Discord.Net.Audio.csproj | 25 ++-- src/Discord.Net.Audio/AudioExtensions.cs | 5 + src/Discord.Net.Audio/AudioService.cs | 3 - src/Discord.Net.Audio/Discord.Net.Audio.xproj | 2 +- ...EventArgs.cs => InternalFrameEventArgs.cs} | 4 +- .../InternalIsSpeakingEventArgs.cs | 14 +++ .../Net/{WebSockets => }/VoiceWebSocket.cs | 18 ++- .../Net/WebSockets/VoiceWebSocket.Events.cs | 33 ----- src/Discord.Net.Audio/Opus/Enums.cs | 49 -------- src/Discord.Net.Audio/Opus/OpusConverter.cs | 109 ++++++++++++++++ src/Discord.Net.Audio/Opus/OpusDecoder.cs | 99 +++------------ src/Discord.Net.Audio/Opus/OpusEncoder.cs | 119 ++++-------------- src/Discord.Net.Audio/Sodium.cs | 5 - src/Discord.Net.Commands/CommandExtensions.cs | 7 +- src/Discord.Net.Commands/CommandService.cs | 8 +- .../Levels/PermissionLevelExtensions.cs | 12 +- .../Userlist/BlacklistExtensions.cs | 10 +- .../Permissions/Userlist/BlacklistService.cs | 10 +- .../Permissions/Userlist/UserlistService.cs | 12 +- .../Userlist/WhitelistExtensions.cs | 10 +- .../Permissions/Userlist/WhitelistService.cs | 10 +- .../Visibility/PrivateExtensions.cs | 4 +- .../Discord.Net.Modules.csproj | 6 +- src/Discord.Net.Modules/ModuleChecker.cs | 4 +- src/Discord.Net.Modules/ModuleExtensions.cs | 20 ++- .../{ModuleType.cs => ModuleFilter.cs} | 8 +- src/Discord.Net.Modules/ModuleManager.cs | 21 ++-- src/Discord.Net.Modules/ModuleService.cs | 3 +- src/Discord.Net/Extensions.cs | 16 +++ 29 files changed, 303 insertions(+), 343 deletions(-) rename src/Discord.Net.Audio/{VoicePacketEventArgs.cs => InternalFrameEventArgs.cs} (71%) create mode 100644 src/Discord.Net.Audio/InternalIsSpeakingEventArgs.cs rename src/Discord.Net.Audio/Net/{WebSockets => }/VoiceWebSocket.cs (95%) delete mode 100644 src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.Events.cs delete mode 100644 src/Discord.Net.Audio/Opus/Enums.cs create mode 100644 src/Discord.Net.Audio/Opus/OpusConverter.cs delete mode 100644 src/Discord.Net.Audio/Sodium.cs rename src/Discord.Net.Modules/{ModuleType.cs => ModuleFilter.cs} (73%) 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;