Decompiled source of OpusDotNet v1.0.3

BepInEx/core/OpusDotNet.dll

Decompiled 5 hours ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("OpusDotNet")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("OpusDotNet")]
[assembly: AssemblyTitle("OpusDotNet")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace OpusDotNet;

internal static class API
{
	[DllImport("opus", CallingConvention = CallingConvention.Cdecl)]
	public static extern SafeEncoderHandle opus_encoder_create(int Fs, int channels, int application, out int error);

	[DllImport("opus", CallingConvention = CallingConvention.Cdecl)]
	[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
	public static extern void opus_encoder_destroy(IntPtr st);

	[DllImport("opus", CallingConvention = CallingConvention.Cdecl)]
	public static extern int opus_encode(SafeEncoderHandle st, IntPtr pcm, int frame_size, IntPtr data, int max_data_bytes);

	[DllImport("opus", CallingConvention = CallingConvention.Cdecl)]
	public static extern int opus_encoder_ctl(SafeEncoderHandle st, int request, out int value);

	[DllImport("opus", CallingConvention = CallingConvention.Cdecl)]
	public static extern int opus_encoder_ctl(SafeEncoderHandle st, int request, int value);

	[DllImport("opus", CallingConvention = CallingConvention.Cdecl)]
	public static extern SafeDecoderHandle opus_decoder_create(int Fs, int channels, out int error);

	[DllImport("opus", CallingConvention = CallingConvention.Cdecl)]
	[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
	public static extern void opus_decoder_destroy(IntPtr st);

	[DllImport("opus", CallingConvention = CallingConvention.Cdecl)]
	public static extern int opus_decode(SafeDecoderHandle st, IntPtr data, int len, IntPtr pcm, int frame_size, int decode_fec);

	public static int GetSampleCount(double frameSize, int sampleRate)
	{
		return (int)(frameSize * (double)sampleRate / 1000.0);
	}

	public static int GetPCMLength(int samples, int channels)
	{
		return samples * channels * 2;
	}

	public static double GetFrameSize(int pcmLength, int sampleRate, int channels)
	{
		return (double)pcmLength / (double)sampleRate / (double)channels / 2.0 * 1000.0;
	}

	public static void ThrowIfError(int result)
	{
		if (result < 0)
		{
			throw new OpusException(result);
		}
	}
}
public enum Application
{
	VoIP = 2048,
	Audio = 2049,
	RestrictedLowDelay = 2051
}
public enum Bandwidth
{
	NarrowBand = 1101,
	MediumBand,
	WideBand,
	SuperWideBand,
	FullBand
}
internal enum Control
{
	SetBitrate = 4002,
	SetMaxBandwidth = 4004,
	GetMaxBandwidth = 4005,
	SetVBR = 4006,
	GetVBR = 4007,
	SetComplexity = 4010,
	GetComplexity = 4011,
	SetInbandFEC = 4012,
	GetInbandFEC = 4013,
	SetPacketLossPerc = 4014,
	GetPacketLossPerc = 4015,
	SetDTX = 4016,
	GetDTX = 4017,
	SetForceChannels = 4022,
	GetForceChannels = 4023
}
public enum ForceChannels
{
	None = -1000,
	Mono = 1,
	Stereo = 2
}
public class OpusDecoder : IDisposable
{
	private readonly SafeDecoderHandle _handle;

	private readonly int _samples;

	private readonly int _pcmLength;

	private bool _fec;

	[Obsolete("This property was used for the old decode method and is deprecated, please use the new decode method instead.")]
	public double? FrameSize { get; }

	public int SampleRate { get; }

	public int Channels { get; }

	[Obsolete("This property was used for the old decode method and is deprecated, please use the new decode method instead.")]
	public bool FEC
	{
		get
		{
			return _fec;
		}
		set
		{
			if (!FrameSize.HasValue)
			{
				throw new InvalidOperationException("A frame size has to be specified in the constructor for FEC to work.");
			}
			_fec = value;
		}
	}

	public OpusDecoder()
		: this(60.0, 48000, 2, frameSizeWasSpecified: false)
	{
	}

	[Obsolete("This constructor was used for the old decode method and is deprecated, please use the new decode method instead.")]
	public OpusDecoder(double frameSize)
		: this(frameSize, 48000, 2, frameSizeWasSpecified: true)
	{
	}

	public OpusDecoder(int sampleRate, int channels)
		: this(60.0, sampleRate, channels, frameSizeWasSpecified: false)
	{
	}

	[Obsolete("This constructor was used for the old decode method and is deprecated, please use the new decode method instead.")]
	public OpusDecoder(double frameSize, int sampleRate, int channels)
		: this(frameSize, sampleRate, channels, frameSizeWasSpecified: true)
	{
	}

	private OpusDecoder(double frameSize, int sampleRate, int channels, bool frameSizeWasSpecified)
	{
		if (frameSize != 2.5 && frameSize != 5.0 && frameSize != 10.0 && frameSize != 20.0 && frameSize != 40.0 && frameSize != 60.0)
		{
			throw new ArgumentException("Value must be one of the following: 2.5, 5, 10, 20, 40 or 60.", "frameSize");
		}
		switch (sampleRate)
		{
		default:
			throw new ArgumentException("Value must be one of the following: 8000, 12000, 16000, 24000 or 48000.", "sampleRate");
		case 8000:
		case 12000:
		case 16000:
		case 24000:
		case 48000:
		{
			if (channels < 1 || channels > 2)
			{
				throw new ArgumentOutOfRangeException("channels", "Value must be between 1 and 2.");
			}
			if (frameSizeWasSpecified)
			{
				FrameSize = frameSize;
			}
			SampleRate = sampleRate;
			Channels = channels;
			_samples = API.GetSampleCount(frameSize, sampleRate);
			_pcmLength = API.GetPCMLength(_samples, channels);
			_handle = API.opus_decoder_create(sampleRate, channels, out var error);
			API.ThrowIfError(error);
			break;
		}
		}
	}

	[Obsolete("This method is deprecated, please use the new decode method instead.")]
	public unsafe byte[] Decode(byte[] opusBytes, int length, out int decodedLength)
	{
		if (opusBytes == null && !FEC)
		{
			throw new ArgumentNullException("opusBytes", "Value cannot be null when FEC is disabled.");
		}
		if (length < 0 && (!FEC || opusBytes != null))
		{
			throw new ArgumentOutOfRangeException("length", "Value cannot be negative when opusBytes is not null or FEC is disabled.");
		}
		if (opusBytes != null && opusBytes.Length < length)
		{
			throw new ArgumentOutOfRangeException("length", "Value cannot be greater than the length of opusBytes.");
		}
		ThrowIfDisposed();
		byte[] array = new byte[_pcmLength];
		int num;
		fixed (byte* ptr = opusBytes)
		{
			fixed (byte* ptr2 = array)
			{
				IntPtr data = (IntPtr)ptr;
				IntPtr pcm = (IntPtr)ptr2;
				num = ((opusBytes == null) ? API.opus_decode(_handle, IntPtr.Zero, 0, pcm, _samples, FEC ? 1 : 0) : API.opus_decode(_handle, data, length, pcm, _samples, 0));
			}
		}
		API.ThrowIfError(num);
		decodedLength = num * Channels * 2;
		return array;
	}

	public unsafe int Decode(byte[] opusBytes, int opusLength, byte[] pcmBytes, int pcmLength)
	{
		if (opusLength < 0 && opusBytes != null)
		{
			throw new ArgumentOutOfRangeException("opusLength", "Value cannot be negative when opusBytes is not null.");
		}
		if (opusBytes != null && opusBytes.Length < opusLength)
		{
			throw new ArgumentOutOfRangeException("opusLength", "Value cannot be greater than the length of opusBytes.");
		}
		if (pcmBytes == null)
		{
			throw new ArgumentNullException("pcmBytes");
		}
		if (pcmLength < 0)
		{
			throw new ArgumentOutOfRangeException("pcmLength", "Value cannot be negative.");
		}
		if (pcmBytes.Length < pcmLength)
		{
			throw new ArgumentOutOfRangeException("pcmLength", "Value cannot be greater than the length of pcmBytes.");
		}
		double frameSize = API.GetFrameSize(pcmLength, SampleRate, Channels);
		if (opusBytes == null && frameSize != 2.5 && frameSize != 5.0 && frameSize != 10.0 && frameSize != 20.0 && frameSize != 40.0 && frameSize != 60.0)
		{
			throw new ArgumentException("When using FEC the frame size must be one of the following: 2.5, 5, 10, 20, 40 or 60.", "pcmLength");
		}
		ThrowIfDisposed();
		int sampleCount = API.GetSampleCount(frameSize, SampleRate);
		int num;
		fixed (byte* ptr = opusBytes)
		{
			fixed (byte* ptr2 = pcmBytes)
			{
				IntPtr data = (IntPtr)ptr;
				IntPtr pcm = (IntPtr)ptr2;
				num = ((opusBytes == null) ? API.opus_decode(_handle, IntPtr.Zero, 0, pcm, sampleCount, 1) : API.opus_decode(_handle, data, opusLength, pcm, sampleCount, 0));
			}
		}
		API.ThrowIfError(num);
		return API.GetPCMLength(num, Channels);
	}

	public void Dispose()
	{
		_handle?.Dispose();
	}

	private void ThrowIfDisposed()
	{
		if (_handle.IsClosed)
		{
			throw new ObjectDisposedException(GetType().FullName);
		}
	}
}
public class OpusEncoder : IDisposable
{
	private readonly SafeEncoderHandle _handle;

	private int _bitrate;

	public Application Application { get; }

	public int SampleRate { get; }

	public int Channels { get; }

	[Obsolete("This property was used for the old encode method and is deprecated, please use the new encode method instead.")]
	public int Bitrate
	{
		get
		{
			return _bitrate;
		}
		set
		{
			if (value < 8000 || value > 512000)
			{
				throw new ArgumentOutOfRangeException("value", "Value must be between 8000 and 512000.");
			}
			_bitrate = value;
		}
	}

	public bool VBR
	{
		get
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4007, out var value));
			return value == 1;
		}
		set
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4006, value ? 1 : 0));
		}
	}

	public Bandwidth MaxBandwidth
	{
		get
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4005, out var value));
			return (Bandwidth)value;
		}
		set
		{
			if (!Enum.IsDefined(typeof(Bandwidth), value))
			{
				throw new ArgumentException("Value is not defined in the enumeration.", "value");
			}
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4004, (int)value));
		}
	}

	public int Complexity
	{
		get
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4011, out var value));
			return value;
		}
		set
		{
			if (value < 0 || value > 10)
			{
				throw new ArgumentOutOfRangeException("value", "Value must be between 0 and 10.");
			}
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4010, value));
		}
	}

	public bool FEC
	{
		get
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4013, out var value));
			return value == 1;
		}
		set
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4012, value ? 1 : 0));
		}
	}

	public int ExpectedPacketLoss
	{
		get
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4015, out var value));
			return value;
		}
		set
		{
			if (value < 0 || value > 100)
			{
				throw new ArgumentOutOfRangeException("value", "Value must be between 0 and 100.");
			}
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4014, value));
		}
	}

	public bool DTX
	{
		get
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4017, out var value));
			return value == 1;
		}
		set
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4016, value ? 1 : 0));
		}
	}

	public ForceChannels ForceChannels
	{
		get
		{
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4023, out var value));
			return (ForceChannels)value;
		}
		set
		{
			if (!Enum.IsDefined(typeof(ForceChannels), value))
			{
				throw new ArgumentException("Value is not defined in the enumeration.", "value");
			}
			ThrowIfDisposed();
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4022, (int)value));
		}
	}

	public OpusEncoder(Application application)
		: this(application, 48000, 2)
	{
	}

	public OpusEncoder(Application application, int sampleRate, int channels)
	{
		if (!Enum.IsDefined(typeof(Application), application))
		{
			throw new ArgumentException("Value is not defined in the enumeration.", "application");
		}
		switch (sampleRate)
		{
		default:
			throw new ArgumentException("Value must be one of the following: 8000, 12000, 16000, 24000 or 48000.", "sampleRate");
		case 8000:
		case 12000:
		case 16000:
		case 24000:
		case 48000:
		{
			if (channels < 1 || channels > 2)
			{
				throw new ArgumentOutOfRangeException("channels", "Value must be between 1 and 2.");
			}
			Application = application;
			SampleRate = sampleRate;
			Channels = channels;
			Bitrate = 128000;
			_handle = API.opus_encoder_create(sampleRate, channels, (int)application, out var error);
			API.ThrowIfError(error);
			API.ThrowIfError(API.opus_encoder_ctl(_handle, 4002, -1));
			break;
		}
		}
	}

	[Obsolete("This method is deprecated, please use the new encode method instead.")]
	public unsafe byte[] Encode(byte[] pcmBytes, int length, out int encodedLength)
	{
		if (pcmBytes == null)
		{
			throw new ArgumentNullException("pcmBytes");
		}
		if (length < 0)
		{
			throw new ArgumentOutOfRangeException("length", "Value cannot be negative.");
		}
		if (pcmBytes.Length < length)
		{
			throw new ArgumentOutOfRangeException("length", "Value cannot be greater than the length of pcmBytes.");
		}
		double frameSize = API.GetFrameSize(length, SampleRate, Channels);
		if (frameSize != 2.5 && frameSize != 5.0 && frameSize != 10.0 && frameSize != 20.0 && frameSize != 40.0 && frameSize != 60.0)
		{
			throw new ArgumentException("The frame size must be one of the following: 2.5, 5, 10, 20, 40 or 60.", "length");
		}
		ThrowIfDisposed();
		byte[] array = new byte[(int)(frameSize * (double)Bitrate / 8.0 / 1000.0)];
		int sampleCount = API.GetSampleCount(frameSize, SampleRate);
		int num;
		fixed (byte* ptr = pcmBytes)
		{
			fixed (byte* ptr2 = array)
			{
				IntPtr pcm = (IntPtr)ptr;
				IntPtr data = (IntPtr)ptr2;
				num = API.opus_encode(_handle, pcm, sampleCount, data, array.Length);
			}
		}
		API.ThrowIfError(num);
		encodedLength = num;
		return array;
	}

	public unsafe int Encode(byte[] pcmBytes, int pcmLength, byte[] opusBytes, int opusLength)
	{
		if (pcmBytes == null)
		{
			throw new ArgumentNullException("pcmBytes");
		}
		if (pcmLength < 0)
		{
			throw new ArgumentOutOfRangeException("pcmLength", "Value cannot be negative.");
		}
		if (pcmBytes.Length < pcmLength)
		{
			throw new ArgumentOutOfRangeException("pcmLength", "Value cannot be greater than the length of pcmBytes.");
		}
		if (opusBytes == null)
		{
			throw new ArgumentNullException("opusBytes");
		}
		if (opusLength < 0)
		{
			throw new ArgumentOutOfRangeException("opusLength", "Value cannot be negative.");
		}
		if (opusBytes.Length < opusLength)
		{
			throw new ArgumentOutOfRangeException("opusLength", "Value cannot be greater than the length of opusBytes.");
		}
		double frameSize = API.GetFrameSize(pcmLength, SampleRate, Channels);
		if (frameSize != 2.5 && frameSize != 5.0 && frameSize != 10.0 && frameSize != 20.0 && frameSize != 40.0 && frameSize != 60.0)
		{
			throw new ArgumentException("The frame size must be one of the following: 2.5, 5, 10, 20, 40 or 60.", "pcmLength");
		}
		ThrowIfDisposed();
		int sampleCount = API.GetSampleCount(frameSize, SampleRate);
		int result;
		fixed (byte* ptr = pcmBytes)
		{
			fixed (byte* ptr2 = opusBytes)
			{
				IntPtr pcm = (IntPtr)ptr;
				IntPtr data = (IntPtr)ptr2;
				result = API.opus_encode(_handle, pcm, sampleCount, data, opusLength);
			}
		}
		API.ThrowIfError(result);
		return result;
	}

	public void Dispose()
	{
		_handle?.Dispose();
	}

	private void ThrowIfDisposed()
	{
		if (_handle.IsClosed)
		{
			throw new ObjectDisposedException(GetType().FullName);
		}
	}
}
public enum OpusError
{
	BadArg = -1,
	BufferTooSmall = -2,
	InternalError = -3,
	InvalidPacket = -4,
	Unimplemented = -5,
	InvalidState = -6,
	AllocFail = -7
}
public class OpusException : Exception
{
	public OpusError Error { get; }

