Browse Source

Cleaned up extension projects, added fluent extension methods

tags/docs-0.9
RogueException 9 years ago
parent
commit
55c5f0dade
29 changed files with 303 additions and 343 deletions
  1. +9
    -16
      src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
  2. +5
    -0
      src/Discord.Net.Audio/AudioExtensions.cs
  3. +0
    -3
      src/Discord.Net.Audio/AudioService.cs
  4. +1
    -1
      src/Discord.Net.Audio/Discord.Net.Audio.xproj
  5. +2
    -2
      src/Discord.Net.Audio/InternalFrameEventArgs.cs
  6. +14
    -0
      src/Discord.Net.Audio/InternalIsSpeakingEventArgs.cs
  7. +13
    -5
      src/Discord.Net.Audio/Net/VoiceWebSocket.cs
  8. +0
    -33
      src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.Events.cs
  9. +0
    -49
      src/Discord.Net.Audio/Opus/Enums.cs
  10. +109
    -0
      src/Discord.Net.Audio/Opus/OpusConverter.cs
  11. +18
    -81
      src/Discord.Net.Audio/Opus/OpusDecoder.cs
  12. +27
    -92
      src/Discord.Net.Audio/Opus/OpusEncoder.cs
  13. +0
    -5
      src/Discord.Net.Audio/Sodium.cs
  14. +6
    -1
      src/Discord.Net.Commands/CommandExtensions.cs
  15. +4
    -4
      src/Discord.Net.Commands/CommandService.cs
  16. +10
    -2
      src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs
  17. +8
    -2
      src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs
  18. +3
    -7
      src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs
  19. +6
    -6
      src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs
  20. +8
    -2
      src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs
  21. +3
    -7
      src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs
  22. +2
    -2
      src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs
  23. +3
    -3
      src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj
  24. +2
    -2
      src/Discord.Net.Modules/ModuleChecker.cs
  25. +19
    -1
      src/Discord.Net.Modules/ModuleExtensions.cs
  26. +4
    -4
      src/Discord.Net.Modules/ModuleFilter.cs
  27. +9
    -12
      src/Discord.Net.Modules/ModuleManager.cs
  28. +2
    -1
      src/Discord.Net.Modules/ModuleService.cs
  29. +16
    -0
      src/Discord.Net/Extensions.cs

+ 9
- 16
src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj View File

@@ -56,14 +56,17 @@
<Compile Include="..\Discord.Net.Audio\IAudioClient.cs">
<Link>IAudioClient.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Audio\Net\WebSockets\VoiceWebSocket.cs">
<Link>Net\WebSockets\VoiceWebSocket.cs</Link>
<Compile Include="..\Discord.Net.Audio\InternalFrameEventArgs.cs">
<Link>InternalFrameEventArgs.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Audio\Net\WebSockets\VoiceWebSocket.Events.cs">
<Link>Net\WebSockets\VoiceWebSocket.Events.cs</Link>
<Compile Include="..\Discord.Net.Audio\InternalIsSpeakingEventArgs.cs">
<Link>InternalIsSpeakingEventArgs.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Audio\Opus\Enums.cs">
<Link>Opus\Enums.cs</Link>
<Compile Include="..\Discord.Net.Audio\Net\VoiceWebSocket.cs">
<Link>Net\VoiceWebSocket.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Audio\Opus\OpusConverter.cs">
<Link>Opus\OpusConverter.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Audio\Opus\OpusDecoder.cs">
<Link>Opus\OpusDecoder.cs</Link>
@@ -74,9 +77,6 @@
<Compile Include="..\Discord.Net.Audio\SimpleAudioClient.cs">
<Link>SimpleAudioClient.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Audio\Sodium.cs">
<Link>Sodium.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Audio\Sodium\SecretBox.cs">
<Link>Sodium\SecretBox.cs</Link>
</Compile>
@@ -89,16 +89,9 @@
<Compile Include="..\Discord.Net.Audio\VoiceDisconnectedEventArgs.cs">
<Link>VoiceDisconnectedEventArgs.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Audio\VoicePacketEventArgs.cs">
<Link>VoicePacketEventArgs.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Discord.Net.Commands.Net45\Discord.Net.Commands.csproj">
<Project>{1b5603b4-6f8f-4289-b945-7baae523d740}</Project>
<Name>Discord.Net.Commands</Name>
</ProjectReference>
<ProjectReference Include="..\Discord.Net.Net45\Discord.Net.csproj">
<Project>{8d71a857-879a-4a10-859e-5ff824ed6688}</Project>
<Name>Discord.Net</Name>


