Concrete class prototype
This commit is contained in:
51
src/Discord.Net.WebSocket/Audio/Opus/OpusConverter.cs
Normal file
51
src/Discord.Net.WebSocket/Audio/Opus/OpusConverter.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
|
||||
namespace Discord.Audio
|
||||
{
|
||||
internal abstract class OpusConverter : IDisposable
|
||||
{
|
||||
protected IntPtr _ptr;
|
||||
|
||||
/// <summary> Gets the bit rate of this converter. </summary>
|
||||
public const int BitsPerSample = 16;
|
||||
/// <summary> Gets the bytes per sample. </summary>
|
||||
public const int SampleSize = (BitsPerSample / 8) * MaxChannels;
|
||||
/// <summary> Gets the maximum amount of channels this encoder supports. </summary>
|
||||
public const int MaxChannels = 2;
|
||||
|
||||
/// <summary> Gets the input sampling rate of this converter. </summary>
|
||||
public int SamplingRate { get; }
|
||||
/// <summary> Gets the number of samples per second for this stream. </summary>
|
||||
public int Channels { get; }
|
||||
|
||||
protected OpusConverter(int samplingRate, int channels)
|
||||
{
|
||||
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));
|
||||
|
||||
SamplingRate = samplingRate;
|
||||
Channels = channels;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/Discord.Net.WebSocket/Audio/Opus/OpusCtl.cs
Normal file
10
src/Discord.Net.WebSocket/Audio/Opus/OpusCtl.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Discord.Audio
|
||||
{
|
||||
internal enum OpusCtl : int
|
||||
{
|
||||
SetBitrateRequest = 4002,
|
||||
GetBitrateRequest = 4003,
|
||||
SetInbandFECRequest = 4012,
|
||||
GetInbandFECRequest = 4013
|
||||
}
|
||||
}
|
||||
49
src/Discord.Net.WebSocket/Audio/Opus/OpusDecoder.cs
Normal file
49
src/Discord.Net.WebSocket/Audio/Opus/OpusDecoder.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Discord.Audio
|
||||
{
|
||||
internal unsafe class OpusDecoder : OpusConverter
|
||||
{
|
||||
[DllImport("opus", EntryPoint = "opus_decoder_create", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern IntPtr CreateDecoder(int Fs, int channels, out OpusError error);
|
||||
[DllImport("opus", EntryPoint = "opus_decoder_destroy", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void DestroyDecoder(IntPtr decoder);
|
||||
[DllImport("opus", EntryPoint = "opus_decode", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int Decode(IntPtr st, byte* data, int len, byte* pcm, int max_frame_size, int decode_fec);
|
||||
|
||||
public OpusDecoder(int samplingRate, int channels)
|
||||
: base(samplingRate, channels)
|
||||
{
|
||||
OpusError error;
|
||||
_ptr = CreateDecoder(samplingRate, channels, out error);
|
||||
if (error != OpusError.OK)
|
||||
throw new Exception($"Opus Error: {error}");
|
||||
}
|
||||
|
||||
/// <summary> Produces PCM samples from Opus-encoded audio. </summary>
|
||||
/// <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>
|
||||
public unsafe int DecodeFrame(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
|
||||
{
|
||||
int result = 0;
|
||||
fixed (byte* inPtr = input)
|
||||
fixed (byte* outPtr = output)
|
||||
result = Decode(_ptr, inPtr + inputOffset, inputCount, outPtr + outputOffset, (output.Length - outputOffset) / SampleSize / MaxChannels, 0);
|
||||
|
||||
if (result < 0)
|
||||
throw new Exception($"Opus Error: {(OpusError)result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_ptr != IntPtr.Zero)
|
||||
{
|
||||
DestroyDecoder(_ptr);
|
||||
_ptr = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs
Normal file
75
src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Discord.Audio
|
||||
{
|
||||
internal unsafe class OpusEncoder : OpusConverter
|
||||
{
|
||||
[DllImport("opus", EntryPoint = "opus_encoder_create", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern IntPtr CreateEncoder(int Fs, int channels, int application, out OpusError error);
|
||||
[DllImport("opus", EntryPoint = "opus_encoder_destroy", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void DestroyEncoder(IntPtr encoder);
|
||||
[DllImport("opus", EntryPoint = "opus_encode", CallingConvention = CallingConvention.Cdecl)]
|
||||
private 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)]
|
||||
private static extern int EncoderCtl(IntPtr st, OpusCtl request, int value);
|
||||
|
||||
/// <summary> Gets the coding mode of the encoder. </summary>
|
||||
public OpusApplication Application { get; }
|
||||
|
||||
public OpusEncoder(int samplingRate, int channels, OpusApplication application = OpusApplication.MusicOrMixed)
|
||||
: base(samplingRate, channels)
|
||||
{
|
||||
Application = application;
|
||||
|
||||
OpusError error;
|
||||
_ptr = CreateEncoder(samplingRate, channels, (int)application, out error);
|
||||
if (error != OpusError.OK)
|
||||
throw new Exception($"Opus Error: {error}");
|
||||
}
|
||||
|
||||
/// <summary> Produces Opus encoded audio from PCM samples. </summary>
|
||||
/// <param name="input">PCM samples to encode.</param>
|
||||
/// <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, int inputCount, byte[] output, int outputOffset)
|
||||
{
|
||||
int result = 0;
|
||||
fixed (byte* inPtr = input)
|
||||
fixed (byte* outPtr = output)
|
||||
result = Encode(_ptr, inPtr + inputOffset, inputCount / SampleSize, outPtr + outputOffset, output.Length - outputOffset);
|
||||
|
||||
if (result < 0)
|
||||
throw new Exception($"Opus Error: {(OpusError)result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary> Gets or sets whether Forward Error Correction is enabled. </summary>
|
||||
public void SetForwardErrorCorrection(bool value)
|
||||
{
|
||||
var result = EncoderCtl(_ptr, OpusCtl.SetInbandFECRequest, value ? 1 : 0);
|
||||
if (result < 0)
|
||||
throw new Exception($"Opus Error: {(OpusError)result}");
|
||||
}
|
||||
|
||||
/// <summary> Gets or sets whether Forward Error Correction is enabled. </summary>
|
||||
public void SetBitrate(int value)
|
||||
{
|
||||
if (value < 1 || value > DiscordVoiceAPIClient.MaxBitrate)
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
|
||||
var result = EncoderCtl(_ptr, OpusCtl.SetBitrateRequest, value * 1000);
|
||||
if (result < 0)
|
||||
throw new Exception($"Opus Error: {(OpusError)result}");
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_ptr != IntPtr.Zero)
|
||||
{
|
||||
DestroyEncoder(_ptr);
|
||||
_ptr = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/Discord.Net.WebSocket/Audio/Opus/OpusError.cs
Normal file
14
src/Discord.Net.WebSocket/Audio/Opus/OpusError.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Discord.Audio
|
||||
{
|
||||
internal enum OpusError : int
|
||||
{
|
||||
OK = 0,
|
||||
BadArg = -1,
|
||||
BufferToSmall = -2,
|
||||
InternalError = -3,
|
||||
InvalidPacket = -4,
|
||||
Unimplemented = -5,
|
||||
InvalidState = -6,
|
||||
AllocFail = -7
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user