	public OpusException(int errorCode)
		: base(GetMessage((OpusError)errorCode))
	{
		Error = (OpusError)errorCode;
	}

	private static string GetMessage(OpusError error)
	{
		return error switch
		{
			OpusError.BadArg => "One or more invalid/out of range arguments.", 
			OpusError.BufferTooSmall => "Not enough bytes allocated in the buffer.", 
			OpusError.InternalError => "An internal error was detected.", 
			OpusError.InvalidPacket => "The compressed data passed is corrupted.", 
			OpusError.Unimplemented => "Invalid/unsupported request number.", 
			OpusError.InvalidState => "An encoder or decoder structure is invalid or already freed.", 
			OpusError.AllocFail => "Memory allocation has failed.", 
			_ => "An unknown error has occurred.", 
		};
	}
}
internal sealed class SafeDecoderHandle : SafeHandle
{
	public override bool IsInvalid => handle == IntPtr.Zero;

	private SafeDecoderHandle()
		: base(IntPtr.Zero, ownsHandle: true)
	{
	}

	[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
	protected override bool ReleaseHandle()
	{
		API.opus_decoder_destroy(handle);
		return true;
	}
}
internal sealed class SafeEncoderHandle : SafeHandle
{
	public override bool IsInvalid => handle == IntPtr.Zero;

	private SafeEncoderHandle()
		: base(IntPtr.Zero, ownsHandle: true)
	{
	}

	[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
	protected override bool ReleaseHandle()
	{
		API.opus_encoder_destroy(handle);
		return true;
	}
}