+ 5
- 0
src/Discord.Net.Audio/AudioExtensions.cs View File

@@ -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<AudioService>(required);
}


+ 0
- 3
src/Discord.Net.Audio/AudioService.cs View File

@@ -17,15 +17,12 @@ namespace Discord.Audio

public event EventHandler Connected = delegate { };
public event EventHandler<VoiceDisconnectedEventArgs> Disconnected = delegate { };
public event EventHandler<VoicePacketEventArgs> PacketReceived = delegate { };
public event EventHandler<UserIsSpeakingEventArgs> 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));



+ 1
- 1
src/Discord.Net.Audio/Discord.Net.Audio.xproj View File

@@ -7,7 +7,7 @@
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>dff7afe3-ca77-4109-bade-b4b49a4f6648</ProjectGuid>
<RootNamespace>Discord</RootNamespace>
<RootNamespace>Discord.Audio</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>


src/Discord.Net.Audio/VoicePacketEventArgs.cs → src/Discord.Net.Audio/InternalFrameEventArgs.cs View File

@@ -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;

+ 14
- 0
src/Discord.Net.Audio/InternalIsSpeakingEventArgs.cs View File

@@ -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;
}
}
}

src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs → src/Discord.Net.Audio/Net/VoiceWebSocket.cs View File

@@ -49,6 +49,14 @@ namespace Discord.Net.WebSockets
public int Ping => _ping;
internal VoiceBuffer OutputBuffer => _sendBuffer;

internal event EventHandler<InternalIsSpeakingEventArgs> UserIsSpeaking = delegate { };
internal event EventHandler<InternalFrameEventArgs> 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<uint, ulong>();
_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<SpeakingEvent>(_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()
{

+ 0
- 33
src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.Events.cs View File

@@ -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<IsTalkingEventArgs> IsSpeaking;
private void RaiseIsSpeaking(ulong userId, bool isSpeaking)
{
if (IsSpeaking != null)
IsSpeaking(this, new IsTalkingEventArgs(userId, isSpeaking));
}

internal event EventHandler<VoicePacketEventArgs> 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));
}
}
}

+ 0
- 49
src/Discord.Net.Audio/Opus/Enums.cs View File

@@ -1,49 +0,0 @@
namespace Discord.Audio.Opus
{
internal enum OpusCtl : int
{
SetBitrateRequest = 4002,
GetBitrateRequest = 4003,
SetInbandFECRequest = 4012,
GetInbandFECRequest = 4013
}

/// <summary>Supported coding modes.</summary>
internal enum OpusApplication : int
{
/// <summary>
/// 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.
/// </summary>
Voip = 2048,
/// <summary>
/// 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.
/// </summary>
Audio = 2049,
/// <summary> Low-delay mode that disables the speech-optimized mode in exchange for slightly reduced delay. </summary>
Restricted_LowLatency = 2051
}

internal enum OpusError : int
{
/// <summary> No error. </summary>
OK = 0,
/// <summary> One or more invalid/out of range arguments. </summary>
BadArg = -1,
/// <summary> The mode struct passed is invalid. </summary>
BufferToSmall = -2,
/// <summary> An internal error was detected. </summary>
InternalError = -3,
/// <summary> The compressed data passed is corrupted. </summary>
InvalidPacket = -4,
/// <summary> Invalid/unsupported request number. </summary>
Unimplemented = -5,
/// <summary> An encoder or decoder structure is invalid or already freed. </summary>
InvalidState = -6,
/// <summary> Memory allocation has failed. </summary>
AllocFail = -7
}

}

