Cleaned up extension projects, added fluent extension methods
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
src/Discord.Net.Audio/InternalIsSpeakingEventArgs.cs
Normal file
14
src/Discord.Net.Audio/InternalIsSpeakingEventArgs.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
src/Discord.Net.Audio/Opus/OpusConverter.cs
Normal file
109
src/Discord.Net.Audio/Opus/OpusConverter.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Discord.Audio
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user