+ 109
- 0
src/Discord.Net.Audio/Opus/OpusConverter.cs View File

@@ -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;

/// <summary> Gets the bit rate of this converter. </summary>
public const int BitsPerSample = 16;
/// <summary> Gets the input sampling rate of this converter. </summary>
public int InputSamplingRate { get; }
/// <summary> Gets the number of channels of this converter. </summary>
public int InputChannels { get; }
/// <summary> Gets the milliseconds per frame. </summary>
public int FrameLength { get; }
/// <summary> Gets the number of samples per frame. </summary>
public int SamplesPerFrame { get; }
/// <summary> Gets the bytes per frame. </summary>
public int FrameSize { get; }
/// <summary> Gets the bytes per sample. </summary>
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
}
}

+ 18
- 81
src/Discord.Net.Audio/Opus/OpusDecoder.cs View File

@@ -1,64 +1,16 @@
using System;
using System.Runtime.InteropServices;
using System.Security;

namespace Discord.Audio.Opus
{
/// <summary> Opus codec wrapper. </summary>
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;

/// <summary> Gets the bit rate of the encoder. </summary>
public const int BitRate = 16;
/// <summary> Gets the input sampling rate of the encoder. </summary>
public int InputSamplingRate { get; private set; }
/// <summary> Gets the number of channels of the encoder. </summary>
public int InputChannels { get; private set; }
/// <summary> Gets the milliseconds per frame. </summary>
public int FrameLength { get; private set; }
/// <summary> Gets the number of samples per frame. </summary>
public int SamplesPerFrame { get; private set; }
/// <summary> Gets the bytes per sample. </summary>
public int SampleSize { get; private set; }
/// <summary> Gets the bytes per frame. </summary>
public int FrameSize { get; private set; }

/// <summary> Creates a new Opus decoder. </summary>
/// <param name="samplingRate">Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000.</param>
/// <param name="channels">Number of channels (1 or 2) in input signal.</param>
/// <param name="frameLength">Length, in milliseconds, that each frame takes. Supported Values: 2.5, 5, 10, 20, 40, 60</param>
/// <param name="application">Coding mode.</param>
/// <returns>A new <c>OpusEncoder</c></returns>
public OpusDecoder(int samplingRate, int channels, int frameLength)
/// <summary> Creates a new Opus decoder. </summary>
/// <param name="samplingRate">Sampling rate of the input PCM (in Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000</param>
/// <param name="frameLength">Length, in milliseconds, of each frame. Supported Values: 2.5, 5, 10, 20, 40, or 60</param>
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
/// <param name="input">PCM samples to decode.</param>
/// <param name="inputOffset">Offset of the frame in input.</param>
/// <param name="output">Buffer to store the decoded frame.</param>
/// <returns>Length of the frame contained in output.</returns>
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;
}
}
}
}

+ 27
- 92
src/Discord.Net.Audio/Opus/OpusEncoder.cs View File

@@ -1,76 +1,31 @@
using System;
using System.Runtime.InteropServices;
using System.Security;

namespace Discord.Audio.Opus
{
/// <summary> Opus codec wrapper. </summary>
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;

/// <summary> Gets the bit rate of the encoder. </summary>
public const int BitsPerSample = 16;
/// <summary> Gets the input sampling rate of the encoder. </summary>
public int InputSamplingRate { get; }
/// <summary> Gets the number of channels of the encoder. </summary>
public int InputChannels { get; }
/// <summary> Gets the milliseconds per frame. </summary>
public int FrameLength { get; }
/// <summary> Gets the number of samples per frame. </summary>
public int SamplesPerFrame { get; }
/// <summary> Gets the bytes per sample. </summary>
public int SampleSize { get; }
/// <summary> Gets the bytes per frame. </summary>
public int FrameSize { get; }
/// <summary> Gets the bit rate in kbit/s. </summary>
public int? BitRate { get; }
/// <summary> Gets the bit rate in kbit/s. </summary>
public int? BitRate { get; }
/// <summary> Gets the coding mode of the encoder. </summary>
public OpusApplication Application { get; }

/// <summary> Creates a new Opus encoder. </summary>
/// <param name="samplingRate">Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000.</param>
/// <param name="channels">Number of channels (1 or 2) in input signal.</param>
/// <param name="frameLength">Length, in milliseconds, that each frame takes. Supported Values: 2.5, 5, 10, 20, 40, 60</param>
/// <param name="bitrate">Bitrate (kbit/s) used for this encoder. Supported Values: 1-512. Null will use the recommended bitrate. </param>
/// <param name="application">Coding mode.</param>
/// <returns>A new <c>OpusEncoder</c></returns>
public OpusEncoder(int samplingRate, int channels, int frameLength, int? bitrate, OpusApplication application)
/// <summary> Creates a new Opus encoder. </summary>
/// <param name="samplingRate">Sampling rate of the input signal (Hz). Supported Values: 8000, 12000, 16000, 24000, or 48000</param>
/// <param name="channels">Number of channels in input signal. Supported Values: 1 or 2</param>
/// <param name="frameLength">Length, in milliseconds, that each frame takes. Supported Values: 2.5, 5, 10, 20, 40, 60</param>
/// <param name="bitrate">Bitrate (kbit/s) used for this encoder. Supported Values: 1-512. Null will use the recommended bitrate. </param>
/// <param name="application">Coding mode.</param>
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
/// <param name="output">Buffer to store the encoded frame.</param>
/// <returns>Length of the frame contained in outputBuffer.</returns>
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;
}

/// <summary> Gets or sets whether Forward Error Correction is enabled. </summary>
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());
}

/// <summary> Gets or sets whether Forward Error Correction is enabled. </summary>
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;
}
}
}
}

+ 0
- 5
src/Discord.Net.Audio/Sodium.cs View File

@@ -1,5 +0,0 @@
using System.Runtime.InteropServices;

namespace Discord.Audio
{
}

+ 6
- 1
src/Discord.Net.Commands/CommandExtensions.cs View File

@@ -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<CommandService>(required);
}
}

+ 4
- 4
src/Discord.Net.Commands/CommandService.cs View File

@@ -21,13 +21,13 @@ namespace Discord.Commands
//Groups store all commands by their module, used for more informative help
internal IEnumerable<CommandMap> Categories => _categories.Values;

public event EventHandler<CommandEventArgs> Command = delegate { };
public event EventHandler<CommandErrorEventArgs> CommandError = delegate { };
public event EventHandler<CommandEventArgs> CommandExecuted = delegate { };
public event EventHandler<CommandErrorEventArgs> 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)
{


+ 10
- 2
src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs View File

@@ -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<User, Channel, int> 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;


+ 8
- 2
src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs View File

@@ -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;


+ 3
- 7
src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs View File

@@ -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<ulong> initialList = null)
public BlacklistService(params ulong[] initialList)
: base(initialList)
{
}

public bool CanRun(User user)
{
return !_userList.ContainsKey(user.Id);
}
=> !_userList.ContainsKey(user.Id);
}
}

+ 6
- 6
src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs View File

@@ -13,12 +13,11 @@ namespace Discord.Commands.Permissions.Userlist
public DiscordClient Client => _client;
public IEnumerable<ulong> UserIds => _userList.Select(x => x.Key);

public UserlistService(IEnumerable<ulong> initialList = null)
public UserlistService(params ulong[] initialUserIds)
{
if (initialList != null)
_userList = new ConcurrentDictionary<ulong, bool>(initialList.Select(x => new KeyValuePair<ulong, bool>(x, true)));
else
_userList = new ConcurrentDictionary<ulong, bool>();
_userList = new ConcurrentDictionary<ulong, bool>();
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;
}


+ 8
- 2
src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs View File

@@ -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;


+ 3
- 7
src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs View File

@@ -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<ulong> initialList = null)
public WhitelistService(params ulong[] initialList)
: base(initialList)
{
}

public bool CanRun(User user)
{
return _userList.ContainsKey(user.Id);
}
=> _userList.ContainsKey(user.Id);
}
}

+ 2
- 2
src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs View File

@@ -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;


+ 3
- 3
src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj View File

@@ -50,15 +50,15 @@
<Compile Include="..\Discord.Net.Modules\ModuleExtensions.cs">
<Link>ModuleExtensions.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Modules\ModuleFilter.cs">
<Link>ModuleFilter.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Modules\ModuleManager.cs">
<Link>ModuleManager.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Modules\ModuleService.cs">
<Link>ModuleService.cs</Link>
</Compile>
<Compile Include="..\Discord.Net.Modules\ModuleType.cs">
<Link>ModuleType.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>


+ 2
- 2
src/Discord.Net.Modules/ModuleChecker.cs View File

@@ -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;


+ 19
- 1
src/Discord.Net.Modules/ModuleExtensions.cs View File

@@ -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<T>(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<T>(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<ModuleService>(required);
}
}

src/Discord.Net.Modules/ModuleType.cs → src/Discord.Net.Modules/ModuleFilter.cs View File

@@ -3,15 +3,15 @@
namespace Discord.Modules
{
[Flags]
public enum FilterType
public enum ModuleFilter
{
/// <summary> Disables the event and command filtesr. </summary>
Unrestricted = 0x0,
/// <summary> Disables the event and command filters. </summary>
None = 0x0,
/// <summary> Uses the server whitelist to filter events and commands. </summary>
ServerWhitelist = 0x1,
/// <summary> Uses the channel whitelist to filter events and commands. </summary>
ChannelWhitelist = 0x2,
/// <summary> Enables this module in all private messages. </summary>
AllowPrivate = 0x4
AlwaysAllowPrivate = 0x4
}
}

+ 9
- 12
src/Discord.Net.Modules/ModuleManager.cs View File

@@ -41,10 +41,7 @@ namespace Discord.Modules
public event EventHandler<MessageEventArgs> MessageDeleted = delegate { };
public event EventHandler<MessageEventArgs> MessageUpdated = delegate { };
public event EventHandler<MessageEventArgs> 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<ulong, Server> _enabledServers;
private readonly ConcurrentDictionary<ulong, Channel> _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<Server> EnabledServers => _enabledServers.Select(x => x.Value);
public IEnumerable<Channel> 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<ulong, Server>();
_enabledChannels = new ConcurrentDictionary<ulong, Channel>();
@@ -115,10 +112,10 @@ namespace Discord.Modules

public void CreateCommands(string prefix, Action<CommandGroupBuilder> 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);
});


+ 2
- 1
src/Discord.Net.Modules/ModuleService.cs View File

@@ -20,7 +20,7 @@ namespace Discord.Modules
Client = client;
}

public void Install<T>(T module, string name, FilterType type)
public T Add<T>(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)


+ 16
- 0
src/Discord.Net/Extensions.cs View File

@@ -8,6 +8,22 @@ using System.Runtime.CompilerServices;

namespace Discord
{
public static class DiscordClientExtensions
{
public static DiscordClient AddService<T>(this DiscordClient client, T instance)
where T : class, IService
{
client.Services.Add(instance);
return client;
}
public static DiscordClient AddService<T>(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;


Loading…
Cancel
Save